Skip to content

Commit 3d0d00b

Browse files
committed
1 parent 11b7d04 commit 3d0d00b

File tree

3 files changed

+130
-7
lines changed

3 files changed

+130
-7
lines changed

lib/index.js

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,81 @@
1-
const semver = require('semver')
1+
const process = require('node:process')
2+
const satisfies = require('semver/functions/satisfies')
3+
const validRange = require('semver/ranges/valid')
24

3-
const checkEngine = (target, npmVer, nodeVer, force = false) => {
5+
/*
6+
7+
interface DevEngines {
8+
cpu?: DevEngineDependency | DevEngineDependency[];
9+
libc?: DevEngineDependency | DevEngineDependency[];
10+
os?: DevEngineDependency | DevEngineDependency[];
11+
packageManager?: DevEngineDependency | DevEngineDependency[];
12+
runtime?: DevEngineDependency | DevEngineDependency[];
13+
}
14+
15+
interface DevEngineDependency {
16+
name: string;
17+
version?: string;
18+
onFail?: 'ignore' | 'warn' | 'error' | 'download';
19+
download?: {
20+
url: string;
21+
algorithm?: string;
22+
digest?: string;
23+
}
24+
}
25+
26+
*/
27+
28+
const envNames = ['packageManager', 'runtime', 'cpu', 'libc', 'os']
29+
30+
// Returns an object with the last failing entry of any given wanted entry, null if no failures
31+
function checkDev (wanted = {}, current = {}, opts = {}) {
32+
const failures = {}
33+
for (const env of envNames) {
34+
let wantedEnv = wanted[env]
35+
if (wantedEnv) {
36+
const currentEnv = current[env]
37+
if (!Array.isArray(wantedEnv)) {
38+
wantedEnv = [wantedEnv]
39+
}
40+
// In case of failure we return the last entry to fail so we walk backwards and return the first failure
41+
for (let i = wantedEnv.length - 1; i > -1; i--) {
42+
const w = wantedEnv[i]
43+
if (!currentEnv) {
44+
failures[env] = w
45+
break
46+
}
47+
if (w.name !== currentEnv.name) {
48+
failures[env] = w
49+
break
50+
}
51+
if (validRange(w.version)) {
52+
if (!satisfies(currentEnv.version, w.version, opts.semver)) {
53+
failures[env] = w
54+
break
55+
}
56+
} else if (currentEnv.version !== w.version) {
57+
failures[env] = w
58+
break
59+
}
60+
}
61+
}
62+
}
63+
if (Object.keys(failures).length) {
64+
return failures
65+
}
66+
return null
67+
}
68+
69+
function checkEngine (target, npmVer, nodeVer, force = false) {
470
const nodev = force ? null : nodeVer
571
const eng = target.engines
672
const opt = { includePrerelease: true }
773
if (!eng) {
874
return
975
}
1076

11-
const nodeFail = nodev && eng.node && !semver.satisfies(nodev, eng.node, opt)
12-
const npmFail = npmVer && eng.npm && !semver.satisfies(npmVer, eng.npm, opt)
77+
const nodeFail = nodev && eng.node && !satisfies(nodev, eng.node, opt)
78+
const npmFail = npmVer && eng.npm && !satisfies(npmVer, eng.npm, opt)
1379
if (nodeFail || npmFail) {
1480
throw Object.assign(new Error('Unsupported engine'), {
1581
pkgid: target._id,
@@ -20,9 +86,11 @@ const checkEngine = (target, npmVer, nodeVer, force = false) => {
2086
}
2187
}
2288

23-
const isMusl = (file) => file.includes('libc.musl-') || file.includes('ld-musl-')
89+
function isMusl (file) {
90+
return file.includes('libc.musl-') || file.includes('ld-musl-')
91+
}
2492

25-
const checkPlatform = (target, force = false, environment = {}) => {
93+
function checkPlatform (target, force = false, environment = {}) {
2694
if (force) {
2795
return
2896
}
@@ -69,7 +137,7 @@ const checkPlatform = (target, force = false, environment = {}) => {
69137
}
70138
}
71139

72-
const checkList = (value, list) => {
140+
function checkList (value, list) {
73141
if (typeof list === 'string') {
74142
list = [list]
75143
}
@@ -96,6 +164,10 @@ const checkList = (value, list) => {
96164
}
97165

98166
module.exports = {
167+
// Used by npm-pick-manifest, "npm install -g npm", and arborist build-ideal-tree/reify
99168
checkEngine,
169+
// used by arborist build-ideal-tree/reify
100170
checkPlatform,
171+
// used by npm install for devEngines
172+
checkDev,
101173
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"type": "commonjs",
1515
"dependencies": {
16+
"proc-log": "^4.2.0",
1617
"semver": "^7.1.1"
1718
},
1819
"devDependencies": {

test/check-dev.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const t = require('tap')
2+
const { checkDev } = require('..')
3+
4+
t.test('empty params', async t => {
5+
t.equal(checkDev(), null)
6+
})
7+
8+
t.test('tests all the right fields', async t => {
9+
for (const env of ['packageManager', 'runtime', 'cpu', 'libc', 'os']) {
10+
t.test(`field - ${env}`, async t => {
11+
t.test('current name does not match, wanted has extra attribute', async t => {
12+
const wanted = { name: `test-${env}-wanted`, extra: `test-${env}-extra` }
13+
const current = { name: `test-${env}-current` }
14+
t.same(checkDev({ [env]: wanted }, { [env]: current }), { [env]: wanted })
15+
})
16+
t.test('current is not given', async t => {
17+
const wanted = { name: `test-${env}-wanted` }
18+
t.same(checkDev({ [env]: wanted }), { [env]: wanted })
19+
})
20+
t.test('non-semver version is not the same', async t => {
21+
const wanted = { name: `test-name`, version: 'test-version-wanted' }
22+
const current = { name: `test-name`, version: 'test-version-current' }
23+
t.same(checkDev({ [env]: wanted }, { [env]: current }), { [env]: wanted })
24+
})
25+
t.test('non-semver version is the same', async t => {
26+
const wanted = { name: `test-name`, version: 'test-version' }
27+
const current = { name: `test-name`, version: 'test-version' }
28+
t.same(checkDev({ [env]: wanted }, { [env]: current }), null)
29+
})
30+
t.test('semver version is not in range', async t => {
31+
const wanted = { name: `test-name`, version: '^1.0.0' }
32+
const current = { name: `test-name`, version: '2.0.0' }
33+
t.same(checkDev({ [env]: wanted }, { [env]: current }), { [env]: wanted })
34+
})
35+
t.test('semver version is in range', async t => {
36+
const wanted = { name: `test-name`, version: '^1.0.0' }
37+
const current = { name: `test-name`, version: '1.0.0' }
38+
t.same(checkDev({ [env]: wanted }, { [env]: current }), null)
39+
})
40+
t.test('returns the last failure', async t => {
41+
const wanted = [
42+
{ name: `test-name`, version: 'test-version-one' },
43+
{ name: `test-name`, version: 'test-version-two' },
44+
]
45+
const current = { name: `test-name`, version: 'test-version-three' }
46+
t.same(checkDev({ [env]: wanted }, { [env]: current }), { [env]: wanted[1] })
47+
})
48+
})
49+
}
50+
})

0 commit comments

Comments
 (0)