@netlify/zip-it-and-ship-it
Version:
74 lines (73 loc) • 3.71 kB
JavaScript
import semver from 'semver';
// Apply the Node.js module logic recursively on its own dependencies, using
// the `package.json` `dependencies`, `peerDependencies` and
// `optionalDependencies` keys
export const getNestedDependencies = function ({ dependencies = {}, peerDependencies = {}, optionalDependencies = {}, }) {
return [
...Object.keys(dependencies),
...Object.keys(peerDependencies).filter(shouldIncludePeerDependency),
...Object.keys(optionalDependencies),
];
};
// Workaround for https://github.com/netlify/zip-it-and-ship-it/issues/73
// TODO: remove this after adding proper modules exclusion as outlined in
// https://github.com/netlify/zip-it-and-ship-it/issues/68
const shouldIncludePeerDependency = function (name) {
return !EXCLUDED_PEER_DEPENDENCIES.has(name);
};
const EXCLUDED_PEER_DEPENDENCIES = new Set(['@prisma/cli', 'prisma2', 'prisma']);
// Modules can be required conditionally (inside an `if` or `try`/`catch` block).
// When a `require()` statement is found but the module is not found, it is
// possible that that block either always evaluates to:
// - `false`: in which case, we should not bundle the dependency
// - `true`: in which case, we should report the dependency as missing
// Those conditional modules might be:
// - present in the `package.json` `dependencies`
// - present in the `package.json` `optionalDependencies`
// - present in the `package.json` `peerDependencies`
// - not present in the `package.json`, if the module author wants its users
// to explicitly install it as an optional dependency.
// The current implementation:
// - when parsing `require()` statements inside function files, always consider
// conditional modules to be included, i.e. report them if not found.
// This is because our current parsing logic does not know whether a
// `require()` is conditional or not.
// - when parsing module dependencies, ignore `require()` statements if not
// present in the `package.json` `*dependencies`. I.e. user must manually
// install them if the module is used.
// `optionalDependencies`:
// - are not reported when missing
// - are included in module dependencies
export const handleModuleNotFound = function ({ error, moduleName, packageJson, }) {
if (error.code === 'MODULE_NOT_FOUND' &&
(isOptionalModule(moduleName, packageJson) || isExternalCrittersModule(moduleName, packageJson))) {
return [];
}
throw error;
};
const isOptionalModule = function (moduleName, { optionalDependencies = {}, peerDependenciesMeta = {}, peerDependencies = {} }) {
return (optionalDependencies[moduleName] !== undefined ||
(peerDependenciesMeta[moduleName] &&
peerDependenciesMeta[moduleName].optional &&
peerDependencies[moduleName] !== undefined));
};
const MIN_NEXT_VERSION = '10.0.4';
const satisfiesRange = (range) => Boolean(semver.validRange(range)) &&
(semver.satisfies(MIN_NEXT_VERSION, range) || semver.ltr(MIN_NEXT_VERSION, range));
// 'critters' is used only in Next.js >= 10.0.4 when enabling an experimental option and has to be installed manually
// we ignore it if it's missing
const isExternalCrittersModule = function (moduleName, { dependencies = {}, devDependencies = {} }) {
if (moduleName !== 'critters') {
return false;
}
const nextVersion = dependencies.next || devDependencies.next;
if (nextVersion === 'latest') {
return true;
}
// can the declared Next.js version resolve to >=10.0.4 ?
// test exact versions
if (semver.valid(nextVersion)) {
return semver.gte(nextVersion, MIN_NEXT_VERSION);
}
return satisfiesRange(nextVersion);
};