codekingpro/portable-devtools
114k
1const satisfies = require('semver/functions/satisfies')
2const validRange = require('semver/ranges/valid')
3
4const recognizedOnFail = [
5 'ignore',
6 'warn',
7 'error',
8 'download',
9]
10
11const recognizedProperties = [
12 'name',
13 'version',
14 'onFail',
15]
16
17const recognizedEngines = [
18 'packageManager',
19 'runtime',
20 'cpu',
21 'libc',
22 'os',
23]
24
25/** checks a devEngine dependency */
26function checkDependency (wanted, current, opts) {
27 const { engine } = opts
28
29 if ((typeof wanted !== 'object' || wanted === null) || Array.isArray(wanted)) {
30 throw new Error(`Invalid non-object value for "${engine}"`)
31 }
32
33 const properties = Object.keys(wanted)
34
35 for (const prop of properties) {
36 if (!recognizedProperties.includes(prop)) {
37 throw new Error(`Invalid property "${prop}" for "${engine}"`)
38 }
39 }
40
41 if (!properties.includes('name')) {
42 throw new Error(`Missing "name" property for "${engine}"`)
43 }
44
45 if (typeof wanted.name !== 'string') {
46 throw new Error(`Invalid non-string value for "name" within "${engine}"`)
47 }
48
49 if (typeof current.name !== 'string' || current.name === '') {
50 throw new Error(`Unable to determine "name" for "${engine}"`)
51 }
52
53 if (properties.includes('onFail')) {
54 if (typeof wanted.onFail !== 'string') {
55 throw new Error(`Invalid non-string value for "onFail" within "${engine}"`)
56 }
57 if (!recognizedOnFail.includes(wanted.onFail)) {
58 throw new Error(`Invalid onFail value "${wanted.onFail}" for "${engine}"`)
59 }
60 }
61
62 if (wanted.name !== current.name) {
63 return new Error(
64 `Invalid name "${wanted.name}" does not match "${current.name}" for "${engine}"`
65 )
66 }
67
68 if (properties.includes('version')) {
69 if (typeof wanted.version !== 'string') {
70 throw new Error(`Invalid non-string value for "version" within "${engine}"`)
71 }
72 if (typeof current.version !== 'string' || current.version === '') {
73 throw new Error(`Unable to determine "version" for "${engine}" "${wanted.name}"`)
74 }
75 if (validRange(wanted.version)) {
76 if (!satisfies(current.version, wanted.version, opts.semver)) {
77 return new Error(
78 // eslint-disable-next-line max-len
79 `Invalid semver version "${wanted.version}" does not match "${current.version}" for "${engine}"`
80 )
81 }
82 } else if (wanted.version !== current.version) {
83 return new Error(
84 `Invalid version "${wanted.version}" does not match "${current.version}" for "${engine}"`
85 )
86 }
87 }
88}
89
90/** checks devEngines package property and returns array of warnings / errors */
91function checkDevEngines (wanted, current = {}, opts = {}) {
92 if ((typeof wanted !== 'object' || wanted === null) || Array.isArray(wanted)) {
93 throw new Error(`Invalid non-object value for "devEngines"`)
94 }
95
96 const errors = []
97
98 for (const engine of Object.keys(wanted)) {
99 if (!recognizedEngines.includes(engine)) {
100 throw new Error(`Invalid property "devEngines.${engine}"`)
101 }
102 const dependencyAsAuthored = wanted[engine]
103 const dependencies = [dependencyAsAuthored].flat()
104 const currentEngine = current[engine] || {}
105
106 // this accounts for empty array eg { runtime: [] } and ignores it
107 if (dependencies.length === 0) {
108 continue
109 }
110
111 const depErrors = []
112 for (const dep of dependencies) {
113 const result = checkDependency(dep, currentEngine, { ...opts, engine })
114 if (result) {
115 depErrors.push(result)
116 }
117 }
118
119 const invalid = depErrors.length === dependencies.length
120
121 if (invalid) {
122 const lastDependency = dependencies[dependencies.length - 1]
123 let onFail = lastDependency.onFail || 'error'
124 if (onFail === 'download') {
125 onFail = 'error'
126 }
127
128 const err = Object.assign(new Error(`Invalid devEngines.${engine}`), {
129 errors: depErrors,
130 engine,
131 isWarn: onFail === 'warn',
132 isError: onFail === 'error',
133 current: currentEngine,
134 required: dependencyAsAuthored,
135 })
136
137 errors.push(err)
138 }
139 }
140 return errors
141}
142
143module.exports = {
144 checkDevEngines,
145}
146 