serverless-webpack
Version:
Serverless plugin to bundle your javascript with Webpack
167 lines (140 loc) • 4.91 kB
JavaScript
;
/**
* NPM packager.
*/
const _ = require('lodash');
const BbPromise = require('bluebird');
const Utils = require('../utils');
const { join } = require('path');
const fse = require('fs-extra');
const fs = require('fs');
class NPM {
// eslint-disable-next-line lodash/prefer-constant
static get lockfileName() {
return 'package-lock.json';
}
// eslint-disable-next-line lodash/prefer-constant
static get mustCopyModules() {
return true;
}
static copyPackageSectionNames(packagerOptions) {
const options = packagerOptions || {};
return options.copyPackageSectionNames || [];
}
static getPackagerVersion(cwd) {
const command = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
const args = ['-v'];
return Utils.spawnProcess(command, args, { cwd })
.catch(err => {
return BbPromise.resolve({ stdout: err.stdout });
})
.then(processOutput => processOutput.stdout);
}
static getProdDependencies(cwd, depth, packagerOptions) {
// Try to use NPM lockfile v2 when possible
const options = packagerOptions || {};
const lockPath = join(cwd, options.lockFile || NPM.lockfileName);
if (fse.pathExistsSync(lockPath)) {
const lock = Utils.safeJsonParse(fs.readFileSync(lockPath));
if (lock.lockfileVersion === 2) {
return BbPromise.resolve(lock);
}
}
// Get first level dependency graph
const command = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
const args = [
'ls',
'-prod', // Only prod dependencies
'-json',
`-depth=${depth || 1}`
];
const ignoredNpmErrors = [
{ npmError: 'code ELSPROBLEMS', log: false }, // npm >= 7
{ npmError: 'extraneous', log: false },
{ npmError: 'missing', log: false },
{ npmError: 'peer dep missing', log: true }
];
return Utils.spawnProcess(command, args, {
cwd: cwd
})
.catch(err => {
if (err instanceof Utils.SpawnError) {
// Only exit with an error if we have critical npm errors for 2nd level inside
// ignoring any extra output from npm >= 7
const lines = _.split(err.stderr, '\n');
const errors = _.takeWhile(lines, line => line !== '{');
const failed = _.reduce(
errors,
(failed, error) => {
if (failed) {
return true;
}
return (
!_.isEmpty(error) &&
!_.some(ignoredNpmErrors, ignoredError => _.startsWith(error, `npm ERR! ${ignoredError.npmError}`))
);
},
false
);
if (!failed && !_.isEmpty(err.stdout)) {
return BbPromise.resolve({ stdout: err.stdout });
}
if (process.env.SLS_DEBUG) {
console.error(`DEBUG: ${err.stdout}\nSTDERR: ${err.stderr}`);
}
}
return BbPromise.reject(err);
})
.then(processOutput => processOutput.stdout)
.then(depJson => BbPromise.try(() => JSON.parse(depJson)));
}
static _rebaseFileReferences(pathToPackageRoot, moduleVersion) {
if (/^file:[^/]{2}/.test(moduleVersion)) {
const filePath = _.replace(moduleVersion, /^file:/, '');
return _.replace(`file:${pathToPackageRoot}/${filePath}`, /\\/g, '/');
}
return moduleVersion;
}
/**
* We should not be modifying 'package-lock.json'
* because this file should be treated as internal to npm.
*
* Rebase package-lock is a temporary workaround and must be
* removed as soon as https://github.com/npm/npm/issues/19183 gets fixed.
*/
static rebaseLockfile(pathToPackageRoot, lockfile) {
if (lockfile.version) {
lockfile.version = NPM._rebaseFileReferences(pathToPackageRoot, lockfile.version);
}
if (lockfile.dependencies) {
_.forIn(lockfile.dependencies, lockedDependency => {
NPM.rebaseLockfile(pathToPackageRoot, lockedDependency);
});
}
return lockfile;
}
static install(cwd, packagerOptions) {
if (packagerOptions.noInstall) {
return BbPromise.resolve();
}
const command = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
const args = ['install'];
if (packagerOptions.ignoreScripts) {
args.push('--ignore-scripts');
}
return Utils.spawnProcess(command, args, { cwd }).return();
}
static prune(cwd) {
const command = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
const args = ['prune'];
return Utils.spawnProcess(command, args, { cwd }).return();
}
static runScripts(cwd, scriptNames) {
const command = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
return BbPromise.mapSeries(scriptNames, scriptName => {
const args = ['run', scriptName];
return Utils.spawnProcess(command, args, { cwd });
}).return();
}
}
module.exports = NPM;