@exodus/stasis 1.0.0-alpha.2 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -3
- package/bin/stasis.js +322 -17
- package/logo.svg +12 -3
- package/package.json +39 -18
- package/src/audit.js +112 -0
- package/src/bundle-from-lockfile.js +65 -0
- package/src/bundle.js +4 -217
- package/src/cmd/build.js +321 -0
- package/src/cmd/bundle.js +950 -59
- package/src/cmd/diff.js +36 -0
- package/src/cmd/extract.js +193 -0
- package/src/cmd/sbom.js +35 -0
- package/src/diff.js +333 -0
- package/src/esbuild.js +5 -56
- package/src/loader-mock.js +19 -0
- package/src/loader.js +6 -132
- package/src/loaders/bash.js +257 -0
- package/src/loaders/php.js +1058 -0
- package/src/loaders/rust.js +336 -0
- package/src/loaders/solidity.js +70 -49
- package/src/lockfile.js +4 -80
- package/src/metro-transformer.js +6 -0
- package/src/metro.js +5 -0
- package/src/mock.js +310 -0
- package/src/parse.js +53 -0
- package/src/prune.js +4 -162
- package/src/sbom.js +266 -0
- package/src/scan.js +373 -0
- package/src/webpack.js +5 -48
- package/src/config.js +0 -205
- package/src/state.js +0 -440
- package/src/state.util.js +0 -25
- package/src/util.js +0 -60
package/src/webpack.js
CHANGED
|
@@ -1,48 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const state = State.instance
|
|
8
|
-
assert.ok(state, 'Stasis preload is not active')
|
|
9
|
-
|
|
10
|
-
// Webpack plugin interface
|
|
11
|
-
export class StasisWebpack {
|
|
12
|
-
#seen = new Set()
|
|
13
|
-
|
|
14
|
-
constructor(options) {
|
|
15
|
-
assert.equal(options, undefined, 'No options expected')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
apply(compiler) {
|
|
19
|
-
compiler.plugin('normal-module-factory', (nmf) => {
|
|
20
|
-
nmf.plugin('after-resolve', (data, callback) => {
|
|
21
|
-
assert(data.resource === data.resourceResolveData.path)
|
|
22
|
-
const filePath = path.resolve(data.resource) // resolve just in case, don't remove
|
|
23
|
-
|
|
24
|
-
// TODO: record resolutions
|
|
25
|
-
/*
|
|
26
|
-
const meta = {
|
|
27
|
-
resource: data.resource,
|
|
28
|
-
source: data.resourceResolveData.context.issuer,
|
|
29
|
-
query: data.rawRequest,
|
|
30
|
-
// Note: data.userRequest is different and can contain loader prefixes
|
|
31
|
-
moduleName: data.resourceResolveData.descriptionFileData.name,
|
|
32
|
-
modulePackage: data.resourceResolveData.descriptionFilePath,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
console.log(meta)
|
|
36
|
-
//state.addImport(parentURL, specifier, url, { conditions: 'webpack ...', format })
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
if (!this.#seen.has(filePath)) {
|
|
40
|
-
state.addFile(pathToFileURL(filePath).toString())
|
|
41
|
-
this.#seen.add(filePath)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return callback(null, data)
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
// Public-export adapter: backs `@exodus/stasis/webpack`. The plugin lives in
|
|
2
|
+
// @exodus/stasis-plugins (which depends on @exodus/stasis-core for State /
|
|
3
|
+
// resolvePluginState). Internal code uses `@exodus/stasis-plugins/webpack` directly;
|
|
4
|
+
// this file exists only to back the package's `exports` map.
|
|
5
|
+
export * from '@exodus/stasis-plugins/webpack'
|
package/src/config.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
|
|
3
|
-
const VALID_SCOPE = new Set(['node_modules', 'full'])
|
|
4
|
-
const VALID_LOCK = new Set(['none', 'ignore', 'add', 'replace', 'frozen'])
|
|
5
|
-
const VALID_BUNDLE = new Set(['none', 'ignore', 'add', 'replace', 'load'])
|
|
6
|
-
|
|
7
|
-
const envDebugBool = (value) => Boolean(value && value !== '0')
|
|
8
|
-
|
|
9
|
-
const OPTION_KEYS = ['scope', 'lock', 'bundle', 'bundleFile', 'debug']
|
|
10
|
-
|
|
11
|
-
// Plugins accept the same options as Config but want to validate without constructing one,
|
|
12
|
-
// since constructing State has side effects. Mirror Config's per-field validation.
|
|
13
|
-
export function validatePluginOptions(label, options) {
|
|
14
|
-
const rest = { ...options }
|
|
15
|
-
for (const key of OPTION_KEYS) delete rest[key]
|
|
16
|
-
assert.equal(Object.keys(rest).length, 0, `Unknown ${label} options: ${Object.keys(rest).join(', ')}`)
|
|
17
|
-
const { scope, lock, bundle, bundleFile, debug } = options
|
|
18
|
-
if (scope !== undefined) assert.ok(VALID_SCOPE.has(scope), `Invalid scope: ${scope}`)
|
|
19
|
-
if (lock !== undefined) assert.ok(VALID_LOCK.has(lock), `Invalid lock: ${lock}`)
|
|
20
|
-
if (bundle !== undefined) assert.ok(VALID_BUNDLE.has(bundle), `Invalid bundle: ${bundle}`)
|
|
21
|
-
if (bundleFile !== undefined) assert.equal(typeof bundleFile, 'string', 'bundleFile must be a string')
|
|
22
|
-
if (debug !== undefined) assert.equal(typeof debug, 'boolean', 'debug must be a boolean')
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// When a plugin runs against a State that already exists (preload path), the active Config
|
|
26
|
-
// is authoritative. Any options the plugin was given must agree with it.
|
|
27
|
-
export function assertOptionsMatchConfig(config, options) {
|
|
28
|
-
const { scope, lock, bundle, bundleFile, debug } = options
|
|
29
|
-
try {
|
|
30
|
-
if (scope !== undefined) assert.equal(config.scope, scope)
|
|
31
|
-
if (lock !== undefined) assert.equal(config.lock, lock)
|
|
32
|
-
if (bundle !== undefined) assert.equal(config.bundleMode, bundle)
|
|
33
|
-
if (bundleFile !== undefined) assert.equal(config.bundleFile, bundleFile)
|
|
34
|
-
if (debug !== undefined) assert.equal(config.debug, debug)
|
|
35
|
-
} catch (cause) {
|
|
36
|
-
throw new Error('Plugin options conflict with active stasis state', { cause })
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class Config {
|
|
41
|
-
#env
|
|
42
|
-
#scope
|
|
43
|
-
#lock
|
|
44
|
-
#bundle
|
|
45
|
-
#bundleFile
|
|
46
|
-
#debug
|
|
47
|
-
|
|
48
|
-
// Options match the CLI flags (lock/bundle/bundleFile/scope/debug). Env vars take effect at
|
|
49
|
-
// construction time; if both env and an option are set they must agree.
|
|
50
|
-
constructor(options = {}) {
|
|
51
|
-
const { scope, lock, bundle, bundleFile, debug, ...rest } = options
|
|
52
|
-
assert.equal(Object.keys(rest).length, 0, `Unknown Config options: ${Object.keys(rest).join(', ')}`)
|
|
53
|
-
|
|
54
|
-
this.#env = {
|
|
55
|
-
scope: process.env.EXODUS_STASIS_SCOPE || undefined,
|
|
56
|
-
lock: process.env.EXODUS_STASIS_LOCK || undefined,
|
|
57
|
-
bundle: process.env.EXODUS_STASIS_BUNDLE || undefined,
|
|
58
|
-
bundleFile: process.env.EXODUS_STASIS_BUNDLE_FILE || undefined,
|
|
59
|
-
debug: process.env.EXODUS_STASIS_DEBUG || undefined,
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
if (this.#env.scope !== undefined && scope !== undefined) assert.equal(this.#env.scope, scope)
|
|
64
|
-
if (this.#env.lock !== undefined && lock !== undefined) assert.equal(this.#env.lock, lock)
|
|
65
|
-
if (this.#env.bundle !== undefined && bundle !== undefined) assert.equal(this.#env.bundle, bundle)
|
|
66
|
-
if (this.#env.bundleFile !== undefined && bundleFile !== undefined) {
|
|
67
|
-
assert.equal(this.#env.bundleFile, bundleFile)
|
|
68
|
-
}
|
|
69
|
-
if (this.#env.debug !== undefined && debug !== undefined) {
|
|
70
|
-
assert.equal(envDebugBool(this.#env.debug), debug)
|
|
71
|
-
}
|
|
72
|
-
} catch (cause) {
|
|
73
|
-
throw new Error('Config options can not override stasis env', { cause })
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this.#scope = this.#env.scope || scope || 'full'
|
|
77
|
-
this.#lock = this.#env.lock || lock || 'add'
|
|
78
|
-
this.#bundle = this.#env.bundle || bundle || 'none'
|
|
79
|
-
this.#bundleFile = this.#env.bundleFile || bundleFile || undefined
|
|
80
|
-
this.#debug = this.#env.debug !== undefined ? envDebugBool(this.#env.debug) : (debug ?? false)
|
|
81
|
-
|
|
82
|
-
this.#checkInvariants()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Per-field validation + cross-field invariants. Constructor (options path) and
|
|
86
|
-
// loadConfig (disk path) both call this so the rules live in one place.
|
|
87
|
-
#checkInvariants() {
|
|
88
|
-
assert.ok(VALID_SCOPE.has(this.#scope), `Invalid scope: ${this.#scope}`)
|
|
89
|
-
assert.ok(VALID_LOCK.has(this.#lock), `Invalid lock: ${this.#lock}`)
|
|
90
|
-
assert.ok(VALID_BUNDLE.has(this.#bundle), `Invalid bundle: ${this.#bundle}`)
|
|
91
|
-
assert.equal(typeof this.#debug, 'boolean', 'debug must be a boolean')
|
|
92
|
-
if (this.#bundleFile !== undefined) assert.equal(typeof this.#bundleFile, 'string', 'bundleFile must be a string')
|
|
93
|
-
|
|
94
|
-
// bundle=load needs a trust root for source bytes: frozen pins each file's sha512 in
|
|
95
|
-
// the lockfile and we cross-check it on load; otherwise the bundle is itself
|
|
96
|
-
// authoritative -- lock=none with no lockfile on disk, or lock=ignore with a lockfile
|
|
97
|
-
// deliberately bypassed.
|
|
98
|
-
// WARNING: lockfile attests only to source BYTES, not bundle metadata.
|
|
99
|
-
if (this.#bundle === 'load' && this.#lock !== 'frozen' && this.#lock !== 'none' && this.#lock !== 'ignore') {
|
|
100
|
-
throw new RangeError('bundle=load requires lock=(frozen|none|ignore)')
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (this.#lock === 'none' && this.#bundle === 'none') {
|
|
104
|
-
throw new RangeError('lock=none requires bundle=(add|replace|load|ignore)')
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
loadConfig(json) {
|
|
109
|
-
const {
|
|
110
|
-
scope = this.#scope,
|
|
111
|
-
lock = this.#lock,
|
|
112
|
-
bundle = this.#bundle,
|
|
113
|
-
debug = this.#debug,
|
|
114
|
-
...rest
|
|
115
|
-
} = JSON.parse(json)
|
|
116
|
-
assert.equal(Object.keys(rest).length, 0)
|
|
117
|
-
this.#scope = scope
|
|
118
|
-
this.#lock = lock
|
|
119
|
-
this.#bundle = bundle
|
|
120
|
-
this.#debug = debug
|
|
121
|
-
this.#checkInvariants()
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
if (this.#env.scope !== undefined) assert.equal(this.#scope, this.#env.scope)
|
|
125
|
-
if (this.#env.lock !== undefined) assert.equal(this.#lock, this.#env.lock)
|
|
126
|
-
if (this.#env.bundle !== undefined) assert.equal(this.#bundle, this.#env.bundle)
|
|
127
|
-
if (this.#env.debug !== undefined) assert.equal(this.#debug, envDebugBool(this.#env.debug))
|
|
128
|
-
} catch (cause) {
|
|
129
|
-
throw new Error('Flags/env can not override stasis.config.json', { cause })
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
get bundleFile() {
|
|
134
|
-
return this.#bundleFile
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
get debug() {
|
|
138
|
-
return this.#debug
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
get values() {
|
|
142
|
-
return { scope: this.#scope }
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
get bundle() {
|
|
146
|
-
return this.#bundle !== 'none' && this.#bundle !== 'ignore'
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
get bundleMode() {
|
|
150
|
-
return this.#bundle
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
get ignoreBundle() {
|
|
154
|
-
return this.#bundle === 'ignore'
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
get writeBundle() {
|
|
158
|
-
return this.#bundle === 'add' || this.#bundle === 'replace'
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
get replaceBundle() {
|
|
162
|
-
return this.#bundle === 'replace'
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
get loadBundle() {
|
|
166
|
-
return this.#bundle === 'load'
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
get full() {
|
|
170
|
-
return this.#scope === 'full'
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
get frozen() {
|
|
174
|
-
return this.#lock === 'frozen'
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
get useLockfile() {
|
|
178
|
-
return this.#lock !== 'none' && this.#lock !== 'ignore'
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
get ignoreLockfile() {
|
|
182
|
-
return this.#lock === 'ignore'
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
get writeLockfile() {
|
|
186
|
-
return this.#lock === 'add' || this.#lock === 'replace'
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
get replaceLockfile() {
|
|
190
|
-
return this.#lock === 'replace'
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
get scope() {
|
|
194
|
-
return this.#scope
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
get lock() {
|
|
198
|
-
return this.#lock
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
get json() {
|
|
202
|
-
const data = { scope: this.#scope, lock: this.#lock, bundle: this.#bundle }
|
|
203
|
-
return JSON.stringify(data, undefined, 2) + '\n'
|
|
204
|
-
}
|
|
205
|
-
}
|