Skip to content

Commit

Permalink
fix: Skip preparation of plugins native files in case they are not ch…
Browse files Browse the repository at this point in the history
…anged

Skip the preparation of plugins' native files in case there's no change of the files since last operation when the files have been prepared.
Do this by using hash sums of files in each plugin's `platforms/<platform>` dir in case such exists. This way, in case you apply a change outside of the `<plugin dir>/platforms/<platform>` dir, the plugin's native files will not be prepared again. This way application will not be rebuilt as there's no need to do this.
  • Loading branch information
rosen-vladimirov committed Jul 23, 2018
1 parent 51597bc commit d159572
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 3 deletions.
1 change: 1 addition & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,4 @@ export class AddPlaformErrors {
}

export const PLUGIN_BUILD_DATA_FILENAME = "plugin-data.json";
export const PLUGINS_BUILD_DATA_FILENAME = ".ns-plugins-build-data.json";
48 changes: 45 additions & 3 deletions lib/services/plugins-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class PluginsService implements IPluginsService {
private $options: IOptions,
private $logger: ILogger,
private $errors: IErrors,
private $filesHashService: IFilesHashService,
private $injector: IInjector) { }

public async add(plugin: string, projectData: IProjectData): Promise<void> {
Expand Down Expand Up @@ -107,7 +108,7 @@ export class PluginsService implements IPluginsService {
return await platformData.platformProjectService.validatePlugins(projectData);
}

public async prepare(dependencyData: IDependencyData, platform: string, projectData: IProjectData, projectFilesConfig: IProjectFilesConfig): Promise<void> {
public async prepare(dependencyData: IDependencyData, platform: string, projectData: IProjectData, projectFilesConfig: IProjectFilesConfig): Promise<void> {
platform = platform.toLowerCase();
const platformData = this.$platformsData.getPlatformData(platform, projectData);
const pluginData = this.convertToPluginData(dependencyData, projectData.projectDir);
Expand Down Expand Up @@ -141,9 +142,26 @@ export class PluginsService implements IPluginsService {

public async preparePluginNativeCode(pluginData: IPluginData, platform: string, projectData: IProjectData): Promise<void> {
const platformData = this.$platformsData.getPlatformData(platform, projectData);

pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform);
await platformData.platformProjectService.preparePluginNativeCode(pluginData, projectData);

const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(platform);
if (this.$fs.exists(pluginPlatformsFolderPath)) {
const pathToPluginsBuildFile = path.join(platformData.projectRoot, constants.PLUGINS_BUILD_DATA_FILENAME);

const allPluginsNativeHashes = this.getAllPluginsNativeHashes(pathToPluginsBuildFile);
const oldPluginNativeHashes = allPluginsNativeHashes[pluginData.name];
const currentPluginNativeHashes = await this.getPluginNativeHashes(pluginPlatformsFolderPath);

if (!oldPluginNativeHashes || this.$filesHashService.hasChangesInShasums(oldPluginNativeHashes, currentPluginNativeHashes)) {
await platformData.platformProjectService.preparePluginNativeCode(pluginData, projectData);
this.setPluginNativeHashes({
pathToPluginsBuildFile,
pluginData,
currentPluginNativeHashes,
allPluginsNativeHashes
});
}
}
}

public async ensureAllDependenciesAreInstalled(projectData: IProjectData): Promise<void> {
Expand Down Expand Up @@ -307,6 +325,30 @@ export class PluginsService implements IPluginsService {

return isValid;
}

private async getPluginNativeHashes(pluginPlatformsDir: string): Promise<IStringDictionary> {
let data: IStringDictionary = {};
if (this.$fs.exists(pluginPlatformsDir)) {
const pluginNativeDataFiles = this.$fs.enumerateFilesInDirectorySync(pluginPlatformsDir);
data = await this.$filesHashService.generateHashes(pluginNativeDataFiles);
}

return data;
}

private getAllPluginsNativeHashes(pathToPluginsBuildFile: string): IDictionary<IStringDictionary> {
let data: IDictionary<IStringDictionary> = {};
if (this.$fs.exists(pathToPluginsBuildFile)) {
data = this.$fs.readJson(pathToPluginsBuildFile);
}

return data;
}

private setPluginNativeHashes(opts: { pathToPluginsBuildFile: string, pluginData: IPluginData, currentPluginNativeHashes: IStringDictionary, allPluginsNativeHashes: IDictionary<IStringDictionary> }): void {
opts.allPluginsNativeHashes[opts.pluginData.name] = opts.currentPluginNativeHashes;
this.$fs.writeJson(opts.pathToPluginsBuildFile, opts.allPluginsNativeHashes);
}
}

$injector.register("pluginsService", PluginsService);
4 changes: 4 additions & 0 deletions test/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ function createTestInjector(projectPath: string, projectName: string, xcode?: IX
on: () => ({})
});
testInjector.register("emulatorHelper", {});
testInjector.register("filesHashService", {
hasChangesInShasums: (oldPluginNativeHashes: IStringDictionary, currentPluginNativeHashes: IStringDictionary) => true,
generateHashes: async (files: string[]): Promise<IStringDictionary> => ({})
});

return testInjector;
}
Expand Down
91 changes: 91 additions & 0 deletions test/plugins-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { SettingsService } from "../lib/common/test/unit-tests/stubs";
import StaticConfigLib = require("../lib/config");
import * as path from "path";
import * as temp from "temp";
import { PLUGINS_BUILD_DATA_FILENAME } from '../lib/constants';
temp.track();

let isErrorThrown = false;
Expand Down Expand Up @@ -119,6 +120,10 @@ function createTestInjector() {
testInjector.register("androidResourcesMigrationService", stubs.AndroidResourcesMigrationServiceStub);

testInjector.register("platformEnvironmentRequirements", {});
testInjector.register("filesHashService", {
hasChangesInShasums: (oldPluginNativeHashes: IStringDictionary, currentPluginNativeHashes: IStringDictionary) => true,
generateHashes: async (files: string[]): Promise<IStringDictionary> => ({})
});
return testInjector;
}

Expand Down Expand Up @@ -541,4 +546,90 @@ describe("Plugins service", () => {
await pluginsService.prepare(pluginJsonData, "android", projectData, {});
});
});

