Skip to content

Commit a888d93

Browse files
authored
Merge pull request #2118 from NativeScript/hdeshev/webpack-plugins
Prepare plugins independently of node_modules
2 parents 1910f40 + e859670 commit a888d93

File tree

6 files changed

+132
-118
lines changed

6 files changed

+132
-118
lines changed

.travis.yml

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ before_script:
1414
- npm install grunt
1515
- node_modules/.bin/grunt enableScripts:false
1616
- npm install
17+
- grunt rebuild
18+
- ./bin/nativescript error-reporting disable # force ~/.local dir creation -- some tests rely on it
19+
- ./bin/nativescript usage-reporting disable
1720
- npm test
1821
- node_modules/.bin/grunt enableScripts:true
1922
script:

lib/definitions/plugins.d.ts

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ interface IPluginsService {
55
prepare(pluginData: IDependencyData, platform: string): IFuture<void>;
66
getAllInstalledPlugins(): IFuture<IPluginData[]>;
77
ensureAllDependenciesAreInstalled(): IFuture<void>;
8-
afterPrepareAllPlugins(): IFuture<void>;
9-
beforePrepareAllPlugins(): IFuture<void>;
108
getDependenciesFromPackageJson(): IFuture<IPackageJsonDepedenciesResult>
119
}
1210

lib/services/platform-service.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -290,13 +290,8 @@ export class PlatformService implements IPlatformService {
290290
let appDir = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
291291
try {
292292
let tnsModulesDestinationPath = path.join(appDir, constants.TNS_MODULES_FOLDER_NAME);
293-
if (!this.$options.bundle) {
294-
// Process node_modules folder
295-
this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime).wait();
296-
} else {
297-
// Clean target node_modules folder. Not needed when bundling.
298-
this.$nodeModulesBuilder.cleanNodeModules(tnsModulesDestinationPath, platform);
299-
}
293+
// Process node_modules folder
294+
this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime).wait();
300295
} catch (error) {
301296
this.$logger.debug(error);
302297
shell.rm("-rf", appDir);

lib/services/plugins-service.ts

+33-33
Original file line numberDiff line numberDiff line change
@@ -109,32 +109,48 @@ export class PluginsService implements IPluginsService {
109109
return (() => {
110110
platform = platform.toLowerCase();
111111
let platformData = this.$platformsData.getPlatformData(platform);
112-
let pluginDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules");
113112
let pluginData = this.convertToPluginData(dependencyData);
114113

115-
let exists = this.$fs.exists(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)).wait();
116-
117-
if (!this.isPluginDataValidForPlatform(pluginData, platform).wait()) {
118-
// Still clean up platform in case other platforms were supported.
119-
shelljs.rm("-rf", path.join(pluginDestinationPath, pluginData.name, "platforms"));
120-
return;
121-
}
122-
123-
if (exists) {
124-
//prepare platform speciffic files, .map and .ts files
125-
this.$projectFilesManager.processPlatformSpecificFiles(pluginDestinationPath, platform).wait();
126-
127-
//deal with platforms/android folder in ns plugin
128-
pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform);
129-
platformData.platformProjectService.preparePluginNativeCode(pluginData).wait();
130-
shelljs.rm("-rf", path.join(pluginDestinationPath, pluginData.name, "platforms"));
114+
let appFolderExists = this.$fs.exists(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)).wait();
115+
if (appFolderExists) {
116+
this.preparePluginScripts(pluginData, platform);
117+
this.preparePluginNativeCode(pluginData, platform);
131118

132119
// Show message
133120
this.$logger.out(`Successfully prepared plugin ${pluginData.name} for ${platform}.`);
134121
}
135122
}).future<void>()();
136123
}
137124

