Skip to content

Commit 17c2a29

Browse files
Fatme HavaluovaFatme Havaluova
Fatme Havaluova
authored and
Fatme Havaluova
committed
Fix symlinks that are added to output package and points to not existing location
1 parent 78df478 commit 17c2a29

File tree

5 files changed

+135
-77
lines changed

5 files changed

+135
-77
lines changed

lib/services/platform-service.ts

+16-7
Original file line numberDiff line numberDiff line change
@@ -148,28 +148,37 @@ export class PlatformService implements IPlatformService {
148148
platform = platform.toLowerCase();
149149

150150
var platformData = this.$platformsData.getPlatformData(platform);
151+
let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
152+
let lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath).wait() ?
153+
this.$fs.getFsStats(appDestinationDirectoryPath).wait().mtime : null;
151154

152155
// Copy app folder to native project
156+
this.$fs.ensureDirectoryExists(appDestinationDirectoryPath).wait();
153157
var appSourceDirectoryPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME);
154-
158+
155159
// Delete the destination app in order to prevent EEXIST errors when symlinks are used.
156-
this.$fs.deleteDirectory(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)).wait();
157-
shell.cp("-R", appSourceDirectoryPath, platformData.appDestinationDirectoryPath);
160+
let contents = this.$fs.readDirectory(appDestinationDirectoryPath).wait();
161+
162+
_(contents)
163+
.filter(directoryName => directoryName !== "tns_modules")
164+
.each(directoryName => this.$fs.deleteDirectory(path.join(appDestinationDirectoryPath, directoryName)).wait())
165+
.value();
166+
shell.cp("-Rf", appSourceDirectoryPath, platformData.appDestinationDirectoryPath);
158167

159168
// Copy App_Resources to project root folder
160169
this.$fs.ensureDirectoryExists(platformData.appResourcesDestinationDirectoryPath).wait(); // Should be deleted
161-
var appResourcesDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME);
170+
var appResourcesDirectoryPath = path.join(appDestinationDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME);
162171
if (this.$fs.exists(appResourcesDirectoryPath).wait()) {
163172
shell.cp("-Rf", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), platformData.appResourcesDestinationDirectoryPath);
164173
this.$fs.deleteDirectory(appResourcesDirectoryPath).wait();
165174
}
166175

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

171180
_.each(contents, d => {
172-
let filePath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, d);
181+
let filePath = path.join(appDestinationDirectoryPath, d);
173182
let fsStat = this.$fs.getFsStats(filePath).wait();
174183
if(fsStat.isDirectory() && d !== constants.APP_RESOURCES_FOLDER_NAME) {
175184
this.processPlatformSpecificFiles(platform, this.$fs.enumerateFilesInDirectorySync(filePath)).wait();
@@ -182,7 +191,7 @@ export class PlatformService implements IPlatformService {
182191
// Process node_modules folder
183192
this.$pluginsService.ensureAllDependenciesAreInstalled().wait();
184193
var tnsModulesDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, PlatformService.TNS_MODULES_FOLDER_NAME);
185-
this.$broccoliBuilder.prepareNodeModules(tnsModulesDestinationPath, this.$projectData.projectDir).wait();
194+
this.$broccoliBuilder.prepareNodeModules(tnsModulesDestinationPath, this.$projectData.projectDir, lastModifiedTime).wait();
186195

187196
this.$logger.out("Project successfully prepared");
188197
}).future<void>()();

lib/tools/broccoli/broccoli.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ interface BroccoliNode {
152152
}
153153

154154
interface IBroccoliBuilder {
155-
prepareNodeModules(outputPath: string, projectDir: string): IFuture<void>;
155+
prepareNodeModules(outputPath: string, projectDir: string, lastModifiedTime?: Date): IFuture<void>;
156156
}
157157