describe("preparePluginNativeCode", () => {
const setupTest = (opts: { hasChangesInShasums?: boolean, newPluginHashes?: IStringDictionary, buildDataFileExists?: boolean, hasPluginPlatformsDir?: boolean }): any => {
const testData: any = {
pluginsService: null,
isPreparePluginNativeCodeCalled: false,
dataPassedToWriteJson: null
};

const unitTestsInjector = new Yok();
unitTestsInjector.register("platformsData", {
getPlatformData: (platform: string, projectData: IProjectData) => ({
projectRoot: "projectRoot",
platformProjectService: {
preparePluginNativeCode: async (pluginData: IPluginData, projData: IProjectData) => {
testData.isPreparePluginNativeCodeCalled = true;
}
}
})
});

const pluginHashes = opts.newPluginHashes || { "file1": "hash1" };
const pluginData: IPluginData = <any>{
fullPath: "plugin_full_path",
name: "plugin_name"
};

unitTestsInjector.register("filesHashService", {
hasChangesInShasums: (oldPluginNativeHashes: IStringDictionary, currentPluginNativeHashes: IStringDictionary) => !!opts.hasChangesInShasums,
generateHashes: async (files: string[]): Promise<IStringDictionary> => pluginHashes
});

unitTestsInjector.register("fs", {
exists: (file: string) => {
if (file.indexOf(PLUGINS_BUILD_DATA_FILENAME) !== -1) {
return !!opts.buildDataFileExists;
}

if (file.indexOf("platforms") !== -1) {
return !!opts.hasPluginPlatformsDir;
}

return true;
},
readJson: (file: string) => ({
[pluginData.name]: pluginHashes
}),
writeJson: (file: string, json: any) => { testData.dataPassedToWriteJson = json; },
enumerateFilesInDirectorySync: (): string[] => ["some_file"]
});

unitTestsInjector.register("npm", {});
unitTestsInjector.register("options", {});
unitTestsInjector.register("logger", {});
unitTestsInjector.register("errors", {});
unitTestsInjector.register("injector", unitTestsInjector);

const pluginsService: PluginsService = unitTestsInjector.resolve(PluginsService);
testData.pluginsService = pluginsService;
testData.pluginData = pluginData;
return testData;
};

const platform = "platform";
const projectData: IProjectData = <any>{};

it("does not prepare the files when plugin does not have platforms dir", async () => {
const testData = setupTest({ hasPluginPlatformsDir: false });
await testData.pluginsService.preparePluginNativeCode(testData.pluginData, platform, projectData);
assert.isFalse(testData.isPreparePluginNativeCodeCalled);
});

it("prepares the files when plugin has platforms dir and has not been built before", async () => {
const newPluginHashes = { "file": "hash" };
const testData = setupTest({ newPluginHashes, hasPluginPlatformsDir: true });
await testData.pluginsService.preparePluginNativeCode(testData.pluginData, platform, projectData);
assert.isTrue(testData.isPreparePluginNativeCodeCalled);
assert.deepEqual(testData.dataPassedToWriteJson, { [testData.pluginData.name]: newPluginHashes });
});

it("does not prepare the files when plugin has platforms dir and files have not changed since then", async () => {
const testData = setupTest({ hasChangesInShasums: false, buildDataFileExists: true, hasPluginPlatformsDir: true });
await testData.pluginsService.preparePluginNativeCode(testData.pluginData, platform, projectData);
assert.isFalse(testData.isPreparePluginNativeCodeCalled);
});
});
});

0 comments on commit d159572

Please sign in to comment.