Skip to content

Handle platform specific files in plugin #588

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ $injector.require("broccoliPluginWrapper", "./tools/broccoli/broccoli-plugin-wra
$injector.require("pluginsService", "./services/plugins-service");
$injector.requireCommand("plugin|add", "./commands/plugin/add-plugin");
$injector.requireCommand("plugin|remove", "./commands/plugin/remove-plugin");
$injector.requireCommand("install", "./commands/install");
$injector.requireCommand("install", "./commands/install");

$injector.require("projectFilesManager", "./services/project-files-manager");
4 changes: 4 additions & 0 deletions lib/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ interface IOptions extends ICommonOptions {
keyStoreAliasPassword: string;
sdk: string;
}

interface IProjectFilesManager {
processPlatformSpecificFiles(directoryPath: string, platform: string, excludedDirs?: string[]): IFuture<void>;
}
46 changes: 5 additions & 41 deletions lib/services/platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export class PlatformService implements IPlatformService {
private $commandsService: ICommandsService,
private $options: IOptions,
private $broccoliBuilder: IBroccoliBuilder,
private $pluginsService: IPluginsService) { }
private $pluginsService: IPluginsService,
private $projectFilesManager: IProjectFilesManager) { }

public addPlatforms(platforms: string[]): IFuture<void> {
return (() => {
Expand Down Expand Up @@ -165,19 +166,9 @@ export class PlatformService implements IPlatformService {
}

// Process platform specific files
var contents = this.$fs.readDirectory(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)).wait();
var files: string[] = [];

_.each(contents, d => {
let filePath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, d);
let fsStat = this.$fs.getFsStats(filePath).wait();
if(fsStat.isDirectory() && d !== constants.APP_RESOURCES_FOLDER_NAME) {
this.processPlatformSpecificFiles(platform, this.$fs.enumerateFilesInDirectorySync(filePath)).wait();
} else if(fsStat.isFile()) {
files.push(filePath);
}
});
this.processPlatformSpecificFiles(platform, files).wait();
let directoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
let excludedDirs = [constants.APP_RESOURCES_FOLDER_NAME];
this.$projectFilesManager.processPlatformSpecificFiles(directoryPath, platform, excludedDirs).wait();

// Process node_modules folder
this.$pluginsService.ensureAllDependenciesAreInstalled().wait();
Expand Down Expand Up @@ -341,33 +332,6 @@ export class PlatformService implements IPlatformService {
return platformData.platformProjectService.isPlatformPrepared(platformData.projectRoot);
}

private static parsePlatformSpecificFileName(fileName: string, platforms: string[]): any {
var regex = util.format("^(.+?)\.(%s)(\..+?)$", platforms.join("|"));
var parsed = fileName.toLowerCase().match(new RegExp(regex, "i"));
if (parsed) {
return {
platform: parsed[2],
onDeviceName: parsed[1] + parsed[3]
};
}
return undefined;
}

private processPlatformSpecificFiles( platform: string, files: string[]): IFuture<void> {
// Renames the files that have `platform` as substring and removes the files from other platform
return (() => {
_.each(files, fileName => {
var platformInfo = PlatformService.parsePlatformSpecificFileName(path.basename(fileName), this.$platformsData.platformsNames);
var shouldExcludeFile = platformInfo && platformInfo.platform !== platform;
if (shouldExcludeFile) {
this.$fs.deleteFile(fileName).wait();
} else if (platformInfo && platformInfo.onDeviceName) {
this.$fs.rename(fileName, path.join(path.dirname(fileName), platformInfo.onDeviceName)).wait();
}
});
}).future<void>()();
}

private getApplicationPackages(buildOutputPath: string, validPackageNames: string[]): IFuture<IApplicationPackage[]> {
return (() => {
// Get latest package that is produced from build
Expand Down
6 changes: 4 additions & 2 deletions lib/services/plugins-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class PluginsService implements IPluginsService {
private $options: IOptions,
private $logger: ILogger,
private $errors: IErrors,
private $injector: IInjector) { }
private $projectFilesManager: IProjectFilesManager) { }

public add(plugin: string): IFuture<void> {
return (() => {
Expand Down Expand Up @@ -101,7 +101,9 @@ export class PluginsService implements IPluginsService {

if(this.$fs.exists(pluginPlatformsFolderPath).wait()) {
shelljs.rm("-rf", pluginPlatformsFolderPath);
}
}

this.$projectFilesManager.processPlatformSpecificFiles(pluginDestinationPath, platform).wait();

// TODO: Add libraries

Expand Down
56 changes: 56 additions & 0 deletions lib/services/project-files-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
///<reference path="../.d.ts"/>
"use strict";
import path = require("path");
import util = require("util");

export class ProjectFilesManager implements IProjectFilesManager {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I add unit tests.

constructor(private $fs: IFileSystem,
private $platformsData: IPlatformsData) { }

public processPlatformSpecificFiles(directoryPath: string, platform: string, excludedDirs?: string[]): IFuture<void> {
return (() => {
var contents = this.$fs.readDirectory(directoryPath).wait();
var files: string[] = [];

_.each(contents, fileName => {
let filePath = path.join(directoryPath, fileName);
let fsStat = this.$fs.getFsStats(filePath).wait();
if(fsStat.isDirectory() && !_.contains(excludedDirs, fileName)) {
this.processPlatformSpecificFilesCore(platform, this.$fs.enumerateFilesInDirectorySync(filePath)).wait();
} else if(fsStat.isFile()) {
files.push(filePath);
}
});
this.processPlatformSpecificFilesCore(platform, files).wait();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can improve the performance here:

let futures: IFuture<void>[];
_.each(contents, fileName => {
    let filePath = path.join(directoryPath, fileName);
    let fsStat = this.$fs.getFsStats(filePath).wait();
    if(fsStat.isDirectory() && !_.contains(excludedDirs, fileName)) {
        future.push(this.processPlatformSpecificFilesCore(platform, this.$fs.enumerateFilesInDirectorySync(filePath)));
    } else if (fsStat.isFile()) {
        files.push(filePath);
    }
    return Future.fromResult();
});
futures.push(this.processPlatformSpecificFilesCore(platform, files));
Future.wait(futures);

I've not tested it and this is not a merge stopper.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after discussion, we've decided to keep the current code


}).future<void>()();
}

private processPlatformSpecificFilesCore(platform: string, files: string[]): IFuture<void> {
// Renames the files that have `platform` as substring and removes the files from other platform
return (() => {
_.each(files, fileName => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not a merge stopper, but consider:

let futures = files.map(fileName => {
    let platformInfo = ProjectFilesManager.parsePlatformSpecificFileName(path.basename(fileName), this.$platformsData.platformsNames);
    let shouldExcludeFile = platformInfo && platformInfo.platform !== platform;
    if (shouldExcludeFile) {
        return this.$fs.deleteFile(fileName);
    } else if (platformInfo && platformInfo.onDeviceName) {
        return this.$fs.rename(fileName, path.join(path.dirname(fileName), platformInfo.onDeviceName));
    }
    return Future.fromResult();
});
Future.wait(futures);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after discussion, we've decided to keep the current code

var platformInfo = ProjectFilesManager.parsePlatformSpecificFileName(path.basename(fileName), this.$platformsData.platformsNames);
var shouldExcludeFile = platformInfo && platformInfo.platform !== platform;
if (shouldExcludeFile) {
this.$fs.deleteFile(fileName).wait();
} else if (platformInfo && platformInfo.onDeviceName) {
this.$fs.rename(fileName, path.join(path.dirname(fileName), platformInfo.onDeviceName)).wait();
}
});
}).future<void>()();
}

private static parsePlatformSpecificFileName(fileName: string, platforms: string[]): any {
var regex = util.format("^(.+?)\\.(%s)(\\..+?)$", platforms.join("|"));
var parsed = fileName.match(new RegExp(regex, "i"));
if (parsed) {
return {
platform: parsed[2],
onDeviceName: parsed[1] + parsed[3]
};
}
return undefined;
}
}
$injector.register("projectFilesManager", ProjectFilesManager);
3 changes: 3 additions & 0 deletions test/npm-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import BroccoliBuilderLib = require("../lib/tools/broccoli/builder");
import NodeModulesTreeLib = require("../lib/tools/broccoli/trees/node-modules-tree");
import PluginsServiceLib = require("../lib/services/plugins-service");
import ChildProcessLib = require("../lib/common/child-process");
import ProjectFilesManagerLib = require("../lib/services/project-files-manager");
import Future = require("fibers/future");

import path = require("path");
import temp = require("temp");
Expand Down Expand Up @@ -53,6 +55,7 @@ function createTestInjector(): IInjector {
testInjector.register("pluginsService", PluginsServiceLib.PluginsService);
testInjector.register("npm", NpmLib.NodePackageManager);
testInjector.register("childProcess", ChildProcessLib.ChildProcess);
testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager);
testInjector.register("commandsServiceProvider", {
registerDynamicSubCommands: () => {}
});
Expand Down
2 changes: 2 additions & 0 deletions test/platform-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import StaticConfigLib = require("../lib/config");
import CommandsServiceLib = require("../lib/common/services/commands-service");
import optionsLib = require("../lib/options");
import hostInfoLib = require("../lib/common/host-info");
import ProjectFilesManagerLib = require("../lib/services/project-files-manager");
import path = require("path");
import Future = require("fibers/future");
var assert = require("chai").assert;
Expand Down Expand Up @@ -115,6 +116,7 @@ function createTestInjector() {
}).future<IPluginData[]>()();
}
});
testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager);

