Skip to content

Commit 2ff6f0d

Browse files
Merge pull request #1781 from NativeScript/milanov/flatten-scoped-dependencies-of-dependency
Flatten scoped dependencies of dependency
2 parents bd4a2a9 + 78de759 commit 2ff6f0d

File tree

3 files changed

+109
-83
lines changed

3 files changed

+109
-83
lines changed

lib/tools/broccoli/node-modules-dest-copy.ts

+81-76
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import Future = require("fibers/future");
1515
* and tees a copy to the given path outside the tmp dir.
1616
*/
1717
export class DestCopy implements IBroccoliPlugin {
18-
private dependencies: IDictionary<any> = null;
19-
private devDependencies: IDictionary<any> = null;
18+
private dependencies: IDictionary<any> = null;
19+
private devDependencies: IDictionary<any> = null;
2020

2121
constructor(
2222
private inputPath: string,
@@ -33,72 +33,72 @@ export class DestCopy implements IBroccoliPlugin {
3333
this.devDependencies = this.getDevDependencies(projectDir);
3434
}
3535

36-
public rebuildChangedDirectories(changedDirectories: string[], platform: string): void {
37-
_.each(changedDirectories, changedDirectoryAbsolutePath => {
38-
if(!this.devDependencies[path.basename(changedDirectoryAbsolutePath)]) {
39-
let pathToPackageJson = path.join(changedDirectoryAbsolutePath, constants.PACKAGE_JSON_FILE_NAME);
40-
let packageJsonFiles = fs.existsSync(pathToPackageJson) ? [pathToPackageJson] : [];
41-
let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, constants.NODE_MODULES_FOLDER_NAME);
42-
packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath));
43-
44-
_.each(packageJsonFiles, packageJsonFilePath => {
45-
let fileContent = require(packageJsonFilePath);
46-
47-
if(!this.devDependencies[fileContent.name]) { // Don't flatten dev dependencies
48-
let currentDependency = {
49-
name: fileContent.name,
50-
version: fileContent.version,
51-
directory: path.dirname(packageJsonFilePath),
52-
nativescript: fileContent.nativescript
53-
};
54-
55-
let addedDependency = this.dependencies[currentDependency.name];
56-
if (addedDependency) {
57-
if (semver.gt(currentDependency.version, addedDependency.version)) {
58-
let currentDependencyMajorVersion = semver.major(currentDependency.version);
59-
let addedDependencyMajorVersion = semver.major(addedDependency.version);
60-
61-
let message = `The depedency located at ${addedDependency.directory} with version ${addedDependency.version} will be replaced with dependency located at ${currentDependency.directory} with version ${currentDependency.version}`;
62-
let logger = $injector.resolve("$logger");
63-
currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message);
64-
36+
public rebuildChangedDirectories(changedDirectories: string[], platform: string): void {
37+
_.each(changedDirectories, changedDirectoryAbsolutePath => {
38+
if (!this.devDependencies[path.basename(changedDirectoryAbsolutePath)]) {
39+
let pathToPackageJson = path.join(changedDirectoryAbsolutePath, constants.PACKAGE_JSON_FILE_NAME);
40+
let packageJsonFiles = fs.existsSync(pathToPackageJson) ? [pathToPackageJson] : [];
41+
let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, constants.NODE_MODULES_FOLDER_NAME);
42+
packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath));
43+
44+
_.each(packageJsonFiles, packageJsonFilePath => {
45+
let fileContent = require(packageJsonFilePath);
46+
47+
if (!this.devDependencies[fileContent.name]) { // Don't flatten dev dependencies
48+
let currentDependency = {
49+
name: fileContent.name,
50+
version: fileContent.version,
51+
directory: path.dirname(packageJsonFilePath),
52+
nativescript: fileContent.nativescript
53+
};
54+
55+
let addedDependency = this.dependencies[currentDependency.name];
56+
if (addedDependency) {
57+
if (semver.gt(currentDependency.version, addedDependency.version)) {
58+
let currentDependencyMajorVersion = semver.major(currentDependency.version);
59+
let addedDependencyMajorVersion = semver.major(addedDependency.version);
60+
61+
let message = `The depedency located at ${addedDependency.directory} with version ${addedDependency.version} will be replaced with dependency located at ${currentDependency.directory} with version ${currentDependency.version}`;
62+
let logger = $injector.resolve("$logger");
63+
currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message);
64+
65+
this.dependencies[currentDependency.name] = currentDependency;
66+
}
67+
} else {
6568
this.dependencies[currentDependency.name] = currentDependency;
6669
}
67-
} else {
68-
this.dependencies[currentDependency.name] = currentDependency;
6970
}
70-
}
71-
});
72-
}
73-
});
71+
});
72+
}
73+
});
7474