125+
private preparePluginScripts(pluginData: IPluginData, platform: string): void {
126+
let platformData = this.$platformsData.getPlatformData(platform);
127+
let pluginScriptsDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules");
128+
let scriptsDestinationExists = this.$fs.exists(pluginScriptsDestinationPath).wait();
129+
if (!scriptsDestinationExists) {
130+
//tns_modules/<plugin> doesn't exist. Assuming we're running a bundled prepare.
131+
return;
132+
}
133+
134+
if (!this.isPluginDataValidForPlatform(pluginData, platform).wait()) {
135+
// Still clean up platform in case other platforms were supported.
136+
shelljs.rm("-rf", path.join(pluginScriptsDestinationPath , pluginData.name, "platforms"));
137+
return;
138+
}
139+
140+
//prepare platform speciffic files, .map and .ts files
141+
this.$projectFilesManager.processPlatformSpecificFiles(pluginScriptsDestinationPath , platform).wait();
142+
}
143+
144+
private preparePluginNativeCode(pluginData: IPluginData, platform: string): void {
145+
let platformData = this.$platformsData.getPlatformData(platform);
146+
let pluginScriptsDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules");
147+
148+
pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform);
149+
platformData.platformProjectService.preparePluginNativeCode(pluginData).wait();
150+
shelljs.rm("-rf", path.join(pluginScriptsDestinationPath , pluginData.name, "platforms"));
151+
152+
}
153+
138154
public ensureAllDependenciesAreInstalled(): IFuture<void> {
139155
return (() => {
140156
let installedDependencies = this.$fs.exists(this.nodeModulesPath).wait() ? this.$fs.readDirectory(this.nodeModulesPath).wait() : [];
@@ -165,22 +181,6 @@ export class PluginsService implements IPluginsService {
165181
}).future<IPluginData[]>()();
166182
}
167183

168-
public afterPrepareAllPlugins(): IFuture<void> {
169-
let action = (pluginDestinationPath: string, platform: string, platformData: IPlatformData) => {
170-
return platformData.platformProjectService.afterPrepareAllPlugins();
171-
};
172-
173-
return this.executeForAllInstalledPlatforms(action);
174-
}
175-
176-
public beforePrepareAllPlugins(): IFuture<void> {
177-
let action = (pluginDestinationPath: string, platform: string, platformData: IPlatformData) => {
178-
return platformData.platformProjectService.beforePrepareAllPlugins();
179-
};
180-
181-
return this.executeForAllInstalledPlatforms(action);
182-
}
183-
184184
public getDependenciesFromPackageJson(): IFuture<IPackageJsonDepedenciesResult> {
185185
return (() => {
186186
let packageJson = this.$fs.readJson(this.getPackageJsonFilePath()).wait();

lib/tools/node-modules/node-modules-builder.ts

+20-13
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as fs from "fs";
33
import * as path from "path";
44
import * as shelljs from "shelljs";
55
import Future = require("fibers/future");
6-
import * as destCopyLib from "./node-modules-dest-copy";
6+
import {NpmDependencyResolver, TnsModulesCopy, NpmPluginPrepare} from "./node-modules-dest-copy";
77
import * as fiberBootstrap from "../../common/fiber-bootstrap";
88
import {sleep} from "../../../lib/common/helpers";
99

@@ -16,7 +16,9 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
1616
private $projectDataService: IProjectDataService,
1717
private $injector: IInjector,
1818
private $logger: ILogger,
19-
private $lockfile: ILockFile) { }
19+
private $lockfile: ILockFile,
20+
private $options: IOptions
21+
) { }
2022

2123
public getChangedNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime?: Date): IFuture<any> {
2224
return (() => {
@@ -108,20 +110,20 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
108110
}
109111

110112
if (!lastModifiedTime || isNodeModulesModified) {
111-
this.listModules(nodeModulesPath, nodeModules);
113+
this.expandScopedModules(nodeModulesPath, nodeModules);
112114
}
113115

114116
return nodeModules;
115117
}).future<any>()();
116118
}
117119

