Skip to content

Commit

Permalink
fix: cordova-fetch with npm@7 (#91)
Browse files Browse the repository at this point in the history
* fix: determine installed package's name on npm 7

* test: make expectations work for npm 5 to 7

This addresses the following changes in behavior.

Saved GitHub URL format in package.json:
- npm 6: git+https://github.com/apache/cordova-android.git#4.1.x
- npm 7: github:apache/cordova-android#4.1.x

Empty devDependencies format in package.json:
- npm 6: `{}`
- npm 7: `undefined`

* ci: add node@15 w/ npm@7

* ci: use npm7 version that fixed npm/cli#2309
  • Loading branch information
raphinesse committed Jan 26, 2021
1 parent 1d96eae commit d001b88
Show file tree
Hide file tree
Showing 6 changed files with 1,211 additions and 140 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [10.x, 12.x, 14.x]
node-version: [10.x, 12.x, 14.x, 15.x]
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
Expand All @@ -36,6 +36,10 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

- name: Update to npm@^7.2 on Node 15
if: matrix.node-version == '15.x'
run: npm i -g npm@^7.2

- name: Environment Information
run: |
node --version
Expand Down
56 changes: 22 additions & 34 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const path = require('path');
const fs = require('fs-extra');
const { CordovaError, events, superspawn } = require('cordova-common');
const npa = require('npm-package-arg');
const pacote = require('pacote');
const semver = require('semver');

// pify's multiArgs unfortunately causes resolve to wrap errors in an Array.
Expand All @@ -47,11 +48,27 @@ module.exports = async function (target, dest, opts = {}) {
// Create dest if it doesn't exist yet
fs.ensureDirSync(dest);

try {
return await pathToInstalledPackage(target, dest);
} catch {
return await installPackage(target, dest, opts);
// First try to determine the name from the spec using npa. This is very cheap.
let { name, rawSpec } = npa(target, dest);

if (!name) {
// If that fails, get out the big guns and fetch a full manifest using pacote.
({ name } = await pacote.manifest(target, { where: dest }));
} else if (semver.validRange(rawSpec)) {
// If the provided spec is a name and a version range, we look for
// an installed package that satisfies the requested version range
try {
const [pkgPath, { version }] = await resolvePathToPackage(name, dest);
if (semver.satisfies(version, rawSpec)) return pkgPath;
} catch (err) {
// Ignore MODULE_NOT_FOUND errors from resolvePathToPackage
if (err.code !== 'MODULE_NOT_FOUND') throw err;
}
}

await installPackage(target, dest, opts);

return (await resolvePathToPackage(name, dest))[0];
} catch (err) {
throw new CordovaError(err);
}
Expand All @@ -67,11 +84,7 @@ async function installPackage (target, dest, opts) {
// Run `npm` to install requested package
const args = npmArgs(target, opts);
events.emit('verbose', `fetch: Installing ${target} to ${dest}`);
const npmInstallOutput = await superspawn.spawn('npm', args, { cwd: dest });

// Resolve path to installed package
const spec = await getTargetPackageSpecFromNpmInstallOutput(npmInstallOutput);
return pathToInstalledPackage(spec, dest);
await superspawn.spawn('npm', args, { cwd: dest });
}

function npmArgs (target, opts) {
Expand All @@ -88,31 +101,6 @@ function npmArgs (target, opts) {
return args;
}

function getTargetPackageSpecFromNpmInstallOutput (npmInstallOutput) {
const packageInfoLine = npmInstallOutput.split('\n')
.find(line => line.startsWith('+ '));
if (!packageInfoLine) {
throw new CordovaError(`Could not determine package name from output:\n${npmInstallOutput}`);
}
return packageInfoLine.slice(2);
}

// Resolves to installation path of package defined by spec if the right version
// is installed, rejects otherwise.
async function pathToInstalledPackage (spec, dest) {
const { name, rawSpec } = npa(spec, dest);
if (!name) {
throw new CordovaError(`Cannot determine package name from spec ${spec}`);
}

const [pkgPath, { version }] = await resolvePathToPackage(name, dest);
if (!semver.satisfies(version, rawSpec)) {
throw new CordovaError(`Installed package ${name}@${version} does not satisfy ${name}@${rawSpec}`);
}

return pkgPath;
}

// Resolves to installation path and package.json of package `name` starting
// from `basedir`
async function resolvePathToPackage (name, basedir) {
Expand Down
Loading

0 comments on commit d001b88

Please sign in to comment.