75-
_.each(this.dependencies, dependency => {
76-
this.copyDependencyDir(dependency);
75+
_.each(this.dependencies, dependency => {
76+
this.copyDependencyDir(dependency);
7777

78-
let isPlugin = !!dependency.nativescript;
79-
if(isPlugin) {
80-
this.$pluginsService.prepare(dependency, platform).wait();
81-
}
78+
let isPlugin = !!dependency.nativescript;
79+
if (isPlugin) {
80+
this.$pluginsService.prepare(dependency, platform).wait();
81+
}
8282

83-
if (dependency.name === constants.TNS_CORE_MODULES_NAME) {
84-
let tnsCoreModulesResourcePath = path.join(this.outputRoot, constants.TNS_CORE_MODULES_NAME);
83+
if (dependency.name === constants.TNS_CORE_MODULES_NAME) {
84+
let tnsCoreModulesResourcePath = path.join(this.outputRoot, constants.TNS_CORE_MODULES_NAME);
8585

86-
// Remove .ts files
87-
let allFiles = this.$fs.enumerateFilesInDirectorySync(tnsCoreModulesResourcePath);
88-
let deleteFilesFutures = allFiles.filter(file => minimatch(file, "**/*.ts", {nocase: true})).map(file => this.$fs.deleteFile(file));
89-
Future.wait(deleteFilesFutures);
86+
// Remove .ts files
87+
let allFiles = this.$fs.enumerateFilesInDirectorySync(tnsCoreModulesResourcePath);
88+
let deleteFilesFutures = allFiles.filter(file => minimatch(file, "**/*.ts", { nocase: true })).map(file => this.$fs.deleteFile(file));
89+
Future.wait(deleteFilesFutures);
9090

91-
shelljs.cp("-Rf", path.join(tnsCoreModulesResourcePath, "*"), this.outputRoot);
92-
this.$fs.deleteDirectory(tnsCoreModulesResourcePath).wait();
93-
}
94-
});
91+
shelljs.cp("-Rf", path.join(tnsCoreModulesResourcePath, "*"), this.outputRoot);
92+
this.$fs.deleteDirectory(tnsCoreModulesResourcePath).wait();
93+
}
94+
});
9595

96-
if(!_.isEmpty(this.dependencies)) {
97-
this.$platformsData.getPlatformData(platform).platformProjectService.afterPrepareAllPlugins().wait();
96+
if (!_.isEmpty(this.dependencies)) {
97+
this.$platformsData.getPlatformData(platform).platformProjectService.afterPrepareAllPlugins().wait();
98+
}
9899
}
99-
}
100100

101-
private copyDependencyDir(dependency: any): void {
101+
private copyDependencyDir(dependency: any): void {
102102
let dependencyDir = path.dirname(dependency.name || "");
103103
let insideNpmScope = /^@/.test(dependencyDir);
104104
let targetDir = this.outputRoot;
@@ -108,42 +108,47 @@ export class DestCopy implements IBroccoliPlugin {
108108
shelljs.mkdir("-p", targetDir);
109109
shelljs.cp("-Rf", dependency.directory, targetDir);
110110
shelljs.rm("-rf", path.join(targetDir, dependency.name, "node_modules"));
111-
}
111+
}
112112

113-
public rebuild(treeDiff: IDiffResult): void {
114-
this.rebuildChangedDirectories(treeDiff.changedDirectories, "");
113+
public rebuild(treeDiff: IDiffResult): void {
114+
this.rebuildChangedDirectories(treeDiff.changedDirectories, "");
115115

116-
// Cache input tree
117-
let projectFilePath = path.join(this.projectDir, constants.PACKAGE_JSON_FILE_NAME);
118-
let projectFileContent = require(projectFilePath);
119-
projectFileContent[constants.NATIVESCRIPT_KEY_NAME][constants.NODE_MODULE_CACHE_PATH_KEY_NAME] = this.inputPath;
120-
fs.writeFileSync(projectFilePath, JSON.stringify(projectFileContent, null, "\t"), { encoding: "utf8" });
121-
}
116+
// Cache input tree
117+
let projectFilePath = path.join(this.projectDir, constants.PACKAGE_JSON_FILE_NAME);
118+
let projectFileContent = require(projectFilePath);
119+
projectFileContent[constants.NATIVESCRIPT_KEY_NAME][constants.NODE_MODULE_CACHE_PATH_KEY_NAME] = this.inputPath;
120+
fs.writeFileSync(projectFilePath, JSON.stringify(projectFileContent, null, "\t"), { encoding: "utf8" });
121+
}
122122

123-
private getDevDependencies(projectDir: string): IDictionary<any> {
124-
let projectFilePath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME);
125-
let projectFileContent = require(projectFilePath);
126-
return projectFileContent.devDependencies || {};
127-
}
123+
private getDevDependencies(projectDir: string): IDictionary<any> {
124+
let projectFilePath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME);
125+
let projectFileContent = require(projectFilePath);
126+
return projectFileContent.devDependencies || {};
127+
}
128128