118-
private listModules(nodeModulesPath: string, nodeModules: any): void {
120+
private expandScopedModules(nodeModulesPath: string, nodeModules: IStringDictionary): void {
119121
let nodeModulesDirectories = this.$fs.exists(nodeModulesPath).wait() ? this.$fs.readDirectory(nodeModulesPath).wait() : [];
120122
_.each(nodeModulesDirectories, nodeModuleDirectoryName => {
121123
let isNpmScope = /^@/.test(nodeModuleDirectoryName);
122124
let nodeModuleFullPath = path.join(nodeModulesPath, nodeModuleDirectoryName);
123125
if (isNpmScope) {
124-
this.listModules(nodeModuleFullPath, nodeModules);
126+
this.expandScopedModules(nodeModuleFullPath, nodeModules);
125127
} else {
126128
nodeModules[nodeModuleFullPath] = nodeModuleFullPath;
127129
}
@@ -135,16 +137,21 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
135137
lastModifiedTime = null;
136138
}
137139
let nodeModules = this.getChangedNodeModules(absoluteOutputPath, platform, lastModifiedTime).wait();
138-
let destCopy = this.$injector.resolve(destCopyLib.DestCopy, {
139-
inputPath: this.$projectData.projectDir,
140-
cachePath: "",
141-
outputRoot: absoluteOutputPath,
142-
projectDir: this.$projectData.projectDir,
143-
platform: platform
144-
});
145140

146-
destCopy.rebuildChangedDirectories(_.keys(nodeModules), platform);
141+
const resolver = new NpmDependencyResolver(this.$projectData.projectDir);
142+
const resolvedDependencies = resolver.resolveDependencies(_.keys(nodeModules), platform);
147143

144+
if (!this.$options.bundle) {
145+
const tnsModulesCopy = this.$injector.resolve(TnsModulesCopy, {
146+
outputRoot: absoluteOutputPath
147+
});
148+
tnsModulesCopy.copyModules(resolvedDependencies, platform);
149+
} else {
150+
this.cleanNodeModules(absoluteOutputPath, platform);
151+
}
152+
153+
const npmPluginPrepare = this.$injector.resolve(NpmPluginPrepare, {});
154+
npmPluginPrepare.preparePlugins(resolvedDependencies, platform);
148155
}).future<void>()();
149156
}
150157

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

+74-63
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,28 @@ import * as constants from "../../constants";
66
import * as minimatch from "minimatch";
77
import Future = require("fibers/future");
88