158158
interface IDiffResult {
@@ -162,6 +162,7 @@ interface IDiffResult {
162162

163163
interface IBroccoliPlugin {
164164
rebuild(diff: IDiffResult): any;
165+
rebuildChangedDirectories?(changedDirectories: string[]): void;
165166
cleanup? () : void;
166167
}
167168

lib/tools/broccoli/builder.ts

+66-14
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,81 @@ let broccoli = require('broccoli');
55
let path = require('path');
66
import Future = require("fibers/future");
77
import {TreeDiffer} from './tree-differ';
8-
import destCopy = require('./node-modules-dest-copy');
8+
import destCopyLib = require('./node-modules-dest-copy');
9+
10+
var gulp = require("gulp");
11+
var vinylFilterSince = require('vinyl-filter-since')
12+
var through = require("through2");
913

1014
export class Builder implements IBroccoliBuilder {
15+
private nodeModules: any = {};
16+
1117
constructor(private $fs: IFileSystem,
1218
private $nodeModulesTree: INodeModulesTree,
1319
private $projectDataService: IProjectDataService,
20+
private $injector: IInjector,
1421
private $logger: ILogger) { }
1522

16-
public prepareNodeModules(absoluteOutputPath: string, projectDir: string): IFuture<void> {
23+
public prepareNodeModules(absoluteOutputPath: string, projectDir: string, lastModifiedTime?: Date): IFuture<void> {
1724
return (() => {
18-
// TODO: figure out a better way for doing this
19-
this.$projectDataService.initialize(projectDir);
20-
let cachedNodeModulesPath = this.$projectDataService.getValue("node-modules-cache-path").wait();
21-
if (cachedNodeModulesPath && this.$fs.exists(cachedNodeModulesPath).wait()) {
22-
let diffTree = new TreeDiffer(cachedNodeModulesPath);
23-
let diffTreeResult = diffTree.diffTree(path.join(projectDir, absoluteOutputPath, "node_modules"));
24-
25-
if(diffTreeResult.changedDirectories.length > 0 || diffTreeResult.removedDirectories.length > 0) {
26-
this.rebuildNodeModulesTree(absoluteOutputPath, projectDir).wait();
27-
}
28-
} else {
29-
this.rebuildNodeModulesTree(absoluteOutputPath, projectDir).wait();
25+
let isNodeModulesModified = false;
26+
let nodeModulesPath = path.join(projectDir, "node_modules");
27+
28+
if(lastModifiedTime) {
29+
let pipeline = gulp.src(path.join(projectDir, "node_modules/**"))
30+
.pipe(vinylFilterSince(lastModifiedTime))
31+
.pipe(through.obj( (chunk: any, enc: any, cb: Function) => {
32+
if(chunk.path === nodeModulesPath) {
33+
isNodeModulesModified = true;
34+
}
35+
36+
if(!isNodeModulesModified) {
37+
let rootModuleName = chunk.path.split(nodeModulesPath)[1].split(path.sep)[1];
38+
let rootModuleFullPath = path.join(nodeModulesPath, rootModuleName);
39+
this.nodeModules[rootModuleFullPath] = rootModuleFullPath;
40+
}
41+
42+
cb(null);
43+
}))
44+
.pipe(gulp.dest(absoluteOutputPath));
45+
46+
let future = new Future<void>();
47+
48+
pipeline.on('end', (err: any, data: any) => {
49+
if(err) {
50+
future.throw(err);
51+
} else {
52+
future.return();
53+
}
54+
});
55+
56+
future.wait();
57+
}
58+
59+
if(isNodeModulesModified && this.$fs.exists(absoluteOutputPath).wait()) {
60+
let currentPreparedTnsModules = this.$fs.readDirectory(absoluteOutputPath).wait();
61+
let tnsModulesInApp = this.$fs.readDirectory(path.join(projectDir, "app", "tns_modules")).wait();
62+
let modulesToDelete = _.difference(currentPreparedTnsModules, tnsModulesInApp);
63+
_.each(modulesToDelete, moduleName => this.$fs.deleteDirectory(path.join(absoluteOutputPath, moduleName)).wait())
64+
}
65+
66+
if(!lastModifiedTime || isNodeModulesModified) {
67+
let nodeModulesDirectories = this.$fs.readDirectory(nodeModulesPath).wait();
68+
_.each(nodeModulesDirectories, nodeModuleDirectoryName => {
69+
let nodeModuleFullPath = path.join(nodeModulesPath, nodeModuleDirectoryName);
70+
this.nodeModules[nodeModuleFullPath] = nodeModuleFullPath;
71+
});
3072
}
73+
74+
let destCopy = this.$injector.resolve(destCopyLib.DestCopy, {
75+
inputPath: projectDir,
76+
cachePath: "",
77+
outputRoot: absoluteOutputPath,
78+
projectDir: projectDir
79+
});
80+
81+
destCopy.rebuildChangedDirectories(_.keys(this.nodeModules));
82+
3183
}).future<void>()();
3284
}
3385

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

+48-55
Original file line numberDiff line numberDiff line change
@@ -14,57 +14,66 @@ import constants = require("./../../constants");
1414
* and tees a copy to the given path outside the tmp dir.
1515
*/
1616
export class DestCopy implements IBroccoliPlugin {
17-
constructor(private inputPath: string, private cachePath: string, private outputRoot: string, private projectDir: string) {}
18-
19-
public rebuild(treeDiff: IDiffResult): void {
20-
let dependencies = this.getDependencies();
21-
let devDependencies = this.getDevDependencies(this.projectDir);
22-
23-
treeDiff.changedDirectories.forEach(changedDirectory => {
24-
let changedDirectoryAbsolutePath = path.join(this.inputPath, constants.NODE_MODULES_FOLDER_NAME, changedDirectory);
25-
let packageJsonFiles = [path.join(changedDirectoryAbsolutePath, "package.json")];
26-
let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, "node_modules");
27-
packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath));
28-
29-
_.each(packageJsonFiles, packageJsonFilePath => {
30-
let fileContent = require(packageJsonFilePath);
31-
let isPlugin = fileContent.nativescript;
17+
private dependencies: IDictionary<any> = null;
18+
private devDependencies: IDictionary<any> = null;
3219

33-
if(!devDependencies[fileContent.name]) { // Don't flatten dev dependencies
34-
35-
let currentDependency = {
36-
name: fileContent.name,
37-
version: fileContent.version,
38-
directory: path.dirname(packageJsonFilePath),
39-
isPlugin: isPlugin
40-
};
20+
constructor(private inputPath: string, private cachePath: string, private outputRoot: string, private projectDir: string) {
21+
this.dependencies = Object.create(null);
22+
this.devDependencies = this.getDevDependencies(projectDir);
23+
}
24+
25+
public rebuildChangedDirectories(changedDirectories: string[]): void {
26+
_.each(changedDirectories, changedDirectoryAbsolutePath => {
27+
let pathToPackageJson = path.join(changedDirectoryAbsolutePath, "package.json");
28+
if(fs.existsSync(pathToPackageJson)) {
29+
let packageJsonFiles = [pathToPackageJson];
30+
let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, "node_modules");
31+
packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath));
4132

42-
let addedDependency = dependencies[currentDependency.name];
43-
if (addedDependency) {
44-
if (semver.gt(currentDependency.version, addedDependency.version)) {
45-
let currentDependencyMajorVersion = semver.major(currentDependency.version);
46-
let addedDependencyMajorVersion = semver.major(addedDependency.version);
33+
_.each(packageJsonFiles, packageJsonFilePath => {
34+
let fileContent = require(packageJsonFilePath);
35+
let isPlugin = fileContent.nativescript;
4736

48-
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}`;
49-
let logger = $injector.resolve("$logger");
50-
currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message);
51-
52-
dependencies[currentDependency.name] = currentDependency;
37+
if(!this.devDependencies[fileContent.name]) { // Don't flatten dev dependencies
38+
39+
let currentDependency = {
40+
name: fileContent.name,
41+
version: fileContent.version,
42+
directory: path.dirname(packageJsonFilePath),
43+
isPlugin: isPlugin
44+
};
45+
46+
let addedDependency = this.dependencies[currentDependency.name];
47+
if (addedDependency) {
48+
if (semver.gt(currentDependency.version, addedDependency.version)) {
49+
let currentDependencyMajorVersion = semver.major(currentDependency.version);
50+
let addedDependencyMajorVersion = semver.major(addedDependency.version);
51+
52+
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}`;
53+
let logger = $injector.resolve("$logger");
54+
currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message);
55+
56+
this.dependencies[currentDependency.name] = currentDependency;
57+
}
58+
} else {
59+
this.dependencies[currentDependency.name] = currentDependency;
5360
}
54-
} else {
55-
dependencies[currentDependency.name] = currentDependency;
5661
}
57-
}
58-
});
62+
});
63+
}
5964
});
6065

61-
_.each(dependencies, dependency => {
62-
shelljs.cp("-R", dependency.directory, this.outputRoot);
66+
_.each(this.dependencies, dependency => {
67+
shelljs.cp("-Rf", dependency.directory, this.outputRoot);
6368
shelljs.rm("-rf", path.join(this.outputRoot, dependency.name, "node_modules"));
6469
if(dependency.isPlugin) {
6570
shelljs.rm("-rf", path.join(this.outputRoot, dependency.name, "platforms"));
6671
}
6772
});
73+
}
74+
75+
public rebuild(treeDiff: IDiffResult): void {
76+
this.rebuildChangedDirectories(treeDiff.changedDirectories);
6877

6978
// Cache input tree
7079
let projectFilePath = path.join(this.projectDir, constants.PACKAGE_JSON_FILE_NAME);
@@ -73,22 +82,6 @@ export class DestCopy implements IBroccoliPlugin {
7382
fs.writeFileSync(projectFilePath, JSON.stringify(projectFileContent, null, "\t"), { encoding: "utf8" });
7483
}
7584

76-
private getDependencies(): IDictionary<any> {
77-
let result = Object.create(null);
78-
if(fs.existsSync(this.outputRoot)) {
79-
let dirs = fs.readdirSync(this.outputRoot);
80-
_.each(dirs, dir => {
81-
let filePath = path.join(dir, constants.PACKAGE_JSON_FILE_NAME);
82-
if(fs.existsSync(filePath)) {
83-
let fileContent = require(filePath);
84-
result[fileContent.name] = fileContent;
85-
}
86-
});
87-
}
88-
89-
return result;
90-
}
91-
9285
private getDevDependencies(projectDir: string): IDictionary<any> {
9386
let projectFilePath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME);
9487
let projectFileContent = require(projectFilePath);

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"fibers": "https://github.com/icenium/node-fibers/tarball/v1.0.5.1",
3838
"filesize": "2.0.3",
3939
"gaze": "0.5.1",
40+
"gulp": "3.9.0",
4041
"iconv-lite": "0.4.4",
4142
"inquirer": "0.8.2",
4243
"ios-sim-portable": "1.0.8",
@@ -65,7 +66,9 @@
6566
"shelljs": "0.3.0",
6667
"tabtab": "https://github.com/Icenium/node-tabtab/tarball/master",
6768
"temp": "0.8.1",
69+
"through2": "2.0.0",
6870
"utf-8-validate": "https://github.com/telerik/utf-8-validate/tarball/master",
71+
"vinyl-filter-since": "2.0.0",
6972
"winreg": "0.0.12",
7073
"ws": "0.7.1",
7174
"xcode": "https://github.com/NativeScript/node-xcode/archive/NativeScript-0.9.tar.gz",

0 commit comments

Comments
 (0)