return testInjector;
}
Expand Down
6 changes: 4 additions & 2 deletions test/platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ProjectDataLib = require("../lib/project-data");
import ProjectHelperLib = require("../lib/common/project-helper");
import optionsLib = require("../lib/options");
import hostInfoLib = require("../lib/common/host-info");
import ProjectFilesManagerLib = require("../lib/services/project-files-manager");

import path = require("path");
import Future = require("fibers/future");
Expand Down Expand Up @@ -63,6 +64,7 @@ function createTestInjector() {
return (() => { }).future<void>()();
}
});
testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager);

return testInjector;
}
Expand Down Expand Up @@ -235,7 +237,7 @@ describe('Platform Service Tests', () => {

// Asserts that the files in app folder are process as platform specific
assert.isTrue(fs.exists(path.join(appDestFolderPath, "app" , "test1.js")).wait());
assert.isTrue(fs.exists(path.join(appDestFolderPath, "app", "test1-js")).wait());
assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test1-js")).wait());
assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test2.js")).wait());
assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test2-js")).wait());

Expand Down Expand Up @@ -286,7 +288,7 @@ describe('Platform Service Tests', () => {

// Asserts that the files in app folder are process as platform specific
assert.isTrue(fs.exists(path.join(appDestFolderPath, "app" , "test2.js")).wait());
assert.isTrue(fs.exists(path.join(appDestFolderPath, "app", "test2-js")).wait());
assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test2-js")).wait());
assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test1.js")).wait());
assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test1-js")).wait());

Expand Down
2 changes: 2 additions & 0 deletions test/plugins-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import ProjectHelperLib = require("../lib/common/project-helper");
import PlatformsDataLib = require("../lib/platforms-data");
import ProjectDataServiceLib = require("../lib/services/project-data-service");
import helpers = require("../lib/common/helpers");
import ProjectFilesManagerLib = require("../lib/services/project-files-manager");
import os = require("os");

import PluginsServiceLib = require("../lib/services/plugins-service");
Expand Down Expand Up @@ -64,6 +65,7 @@ function createTestInjector() {
checkConsent: () => { return (() => { }).future<void>()(); },
trackFeature: () => { return (() => { }).future<void>()(); }
});
testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager);

return testInjector;
}
Expand Down
81 changes: 81 additions & 0 deletions test/project-files-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/// <reference path=".d.ts" />
"use strict";

import yok = require('../lib/common/yok');
import fsLib = require("../lib/common/file-system");
import projectFilesManagerLib = require("../lib/services/project-files-manager");
import hostInfoLib = require("../lib/common/host-info");
import StaticConfigLib = require("../lib/config");
import ErrorsLib = require("../lib/common/errors");
import path = require("path");
import temp = require("temp");
temp.track();

var assert = require("chai").assert;

function createTestInjector() {
let testInjector = new yok.Yok();
testInjector.register("fs", fsLib.FileSystem);
testInjector.register("hostInfo", hostInfoLib.HostInfo);
testInjector.register("staticConfig", StaticConfigLib.StaticConfig);
testInjector.register("projectFilesManager", projectFilesManagerLib.ProjectFilesManager);
testInjector.register("errors", ErrorsLib.Errors);
testInjector.register("platformsData", {
platformsNames: ["ios", "android"]
});

return testInjector;
}

function createFiles(testInjector: IInjector, filesToCreate: string[]): IFuture<string> {
return (() => {
let fs = testInjector.resolve("fs");
let directoryPath = temp.mkdirSync("Project Files Manager Tests");

_.each(filesToCreate, file => fs.writeFile(path.join(directoryPath, file), "").wait());

return directoryPath;
}).future<string>()();
}

describe('Project Files Manager Tests', () => {
let testInjector: IInjector, projectFilesManager: IProjectFilesManager;
beforeEach(() => {
testInjector = createTestInjector();
projectFilesManager = testInjector.resolve("projectFilesManager");
});
it("filters android specific files", () => {
let files = ["test.ios.x", "test.android.x"];
let directoryPath = createFiles(testInjector, files).wait();

projectFilesManager.processPlatformSpecificFiles(directoryPath, "android").wait();

let fs = testInjector.resolve("fs");
assert.isFalse(fs.exists(path.join(directoryPath, "test.ios.x")).wait());
assert.isTrue(fs.exists(path.join(directoryPath, "test.x")).wait());
assert.isFalse(fs.exists(path.join(directoryPath, "test.android.x")).wait());
});
it("filters ios specific files", () => {
let files = ["index.ios.html", "index1.android.html", "a.test"];
let directoryPath = createFiles(testInjector, files).wait();

projectFilesManager.processPlatformSpecificFiles(directoryPath, "ios").wait();

let fs = testInjector.resolve("fs");
assert.isFalse(fs.exists(path.join(directoryPath, "index1.android.html")).wait());
assert.isFalse(fs.exists(path.join(directoryPath, "index1.html")).wait());
assert.isTrue(fs.exists(path.join(directoryPath, "index.html")).wait());
assert.isTrue(fs.exists(path.join(directoryPath, "a.test")).wait());
});
it("doesn't filter non platform specific files", () => {
let files = ["index1.js", "index2.js", "index3.js"];
let directoryPath = createFiles(testInjector, files).wait();

projectFilesManager.processPlatformSpecificFiles(directoryPath, "ios").wait();

let fs = testInjector.resolve("fs");
assert.isTrue(fs.exists(path.join(directoryPath, "index1.js")).wait());
assert.isTrue(fs.exists(path.join(directoryPath, "index2.js")).wait());
assert.isTrue(fs.exists(path.join(directoryPath, "index3.js")).wait());
});
});