9-
/**
10-
* Intercepts each directory as it is copied to the destination tempdir,
11-
* and tees a copy to the given path outside the tmp dir.
12-
*/
13-
export class DestCopy {
14-
private dependencies: IDictionary<any> = null;
15-
private devDependencies: IDictionary<any> = null;
9+
export interface ILocalDependencyData extends IDependencyData {
10+
directory: string;
11+
}
1612

13+
export class NpmDependencyResolver {
1714
constructor(
18-
private inputPath: string,
19-
private cachePath: string,
20-
private outputRoot: string,
21-
private projectDir: string,
22-
private platform: string,
23-
private $fs: IFileSystem,
24-
private $projectFilesManager: IProjectFilesManager,
25-
private $pluginsService: IPluginsService,
26-
private $platformsData: IPlatformsData
15+
private projectDir: string
2716
) {
28-
this.dependencies = Object.create(null);
29-
this.devDependencies = this.getDevDependencies(projectDir);
3017
}
3118

32-
public rebuildChangedDirectories(changedDirectories: string[], platform: string): void {
19+
private getDevDependencies(projectDir: string): IDictionary<any> {
20+
let projectFilePath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME);
21+
let projectFileContent = require(projectFilePath);
22+
return projectFileContent.devDependencies || {};
23+
}
24+
25+
public resolveDependencies(changedDirectories: string[], platform: string): IDictionary<ILocalDependencyData> {
26+
const devDependencies = this.getDevDependencies(this.projectDir);
27+
const dependencies: IDictionary<ILocalDependencyData> = Object.create(null);
3328

3429
_.each(changedDirectories, changedDirectoryAbsolutePath => {
35-
if (!this.devDependencies[path.basename(changedDirectoryAbsolutePath)]) {
30+
if (!devDependencies[path.basename(changedDirectoryAbsolutePath)]) {
3631
let pathToPackageJson = path.join(changedDirectoryAbsolutePath, constants.PACKAGE_JSON_FILE_NAME);
3732
let packageJsonFiles = fs.existsSync(pathToPackageJson) ? [pathToPackageJson] : [];
3833
let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, constants.NODE_MODULES_FOLDER_NAME);
@@ -41,15 +36,15 @@ export class DestCopy {
4136
_.each(packageJsonFiles, packageJsonFilePath => {
4237
let fileContent = require(packageJsonFilePath);
4338

44-
if (!this.devDependencies[fileContent.name] && fileContent.name && fileContent.version) { // Don't flatten dev dependencies and flatten only dependencies with valid package.json
45-
let currentDependency = {
39+
if (!devDependencies[fileContent.name] && fileContent.name && fileContent.version) { // Don't flatten dev dependencies and flatten only dependencies with valid package.json
40+
let currentDependency: ILocalDependencyData = {
4641
name: fileContent.name,
4742
version: fileContent.version,
4843
directory: path.dirname(packageJsonFilePath),
4944
nativescript: fileContent.nativescript
5045
};
5146

52-
let addedDependency = this.dependencies[currentDependency.name];
47+
let addedDependency = dependencies[currentDependency.name];
5348
if (addedDependency) {
5449
if (semver.gt(currentDependency.version, addedDependency.version)) {
5550
let currentDependencyMajorVersion = semver.major(currentDependency.version);
@@ -59,26 +54,53 @@ export class DestCopy {
5954
let logger = $injector.resolve("$logger");
6055
currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message);
6156

62-
this.dependencies[currentDependency.name] = currentDependency;
57+
dependencies[currentDependency.name] = currentDependency;
6358
}
6459
} else {
65-
this.dependencies[currentDependency.name] = currentDependency;
60+
dependencies[currentDependency.name] = currentDependency;
6661
}
6762
}
6863
});
6964
}
7065
});
71-
if (!_.isEmpty(this.dependencies)) {
72-
this.$platformsData.getPlatformData(platform).platformProjectService.beforePrepareAllPlugins().wait();
73-
}
66+
return dependencies;
67+
}
7468

75-
_.each(this.dependencies, dependency => {
76-
this.copyDependencyDir(dependency);
69+
private enumeratePackageJsonFilesSync(nodeModulesDirectoryPath: string, foundFiles?: string[]): string[] {
70+
foundFiles = foundFiles || [];
71+
if (fs.existsSync(nodeModulesDirectoryPath)) {
72+
let contents = fs.readdirSync(nodeModulesDirectoryPath);
73+
for (let i = 0; i < contents.length; ++i) {
74+
let moduleName = contents[i];
75+
let moduleDirectoryInNodeModules = path.join(nodeModulesDirectoryPath, moduleName);
76+
let packageJsonFilePath = path.join(moduleDirectoryInNodeModules, constants.PACKAGE_JSON_FILE_NAME);
77+
if (fs.existsSync(packageJsonFilePath)) {
78+
foundFiles.push(packageJsonFilePath);
79+
}
7780

78-
let isPlugin = !!dependency.nativescript;
79-
if (isPlugin) {
80-
this.$pluginsService.prepare(dependency, platform).wait();
81+
let directoryPath = path.join(moduleDirectoryInNodeModules, constants.NODE_MODULES_FOLDER_NAME);
82+
if (fs.existsSync(directoryPath)) {
83+
this.enumeratePackageJsonFilesSync(directoryPath, foundFiles);
84+
} else if (fs.statSync(moduleDirectoryInNodeModules).isDirectory()) {
85+
// Scoped modules (e.g. @angular) are grouped in a subfolder and we need to enumerate them too.
86+
this.enumeratePackageJsonFilesSync(moduleDirectoryInNodeModules, foundFiles);
87+
}
8188
}
89+
}
90+
return foundFiles;
91+
}
92+
}
93+
94+
export class TnsModulesCopy {
95+
constructor(
96+
private outputRoot: string,
97+
private $fs: IFileSystem
98+
) {
99+
}
100+
101+
public copyModules(dependencies: IDictionary<ILocalDependencyData>, platform: string): void {
102+
_.each(dependencies, dependency => {
103+
this.copyDependencyDir(dependency);
82104

83105
if (dependency.name === constants.TNS_CORE_MODULES_NAME) {
84106
let tnsCoreModulesResourcePath = path.join(this.outputRoot, constants.TNS_CORE_MODULES_NAME);
@@ -92,10 +114,6 @@ export class DestCopy {
92114
this.$fs.deleteDirectory(tnsCoreModulesResourcePath).wait();
93115
}
94116
});
95-
96-
if (!_.isEmpty(this.dependencies)) {
97-
this.$platformsData.getPlatformData(platform).platformProjectService.afterPrepareAllPlugins().wait();
98-
}
99117
}
100118

101119
private copyDependencyDir(dependency: any): void {
@@ -109,36 +127,29 @@ export class DestCopy {
109127
shelljs.cp("-Rf", dependency.directory, targetDir);
110128
shelljs.rm("-rf", path.join(targetDir, dependency.name, "node_modules"));
111129
}
130+
}
112131

113-
private getDevDependencies(projectDir: string): IDictionary<any> {
114-
let projectFilePath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME);
115-
let projectFileContent = require(projectFilePath);
116-
return projectFileContent.devDependencies || {};
132+
export class NpmPluginPrepare {
133+
constructor(
134+
private $fs: IFileSystem,
135+
private $pluginsService: IPluginsService,
136+
private $platformsData: IPlatformsData
137+
) {
117138
}
118139

119-
private enumeratePackageJsonFilesSync(nodeModulesDirectoryPath: string, foundFiles?: string[]): string[] {
120-
foundFiles = foundFiles || [];
121-
if (fs.existsSync(nodeModulesDirectoryPath)) {
122-
let contents = fs.readdirSync(nodeModulesDirectoryPath);
123-
for (let i = 0; i < contents.length; ++i) {
124-
let moduleName = contents[i];
125-
let moduleDirectoryInNodeModules = path.join(nodeModulesDirectoryPath, moduleName);
126-
let packageJsonFilePath = path.join(moduleDirectoryInNodeModules, constants.PACKAGE_JSON_FILE_NAME);
127-
if (fs.existsSync(packageJsonFilePath)) {
128-
foundFiles.push(packageJsonFilePath);
129-
}
140+
public preparePlugins(dependencies: IDictionary<IDependencyData>, platform: string): void {
141+
if (_.isEmpty(dependencies)) {
142+
return;
143+
}
130144

131-
let directoryPath = path.join(moduleDirectoryInNodeModules, constants.NODE_MODULES_FOLDER_NAME);
132-
if (fs.existsSync(directoryPath)) {
133-
this.enumeratePackageJsonFilesSync(directoryPath, foundFiles);
134-
} else if (fs.statSync(moduleDirectoryInNodeModules).isDirectory()) {
135-
// Some modules can be grouped in one folder and we need to enumerate them too (e.g. @angular).
136-
this.enumeratePackageJsonFilesSync(moduleDirectoryInNodeModules, foundFiles);
137-
}
145+
this.$platformsData.getPlatformData(platform).platformProjectService.beforePrepareAllPlugins().wait();
146+
_.each(dependencies, dependency => {
147+
let isPlugin = !!dependency.nativescript;
148+
if (isPlugin) {
149+
console.log("preparing: " + dependency.name);
150+
this.$pluginsService.prepare(dependency, platform).wait();
138151
}
139-
}
140-
return foundFiles;
152+
});
153+
this.$platformsData.getPlatformData(platform).platformProjectService.afterPrepareAllPlugins().wait();
141154
}
142155
}
143-
144-
export default DestCopy;

0 commit comments

Comments
 (0)