|
| 1 | +const semver = require('semver') |
| 2 | +const { relative, join } = require('path') |
| 3 | +const Arborist = require('@npmcli/arborist') |
| 4 | + |
| 5 | +const run = async ({ root, path, pkg, config: { omitEngines = [] } }) => { |
| 6 | + const pkgPath = join(relative(root, path), 'package.json') |
| 7 | + const arb = new Arborist({ path }) |
| 8 | + const tree = await arb.loadActual({ forceActual: true }) |
| 9 | + |
| 10 | + const engines = pkg.engines.node |
| 11 | + const deps = await tree.querySelectorAll(`#${pkg.name} > .prod:attr(engines, [node])`) |
| 12 | + |
| 13 | + const invalid = [] |
| 14 | + for (const dep of deps) { |
| 15 | + if (omitEngines.includes(dep.name)) { |
| 16 | + continue |
| 17 | + } |
| 18 | + |
| 19 | + const depEngines = dep.target.package.engines.node |
| 20 | + if (!semver.subset(engines, depEngines)) { |
| 21 | + invalid.push({ |
| 22 | + name: `${dep.name}@${dep.version}`, |
| 23 | + location: dep.location, |
| 24 | + engines: depEngines, |
| 25 | + }) |
| 26 | + } |
| 27 | + } |
| 28 | + |
| 29 | + if (invalid.length) { |
| 30 | + const title = `The following production dependencies are not compatible with ` + |
| 31 | + `\`engines.node: ${engines}\` found in \`${pkgPath}\`:` |
| 32 | + return { |
| 33 | + title, |
| 34 | + body: invalid.map((dep) => [ |
| 35 | + `${dep.name}:`, |
| 36 | + ` engines.node: ${dep.engines}`, |
| 37 | + ` location: ${dep.location}`, |
| 38 | + ].join('\n')).join('\n'), |
| 39 | + solution: 'Remove them or move them to devDependencies.', |
| 40 | + } |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +module.exports = { |
| 45 | + run, |
| 46 | + when: ({ pkg, config: c }) => c.applyModule && pkg.engines?.node, |
| 47 | + name: 'check-engines', |
| 48 | +} |
0 commit comments