129-
private enumeratePackageJsonFilesSync(nodeModulesDirectoryPath: string, foundFiles?: string[]): string[] {
129+
private enumeratePackageJsonFilesSync(nodeModulesDirectoryPath: string, foundFiles?: string[]): string[] {
130130
foundFiles = foundFiles || [];
131-
if(fs.existsSync(nodeModulesDirectoryPath)) {
131+
if (fs.existsSync(nodeModulesDirectoryPath)) {
132132
let contents = fs.readdirSync(nodeModulesDirectoryPath);
133133
for (let i = 0; i < contents.length; ++i) {
134-
let packageJsonFilePath = path.join(nodeModulesDirectoryPath, contents[i], constants.PACKAGE_JSON_FILE_NAME);
134+
let moduleName = contents[i];
135+
let moduleDirectoryInNodeModules = path.join(nodeModulesDirectoryPath, moduleName);
136+
let packageJsonFilePath = path.join(moduleDirectoryInNodeModules, constants.PACKAGE_JSON_FILE_NAME);
135137
if (fs.existsSync(packageJsonFilePath)) {
136138
foundFiles.push(packageJsonFilePath);
137139
}
138140

139-
let directoryPath = path.join(nodeModulesDirectoryPath, contents[i], constants.NODE_MODULES_FOLDER_NAME);
141+
let directoryPath = path.join(moduleDirectoryInNodeModules, constants.NODE_MODULES_FOLDER_NAME);
140142
if (fs.existsSync(directoryPath)) {
141143
this.enumeratePackageJsonFilesSync(directoryPath, foundFiles);
144+
} else if (fs.statSync(moduleDirectoryInNodeModules).isDirectory()) {
145+
// Some modules can be grouped in one folder and we need to enumerate them too (e.g. @angular).
146+
this.enumeratePackageJsonFilesSync(moduleDirectoryInNodeModules, foundFiles);
142147
}
143148
}
144149
}
145150
return foundFiles;
146-
}
151+
}
147152
}
148153

149154
export default wrapBroccoliPlugin(DestCopy);
1.69 KB
Binary file not shown.

test/npm-support.ts

+28-7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import shelljs = require("shelljs");
3737
temp.track();
3838

3939
let assert = require("chai").assert;
40+
let nodeModulesFolderName = "node_modules";
4041

