Skip to content

remove npm2 dependency #2209

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 6 commits into from
Nov 17, 2016
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
22 changes: 21 additions & 1 deletion lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ export let TEST_RUNNER_NAME = "nativescript-unit-test-runner";
export let LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"];
export let XML_FILE_EXTENSION = ".xml";

export class PackageVersion {
static NEXT = "next";
static LATEST = "latest";
}

export class SaveOptions {
static PRODUCTION = "save";
static DEV = "save-dev";
static OPTIONAL = "save-optional";
static EXACT = "save-exact";
}

export class ReleaseType {
static MAJOR = "major";
static PREMAJOR = "premajor";
Expand All @@ -26,6 +38,14 @@ export class ReleaseType {
static PRERELEASE = "prerelease";
}

export let RESERVED_TEMPLATE_NAMES: IStringDictionary = {
"default": "tns-template-hello-world",
"tsc": "tns-template-hello-world-ts",
"typescript": "tns-template-hello-world-ts",
"ng": "tns-template-hello-world-ng",
"angular": "tns-template-hello-world-ng"
};

export class ITMSConstants {
static ApplicationMetadataFile = "metadata.xml";
static VerboseLoggingLevels = {
Expand All @@ -44,4 +64,4 @@ class ItunesConnectApplicationTypesClass implements IiTunesConnectApplicationTyp
export let ItunesConnectApplicationTypes = new ItunesConnectApplicationTypesClass();

export let ANGULAR_NAME = "angular";
export let TYPESCRIPT_NAME = "TypeScript";
export let TYPESCRIPT_NAME = "typescript";
21 changes: 7 additions & 14 deletions lib/declarations.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
interface INodePackageManager {
getCache(): string;
load(config?: any): IFuture<void>;
install(packageName: string, pathToSave: string, config?: any): IFuture<any>;
uninstall(packageName: string, config?: any, path?: string): IFuture<any>;
cache(packageName: string, version: string, cache?: any): IFuture<IDependencyData>;
cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void>;
view(packageName: string, propertyName: string): IFuture<any>;
executeNpmCommand(npmCommandName: string, currentWorkingDirectory: string): IFuture<any>;
search(filter: string[], silent: boolean): IFuture<any>;
view(packageName: string, config: any): IFuture<any>;
search(filter: string[], config: any): IFuture<any>;
}

interface INpmInstallationManager {
getCacheRootPath(): string;
addToCache(packageName: string, version: string): IFuture<any>;
cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void>;
install(packageName: string, options?: INpmInstallOptions): IFuture<string>;
install(packageName: string, packageDir: string, options?: INpmInstallOptions): IFuture<any>;
getLatestVersion(packageName: string): IFuture<string>;
getNextVersion(packageName: string): IFuture<string>;
getLatestCompatibleVersion(packageName: string): IFuture<string>;
getCachedPackagePath(packageName: string, version: string): string;
}

interface INpmInstallOptions {
pathToSave?: string;
version?: string;
dependencyType?: string;
}

interface IDependencyData {
Expand Down Expand Up @@ -81,7 +74,7 @@ interface IOptions extends ICommonOptions {
frameworkName: string;
frameworkPath: string;
frameworkVersion: string;
ignoreScripts: boolean;
ignoreScripts: boolean; //npm flag
disableNpmInstall: boolean;
ipa: string;
keyStoreAlias: string;
Expand All @@ -95,7 +88,7 @@ interface IOptions extends ICommonOptions {
bundle: boolean;
platformTemplate: string;
port: Number;
production: boolean;
production: boolean; //npm flag
sdk: string;
symlink: boolean;
tnsModulesVersion: string;
Expand Down
14 changes: 2 additions & 12 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ interface IProjectDataService {
* Describes working with templates.
*/
interface IProjectTemplatesService {
/**
* Defines the path where unpacked default template can be found.
*/
defaultTemplatePath: IFuture<string>;

/**
* Prepares template for project creation.
* In case templateName is not provided, use defaultTemplatePath.
Expand All @@ -39,7 +34,7 @@ interface IProjectTemplatesService {
* @param {string} templateName The name of the template.
* @return {string} Path to the directory where extracted template can be found.
*/
prepareTemplate(templateName: string): IFuture<string>;
prepareTemplate(templateName: string, projectDir: string): IFuture<string>;
}

interface IPlatformProjectServiceBase {
Expand Down Expand Up @@ -77,12 +72,7 @@ interface IPlatformProjectService {
prepareProject(): IFuture<void>;
prepareAppResources(appResourcesDirectoryPath: string): IFuture<void>;
isPlatformPrepared(projectRoot: string): IFuture<boolean>;
canUpdatePlatform(currentVersion: string, newVersion: string): IFuture<boolean>;
/**
* Provides a platform specific update logic for the specified runtime versions.
* @return true in cases when the update procedure should continue.
*/
updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean, addPlatform?: Function, removePlatform?: (platforms: string[]) => IFuture<void>): IFuture<boolean>;
canUpdatePlatform(newInstalledModuleDir: string): IFuture<boolean>;
preparePluginNativeCode(pluginData: IPluginData, options?: any): IFuture<void>;
removePluginNativeCode(pluginData: IPluginData): IFuture<void>;
afterPrepareAllPlugins(): IFuture<void>;
Expand Down
172 changes: 77 additions & 95 deletions lib/node-package-manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Future = require("fibers/future");
import * as npm from "npm";
import * as path from "path";

interface INpmOpts {
config?: any;
Expand All @@ -8,35 +7,13 @@ interface INpmOpts {
}

export class NodePackageManager implements INodePackageManager {
constructor(private $childProcess: IChildProcess,
constructor(private $fs: IFileSystem,
private $hostInfo: IHostInfo,
private $errors: IErrors,
private $childProcess: IChildProcess,
private $logger: ILogger,
private $options: IOptions) { }

public getCache(): string {
return npm.cache;
}

public load(config?: any): IFuture<void> {
if (npm.config.loaded) {
let data = npm.config.sources.cli.data;
Object.keys(data).forEach(k => delete data[k]);
if (config) {
_.assign(data, config);
}
return Future.fromResult();
} else {
let future = new Future<void>();
npm.load(config, (err: Error) => {
if (err) {
future.throw(err);
} else {
future.return();
}
});
return future;
}
}

public install(packageName: string, pathToSave: string, config?: any): IFuture<any> {
return (() => {
if (this.$options.disableNpmInstall) {
Expand All @@ -47,100 +24,105 @@ export class NodePackageManager implements INodePackageManager {
config["ignore-scripts"] = true;
}

let jsonContentBefore = this.$fs.readJson(path.join(pathToSave, "package.json")).wait();
// let dependenciesBefore: Array<string> = [];
let dependenciesBefore = _.keys(jsonContentBefore.dependencies).concat(_.keys(jsonContentBefore.devDependencies));

let flags = this.getFlagsString(config, true);
let params = ["install"];
if(packageName !== pathToSave) {
params.push(packageName); //because npm install ${pwd} on mac tries to install itself as a dependency (windows and linux have no such issues)
}
params = params.concat(flags);
try {
return this.loadAndExecute("install", [pathToSave, packageName], { config: config }).wait();
this.$childProcess.spawnFromEvent(this.getNpmExecutableName(), params, "close", { cwd: pathToSave }).wait();
} catch (err) {
if (err.code === "EPEERINVALID") {
if (err.message && err.message.indexOf("EPEERINVALID") !== -1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In case npm install fails with EPEERINVALID error, the method will show it as warning and will skip all of the checks regarding jsonContentBefore and jsonContentAfter. Also the method will not return anything. Is this expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not, I'll fix it

// Not installed peer dependencies are treated by npm 2 as errors, but npm 3 treats them as warnings.
// We'll show them as warnings and let the user install them in case they are needed.
// The strucutre of the error object in such case is:
// { [Error: The package @angular/[email protected] does not satisfy its siblings' peerDependencies requirements!]
// code: 'EPEERINVALID',
// packageName: '@angular/core',
// packageVersion: '2.1.0-beta.0',
// peersDepending:
// { '@angular/[email protected]': '2.1.0-beta.0',
// '@angular/[email protected]': '2.1.0-beta.0',
// '@angular/[email protected]': '2.1.0-beta.0',
// '@angular/[email protected]': '2.1.0-beta.0',
// '@angular/[email protected]': '2.1.0-beta.0',
// '@angular/[email protected]': '2.1.0-beta.0',
// '@angular/[email protected]': '2.1.0-beta.0',
// '@angular/[email protected]': '2.1.0-beta.0',
// '@ngrx/[email protected]': '^2.0.0',
// '@ngrx/[email protected]': '^2.0.0',
// '[email protected]': '~2.0.0' } }
this.$logger.warn(err.message);
this.$logger.trace("Required peerDependencies are: ", err.peersDepending);
} else {
// All other errors should be handled by the caller code.
throw err;
}
}

let jsonContentAfter = this.$fs.readJson(path.join(pathToSave, "package.json")).wait();
let dependenciesAfter = _.keys(jsonContentAfter.dependencies).concat(_.keys(jsonContentAfter.devDependencies));

/** This diff is done in case the installed pakcage is a URL address, a path to local directory or a .tgz file
* in these cases we don't have the package name and we can't rely on "npm install --json"" option
* to get the project name because we have to parse the output from the stdout and we have no controll over it (so other messages may be mangled in stdout)
* The solution is to compare package.json project dependencies before and after install and get the name of the installed package,
* even if it's installed through local path or URL. If command installes more than one package, only the package originally installed is returned.
*/
let dependencyDiff = _(jsonContentAfter.dependencies)
.omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.dependencies && jsonContentBefore.dependencies[key] && jsonContentBefore.dependencies[key] === val)
.keys()
.value();

let devDependencyDiff = _(jsonContentAfter.devDependencies)
.omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.devDependencies && jsonContentBefore.devDependencies[key] && jsonContentBefore.devDependencies[key] === val)
.keys()
.value();

let diff = dependencyDiff.concat(devDependencyDiff);

if(diff.length <= 0 && dependenciesBefore.length === dependenciesAfter.length && packageName !== pathToSave) {
this.$errors.failWithoutHelp(`The plugin ${packageName} is already installed`);
}
if(diff.length <= 0 && dependenciesBefore.length !== dependenciesAfter.length) {
this.$errors.failWithoutHelp(`Couldn't install package correctly`);
}

return diff;
}).future<any>()();
}

public uninstall(packageName: string, config?: any, path?: string): IFuture<any> {
return this.loadAndExecute("uninstall", [[packageName]], { config, path });
let flags = this.getFlagsString(config, false);
return this.$childProcess.exec(`npm uninstall ${packageName} ${flags}`, { cwd: path });
}

public search(filter: string[], silent: boolean): IFuture<any> {
let args = (<any[]>([filter] || [])).concat(silent);
return this.loadAndExecute("search", args);
public search(filter: string[], config: any): IFuture<any> {
let args = (<any[]>([filter] || [])).concat(config.silent);
return this.$childProcess.exec(`npm search ${args.join(" ")}`);
}

public cache(packageName: string, version: string, config?: any): IFuture<IDependencyData> {
// function cache (pkg, ver, where, scrub, cb)
return this.loadAndExecute("cache", [packageName, version, undefined, false], { subCommandName: "add", config: config });
}

public cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void> {
// function unpack (pkg, ver, unpackTarget, dMode, fMode, uid, gid, cb)
return this.loadAndExecute("cache", [packageName, version, unpackTarget, null, null, null, null], { subCommandName: "unpack" });
public view(packageName: string, config: any): IFuture<any> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This method should return future, you should wrap the code in:

return (() => {

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

return (() => {
let flags = this.getFlagsString(config, false);
let viewResult = this.$childProcess.exec(`npm view ${packageName} ${flags}`).wait();
return JSON.parse(viewResult);
}).future<any>()();
}

public view(packageName: string, propertyName: string): IFuture<any> {
return this.loadAndExecute("view", [[packageName, propertyName], [false]]);
}
private getNpmExecutableName(): string {
let npmExecutableName = "npm";

public executeNpmCommand(npmCommandName: string, currentWorkingDirectory: string): IFuture<any> {
return this.$childProcess.exec(npmCommandName, { cwd: currentWorkingDirectory });
}
if (this.$hostInfo.isWindows) {
npmExecutableName += ".cmd";
}

private loadAndExecute(commandName: string, args: any[], opts?: INpmOpts): IFuture<any> {
return (() => {
opts = opts || {};
this.load(opts.config).wait();
return this.executeCore(commandName, args, opts).wait();
}).future<any>()();
return npmExecutableName;
}

private executeCore(commandName: string, args: any[], opts?: INpmOpts): IFuture<any> {
let future = new Future<any>();
let oldNpmPath: string = undefined;
let callback = (err: Error, data: any) => {
if (oldNpmPath) {
(<any>npm).prefix = oldNpmPath;
}

if (err) {
future.throw(err);
} else {
future.return(data);
private getFlagsString(config: any, asArray: boolean) : any{
let array:Array<string> = [];
for(let flag in config) {
if(config[flag]) {
if(flag==="dist-tags" || flag==="versions") {
array.push(` ${flag}`);
continue;
}
array.push(`--${flag}`);
}
};
args.push(callback);

if (opts && opts.path) {
oldNpmPath = npm.prefix;
(<any>npm).prefix = opts.path;
}
if(asArray) {
return array;
}

let subCommandName: string = opts.subCommandName;
let command = subCommandName ? npm.commands[commandName][subCommandName] : npm.commands[commandName];
command.apply(this, args);

return future;
return array.join(" ");
}
}
$injector.register("npm", NodePackageManager);
Loading