@exodus/stasis 1.0.0-beta.7 → 1.0.0-beta.9
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 +27 -24
- package/bin/stasis.js +106 -177
- package/package.json +7 -5
- package/src/audit-corrections.js +45 -21
- package/src/cmd/bundle.js +194 -47
- package/src/installed-commits.js +182 -0
- package/src/loaders/php.js +43 -16
- package/src/resolve-fields.js +62 -30
- package/src/resolve-node.js +9 -6
- package/src/scan.js +8 -3
- package/src/vfs-bundle/entries.js +9 -5
- package/src/vfs-bundle/github.js +4 -1
- package/src/vfs-bundle/tree.js +49 -15
- package/src/vfs-bundle.js +13 -8
package/bin/stasis.js
CHANGED
|
@@ -18,109 +18,34 @@ const jsname = argv.shift()
|
|
|
18
18
|
const pathsEqual = (a, b) => a === b || (existsSync(a) && realpathSync(a) === b)
|
|
19
19
|
assert(basename(jsname) === 'stasis' || pathsEqual(jsname, fileURLToPath(import.meta.url)))
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
console.error(`${prefix}\nUsage:
|
|
21
|
+
const HELP = `Usage:
|
|
23
22
|
stasis run --lock=(add|replace|frozen|ignore) [--bundle=(add|replace|load|frozen|ignore)] [--bundle-file=path/to/bundle.br] [--resources-bundle-file=path/to/resources.br] [--dependencies] [--child-process] [--package-json] [--mock] [--import=module ...] [--fs=(sync|async)] [--resources=ext,ext] [--brotli-quality=0..11] path/to/file.js ...
|
|
24
|
-
(--
|
|
25
|
-
e.g. --import=./instrument.mjs for a setup/instrumentation preload; a preload's own module
|
|
26
|
-
graph is runner infrastructure like stasis's loader itself, so it stays out of the
|
|
27
|
-
lockfile/bundle -- except modules the app graph also reaches, which are attested like any
|
|
28
|
-
other app code; with --mock, preloads run under the mock's side-effect denials, so a preload
|
|
29
|
-
that spawns helper processes cannot work there)
|
|
23
|
+
stasis bundle [--scope=(node_modules|full)] [--conditions=cond1,cond2] [--mainFields=field1,field2] [--metro [--metro-resolver] --platforms=ios,android] [--jsx] [--flow] [--typescript [--tsconfig=path/to/tsconfig.json]] [--resources=ext,ext] [--package-json] [--lockfile=path/to/stasis.lock.json] [--brotli-quality=0..11] [--add] [--output=(path|-)] path/to/file.(js|ts|jsx|tsx) ...
|
|
30
24
|
stasis bundle [--mapping=path/to/remappings(.txt|.toml)] [--manifests] [--add] [--output=(path|-)] path/to/(file.sol|dir) ...
|
|
31
|
-
(Solidity: a directory stands for every .sol file under it (a missing or empty one is skipped) --
|
|
32
|
-
"stasis bundle src test script" is what "forge build" compiles, "stasis bundle contracts" what
|
|
33
|
-
Hardhat does; imports resolve through the remappings "forge build" uses (remappings.txt,
|
|
34
|
-
foundry.toml's profile, FOUNDRY_PROFILE, lib/ auto-detection incl. dependencies' own configs),
|
|
35
|
-
else a root remappings.txt, then node_modules by file path; --mapping instead takes exactly the
|
|
36
|
-
remappings that one file lists; an import must reach a .sol file inside the project, and a
|
|
37
|
-
dependency's only other dependencies' files; --manifests also carries foundry.toml,
|
|
38
|
-
remappings.txt, foundry.lock, soldeer.lock, .gitmodules and package.json of the project and its
|
|
39
|
-
bundled dependencies, as written: RPC/Etherscan keys and URL credentials in them included)
|
|
40
|
-
stasis bundle [--add] [--output=(path|-)] path/to/file.php ...
|
|
41
|
-
stasis bundle [--scope=(node_modules|full)] [--conditions=cond1,cond2] [--mainFields=field1,field2] [--jsx] [--flow] [--typescript [--tsconfig=path/to/tsconfig.json]] [--resources=ext,ext] [--package-json] [--lockfile=path/to/stasis.lock.json] [--add] [--output=(path|-)] path/to/file.(js|ts|jsx|tsx) ...
|
|
42
|
-
stasis bundle --metro [--metro-resolver] --platforms=ios,android [--platforms=web] [--jsx] [--flow] [--typescript [--tsconfig=path/to/tsconfig.json]] [--resources=ext,ext] [--package-json] [--lockfile=path/to/stasis.lock.json] [--add] [--output=(path|-)] path/to/file.(js|ts|jsx|tsx) ...
|
|
43
|
-
stasis bundle [--add] [--output=(path|-)] path/to/file.(sh|bash) ...
|
|
44
25
|
stasis bundle [--cargo] [--cargo-features=a,b,pkg/c] [--cargo-no-default-features] [--cargo-all-features] [--cargo-target=(triple|host)] [--cargo-manifests] [--add] [--output=(path|-)] path/to/file.rs ...
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
when there is a Cargo.lock (version 3 or 4; an older one stops the build), --cargo-target and every
|
|
48
|
-
locked package in-tree, by replaying the manifests otherwise (said, with why); --cargo takes the resolution
|
|
49
|
-
from "cargo metadata" instead -- it runs cargo, so only on a project you trust; the --cargo-*
|
|
50
|
-
feature flags are cargo's --features / --no-default-features / --all-features for those packages;
|
|
51
|
-
--cargo-target asks rustc for the target's cfgs so #[cfg(unix)]-style code for other targets stays
|
|
52
|
-
out too, otherwise it is all kept; --cargo-manifests also bundles each bundled package's Cargo.toml
|
|
53
|
-
and build script (a vendored crate's .cargo-checksum.json too), the workspace Cargo.toml, Cargo.lock
|
|
54
|
-
and cargo configs, as written: tokens and URL credentials in them included)
|
|
55
|
-
(writes to stasis.code.br by default, a JS bundle's in the project root its paths are relative to,
|
|
56
|
-
where stasis run --bundle=load finds it; --output=- streams to stdout; --add merges into an
|
|
57
|
-
existing bundle instead of replacing it (not with --output=-); --brotli-quality=0..11, default 9;
|
|
58
|
-
--jsx parses JSX in .js/.cjs/.mjs files, e.g. React Native source (.jsx/.tsx files need no flag;
|
|
59
|
-
put JSX-in-TS in a .tsx file);
|
|
60
|
-
--flow strips Flow types from .js/.cjs/.mjs sources oxc can't parse (needs the optional flow-remove-types dep);
|
|
61
|
-
--typescript resolves TS the way tsc does: an import of ./x.js lands on ./x.ts when no .js is on disk
|
|
62
|
-
(likewise .mjs/.cjs -> .mts/.cts, extensionless ./x -> ./x.ts, incl. exports/imports/main targets),
|
|
63
|
-
honouring tsconfig compilerOptions.paths aliases (from the tsconfig.json beside the importing
|
|
64
|
-
file's package.json -- its workspace package's in a monorepo, else the project's -- or --tsconfig=path);
|
|
65
|
-
never from or into node_modules (by real path, so linked workspace packages still are); not with --metro-resolver; implied when every entry is TS and each relative ./x.js-style import
|
|
66
|
-
in them exists only as its TS source (otherwise a failing import it would resolve names it);
|
|
67
|
-
--resources carries reached assets (e.g. --resources=png,svg) as resources instead of failing to bundle them;
|
|
68
|
-
--package-json auto-includes each bundled module's package.json, even ones the scan never reached)
|
|
69
|
-
stasis github-bundle --github=owner/name [--sha=commit|--tag=name] [--directory=path] [--package-manager=(pnpm|yarn1|npm|soldeer)] [--package-manager-version=version] [--generate=prisma] [--lockfile=path/to/stasis.lock.json] [--output=(path|-)] [stasis bundle's options for the entries] [path/in/repo/to/(file.(js|ts|jsx|tsx)|file.sol|dir) ...]
|
|
70
|
-
(bundles a GitHub repo at a commit, or the one --tag names, its default branch's head without
|
|
71
|
-
either, as "stasis bundle" bundles a clone of it once installed: the tree is fetched (with
|
|
72
|
-
GITHUB_TOKEN where set) and held to its git tree id, and the dependencies are laid out in memory
|
|
73
|
-
from the lockfile alone, as "pnpm install --frozen-lockfile --ignore-scripts" (pnpm 9, 10, 11 or
|
|
74
|
-
12), "yarn install --frozen-lockfile --ignore-scripts" (yarn 1.22), "npm ci --ignore-scripts"
|
|
75
|
-
(npm 10.9.3 to 10.9.9, or 11.11.1 to 11.21.0) or "soldeer install" (0.12, for .sol entries)
|
|
76
|
-
would: nothing is installed, no package script runs, and every tarball and zip is held to the
|
|
77
|
-
lockfile, all cached where "stasis audit" caches. Without --package-manager, the one whose
|
|
78
|
-
lockfile installs --directory (or the repo's root), if only one's does; at
|
|
79
|
-
--package-manager-version, else at the root package.json's packageManager pin, else pnpm
|
|
80
|
-
10.33.4, yarn 1.22.22, npm 11.21.0 (which no packageManager pins) or Soldeer 0.12.0. The entries
|
|
81
|
-
are paths in --directory, or in the repo; without them, the JS files its package.json names as
|
|
82
|
-
main, exports and bin, resolved as the build resolves them (--conditions, --mainFields, --metro
|
|
83
|
-
--platforms, --typescript), or for Soldeer its .sol files directly in it, under contracts/, and
|
|
84
|
-
under its source directory (foundry.toml's src, else src/), but tests, scripts, mocks and
|
|
85
|
-
dependency or build directories; its paths, and the directory its repo names, are
|
|
86
|
-
--directory's, or of a JS bundle, the innermost package's at or above it holding every file it
|
|
87
|
-
bundles, the project root's where one is outside; not with --metro-resolver, --cargo* or --add;
|
|
88
|
-
--generate=prisma first writes, for each project with a "prisma-client" generator, the Prisma
|
|
89
|
-
Client its "prisma generate" would (Prisma 7.4.0 to 7.10.0), with the optional
|
|
90
|
-
@prisma/client-generator-ts 7.10.0 dependency: nothing of the repo runs, its schema and config
|
|
91
|
-
are read as data, and its prisma for its version alone;
|
|
92
|
-
writes to owner-name.<commit's first 7>.stasis.code.br by default,
|
|
93
|
-
owner-name.<--directory, its / made ->.<commit's first 7>.stasis.code.br with --directory, each
|
|
94
|
-
character outside [A-Za-z0-9._-] made _, and a directory too deep to fit in 255 characters cut,
|
|
95
|
-
with a hash of it; --output=- streams to stdout)
|
|
26
|
+
stasis bundle [--add] [--output=(path|-)] path/to/file.(php|sh|bash) ...
|
|
27
|
+
stasis github-bundle --github=owner/name [--sha=commit|--tag=name] [--directory=path] [--package-manager=(pnpm|yarn1|npm|soldeer) [--package-manager-version=version]] [--generate=prisma] [--lockfile=path/to/stasis.lock.json] [--output=(path|-)] [stasis bundle's options for the entries] [path/in/repo/to/(file.(js|ts|jsx|tsx)|file.sol|dir) ...]
|
|
96
28
|
stasis add path/to/(file|dir) ...
|
|
97
|
-
(adds the listed files to the project's bundle(s) with no dependency resolution;
|
|
98
|
-
a directory expands to its files. Requires a stasis.config.json (all fields optional).)
|
|
99
29
|
stasis build --output=(dir|file.js) [--format=(esm|cjs|iife)] [--platform=(node|browser|neutral|hermes)] [--babel] [--minify] [--sourcemap] [--define=K=V ...] [--external=pkg ...] [--loader=.ext:name ...] path/to/(stasis.code.br|stasis.lock.json) [entry]
|
|
100
|
-
(--platform=hermes downlevels every file for Hermes (classes, let/const, arrows, ...; via esbuild + Babel)
|
|
101
|
-
and defaults --format to iife; --babel instead transforms every file with the project's own
|
|
102
|
-
babel.config.(js|cjs|mjs|json); both resolve @babel/core and plugins from the project's dev deps)
|
|
103
30
|
stasis extract [--output=path/to/dir] path/to/bundle.stasis.code.br
|
|
104
31
|
stasis diff --stat [--imports] path/to/(lockfile|bundle) path/to/(lockfile|bundle)
|
|
105
32
|
stasis prune [path/to/project]
|
|
106
|
-
stasis audit [--why|--why-deep] [--why-full] [--reason=consumer] [--repo-advisories] path/to/
|
|
107
|
-
(audits the dependencies the files hold: npm packages by npm's advisories, vendored crates and
|
|
108
|
-
Composer packages by OSV's, and Soldeer packages and GitHub repos (Foundry's lib/) by those their
|
|
109
|
-
GitHub repository publishes, asked with GITHUB_TOKEN where it is set; a Composer dev version is
|
|
110
|
-
listed as not audited, no database having one;
|
|
111
|
-
--why lists, per advisory, the cross-module import paths that pull an npm package in,
|
|
112
|
-
prefixed by the bundle consumer that imports each chain at the top level, e.g. "run: a -> b -> c";
|
|
113
|
-
it skips chains whose full tail is already listed as its own chain -- --why-deep keeps them all;
|
|
114
|
-
--why-full spells every chain out instead of collapsing repeated tails to "a -> b -> ... -> d";
|
|
115
|
-
--reason=consumer shows only advisories related to that consumer, and with --why only its chains;
|
|
116
|
-
--repo-advisories also asks each package's GitHub repository for the advisories its maintainers
|
|
117
|
-
published there, before GitHub reviews them into the databases above; it needs a token in
|
|
118
|
-
GITHUB_TOKEN, and asks the repository each package's artifact records, else the one the
|
|
119
|
-
package.json a bundle carries of an npm package names, else looks one up and caches it for a
|
|
120
|
-
month in the user cache dir, e.g. ~/.cache/stasis)
|
|
33
|
+
stasis audit [--why|--why-deep] [--why-full] [--reason=consumer] [--repo-advisories] path/to/(lockfile|bundle) ...
|
|
121
34
|
stasis sbom --format=(spdx|cyclonedx) [--output=(path|-)] path/to/(lockfile|bundle) ...
|
|
122
|
-
|
|
123
|
-
|
|
35
|
+
stasis --version
|
|
36
|
+
|
|
37
|
+
Each command's options: https://github.com/PreventiveMeasures/stasis/blob/main/doc/<command>.md
|
|
38
|
+
(run, bundle, github-bundle, build, extract, diff, prune, audit, sbom; add is in bundle.md)`
|
|
39
|
+
|
|
40
|
+
// The help, on stderr. Exits 0 when asked for (--help), 1 when printed for want of a command.
|
|
41
|
+
function usage(asked = false) {
|
|
42
|
+
console.error(HELP)
|
|
43
|
+
process.exit(asked ? 0 : 1)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// A usage error: the message and where the help is, never the help itself.
|
|
47
|
+
function fail(message) {
|
|
48
|
+
console.error(`${message}\nRun 'stasis --help' for usage.`)
|
|
124
49
|
process.exit(1)
|
|
125
50
|
}
|
|
126
51
|
|
|
@@ -143,6 +68,8 @@ const command = argv.shift()
|
|
|
143
68
|
if (command === '-v' || command === '--version') {
|
|
144
69
|
console.log(`v${pkg.version}`)
|
|
145
70
|
process.exit(0)
|
|
71
|
+
} else if (command === '-h' || command === '--help') {
|
|
72
|
+
usage(true)
|
|
146
73
|
} else if (command === 'run') {
|
|
147
74
|
const options = {
|
|
148
75
|
lock: { type: 'string', default: 'none' },
|
|
@@ -161,27 +88,27 @@ if (command === '-v' || command === '--version') {
|
|
|
161
88
|
}
|
|
162
89
|
const values = parseLeadingOptions(argv, options, {
|
|
163
90
|
valueFlags: ['--bundle', '--bundle-file', '--resources-bundle-file', '--lock', '--import', '--resources', '--brotli-quality'],
|
|
164
|
-
onError:
|
|
91
|
+
onError: fail,
|
|
165
92
|
})
|
|
166
|
-
if (argv.length === 0)
|
|
167
|
-
if (!['none', 'ignore', 'add', 'replace', 'frozen'].includes(values.lock))
|
|
93
|
+
if (argv.length === 0) fail('Nothing to run: no path to file given')
|
|
94
|
+
if (!['none', 'ignore', 'add', 'replace', 'frozen'].includes(values.lock)) fail('Error: invalid --lock value')
|
|
168
95
|
const lock = values.lock
|
|
169
96
|
const scope = values.dependencies ? 'node_modules' : 'full'
|
|
170
97
|
const bundle = values.bundle
|
|
171
98
|
const bundleFile = values['bundle-file'] ? resolve(values['bundle-file']) : ''
|
|
172
99
|
const resourcesBundleFile = values['resources-bundle-file'] ? resolve(values['resources-bundle-file']) : ''
|
|
173
100
|
const debug = values.debug ? '1' : ''
|
|
174
|
-
if (!['none', 'ignore', 'add', 'replace', 'load', 'frozen'].includes(bundle))
|
|
175
|
-
if (bundleFile && bundle === 'none')
|
|
101
|
+
if (!['none', 'ignore', 'add', 'replace', 'load', 'frozen'].includes(bundle)) fail('Error: invalid --bundle value')
|
|
102
|
+
if (bundleFile && bundle === 'none') fail('Error: --bundle-file requires --bundle=(add|replace|load|frozen|ignore)')
|
|
176
103
|
// --resources-bundle-file needs an active bundle mode (unlike --bundle-file, inert under ignore).
|
|
177
|
-
if (resourcesBundleFile && (bundle === 'none' || bundle === 'ignore'))
|
|
178
|
-
if (bundle === 'load' && lock !== 'frozen' && lock !== 'none' && lock !== 'ignore')
|
|
179
|
-
if (lock === 'none' && bundle === 'none')
|
|
180
|
-
if (values.mock && bundle === 'load')
|
|
104
|
+
if (resourcesBundleFile && (bundle === 'none' || bundle === 'ignore')) fail('Error: --resources-bundle-file requires --bundle=(add|replace|load|frozen)')
|
|
105
|
+
if (bundle === 'load' && lock !== 'frozen' && lock !== 'none' && lock !== 'ignore') fail('Error: --bundle=load is incompatible with --lock=(add|replace)')
|
|
106
|
+
if (lock === 'none' && bundle === 'none') fail('Error: stasis needs a lockfile or a bundle: set --lock or --bundle')
|
|
107
|
+
if (values.mock && bundle === 'load') fail('Error: --mock is for capturing imports while building a bundle; not compatible with --bundle=load')
|
|
181
108
|
// --fs requires a bundle mode (add|replace|load); `sync` patches the sync fs readers,
|
|
182
109
|
// `async` adds their callback + fs.promises counterparts on top.
|
|
183
|
-
if (values.fs !== undefined && !['sync', 'async'].includes(values.fs))
|
|
184
|
-
if (values.fs !== undefined && !['add', 'replace', 'load'].includes(bundle))
|
|
110
|
+
if (values.fs !== undefined && !['sync', 'async'].includes(values.fs)) fail("Error: --fs must be 'sync' or 'async'")
|
|
111
|
+
if (values.fs !== undefined && !['add', 'replace', 'load'].includes(bundle)) fail('Error: --fs requires --bundle=(add|replace|load)')
|
|
185
112
|
const captureFs = values.fs ?? ''
|
|
186
113
|
// --resources: comma-separated extension/filename allowlist for `--fs` captures.
|
|
187
114
|
const resources = values.resources ?? ''
|
|
@@ -190,13 +117,13 @@ if (command === '-v' || command === '--version') {
|
|
|
190
117
|
try {
|
|
191
118
|
brotliQuality = parseBrotliQuality('--brotli-quality', values['brotli-quality'])
|
|
192
119
|
} catch (cause) {
|
|
193
|
-
|
|
120
|
+
fail(`Error: ${cause.message}`)
|
|
194
121
|
}
|
|
195
122
|
}
|
|
196
123
|
// --import: extra preload module(s) passed through to the spawned node. Node resolves each
|
|
197
124
|
// against the project cwd; they ride AFTER stasis's own loader import.
|
|
198
125
|
const imports = values.import ?? []
|
|
199
|
-
if (imports.some((s) => s === ''))
|
|
126
|
+
if (imports.some((s) => s === '')) fail('Error: --import requires a module specifier (e.g. --import=./instrument.mjs)')
|
|
200
127
|
// Not a usage error: plain instrumentation/setup preloads still work under the mock; only ones
|
|
201
128
|
// that need the denied surfaces (spawning helper processes, network) die, and they do so loudly.
|
|
202
129
|
if (values.mock && imports.length > 0) {
|
|
@@ -207,7 +134,7 @@ if (command === '-v' || command === '--version') {
|
|
|
207
134
|
// --package-json: auto-include every bundled module's package.json. Only meaningful while WRITING
|
|
208
135
|
// a bundle (add|replace) -- there's nothing to fold the manifests into otherwise.
|
|
209
136
|
if (values['package-json'] && bundle !== 'add' && bundle !== 'replace') {
|
|
210
|
-
|
|
137
|
+
fail('Error: --package-json requires --bundle=(add|replace)')
|
|
211
138
|
}
|
|
212
139
|
const packageJSON = values['package-json'] ? '1' : ''
|
|
213
140
|
console.warn('[stasis] Running stasis with config:', { lock, scope, bundle, ...(bundleFile && { bundleFile }), ...(resourcesBundleFile && { resourcesBundleFile }), ...(childProcess && { childProcess: true }), ...(packageJSON && { packageJSON: true }), ...(values.mock && { mock: true }), ...(imports.length > 0 && { import: imports }), ...(values.fs && { fs: values.fs }), ...(resources && { resources }), ...(brotliQuality !== undefined && { brotliQuality }) })
|
|
@@ -240,7 +167,7 @@ if (command === '-v' || command === '--version') {
|
|
|
240
167
|
while (!existsSync(p) && dirname(p) !== p) p = dirname(p)
|
|
241
168
|
// Refuse write access to the filesystem root -- it would disable the --permission layer everywhere.
|
|
242
169
|
if (p === dirname(p)) {
|
|
243
|
-
|
|
170
|
+
fail(`Error: no existing parent directory for ${flag}=${file}; create one first or choose a path under an existing directory`)
|
|
244
171
|
}
|
|
245
172
|
if (!writeAllow.includes(p)) writeAllow.push(p)
|
|
246
173
|
}
|
|
@@ -290,36 +217,36 @@ if (command === '-v' || command === '--version') {
|
|
|
290
217
|
}
|
|
291
218
|
const values = parseLeadingOptions(argv, options, {
|
|
292
219
|
valueFlags: ['--mapping', '--output', '--scope', '--lockfile', '--conditions', '--mainFields', '--platforms', '--resources', '--tsconfig', '--cargo-features', '--cargo-target', '--brotli-quality', '-o'],
|
|
293
|
-
onError:
|
|
220
|
+
onError: fail,
|
|
294
221
|
})
|
|
295
|
-
if (argv.length === 0)
|
|
222
|
+
if (argv.length === 0) fail('Nothing to bundle: no entry file given')
|
|
296
223
|
// A directory entry stands for the .sol files under it (Solidity only); an extensionless path
|
|
297
224
|
// that doesn't exist is a missing one (skipped with a warning).
|
|
298
225
|
const { directoryEntryError, isSolidityEntry } = await import('../src/cmd/bundle.js')
|
|
299
226
|
const dirError = directoryEntryError(argv)
|
|
300
|
-
if (dirError !== null)
|
|
227
|
+
if (dirError !== null) fail(`Error: ${dirError}`)
|
|
301
228
|
const allSol = argv.every((f) => isSolidityEntry(f))
|
|
302
229
|
const allPhp = argv.every((f) => f.endsWith('.php'))
|
|
303
230
|
const allJs = argv.every((f) => /\.(?:js|cjs|mjs|ts|cts|mts|jsx|tsx)$/u.test(f))
|
|
304
231
|
const allBash = argv.every((f) => /\.(?:sh|bash)$/u.test(f))
|
|
305
232
|
const allRust = argv.every((f) => f.endsWith('.rs'))
|
|
306
233
|
if (!allSol && !allPhp && !allJs && !allBash && !allRust) {
|
|
307
|
-
|
|
234
|
+
fail('Error: bundle entries must all be .sol, all be .php, all be .js/.cjs/.mjs/.ts/.cts/.mts/.jsx/.tsx, all be .sh/.bash, or all be .rs')
|
|
308
235
|
}
|
|
309
|
-
if (values.mapping && !allSol)
|
|
236
|
+
if (values.mapping && !allSol) fail('Error: --mapping is only valid for .sol bundles')
|
|
310
237
|
// --manifests: carry the Solidity build's description files (foundry.toml, remappings.txt, ...).
|
|
311
238
|
const manifests = Boolean(values.manifests)
|
|
312
|
-
if (manifests && !allSol)
|
|
239
|
+
if (manifests && !allSol) fail('Error: --manifests is only valid for .sol bundles')
|
|
313
240
|
// --cargo: take the Rust feature/dependency resolution from `cargo metadata` (runs cargo; opt-in).
|
|
314
241
|
const cargo = Boolean(values.cargo)
|
|
315
|
-
if (cargo && !allRust)
|
|
242
|
+
if (cargo && !allRust) fail('Error: --cargo is only valid for Rust bundles')
|
|
316
243
|
// --cargo-features / --cargo-no-default-features / --cargo-all-features: cargo's own feature flags
|
|
317
244
|
// for the entries' packages (`pkg/feat` targets one, or a dependency). --cargo-features is
|
|
318
245
|
// repeatable and/or comma-separated; parseFeatureList is the one splitter (the resolver reuses it).
|
|
319
246
|
const { parseFeatureList } = await import('../src/loaders/cargo.js')
|
|
320
247
|
const cargoFeatures = parseFeatureList(values['cargo-features'] ?? [])
|
|
321
248
|
if (values['cargo-features'] !== undefined && cargoFeatures.length === 0) {
|
|
322
|
-
|
|
249
|
+
fail('Error: --cargo-features must list at least one feature (e.g. --cargo-features=serde,app/tls)')
|
|
323
250
|
}
|
|
324
251
|
const cargoNoDefaultFeatures = Boolean(values['cargo-no-default-features'])
|
|
325
252
|
const cargoAllFeatures = Boolean(values['cargo-all-features'])
|
|
@@ -327,50 +254,50 @@ if (command === '-v' || command === '--version') {
|
|
|
327
254
|
// `#[cfg(unix)]`-style code. A triple is letters, digits, `-`, `_` and `.` (`x86_64-unknown-linux-gnu`).
|
|
328
255
|
const cargoTarget = values['cargo-target'] ?? null
|
|
329
256
|
if (cargoTarget !== null && !/^[\w.-]+$/u.test(cargoTarget)) {
|
|
330
|
-
|
|
257
|
+
fail('Error: --cargo-target must be a target triple or "host" (e.g. --cargo-target=aarch64-apple-darwin)')
|
|
331
258
|
}
|
|
332
259
|
// --cargo-manifests: carry each bundled package's Cargo.toml and build script, the workspace
|
|
333
260
|
// Cargo.toml, Cargo.lock and .cargo/config.toml too (the Rust counterpart of --package-json).
|
|
334
261
|
const cargoManifests = Boolean(values['cargo-manifests'])
|
|
335
262
|
if (!allRust) {
|
|
336
263
|
const given = { 'cargo-features': cargoFeatures.length > 0, 'cargo-no-default-features': cargoNoDefaultFeatures, 'cargo-all-features': cargoAllFeatures, 'cargo-target': cargoTarget !== null, 'cargo-manifests': cargoManifests }
|
|
337
|
-
for (const [flag, on] of Object.entries(given)) if (on)
|
|
264
|
+
for (const [flag, on] of Object.entries(given)) if (on) fail(`Error: --${flag} is only valid for Rust bundles`)
|
|
338
265
|
}
|
|
339
|
-
if (values.scope && !allJs)
|
|
266
|
+
if (values.scope && !allJs) fail('Error: --scope is only valid for JS bundles')
|
|
340
267
|
if (values.scope && !['node_modules', 'full'].includes(values.scope)) {
|
|
341
|
-
|
|
268
|
+
fail('Error: --scope must be node_modules or full')
|
|
342
269
|
}
|
|
343
|
-
if (values.lockfile && !allJs)
|
|
270
|
+
if (values.lockfile && !allJs) fail('Error: --lockfile is only valid for JS bundles')
|
|
344
271
|
// --mainFields/--metro always emit a full-scope bundle, so they'd silently ignore --scope.
|
|
345
272
|
if (values.scope !== undefined && (values.mainFields !== undefined || values.metro)) {
|
|
346
|
-
|
|
273
|
+
fail('Error: --scope is not supported with --mainFields or --metro')
|
|
347
274
|
}
|
|
348
275
|
// --conditions: extra exports/imports resolution conditions merged onto Node's defaults;
|
|
349
276
|
// they don't honour legacy `mainFields` or platform suffixes (see --mainFields).
|
|
350
|
-
if (values.conditions !== undefined && !allJs)
|
|
277
|
+
if (values.conditions !== undefined && !allJs) fail('Error: --conditions is only valid for JS bundles')
|
|
351
278
|
const conditions = values.conditions === undefined
|
|
352
279
|
? []
|
|
353
280
|
: values.conditions.split(',').map((s) => s.trim()).filter(Boolean)
|
|
354
281
|
if (values.conditions !== undefined && conditions.length === 0) {
|
|
355
|
-
|
|
282
|
+
fail('Error: --conditions must list at least one condition name (e.g. --conditions=react-native,browser)')
|
|
356
283
|
}
|
|
357
284
|
// --flow: strip Flow type syntax (via the optional flow-remove-types dep) before parsing; JS-only.
|
|
358
|
-
if (values.flow && !allJs)
|
|
285
|
+
if (values.flow && !allJs) fail('Error: --flow is only valid for JS bundles')
|
|
359
286
|
const flow = Boolean(values.flow)
|
|
360
287
|
// --typescript: resolve like tsc -- a specifier naming a missing JS output (./x.js, .mjs, .cjs,
|
|
361
288
|
// extensionless) lands on its on-disk TS source (./x.ts, .mts, .cts); an existing .js always wins.
|
|
362
|
-
if (values.typescript && !allJs)
|
|
289
|
+
if (values.typescript && !allJs) fail('Error: --typescript is only valid for JS bundles')
|
|
363
290
|
const typescript = Boolean(values.typescript)
|
|
364
291
|
// --tsconfig: the config whose compilerOptions.paths aliases --typescript honours (default: the
|
|
365
292
|
// tsconfig.json beside each importing file's package.json). Meaningless without --typescript.
|
|
366
|
-
if (values.tsconfig !== undefined && !typescript)
|
|
293
|
+
if (values.tsconfig !== undefined && !typescript) fail('Error: --tsconfig is only valid with --typescript')
|
|
367
294
|
// --mainFields: legacy package entry fields (e.g. react-native,browser,main) for the non-exports resolver.
|
|
368
|
-
if (values.mainFields !== undefined && !allJs)
|
|
295
|
+
if (values.mainFields !== undefined && !allJs) fail('Error: --mainFields is only valid for JS bundles')
|
|
369
296
|
const mainFields = values.mainFields === undefined
|
|
370
297
|
? undefined
|
|
371
298
|
: values.mainFields.split(',').map((s) => s.trim()).filter(Boolean)
|
|
372
299
|
if (values.mainFields !== undefined && mainFields.length === 0) {
|
|
373
|
-
|
|
300
|
+
fail('Error: --mainFields must list at least one field (e.g. --mainFields=react-native,browser,main)')
|
|
374
301
|
}
|
|
375
302
|
// --metro: resolve like Metro/RN (its own conditions + mainFields, platform suffixes per
|
|
376
303
|
// --platforms target). --platforms is repeatable and/or comma-separated; the values union.
|
|
@@ -381,55 +308,55 @@ if (command === '-v' || command === '--version') {
|
|
|
381
308
|
const platforms = [...new Set((values.platforms ?? []).flatMap((p) => p.split(',')).map((s) => s.trim()).filter(Boolean))]
|
|
382
309
|
// A platform name becomes an edge key: reject '/' (parsers reject it) and '*' (reserved placeholder).
|
|
383
310
|
for (const p of platforms) {
|
|
384
|
-
if (p === '*' || p.includes('/'))
|
|
311
|
+
if (p === '*' || p.includes('/')) fail(`Error: invalid --platforms value '${p}' (a platform name can't contain '/' or be '*')`)
|
|
385
312
|
}
|
|
386
|
-
if ((metro || platforms.length > 0) && !allJs)
|
|
313
|
+
if ((metro || platforms.length > 0) && !allJs) fail('Error: --metro is only valid for JS bundles')
|
|
387
314
|
// --jsx: parse React Native's JSX-in-.js source in the static scanner (off by default; oxc,
|
|
388
315
|
// like tsc, auto-enables JSX for .jsx/.tsx only). Orthogonal to the resolver, so it pairs with
|
|
389
316
|
// plain/--mainFields/--metro alike -- JS-only, since no other entry language is scanned.
|
|
390
317
|
const jsx = Boolean(values.jsx)
|
|
391
|
-
if (jsx && !allJs)
|
|
318
|
+
if (jsx && !allJs) fail('Error: --jsx is only valid for JS bundles')
|
|
392
319
|
// --package-json: fold each bundled module's package.json into the bundle. JS-only (only JS
|
|
393
320
|
// modules carry an npm package.json; the sol/php/bash/rust bucketizers have no such manifest).
|
|
394
321
|
const packageJSON = Boolean(values['package-json'])
|
|
395
|
-
if (packageJSON && !allJs)
|
|
322
|
+
if (packageJSON && !allJs) fail('Error: --package-json is only valid for JS bundles')
|
|
396
323
|
// --resources: comma-separated extension/filename allowlist for assets reached through the graph
|
|
397
324
|
// (e.g. --resources=png,svg). They're carried as resources instead of failing "can't carry" --
|
|
398
325
|
// for graphs that aren't fully loadable in JS (Metro consumes such assets). JS-only.
|
|
399
|
-
if (values.resources !== undefined && !allJs)
|
|
326
|
+
if (values.resources !== undefined && !allJs) fail('Error: --resources is only valid for JS bundles')
|
|
400
327
|
const resources = values.resources === undefined
|
|
401
328
|
? []
|
|
402
329
|
: values.resources.split(',').map((s) => s.trim()).filter(Boolean)
|
|
403
330
|
if (values.resources !== undefined && resources.length === 0) {
|
|
404
|
-
|
|
331
|
+
fail('Error: --resources must list at least one extension or filename (e.g. --resources=png,svg)')
|
|
405
332
|
}
|
|
406
333
|
try {
|
|
407
334
|
parseResourcesOption('--resources', resources) // validate early for a clean usage error (code exts / bad chars rejected)
|
|
408
335
|
} catch (cause) {
|
|
409
|
-
|
|
336
|
+
fail(`Error: ${cause.message}`)
|
|
410
337
|
}
|
|
411
338
|
if (metro) {
|
|
412
|
-
if (conditions.length > 0)
|
|
413
|
-
if (mainFields !== undefined)
|
|
414
|
-
if (platforms.length === 0)
|
|
339
|
+
if (conditions.length > 0) fail("Error: --conditions can't be combined with --metro (it sets its own conditions)")
|
|
340
|
+
if (mainFields !== undefined) fail("Error: --mainFields can't be combined with --metro (it sets its own mainFields)")
|
|
341
|
+
if (platforms.length === 0) fail('Error: --metro requires --platforms (e.g. --platforms=ios,android)')
|
|
415
342
|
} else if (platforms.length > 0) {
|
|
416
|
-
|
|
343
|
+
fail('Error: --platforms is only valid with --metro')
|
|
417
344
|
}
|
|
418
|
-
if (metroResolver && !metro)
|
|
345
|
+
if (metroResolver && !metro) fail('Error: --metro-resolver is only valid with --metro')
|
|
419
346
|
// metro-resolver can't substitute .js -> .ts, so --typescript would silently not apply -- reject it.
|
|
420
|
-
if (typescript && metroResolver)
|
|
347
|
+
if (typescript && metroResolver) fail("Error: --typescript is not supported with --metro-resolver (the project's metro-resolver doesn't substitute .js -> .ts)")
|
|
421
348
|
// --brotli-quality: valid for every entry language (no allJs gate).
|
|
422
349
|
let brotliQuality
|
|
423
350
|
if (values['brotli-quality'] !== undefined) {
|
|
424
351
|
try {
|
|
425
352
|
brotliQuality = parseBrotliQuality('--brotli-quality', values['brotli-quality'])
|
|
426
353
|
} catch (cause) {
|
|
427
|
-
|
|
354
|
+
fail(`Error: ${cause.message}`)
|
|
428
355
|
}
|
|
429
356
|
}
|
|
430
357
|
// --add merges into the existing bundle at --output, so it can't target write-only stdout.
|
|
431
358
|
const add = Boolean(values.add)
|
|
432
|
-
if (add && values.output === '-')
|
|
359
|
+
if (add && values.output === '-') fail('Error: --add cannot be combined with --output=-')
|
|
433
360
|
const { bundleCommand } = await import('../src/cmd/bundle.js')
|
|
434
361
|
await bundleCommand({
|
|
435
362
|
cwd: process.cwd(),
|
|
@@ -486,10 +413,10 @@ if (command === '-v' || command === '--version') {
|
|
|
486
413
|
generate: { type: 'string', multiple: true },
|
|
487
414
|
}, {
|
|
488
415
|
valueFlags: ['--github', '--sha', '--tag', '--directory', '--package-manager', '--package-manager-version', '--mapping', '--output', '--scope', '--lockfile', '--conditions', '--mainFields', '--platforms', '--tsconfig', '--resources', '--brotli-quality', '--generate', '-o'],
|
|
489
|
-
onError:
|
|
416
|
+
onError: fail,
|
|
490
417
|
})
|
|
491
|
-
if (values.github === undefined)
|
|
492
|
-
if (values.sha !== undefined && values.tag !== undefined)
|
|
418
|
+
if (values.github === undefined) fail('Error: github-bundle requires --github=owner/name, the repo to bundle')
|
|
419
|
+
if (values.sha !== undefined && values.tag !== undefined) fail('Error: github-bundle takes --sha or --tag, not both')
|
|
493
420
|
// Whether each option applies to the entries is buildGitHubBundle's to say.
|
|
494
421
|
const list = (value) => [value ?? []].flat().flatMap((v) => v.split(',')).map((s) => s.trim()).filter(Boolean)
|
|
495
422
|
let brotliQuality
|
|
@@ -497,7 +424,7 @@ if (command === '-v' || command === '--version') {
|
|
|
497
424
|
try {
|
|
498
425
|
brotliQuality = parseBrotliQuality('--brotli-quality', values['brotli-quality'])
|
|
499
426
|
} catch (cause) {
|
|
500
|
-
|
|
427
|
+
fail(`Error: ${cause.message}`)
|
|
501
428
|
}
|
|
502
429
|
}
|
|
503
430
|
// Tarballs, zips and GitHub trees are cached where `stasis audit` caches.
|
|
@@ -533,10 +460,10 @@ if (command === '-v' || command === '--version') {
|
|
|
533
460
|
} else if (command === 'add') {
|
|
534
461
|
// add packs the listed files (no resolver), taking split targets + resource allowlist from
|
|
535
462
|
// stasis.config.json; it takes no flags.
|
|
536
|
-
if (argv.length === 0)
|
|
537
|
-
if (argv.some((a) => a.startsWith('-')))
|
|
463
|
+
if (argv.length === 0) fail('Nothing to add: no file given')
|
|
464
|
+
if (argv.some((a) => a.startsWith('-'))) fail('Error: add takes no options; its targets and resource allowlist come from stasis.config.json')
|
|
538
465
|
const { addCommand } = await import('@exodus/stasis-core/add')
|
|
539
|
-
// Let addCommand's runtime errors propagate as-is; wrapping in
|
|
466
|
+
// Let addCommand's runtime errors propagate as-is; wrapping in fail() would bury the cause.
|
|
540
467
|
addCommand({ cwd: process.cwd(), entries: argv, logLabel: 'stasis' })
|
|
541
468
|
} else if (command === 'build') {
|
|
542
469
|
const options = {
|
|
@@ -553,37 +480,37 @@ if (command === '-v' || command === '--version') {
|
|
|
553
480
|
}
|
|
554
481
|
const values = parseLeadingOptions(argv, options, {
|
|
555
482
|
valueFlags: ['--output', '-o', '--format', '--platform', '--define', '--external', '--loader'],
|
|
556
|
-
onError:
|
|
483
|
+
onError: fail,
|
|
557
484
|
})
|
|
558
|
-
if (argv.length === 0)
|
|
559
|
-
if (argv.length > 2)
|
|
485
|
+
if (argv.length === 0) fail('Nothing to build: no bundle or lockfile given')
|
|
486
|
+
if (argv.length > 2) fail('Error: build takes a bundle or lockfile and an optional entry point')
|
|
560
487
|
// esbuild output knobs; the import graph itself is fixed by the artifact, not these.
|
|
561
488
|
if (values.format !== undefined && !['esm', 'cjs', 'iife'].includes(values.format)) {
|
|
562
|
-
|
|
489
|
+
fail('Error: --format must be esm, cjs, or iife')
|
|
563
490
|
}
|
|
564
491
|
if (values.platform !== undefined && !['node', 'browser', 'neutral', 'hermes'].includes(values.platform)) {
|
|
565
|
-
|
|
492
|
+
fail('Error: --platform must be node, browser, neutral, or hermes')
|
|
566
493
|
}
|
|
567
494
|
// Hermes has no import/export; --format defaults to iife under --platform=hermes (see buildCommand).
|
|
568
495
|
if (values.platform === 'hermes' && values.format === 'esm') {
|
|
569
|
-
|
|
496
|
+
fail('Error: --platform=hermes cannot emit ESM; use --format=iife (the hermes default) or cjs')
|
|
570
497
|
}
|
|
571
|
-
if (!values.output)
|
|
498
|
+
if (!values.output) fail('Error: --output is required (a .js/.cjs/.mjs file, or a directory)')
|
|
572
499
|
// build can't stream to stdout; without this, --output=- would create a dir literally named `-`.
|
|
573
|
-
if (values.output === '-')
|
|
500
|
+
if (values.output === '-') fail('Error: stasis build cannot stream to stdout; --output must be a .js/.cjs/.mjs file or a directory')
|
|
574
501
|
// --define=KEY=VALUE -> esbuild define map; VALUE is forwarded verbatim and must be valid JS
|
|
575
502
|
// (a JSON literal or an identifier), e.g. --define=DEBUG=false.
|
|
576
503
|
const define = {}
|
|
577
504
|
for (const entry of values.define ?? []) {
|
|
578
505
|
const eq = entry.indexOf('=')
|
|
579
|
-
if (eq <= 0)
|
|
506
|
+
if (eq <= 0) fail(`Error: --define must be KEY=VALUE (got '${entry}')`)
|
|
580
507
|
define[entry.slice(0, eq)] = entry.slice(eq + 1)
|
|
581
508
|
}
|
|
582
509
|
// --loader=.ext:NAME -> esbuild loader override for that extension (e.g. --loader=.js:jsx).
|
|
583
510
|
const loader = {}
|
|
584
511
|
for (const entry of values.loader ?? []) {
|
|
585
512
|
const colon = entry.indexOf(':')
|
|
586
|
-
if (colon <= 0 || !entry.startsWith('.'))
|
|
513
|
+
if (colon <= 0 || !entry.startsWith('.')) fail(`Error: --loader must be .EXT:LOADER (got '${entry}')`)
|
|
587
514
|
loader[entry.slice(0, colon)] = entry.slice(colon + 1)
|
|
588
515
|
}
|
|
589
516
|
const { buildCommand } = await import('../src/cmd/build.js')
|
|
@@ -605,9 +532,9 @@ if (command === '-v' || command === '--version') {
|
|
|
605
532
|
const options = {
|
|
606
533
|
output: { type: 'string', short: 'o' },
|
|
607
534
|
}
|
|
608
|
-
const values = parseLeadingOptions(argv, options, { valueFlags: ['--output', '-o'], onError:
|
|
609
|
-
if (argv.length === 0)
|
|
610
|
-
if (argv.length > 1)
|
|
535
|
+
const values = parseLeadingOptions(argv, options, { valueFlags: ['--output', '-o'], onError: fail })
|
|
536
|
+
if (argv.length === 0) fail('Nothing to extract: no bundle file given')
|
|
537
|
+
if (argv.length > 1) fail('Error: extract takes exactly one bundle file')
|
|
611
538
|
const { extractCommand } = await import('@exodus/stasis-core/extract')
|
|
612
539
|
extractCommand({ cwd: process.cwd(), bundleFile: argv[0], output: values.output, logLabel: 'stasis' })
|
|
613
540
|
} else if (command === 'diff') {
|
|
@@ -615,27 +542,27 @@ if (command === '-v' || command === '--version') {
|
|
|
615
542
|
stat: { type: 'boolean' },
|
|
616
543
|
imports: { type: 'boolean' },
|
|
617
544
|
}
|
|
618
|
-
const values = parseLeadingOptions(argv, options, { onError:
|
|
545
|
+
const values = parseLeadingOptions(argv, options, { onError: fail })
|
|
619
546
|
// --stat is required for now (it reports what changed, not the content); bare `stasis diff`
|
|
620
547
|
// is reserved for a future content-level diff.
|
|
621
|
-
if (!values.stat)
|
|
622
|
-
if (argv.length !== 2)
|
|
548
|
+
if (!values.stat) fail('Error: stasis diff currently requires --stat')
|
|
549
|
+
if (argv.length !== 2) fail('Error: stasis diff takes exactly two files (lockfile or bundle)')
|
|
623
550
|
// --imports also diffs the resolution graphs (added/removed/redirected edges); off by default (verbose).
|
|
624
551
|
const { diffCommand } = await import('../src/cmd/diff.js')
|
|
625
552
|
const { differences } = diffCommand({ cwd: process.cwd(), left: argv[0], right: argv[1], stat: true, imports: values.imports })
|
|
626
553
|
// Exit non-zero when the artifacts differ, so it composes in CI.
|
|
627
554
|
process.exitCode = differences ? 1 : 0
|
|
628
555
|
} else if (command === 'prune') {
|
|
629
|
-
if (argv.length > 1)
|
|
556
|
+
if (argv.length > 1) fail('Error: prune takes at most one path argument')
|
|
630
557
|
const root = argv[0] ? resolve(argv[0]) : process.cwd()
|
|
631
558
|
const { prune } = await import('@exodus/stasis-core/prune')
|
|
632
559
|
const { removed, validated } = prune({ root })
|
|
633
560
|
console.warn(`[stasis] prune: validated ${validated.length} file(s), removed ${removed.length} file(s)`)
|
|
634
561
|
} else if (command === 'audit') {
|
|
635
|
-
const values = parseLeadingOptions(argv, { why: { type: 'boolean' }, 'why-deep': { type: 'boolean' }, 'why-full': { type: 'boolean' }, reason: { type: 'string' }, 'repo-advisories': { type: 'boolean' } }, { valueFlags: ['--reason'], onError:
|
|
636
|
-
if (argv.length === 0)
|
|
562
|
+
const values = parseLeadingOptions(argv, { why: { type: 'boolean' }, 'why-deep': { type: 'boolean' }, 'why-full': { type: 'boolean' }, reason: { type: 'string' }, 'repo-advisories': { type: 'boolean' } }, { valueFlags: ['--reason'], onError: fail })
|
|
563
|
+
if (argv.length === 0) fail('Nothing to audit: no path to file given')
|
|
637
564
|
const repoAdvisories = Boolean(values['repo-advisories'])
|
|
638
|
-
if (repoAdvisories && !process.env.GITHUB_TOKEN)
|
|
565
|
+
if (repoAdvisories && !process.env.GITHUB_TOKEN) fail('Error: --repo-advisories requires a GitHub token in GITHUB_TOKEN')
|
|
639
566
|
// A Soldeer package's and a GitHub repo's advisories are their repository's, asked of GitHub with
|
|
640
567
|
// GITHUB_TOKEN where it is set; the client asks nothing for any other package by itself.
|
|
641
568
|
const github = (await import('@preventive/upstream/github.js')).createClient({ token: process.env.GITHUB_TOKEN || null })
|
|
@@ -653,10 +580,10 @@ if (command === '-v' || command === '--version') {
|
|
|
653
580
|
format: { type: 'string' },
|
|
654
581
|
output: { type: 'string', short: 'o' },
|
|
655
582
|
}
|
|
656
|
-
const values = parseLeadingOptions(argv, options, { valueFlags: ['--format', '--output', '-o'], onError:
|
|
657
|
-
if (!values.format)
|
|
658
|
-
if (!['spdx', 'cyclonedx'].includes(values.format))
|
|
659
|
-
if (argv.length === 0)
|
|
583
|
+
const values = parseLeadingOptions(argv, options, { valueFlags: ['--format', '--output', '-o'], onError: fail })
|
|
584
|
+
if (!values.format) fail('Error: stasis sbom requires --format=(spdx|cyclonedx)')
|
|
585
|
+
if (!['spdx', 'cyclonedx'].includes(values.format)) fail('Error: --format must be spdx or cyclonedx')
|
|
586
|
+
if (argv.length === 0) fail('Nothing to export: no lockfile or bundle given')
|
|
660
587
|
const { sbomCommand } = await import('../src/cmd/sbom.js')
|
|
661
588
|
sbomCommand({
|
|
662
589
|
cwd: process.cwd(),
|
|
@@ -664,6 +591,8 @@ if (command === '-v' || command === '--version') {
|
|
|
664
591
|
format: values.format,
|
|
665
592
|
output: values.output,
|
|
666
593
|
})
|
|
667
|
-
} else {
|
|
594
|
+
} else if (command === undefined) {
|
|
668
595
|
usage()
|
|
596
|
+
} else {
|
|
597
|
+
fail(`Error: unknown command '${command}'`)
|
|
669
598
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/stasis",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.9",
|
|
4
4
|
"description": "File-level source bundles and lockfiles",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"./lockfile": "./src/lockfile.js",
|
|
20
20
|
"./prune": "./src/prune.js",
|
|
21
21
|
"./sbom": "./src/sbom.js",
|
|
22
|
+
"./audit-corrections": "./src/audit-corrections.js",
|
|
22
23
|
"./vfs-bundle": "./src/vfs-bundle.js",
|
|
23
24
|
"./logo.svg": "./logo.svg"
|
|
24
25
|
},
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"src/cmd/sbom.js",
|
|
41
42
|
"src/diff.js",
|
|
42
43
|
"src/esbuild.js",
|
|
44
|
+
"src/installed-commits.js",
|
|
43
45
|
"src/json-stream.js",
|
|
44
46
|
"src/loader.js",
|
|
45
47
|
"src/loader-mock.js",
|
|
@@ -93,12 +95,12 @@
|
|
|
93
95
|
"homepage": "https://github.com/PreventiveMeasures/stasis#readme",
|
|
94
96
|
"dependencies": {
|
|
95
97
|
"@exodus/bytes": "^1.16.0",
|
|
96
|
-
"@exodus/stasis-core": "1.0.0-beta.
|
|
97
|
-
"@exodus/stasis-plugins": "1.0.0-beta.
|
|
98
|
+
"@exodus/stasis-core": "1.0.0-beta.9",
|
|
99
|
+
"@exodus/stasis-plugins": "1.0.0-beta.9",
|
|
98
100
|
"@preventive/archive": "1.0.0-beta.4",
|
|
99
|
-
"@preventive/deptree": "1.0.0-alpha.
|
|
101
|
+
"@preventive/deptree": "1.0.0-alpha.11",
|
|
100
102
|
"@preventive/lockfile": "1.0.0-alpha.7",
|
|
101
|
-
"@preventive/upstream": "1.0.0-alpha.
|
|
103
|
+
"@preventive/upstream": "1.0.0-alpha.12",
|
|
102
104
|
"@preventive/vfs": "1.0.0-beta.4",
|
|
103
105
|
"oxc-parser": "^0.152.0"
|
|
104
106
|
},
|