4142
function createTestInjector(): IInjector {
4243
let testInjector = new yok.Yok();
@@ -171,7 +172,7 @@ function addDependencies(testInjector: IInjector, projectFolder: string, depende
171172
let currentDependencies = packageJsonData.dependencies;
172173
_.extend(currentDependencies, dependencies);
173174

174-
if(devDependencies) {
175+
if (devDependencies) {
175176
let currentDevDependencies = packageJsonData.devDependencies;
176177
_.extend(currentDevDependencies, devDependencies);
177178
}
@@ -194,7 +195,7 @@ describe("Npm support tests", () => {
194195
});
195196
it("Ensures that the installed dependencies are prepared correctly", () => {
196197
// Setup
197-
addDependencies(testInjector, projectFolder, {"bplist": "0.0.4"}).wait();
198+
addDependencies(testInjector, projectFolder, { "bplist": "0.0.4" }).wait();
198199

199200
// Act
200201
preparePlatform(testInjector).wait();
@@ -216,15 +217,15 @@ describe("Npm support tests", () => {
216217
// Setup
217218
let fs = testInjector.resolve("fs");
218219
let scopedName = "@reactivex/rxjs";
219-
let scopedModule = path.join(projectFolder, "node_modules", "@reactivex/rxjs");
220+
let scopedModule = path.join(projectFolder, nodeModulesFolderName, "@reactivex/rxjs");
220221
let scopedPackageJson = path.join(scopedModule, "package.json");
221222
let dependencies: any = {};
222223
dependencies[scopedName] = "0.0.0-prealpha.3";
223224
// Do not pass dependencies object as the sinopia cannot work with scoped dependencies. Instead move them manually.
224225
addDependencies(testInjector, projectFolder, {}).wait();
225226
//create module dir, and add a package.json
226227
shelljs.mkdir("-p", scopedModule);
227-
fs.writeFile(scopedPackageJson, JSON.stringify({name: scopedName})).wait();
228+
fs.writeFile(scopedPackageJson, JSON.stringify({ name: scopedName })).wait();
228229

229230
// Act
230231
preparePlatform(testInjector).wait();
@@ -236,6 +237,26 @@ describe("Npm support tests", () => {
236237
assert.isTrue(fs.exists(scopedDependencyPath).wait());
237238
});
238239

240+
it("Ensures that scoped dependencies are prepared correctly when are not in root level", () => {
241+
// Setup
242+
let customPluginName = "plugin-with-scoped-dependency";
243+
let customPluginDirectory = temp.mkdirSync("custom-plugin-directory");
244+
245+
let fs: IFileSystem = testInjector.resolve("fs");
246+
fs.unzip(path.join("resources", "test", `${customPluginName}.zip`), customPluginDirectory).wait();
247+
248+
addDependencies(testInjector, projectFolder, { "plugin-with-scoped-dependency": `file:${path.join(customPluginDirectory, customPluginName)}` }).wait();
249+
250+
// Act
251+
preparePlatform(testInjector).wait();
252+
253+
// Assert
254+
let tnsModulesFolderPath = path.join(appDestinationFolderPath, "app", "tns_modules");
255+
256+
let scopedDependencyPath = path.join(tnsModulesFolderPath, "@scoped-plugin", "inner-plugin");
257+
assert.isTrue(fs.exists(scopedDependencyPath).wait());
258+
});
259+
239260
it("Ensures that tns_modules absent when bundling", () => {
240261
let fs = testInjector.resolve("fs");
241262
let lockfile = testInjector.resolve("lockfile");
@@ -294,19 +315,19 @@ describe("Flatten npm modules tests", () => {
294315
assert.isFalse(fs.exists(gulpJshint).wait());
295316

296317
// Get all gulp dependencies
297-
let gulpDependencies = fs.readDirectory(path.join(projectFolder, "node_modules", "gulp", "node_modules")).wait();
318+
let gulpDependencies = fs.readDirectory(path.join(projectFolder, nodeModulesFolderName, "gulp", nodeModulesFolderName)).wait();
298319
_.each(gulpDependencies, dependency => {
299320
assert.isFalse(fs.exists(path.join(tnsModulesFolderPath, dependency)).wait());
300321
});
301322

302323
// Get all gulp-jscs dependencies
303-
let gulpJscsDependencies = fs.readDirectory(path.join(projectFolder, "node_modules", "gulp-jscs", "node_modules")).wait();
324+
let gulpJscsDependencies = fs.readDirectory(path.join(projectFolder, nodeModulesFolderName, "gulp-jscs", nodeModulesFolderName)).wait();
304325
_.each(gulpJscsDependencies, dependency => {
305326
assert.isFalse(fs.exists(path.join(tnsModulesFolderPath, dependency)).wait());
306327
});
307328

308329
// Get all gulp-jshint dependencies
309-
let gulpJshintDependencies = fs.readDirectory(path.join(projectFolder, "node_modules", "gulp-jshint", "node_modules")).wait();
330+
let gulpJshintDependencies = fs.readDirectory(path.join(projectFolder, nodeModulesFolderName, "gulp-jshint", nodeModulesFolderName)).wait();
310331
_.each(gulpJshintDependencies, dependency => {
311332
assert.isFalse(fs.exists(path.join(tnsModulesFolderPath, dependency)).wait());
312333
});

0 commit comments

Comments
 (0)