Skip to content

Commit 52e3979

Browse files
FatmeFatme
authored andcommitted
Merge pull request #524 from NativeScript/fatme/npm-as-package-manager
Npm as package manager
2 parents cc9fca3 + e64054a commit 52e3979

31 files changed

+1495
-191
lines changed

.idea/nativescript-cli.iml

Lines changed: 0 additions & 10 deletions
This file was deleted.

lib/bootstrap.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,18 @@ $injector.requireCommand("emulate|android", "./commands/emulate");
4444
$injector.requireCommand("emulate|ios", "./commands/emulate");
4545

4646
$injector.require("npm", "./node-package-manager");
47+
$injector.require("npmInstallationManager", "./npm-installation-manager");
4748
$injector.require("lockfile", "./lockfile");
4849
$injector.require("dynamicHelpProvider", "./dynamic-help-provider");
4950
$injector.require("mobilePlatformsCapabilities", "./mobile-platforms-capabilities");
5051
$injector.require("commandsServiceProvider", "./providers/commands-service-provider");
5152

5253
$injector.require("logcatPrinter", "./providers/logcat-printer");
54+
55+
$injector.require("broccoliBuilder", "./tools/broccoli/builder");
56+
$injector.require("nodeModulesTree", "./tools/broccoli/trees/node-modules-tree");
57+
$injector.require("broccoliPluginWrapper", "./tools/broccoli/broccoli-plugin-wrapper");
58+
59+
$injector.require("pluginsService", "./services/plugins-service");
60+
$injector.requireCommand("plugin|add", "./commands/plugin/add-plugin");
61+
$injector.requireCommand("plugin|remove", "./commands/plugin/remove-plugin");

lib/commands/plugin/add-plugin.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
///<reference path="../../.d.ts"/>
2+
"use strict";
3+
4+
export class AddPluginCommand implements ICommand {
5+
constructor(private $pluginsService: IPluginsService,
6+
private $errors: IErrors) { }
7+
8+
execute(args: string[]): IFuture<void> {
9+
return this.$pluginsService.add(args[0]);
10+
}
11+
12+
canExecute(args: string[]): IFuture<boolean> {
13+
return (() => {
14+
if(!args[0]) {
15+
this.$errors.fail("You must specify plugin name.");
16+
}
17+
18+
let installedPlugins = this.$pluginsService.getAllInstalledPlugins().wait();
19+
let pluginName = args[0].toLowerCase();
20+
if(_.any(installedPlugins, (plugin: IPluginData) => plugin.name.toLowerCase() === pluginName)) {
21+
this.$errors.failWithoutHelp(`Plugin "${pluginName}" is already installed.`);
22+
}
23+
24+
return true;
25+
}).future<boolean>()();
26+
}
27+
28+
public allowedParameters: ICommandParameter[] = [];
29+
}
30+
$injector.registerCommand("plugin|add", AddPluginCommand);

lib/commands/plugin/remove-plugin.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
///<reference path="../../.d.ts"/>
2+
"use strict";
3+
4+
export class RemovePluginCommand implements ICommand {
5+
constructor(private $pluginsService: IPluginsService,
6+
private $errors: IErrors) { }
7+
8+
execute(args: string[]): IFuture<void> {
9+
return this.$pluginsService.remove(args[0]);
10+
}
11+
12+
canExecute(args: string[]): IFuture<boolean> {
13+
return (() => {
14+
if(!args[0]) {
15+
this.$errors.fail("You must specify plugin name.");
16+
}
17+
18+
let installedPlugins = this.$pluginsService.getAllInstalledPlugins().wait();
19+
let pluginName = args[0].toLowerCase();
20+
if(!_.any(installedPlugins, (plugin: IPluginData) => plugin.name.toLowerCase() === pluginName)) {
21+
this.$errors.failWithoutHelp(`Plugin "${pluginName}" is not installed.`);
22+
}
23+
24+
return true;
25+
}).future<boolean>()();
26+
}
27+
28+
public allowedParameters: ICommandParameter[] = [];
29+
}
30+
$injector.registerCommand("plugin|remove", RemovePluginCommand);

