Skip to content

Commit

Permalink
fix: make npa error more clear (#352)
Browse files Browse the repository at this point in the history
* fix: make npa error more clear

* test: fix unit test

* fix: add deps path

* test: fix unit test

* fix: unit test

* fix: refactor nested, add install context
  • Loading branch information
gemwuu committed Aug 12, 2021
1 parent c85e54a commit 6166be2
Show file tree
Hide file tree
Showing 17 changed files with 269 additions and 47 deletions.
14 changes: 9 additions & 5 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
'use strict';

const debug = require('debug')('npminstall:bin:install');
const npa = require('npm-package-arg');
const npa = require('../lib/npa');
const chalk = require('chalk');
const path = require('path');
const util = require('util');
Expand All @@ -20,6 +20,7 @@ const {
REMOTE_TYPES,
ALIAS_TYPES,
} = require('../lib/npa_types');
const Context = require('../lib/context');

const orignalArgv = process.argv.slice(2);
const argv = parseArgs(orignalArgv, {
Expand Down Expand Up @@ -144,11 +145,14 @@ if (process.env.NPMINSTALL_BY_UPDATE) {
argv._ = [];
}

const context = new Context();
for (const name of argv._) {

context.nested.update([ name ]);
const [
aliasPackageName,
] = parsePackageName(name);
const p = npa(name, argv.root);
] = parsePackageName(name, context.nested);
const p = npa(name, { where: argv.root, nested: context.nested });
pkgs.push({
name: p.name,
// `mozilla/nunjucks#0f8b21b8df7e8e852b2e1889388653b7075f0d09` should be rawSpec
Expand Down Expand Up @@ -316,7 +320,7 @@ debug('argv: %j, env: %j', argv, env);
const meta = utils.getGlobalInstallMeta(argv.prefix);
config.targetDir = meta.targetDir;
config.binDir = meta.binDir;
await installGlobal(config);
await installGlobal(config, context);
} else {
if (pkgs.length === 0) {
if (config.production) {
Expand Down Expand Up @@ -384,7 +388,7 @@ debug('argv: %j, env: %j', argv, env);
}
}
}
await installLocal(config);
await installLocal(config, context);
if (pkgs.length > 0) {
// support --save, --save-dev, --save-optional, --save-client, --save-build and --save-isomorphic
const map = {
Expand Down
6 changes: 3 additions & 3 deletions lib/alias.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';

const npa = require('npm-package-arg');
const npa = require('./npa');

exports.parsePackageName = pkgName => {
const p = npa(pkgName);
exports.parsePackageName = (pkgName, nested) => {
const p = npa(pkgName, { nested });
const {
name,
type,
Expand Down
11 changes: 11 additions & 0 deletions lib/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

const Nested = require('./nested');

class Context {
constructor() {
this.nested = new Nested([]);
}
}

module.exports = Context;
20 changes: 12 additions & 8 deletions lib/dependencies.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'use strict';

const npa = require('npm-package-arg');
const npa = require('./npa');
const {
parsePackageName,
} = require('./alias');

module.exports = function dependencies(pkg, options) {
module.exports = function dependencies(pkg, options, nested) {
const all = {};
const prod = {};
const client = {};
Expand Down Expand Up @@ -57,29 +57,30 @@ module.exports = function dependencies(pkg, options) {
},

get all() {
return mergeOptional(all, optionalDependencies);
return mergeOptional(all, optionalDependencies, nested);
},

get prodMap() {
return prod;
},

get prod() {
return mergeOptional(prod, optionalDependencies);
return mergeOptional(prod, optionalDependencies, nested);
},

get clientMap() {
return client;
},

get client() {
return mergeOptional(client, optionalDependencies);
return mergeOptional(client, optionalDependencies, nested);
},
};
};

function mergeOptional(deps, optional) {
function mergeOptional(deps, optional, nested) {
const results = [];

for (const name in deps) {
const version = deps[name];
const pkg = {
Expand All @@ -88,16 +89,19 @@ function mergeOptional(deps, optional) {
optional: optional.hasOwnProperty(name),
};

const raw = `${name}@${version}`;
nested.update([ raw ]);

const [
aliasPackageName,
realPackageName,
] = parsePackageName(`${name}@${version}`);
] = parsePackageName(raw, nested);

if (aliasPackageName) {
const {
name,
fetchSpec,
} = npa(realPackageName);
} = npa(realPackageName, { nested });
pkg.alias = aliasPackageName;
pkg.name = name;
pkg.version = fetchSpec;
Expand Down
9 changes: 5 additions & 4 deletions lib/global_install.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ const path = require('path');
const utility = require('utility');
const fs = require('mz/fs');
const chalk = require('chalk');
const npa = require('npm-package-arg');
const npa = require('./npa');
const installLocal = require('./local_install');
const utils = require('./utils');
const download = require('./download');
const bin = require('./bin');
const formatInstallOptions = require('./format_install_options');

module.exports = async options => {
module.exports = async (options, context) => {
const pkgs = options.pkgs || [];
const globalTargetDir = options.targetDir;
const globalBinDir = options.binDir;
Expand All @@ -24,6 +24,7 @@ module.exports = async options => {
options.binDir = null;

const opts = Object.assign({}, options);
context.nested.update(pkgs.map(pkg => `${pkg.name}@${pkg.version}`));
for (const pkg of pkgs) {
const {
name: pkgName,
Expand All @@ -49,7 +50,7 @@ module.exports = async options => {

const logName = alias ? `${name}(${pkgName})` : `${pkgName}`;
console.info(chalk.gray(`Downloading ${logName} to ${tmpDir}`));
const p = npa(pkg.name ? `${pkg.name}@${pkg.version}` : pkg.version, options.root);
const p = npa(pkg.name ? `${pkg.name}@${pkg.version}` : pkg.version, { where: options.root, nested: context.nested });
const result = await download(p, installOptions);

// read the real package.json and get the pakcage's name
Expand All @@ -68,7 +69,7 @@ module.exports = async options => {
production: true,
});

await installLocal(pkgOptions);
await installLocal(pkgOptions, context);

// handle bin link
pkgOptions.binDir = globalBinDir;
Expand Down
30 changes: 17 additions & 13 deletions lib/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const debug = require('debug')('npminstall:install');
const path = require('path');
const fs = require('mz/fs');
const npa = require('npm-package-arg');
const npa = require('./npa');
const chalk = require('chalk');
const semver = require('semver');
const pMap = require('p-map');
Expand All @@ -21,9 +21,9 @@ const {

module.exports = install;

async function install(parentDir, pkg, ancestors, options) {
async function install(parentDir, pkg, ancestors, options, context) {
try {
return await _install(parentDir, pkg, ancestors, options);
return await _install(parentDir, pkg, ancestors, options, context);
} catch (err) {
if (pkg.optional) {
let message = err.stack;
Expand All @@ -38,7 +38,7 @@ async function install(parentDir, pkg, ancestors, options) {
}


async function _install(parentDir, pkg, ancestors, options) {
async function _install(parentDir, pkg, ancestors, options, context) {
const rootPkgDependencies = options.production ? options.rootPkgDependencies.prodMap : options.rootPkgDependencies.allMap;
const ancestorsWithRoot = [{ dependencies: rootPkgDependencies, name: 'root package.json' }].concat(ancestors);

Expand All @@ -56,15 +56,15 @@ async function _install(parentDir, pkg, ancestors, options) {
if (options.spinner) {
options.spinner.text = `[${options.progresses.finishedInstallTasks}/${options.progresses.installTasks}] Installing ${pkg.name}@${pkg.version}`;
}
let p = npa(pkg.name ? `${pkg.name}@${pkg.version}` : pkg.version, options.root);
let p = npa(pkg.name ? `${pkg.name}@${pkg.version}` : pkg.version, { where: options.root, nested: context.nested });
const displayName = p.displayName = utils.getDisplayName(pkg, ancestors);

if (options.registryOnly && REGISTRY_TYPES.includes(p.type)) {
throw new Error(`Only allow install package from registry, but "${displayName}" is ${p.type}`);
}

if (options.flatten || forceFlatten(pkg)) {
const res = await matchAncestorDependencies(p, ancestorsWithRoot, options);
const res = await matchAncestorDependencies(p, ancestorsWithRoot, options, context);
if (res) {
// output anti semver info if not the same version
if (res.childResolved !== res.ancestorResolved) {
Expand All @@ -81,7 +81,7 @@ async function _install(parentDir, pkg, ancestors, options) {
]);
}
// use ancestor's spec
p = npa(`${res.name}@${res.ancestorSpec}`, options.root);
p = npa(`${res.name}@${res.ancestorSpec}`, { where: options.root, nested: context.nested });
pkg = Object.assign({}, pkg, { version: res.ancestorSpec });
}
}
Expand Down Expand Up @@ -228,7 +228,7 @@ async function _install(parentDir, pkg, ancestors, options) {
const bundledDependencies = await getBundleDependencies(realPkg, realPkgDir);
await Promise.all(bundledDependencies.map(name => bundleBin(name, realPkgDir, options)));

const deps = dependencies(realPkg, options);
const deps = dependencies(realPkg, options, context.nested);
const pkgs = deps.prod;
const pkgMaps = deps.prodMap;

Expand All @@ -240,14 +240,16 @@ async function _install(parentDir, pkg, ancestors, options) {
const reverseAncestors = ancestorsWithRoot.slice().reverse();
for (const name in peerDependencies) {
const version = peerDependencies[name];
const raw = `${name}@${version}`;
context.nested.update([ raw ], p.raw);
// don't need to check if peer dependency is in dependencies
if (pkgMaps[name]) continue;

// if we can get any matched version from ancestor
// install it as dependency
const childPkg = npa(`${name}@${version}`, options.root);
const childPkg = npa(raw, { where: options.root, nested: context.nested });
// check in reverse
const res = await matchAncestorDependencies(childPkg, reverseAncestors, options);
const res = await matchAncestorDependencies(childPkg, reverseAncestors, options, context);
if (res) {
pkgs.push({ name, version: res.ancestorSpec, peer: true });
} else {
Expand All @@ -266,12 +268,14 @@ async function _install(parentDir, pkg, ancestors, options) {
if (pkgs.length > 0) {
await utils.mkdirp(nodeModulesDir);
const needPkgs = pkgs.filter(childPkg => !bundledDependencies.includes(childPkg.name));
context.nested.update(needPkgs.map(pkg => `${pkg.name}@${pkg.version}`), `${realPkg.name}@${realPkg.version}`);

const mapper = async childPkg => {
await install(realPkgDir, childPkg, ancestors.concat({
displayName: `${realPkg.name}@${realPkg.version}`,
name: realPkg.name,
dependencies: deps.prodMap,
}), options);
}), options, context);
};
await pMap(needPkgs, mapper, 10);
}
Expand Down Expand Up @@ -320,14 +324,14 @@ async function bundleBin(name, parentDir, options) {
await bin(parentDir, pkg, pkgDir, options);
}

async function matchAncestorDependencies(childPkg, ancestors, options) {
async function matchAncestorDependencies(childPkg, ancestors, options, context) {
// only need check npm types
if (!REGISTRY_TYPES.includes(childPkg.type)) return;

for (const ancestor of ancestors) {
const ancestorVersion = ancestor.dependencies[childPkg.name];
if (!ancestorVersion) continue;
const ancestorPkg = npa(`${childPkg.name}@${ancestorVersion}`, options.root);
const ancestorPkg = npa(`${childPkg.name}@${ancestorVersion}`, { where: options.root, nested: context.nested });
if (!REGISTRY_TYPES.includes(ancestorPkg.type)) continue;
ancestorPkg.parent = ancestor.name;
const satisfied = await satisfiesRange(childPkg, ancestorPkg, options);
Expand Down
16 changes: 9 additions & 7 deletions lib/local_install.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ const bin = require('./bin');
* - {Boolean} [trace] - show memory and cpu usages traces of installation
* - {Boolean} [flatten] - flatten dependencies by matching ancestors' dependencies
* - {Boolean} [disableDedupe] - disable dedupe mode, will back to npm@2 node_modules tree
* @param {Object} context - install context
*/
module.exports = async options => {
module.exports = async (options, context) => {
options = formatInstallOptions(options);
options.spinner && options.spinner.start();
let traceTimer;
Expand Down Expand Up @@ -88,7 +89,7 @@ module.exports = async options => {
}

try {
await _install(options);
await _install(options, context);
} catch (err) {
if (options.spinner) {
options.spinner.fail(`Install fail! ${err}`);
Expand All @@ -106,12 +107,12 @@ module.exports = async options => {

exports.runPostInstallTasks = runPostInstallTasks;

async function _install(options) {
async function _install(options, context) {
const rootPkgFile = path.join(options.root, 'package.json');
const rootPkg = await utils.readJSON(rootPkgFile);
const displayName = `${rootPkg.name}@${rootPkg.version}`;
let pkgs = options.pkgs;
const rootPkgDependencies = dependencies(rootPkg, options);
const rootPkgDependencies = dependencies(rootPkg, options, context.nested);
options.rootPkgDependencies = rootPkgDependencies;
options.resolution = createResolution(rootPkg, options);
if (pkgs.length === 0) {
Expand All @@ -136,14 +137,15 @@ async function _install(options) {

if (options.installRoot) await preinstall(rootPkg, options.root, displayName, options);

context.nested.update(pkgs.map(pkg => `${pkg.name}@${pkg.version}`), rootPkg.name && rootPkg.version ? displayName : 'root');
const nodeModulesDir = path.join(options.targetDir, 'node_modules');
await utils.mkdirp(nodeModulesDir);
const rootPkgsMap = new Map();
const mapper = async childPkg => {
childPkg.name = childPkg.name || '';
rootPkgsMap.set(childPkg.name, true);
options.progresses.installTasks++;
await installOne(options.targetDir, childPkg, options);
await installOne(options.targetDir, childPkg, options, context);
};

await pMap(pkgs, mapper, 10);
Expand Down Expand Up @@ -191,7 +193,7 @@ async function _install(options) {
finishInstall(options);
}

async function installOne(parentDir, childPkg, options) {
async function installOne(parentDir, childPkg, options, context) {
if (!(await needInstall(parentDir, childPkg, options))) {
options.progresses.finishedInstallTasks++;
options.console.info(
Expand All @@ -204,7 +206,7 @@ async function installOne(parentDir, childPkg, options) {
return;
}

const res = await install(parentDir, childPkg, [], options);
const res = await install(parentDir, childPkg, [], options, context);
options.progresses.finishedInstallTasks++;
if (res) {
options.console.info(
Expand Down

0 comments on commit 6166be2

Please sign in to comment.