Skip to content
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

fix: cordova-fetch with npm@7 #91

Merged
merged 4 commits into from
Jan 26, 2021
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
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