lib/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
export var APP_FOLDER_NAME = "app";
44
export var APP_RESOURCES_FOLDER_NAME = "App_Resources";
55
export var PROJECT_FRAMEWORK_FOLDER_NAME = "framework";
6+
export var NATIVESCRIPT_KEY_NAME = "nativescript";
7+
export var NODE_MODULES_FOLDER_NAME = "node_modules";
8+
export var PACKAGE_JSON_FILE_NAME = "package.json";
9+
export var NODE_MODULE_CACHE_PATH_KEY_NAME = "node-modules-cache-path";
610

711
export class ReleaseType {
812
static MAJOR = "major";

lib/declarations.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
interface INodePackageManager {
2+
getCache(): string;
3+
load(config?: any): IFuture<void>;
4+
install(packageName: string, pathToSave: string, config?: any): IFuture<any>;
5+
uninstall(packageName: string, config?: any): IFuture<any>;
6+
cache(packageName: string, version: string, cache?: any): IFuture<ICacheData>;
7+
cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void>;
8+
view(packageName: string, propertyName: string): IFuture<any>;
9+
}
10+
11+
interface INpmInstallationManager {
212
getCacheRootPath(): string;
313
addToCache(packageName: string, version: string): IFuture<void>;
414
cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void>;
5-
load(config?: any): IFuture<void>;
615
install(packageName: string, options?: INpmInstallOptions): IFuture<string>;
716
getLatestVersion(packageName: string): IFuture<string>;
817
getCachedPackagePath(packageName: string, version: string): string;
@@ -13,6 +22,14 @@ interface INpmInstallOptions {
1322
version?: string;
1423
}
1524

25+
interface ICacheData {
26+
name: string;
27+
version: string;
28+
dependencies: IStringDictionary;
29+
devDependencies: IStringDictionary;
30+
nativescript?: any;
31+
}
32+
1633
interface IStaticConfig extends Config.IStaticConfig { }
1734

1835
interface IConfiguration extends Config.IConfig { }
@@ -40,6 +57,7 @@ interface IOptions extends ICommonOptions {
4057
symlink: boolean;
4158
forDevice: boolean;
4259
client: boolean;
60+
production: boolean;
4361
keyStorePath: string;
4462
keyStorePassword: string;
4563
keyStoreAlias: string;

lib/definitions/platform.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ interface IPlatformData {
3434
frameworkDirectoriesExtensions?: string[];
3535
frameworkDirectoriesNames?: string[];
3636
targetedOS?: string[];
37+
configurationFileName?: string;
38+
configurationFilePath?: string;
3739
}
3840

3941
interface IPlatformsData {

lib/definitions/plugins.d.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
interface IPluginsService {
2+
add(plugin: string): IFuture<void>; // adds plugin by name, github url, local path and et.
3+
remove(pluginName: string): IFuture<void>; // removes plugin only by name
4+
prepare(pluginData: IPluginData): IFuture<void>;
5+
getAllInstalledPlugins(): IFuture<IPluginData[]>;
6+
ensureAllDependenciesAreInstalled(): IFuture<void>;
7+
}
8+
9+
interface IPluginData extends INodeModuleData {
10+
platformsData: IPluginPlatformsData;
11+
}
12+
13+
interface INodeModuleData {
14+
name: string;
15+
version: string;
16+
fullPath: string;
17+
isPlugin: boolean;
18+
moduleInfo: any;
19+
}
20+
21+
interface IPluginPlatformsData {
22+
ios: string;
23+
android: string;
24+
}

lib/definitions/shelljs.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ declare module "shelljs" {
33
function cp(arg: string, sourcePath: string[], destinationPath: string): void;
44
function sed(arg: string, oldValue: any, newValue: string, filePath: string): void;
55
function mv(source: string[], destination: string): void;
6+
function rm(option: string, filePath: string): void;
67
function grep(what: any, where: string): any;
78
}

lib/node-package-manager.ts

Lines changed: 36 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,18 @@
33

44
import Future = require("fibers/future");
55
import npm = require("npm");
6-
import path = require("path");
7-
import semver = require("semver");
8-
import shell = require("shelljs");
9-
import helpers = require("./common/helpers");
10-
import constants = require("./constants");
116

127
export class NodePackageManager implements INodePackageManager {
13-
private static NPM_LOAD_FAILED = "Failed to retrieve data from npm. Please try again a little bit later.";
14-
private static NPM_REGISTRY_URL = "http://registry.npmjs.org/";
15-
16-
private versionsCache: IDictionary<string[]>;
17-
188
constructor(private $logger: ILogger,
199
private $errors: IErrors,
2010
private $fs: IFileSystem,
2111
private $lockfile: ILockFile,
22-
private $options: IOptions) {
23-
this.versionsCache = {};
24-
this.load().wait();
25-
}
12+
private $options: IOptions) { }
2613

27-
public getCacheRootPath(): string {
14+
public getCache(): string {
2815
return npm.cache;
2916
}
30-
31-
public addToCache(packageName: string, version: string): IFuture<void> {
32-
return (() => {
33-
this.addToCacheCore(packageName, version).wait();
34-
35-
var packagePath = path.join(npm.cache, packageName, version, "package");
36-
if(!this.isPackageUnpacked(packagePath).wait()) {
37-
this.cacheUnpack(packageName, version).wait();
38-
}
39-
}).future<void>()();
40-
}
41-
17+
4218
public load(config?: any): IFuture<void> {
4319
var future = new Future<void>();
4420
npm.load(config, (err) => {
@@ -50,128 +26,51 @@ export class NodePackageManager implements INodePackageManager {
5026
});
5127
return future;
5228
}
53-
54-
public install(packageName: string, opts?: INpmInstallOptions): IFuture<string> {
55-
return (() => {
56-
this.$lockfile.lock().wait();
57-
58-
try {
59-
var packageToInstall = packageName;
60-
var pathToSave = (opts && opts.pathToSave) || npm.cache;
61-
var version = (opts && opts.version) || null;
62-
63-
return this.installCore(packageToInstall, pathToSave, version).wait();
64-
} catch(error) {
65-
this.$logger.debug(error);
66-
this.$errors.fail("%s. Error: %s", NodePackageManager.NPM_LOAD_FAILED, error);
67-
} finally {
68-
this.$lockfile.unlock().wait();
69-
}
70-
71-
}).future<string>()();
29+
30+
public install(packageName: string, pathToSave: string, config?: any): IFuture<any> {
31+
return this.loadAndExecute("install", [pathToSave, packageName], { config: config });
7232
}
73-
74-
public getLatestVersion(packageName: string): IFuture<string> {
75-
var future = new Future<string>();
76-
77-
npm.commands["view"]([packageName, "dist-tags"], [false], (err: any, data: any) => { // [false] - silent
78-
if(err) {
79-
future.throw(err);
80-
} else {
81-
var latestVersion = _.first(_.keys(data));
82-
this.$logger.trace("Using version %s. ", latestVersion);
83-
84-
future.return(latestVersion);
85-
}
86-
});
87-
88-
return future;
33+
34+
public uninstall(packageName: string, config?: any): IFuture<any> {
35+
return this.loadAndExecute("uninstall", [[packageName]], { config: config });
8936
}
9037

91-
public getCachedPackagePath(packageName: string, version: string): string {
92-
return path.join(npm.cache, packageName, version, "package");
38+
public cache(packageName: string, version: string, config?: any): IFuture<ICacheData> {
39+
// function cache (pkg, ver, where, scrub, cb)
40+
return this.loadAndExecute("cache", [packageName, version, undefined, false], { subCommandName: "add", config: config });
9341
}
94-
95-
private installCore(packageName: string, pathToSave: string, version: string): IFuture<string> {
96-
return (() => {
97-
if (this.$options.frameworkPath) {
98-
if (this.$fs.getFsStats(this.$options.frameworkPath).wait().isFile()) {
99-
this.npmInstall(packageName, pathToSave, version).wait();
100-
var pathToNodeModules = path.join(pathToSave, "node_modules");
101-
var folders = this.$fs.readDirectory(pathToNodeModules).wait();
102-
return path.join(pathToNodeModules, folders[0]);
103-
}
104-
return this.$options.frameworkPath;
105-
} else {
106-
version = version || this.getLatestVersion(packageName).wait();
107-
var packagePath = this.getCachedPackagePath(packageName, version);
108-
if (!this.isPackageCached(packagePath).wait()) {
109-
this.addToCacheCore(packageName, version).wait();
110-
}
111-
112-
if(!this.isPackageUnpacked(packagePath).wait()) {
113-
this.cacheUnpack(packageName, version).wait();
114-
}
115-
return packagePath;
116-
}
117-
}).future<string>()();
118-
}
119-
120-
private npmInstall(packageName: string, pathToSave: string, version: string): IFuture<void> {
121-
this.$logger.out("Installing ", packageName);
122-
123-
var incrementedVersion = semver.inc(version, constants.ReleaseType.MINOR);
124-
if (!this.$options.frameworkPath && packageName.indexOf("@") < 0) {
125-
packageName = packageName + "@<" + incrementedVersion;
126-
}
127-
128-
var future = new Future<void>();
129-
npm.commands["install"](pathToSave, packageName, (err: Error, data: any) => {
130-
if(err) {
131-
future.throw(err);
132-
} else {
133-
this.$logger.out("Installed ", packageName);
134-
future.return(data);
135-
}
136-
});
137-
return future;
42+
43+
public cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void> {
44+
// function unpack (pkg, ver, unpackTarget, dMode, fMode, uid, gid, cb)
45+
return this.loadAndExecute("cache", [packageName, version, unpackTarget, null, null, null, null], { subCommandName: "unpack" });
13846
}
139-
140-
private isPackageCached(packagePath: string): IFuture<boolean> {
141-
return this.$fs.exists(packagePath);
47+
48+
public view(packageName: string, propertyName: string): IFuture<any> {
49+
return this.loadAndExecute("view", [[packageName, propertyName], [false]]);
14250
}
143-
144-
private isPackageUnpacked(packagePath: string): IFuture<boolean> {
51+
52+
private loadAndExecute(commandName: string, args: any[], opts?: { config?: any, subCommandName?: string }): IFuture<any> {
14553
return (() => {
146-
return this.$fs.getFsStats(packagePath).wait().isDirectory() &&
147-
this.$fs.enumerateFilesInDirectorySync(packagePath).length > 1;
148-
}).future<boolean>()();
149-
}
150-
151-
private addToCacheCore(packageName: string, version: string): IFuture<void> {
152-
var future = new Future<void>();
153-
// cache.add = function (pkg, ver, where, scrub, cb)
154-
npm.commands["cache"].add(packageName, version, undefined, false, (err: Error, data: any) => {
155-
if(err) {
156-
future.throw(err);
157-
} else {
158-
future.return();
159-
}
160-
});
161-
return future;
54+
opts = opts || {};
55+
this.load(opts.config).wait();
56+
return this.executeCore(commandName, args, opts.subCommandName).wait();
57+
}).future<any>()();
16258
}
163-
164-
public cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void> {
165-
var future = new Future<void>();
166-
unpackTarget = unpackTarget || path.join(npm.cache, packageName, version, "package");
167-
// function unpack (pkg, ver, unpackTarget, dMode, fMode, uid, gid, cb)
168-
npm.commands["cache"].unpack(packageName, version, unpackTarget, null, null, null, null, (err: Error, data: any) => {
59+
60+
private executeCore(commandName: string, args: any[], subCommandName?: string): IFuture<any> {
61+
let future = new Future<any>();
62+
let callback = (err: Error, data: any) => {
16963
if(err) {
17064
future.throw(err);
17165
} else {
172-
future.return();
66+
future.return(data);
17367
}
174-
});
68+
}
69+
args.push(callback);
70+
71+
let command = subCommandName ? npm.commands[commandName][subCommandName] : npm.commands[commandName];
72+
command.apply(this, args);
73+
17574
return future;
17675
}
17776
}

0 commit comments

Comments
 (0)