@nimbus-sh/worker 0.12.0 → 0.13.1
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/dist/esbuild-cli-artifact.generated.d.ts +1 -1
- package/dist/esbuild-cli-artifact.generated.js +4 -4
- package/dist/esbuild-wasm-bundle.generated.d.ts +11 -10
- package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
- package/dist/esbuild-wasm-bundle.generated.js +11 -10
- package/dist/facets/build-facet.d.ts +53 -0
- package/dist/facets/build-facet.d.ts.map +1 -0
- package/dist/facets/build-facet.js +346 -0
- package/dist/facets/cirrus-real.d.ts +3 -0
- package/dist/facets/cirrus-real.d.ts.map +1 -1
- package/dist/facets/cirrus-real.js +1 -2
- package/dist/facets/compose.d.ts +1 -0
- package/dist/facets/compose.d.ts.map +1 -1
- package/dist/facets/compose.js +3 -0
- package/dist/facets/data-plan.d.ts +14 -30
- package/dist/facets/data-plan.d.ts.map +1 -1
- package/dist/facets/data-plan.js +153 -13
- package/dist/facets/dev-stylesheet.d.ts +27 -0
- package/dist/facets/dev-stylesheet.d.ts.map +1 -0
- package/dist/facets/dev-stylesheet.js +65 -0
- package/dist/facets/esbuild-transform.d.ts +23 -18
- package/dist/facets/esbuild-transform.d.ts.map +1 -1
- package/dist/facets/esbuild-transform.js +63 -109
- package/dist/facets/manager.d.ts +49 -9
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +238 -69
- package/dist/facets/oxc-transform.d.ts +29 -0
- package/dist/facets/oxc-transform.d.ts.map +1 -0
- package/dist/facets/oxc-transform.js +198 -0
- package/dist/facets/prebundle-pool.d.ts +23 -0
- package/dist/facets/prebundle-pool.d.ts.map +1 -0
- package/dist/facets/prebundle-pool.js +55 -0
- package/dist/facets/process.d.ts +13 -6
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +37 -31
- package/dist/facets/transform-store.d.ts +4 -4
- package/dist/facets/transform-store.js +4 -4
- package/dist/facets/vite-dev-server.d.ts +89 -12
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +66 -43
- package/dist/hosted/commands.d.ts.map +1 -1
- package/dist/hosted/commands.js +20 -4
- package/dist/hosted/runtime.d.ts.map +1 -1
- package/dist/hosted/runtime.js +7 -2
- package/dist/hosted/services.d.ts +3 -3
- package/dist/hosted/services.d.ts.map +1 -1
- package/dist/hosted/services.js +102 -95
- package/dist/hosted/session.d.ts +1 -0
- package/dist/hosted/session.d.ts.map +1 -1
- package/dist/loaders/generated-workers.d.ts +3 -0
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +3 -0
- package/dist/napi-wasm-artifacts.generated.js +2 -2
- package/dist/node-shims-artifact.generated.d.ts +14 -1
- package/dist/node-shims-artifact.generated.d.ts.map +1 -1
- package/dist/node-shims-artifact.generated.js +23 -10
- package/dist/npm/bin-links.d.ts +7 -4
- package/dist/npm/bin-links.d.ts.map +1 -1
- package/dist/npm/bin-links.js +14 -55
- package/dist/npm/cache.d.ts +6 -0
- package/dist/npm/cache.d.ts.map +1 -1
- package/dist/npm/cache.js +26 -4
- package/dist/npm/installer.d.ts +1 -1
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +26 -52
- package/dist/npm/pre-bundle-facet.d.ts +15 -117
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +13 -511
- package/dist/oxc-facet-artifact.generated.d.ts +18 -0
- package/dist/oxc-facet-artifact.generated.d.ts.map +1 -0
- package/dist/oxc-facet-artifact.generated.js +17 -0
- package/dist/oxc-wasm-artifact.generated.d.ts +20 -0
- package/dist/oxc-wasm-artifact.generated.d.ts.map +1 -0
- package/dist/oxc-wasm-artifact.generated.js +19 -0
- package/dist/rolldown-facet-artifact.generated.d.ts +18 -0
- package/dist/rolldown-facet-artifact.generated.d.ts.map +1 -0
- package/dist/rolldown-facet-artifact.generated.js +17 -0
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +9 -0
- package/dist/runtime/bash-repl.d.ts.map +1 -1
- package/dist/runtime/bash-repl.js +1 -4
- package/dist/runtime/cpython-resident.d.ts.map +1 -1
- package/dist/runtime/cpython-resident.js +1 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts +25 -23
- package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
- package/dist/runtime/esbuild-wasm-bytes.js +30 -25
- package/dist/runtime/facet-loader-host.d.ts +5 -4
- package/dist/runtime/facet-loader-host.d.ts.map +1 -1
- package/dist/runtime/facet-loader-host.js +5 -4
- package/dist/runtime/node-runner.d.ts +2 -0
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-runner.js +1 -0
- package/dist/runtime/node-shims-artifact.d.ts +8 -1
- package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
- package/dist/runtime/node-shims-artifact.js +32 -4
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +145 -109
- package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
- package/dist/runtime/opencode-facet-runner.js +6 -2
- package/dist/runtime/oxc-wasm-bytes.d.ts +19 -0
- package/dist/runtime/oxc-wasm-bytes.d.ts.map +1 -0
- package/dist/runtime/oxc-wasm-bytes.js +39 -0
- package/dist/runtime/package-manager.js +1 -1
- package/dist/runtime/process-logs-api.d.ts.map +1 -1
- package/dist/runtime/process-logs-api.js +2 -0
- package/dist/runtime/project-fs.d.ts +8 -1
- package/dist/runtime/project-fs.d.ts.map +1 -1
- package/dist/runtime/project-fs.js +15 -1
- package/dist/runtime/python-repl.d.ts +40 -1
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +19 -11
- package/dist/runtime/ruby-repl.d.ts +38 -0
- package/dist/runtime/ruby-repl.d.ts.map +1 -1
- package/dist/runtime/ruby-repl.js +24 -5
- package/dist/runtime/ruby-resident.d.ts.map +1 -1
- package/dist/runtime/ruby-resident.js +2 -0
- package/dist/session/nimbus-session.d.ts +13 -8
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +11 -5
- package/dist/session/programmatic.d.ts +25 -30
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +65 -93
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +23 -7
- package/dist/session/rpc.d.ts +16 -6
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +53 -13
- package/dist/session/start-real-vite.d.ts +24 -9
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +40 -4
- package/dist/session/supervisor-op.d.ts +14 -0
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +49 -0
- package/dist/session/supervisor-rpc.d.ts +11 -0
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +18 -0
- package/dist/session/vite-command.d.ts.map +1 -1
- package/dist/session/vite-command.js +23 -19
- package/dist/session/ws-relay.d.ts.map +1 -1
- package/dist/session/ws-relay.js +13 -1
- package/dist/shell/npm-bin-entrypoints.d.ts +3 -1
- package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
- package/dist/shell/npm-bin-entrypoints.js +53 -25
- package/dist/vfs/facet-resident-store.d.ts.map +1 -1
- package/dist/vfs/facet-resident-store.js +11 -5
- package/dist/wrangler/nimbus-wrangler.d.ts +8 -0
- package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
- package/dist/wrangler/nimbus-wrangler.js +54 -57
- package/package.json +7 -5
- package/public/_assets/esbuild-0.24.2.wasm +0 -0
- package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/napi-wasm-loader.mjs +4 -4
- package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/provenance.json +2 -2
- package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.provenance.json +72 -0
- package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.wasm +0 -0
- package/public/_assets/runtime/esbuild-cli-f13e6a112b03ac07.js +1143 -0
- package/public/_assets/runtime/js-interpreter-3c37c48024f0f651.js +23 -0
- package/public/_assets/runtime/js-interpreter-ops-2f4dfec6798e70fc.js +290 -0
- package/public/_assets/runtime/js-interpreter-primordials-e620a61b7e9800fa.js +1 -0
- package/public/_assets/runtime/{node-shims-f92f16c7d7c532e9.js → node-shims-e3adc4576da31e6e.js} +651 -103
- package/public/_assets/runtime/{esbuild-cli-b7aecffd4c6a36fe.js → oxc-facet-fd86fefcdf16bf9c.js} +217 -1047
- package/public/_assets/runtime/{resident-store-92386265e8b306dc.js → resident-store-009f39539ac989fc.js} +11 -5
- package/public/_assets/runtime/rolldown-facet-09983434e2dbe93e.js +15165 -0
- package/public/_assets/runtime/{vfs-write-ledger-d33ee0034446c3e0.js → vfs-write-ledger-9e9594591425a9b8.js} +4 -1
- package/scripts/acorn-primordials.mjs +637 -0
- package/scripts/bundle-esbuild-wasm.mjs +42 -50
- package/scripts/bundle-facet-workers.mjs +247 -18
- package/scripts/bundle-node-shims.mjs +29 -2
- package/scripts/bundle-oxc-wasm.mjs +105 -0
- package/scripts/interpreter-bundle.mjs +105 -0
- package/scripts/napi-wasm/build.mjs +4 -1
- package/scripts/napi-wasm/loader/napi-wasm-loader.ts +38 -3
- package/scripts/oxc-wasm/Cargo.lock +1246 -0
- package/scripts/oxc-wasm/Cargo.toml +30 -0
- package/scripts/oxc-wasm/build.mjs +249 -0
- package/scripts/oxc-wasm/examples/memory.rs +51 -0
- package/scripts/oxc-wasm/examples/transform.rs +20 -0
- package/scripts/oxc-wasm/inputs.mjs +32 -0
- package/scripts/oxc-wasm/rust-toolchain.toml +5 -0
- package/scripts/oxc-wasm/src/abi.rs +136 -0
- package/scripts/oxc-wasm/src/diagnostics.rs +49 -0
- package/scripts/oxc-wasm/src/helpers.rs +139 -0
- package/scripts/oxc-wasm/src/lib.rs +247 -0
- package/scripts/oxc-wasm/src/module.rs +978 -0
- package/scripts/oxc-wasm/src/names.rs +175 -0
- package/scripts/oxc-wasm/src/options.rs +154 -0
- package/scripts/parser-audit.mjs +492 -0
- package/scripts/rolldown-facet/entry.mjs +21 -0
- package/scripts/rolldown-facet/shims.mjs +64 -0
- package/dist/facets/esbuild-bundle-pool.d.ts +0 -43
- package/dist/facets/esbuild-bundle-pool.d.ts.map +0 -1
- package/dist/facets/esbuild-bundle-pool.js +0 -80
- package/dist/loaders/pre-bundle-preamble.d.ts +0 -44
- package/dist/loaders/pre-bundle-preamble.d.ts.map +0 -1
- package/dist/loaders/pre-bundle-preamble.js +0 -116
- package/dist/runtime/host-wasm.d.ts +0 -8
- package/dist/runtime/host-wasm.d.ts.map +0 -1
- package/dist/runtime/host-wasm.js +0 -33
- /package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/wasi-trampoline.wasm +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Nimbus's Oxc transform, built as wasm by build.mjs (see src/lib.rs).
|
|
2
|
+
[package]
|
|
3
|
+
name = "nimbus_oxc"
|
|
4
|
+
version = "0.1.0"
|
|
5
|
+
edition = "2024"
|
|
6
|
+
publish = false
|
|
7
|
+
license = "MIT"
|
|
8
|
+
|
|
9
|
+
# Its own workspace: build.mjs copies the crate into a work directory.
|
|
10
|
+
[workspace]
|
|
11
|
+
|
|
12
|
+
[lib]
|
|
13
|
+
# cdylib for the wasm; rlib for the examples, which run the same transform natively.
|
|
14
|
+
crate-type = ["cdylib", "rlib"]
|
|
15
|
+
doctest = false
|
|
16
|
+
|
|
17
|
+
[dependencies]
|
|
18
|
+
oxc = { version = "=0.152.0", default-features = false, features = ["transformer", "semantic", "codegen", "ast_visit"] }
|
|
19
|
+
oxc_ecmascript = "=0.152.0"
|
|
20
|
+
oxc_traverse = "=0.152.0"
|
|
21
|
+
memchr = "2"
|
|
22
|
+
rustc-hash = "2"
|
|
23
|
+
|
|
24
|
+
[profile.release]
|
|
25
|
+
codegen-units = 1
|
|
26
|
+
lto = "fat"
|
|
27
|
+
opt-level = 3
|
|
28
|
+
panic = "abort"
|
|
29
|
+
debug = false
|
|
30
|
+
strip = "symbols"
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* build.mjs — build Nimbus's Oxc transform (this crate) as wasm, reproducibly.
|
|
4
|
+
*
|
|
5
|
+
* bun packages/worker/scripts/oxc-wasm/build.mjs --work <dir> --out <dir>
|
|
6
|
+
*
|
|
7
|
+
* Cargo may run in its own bounded cgroup (keep this Bun orchestrator small):
|
|
8
|
+
* NIMBUS_CARGO_RUNNER=/mnt/scratch/nimbus/run-bounded NIMBUS_CARGO_JOBS=4 \
|
|
9
|
+
* NIMBUS_TEST_MEMORY_MAX=2G NIMBUS_TEST_TIMEOUT=3600 run-bounded bun .../build.mjs ...
|
|
10
|
+
*
|
|
11
|
+
* Every input is pinned and every check fails loudly:
|
|
12
|
+
* 1. The crate (Cargo.toml, Cargo.lock, rust-toolchain.toml, src/) is copied
|
|
13
|
+
* into <work>/nimbus-oxc, so the build path is the work directory's, and
|
|
14
|
+
* built with `cargo --locked` (crates.io checksums in Cargo.lock) by the
|
|
15
|
+
* exact rustc rust-toolchain.toml names, for wasm32-unknown-unknown.
|
|
16
|
+
* 2. binaryen's release archive is checked against its pinned SHA-256, and
|
|
17
|
+
* its wasm-opt drops the wasm-bindgen exports oxc-browserslist's js-sys
|
|
18
|
+
* dependency leaves (no code path here calls into JavaScript), replaces
|
|
19
|
+
* the one import they keep, a diverging `throw`, with a trap, and
|
|
20
|
+
* optimizes (-O3).
|
|
21
|
+
* 3. The result must import nothing, export exactly the ABI (src/abi.rs),
|
|
22
|
+
* and define an unshared memory of at most OXC_MAX_INITIAL_PAGES.
|
|
23
|
+
* Output (--out): nimbus-oxc.wasm and provenance.json (inputs, toolchain,
|
|
24
|
+
* flags, digests). Stage it with
|
|
25
|
+
* NIMBUS_OXC_WASM_ARTIFACTS=<out> node packages/worker/scripts/bundle-oxc-wasm.mjs
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { createHash } from 'node:crypto';
|
|
29
|
+
import { spawnSync } from 'node:child_process';
|
|
30
|
+
import { promises as fs, existsSync } from 'node:fs';
|
|
31
|
+
import path from 'node:path';
|
|
32
|
+
import { fileURLToPath } from 'node:url';
|
|
33
|
+
import { changedInputs, oxcWasmInputs } from './inputs.mjs';
|
|
34
|
+
|
|
35
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
const TARGET = 'wasm32-unknown-unknown';
|
|
37
|
+
/**
|
|
38
|
+
* Bytes of linear memory the wasm shadow stack takes. Call frames live on the
|
|
39
|
+
* host's native stack; this one holds what they spill, and at 4 MiB it is not
|
|
40
|
+
* what limits nesting depth under workerd's or Node's default stack.
|
|
41
|
+
*/
|
|
42
|
+
const WASM_STACK_BYTES = 4 * 1024 * 1024;
|
|
43
|
+
/**
|
|
44
|
+
* The ceiling on the module's floor, the stack plus static data, in 64 KiB
|
|
45
|
+
* pages: 5 MiB. Napi-rs's wasm builds start at 981 pages (a 64 MB stack).
|
|
46
|
+
*/
|
|
47
|
+
const OXC_MAX_INITIAL_PAGES = 80;
|
|
48
|
+
const RUSTFLAGS = ['-Ctarget-feature=+simd128', `-Clink-arg=-zstack-size=${WASM_STACK_BYTES}`];
|
|
49
|
+
const BINARYEN = {
|
|
50
|
+
version: 'version_133',
|
|
51
|
+
url: 'https://github.com/WebAssembly/binaryen/releases/download/version_133/binaryen-version_133-x86_64-linux.tar.gz',
|
|
52
|
+
sha256: '2dc9c7813f5375db93d96ead4b78222fcc3e2677bbb832297af4797782a37489',
|
|
53
|
+
};
|
|
54
|
+
const WASM_OPT_ARGS = [
|
|
55
|
+
// The features rustc 1.98 enables for wasm32-unknown-unknown, plus simd128.
|
|
56
|
+
'--enable-simd', '--enable-bulk-memory', '--enable-nontrapping-float-to-int', '--enable-sign-ext',
|
|
57
|
+
'--enable-mutable-globals', '--enable-reference-types', '--enable-multivalue',
|
|
58
|
+
'--remove-exports', '--pass-arg=remove-exports@__*',
|
|
59
|
+
'--remove-imports',
|
|
60
|
+
'-O3',
|
|
61
|
+
];
|
|
62
|
+
const EXPORTS = ['memory', 'nimbus_oxc_alloc', 'nimbus_oxc_realloc', 'nimbus_oxc_release', 'nimbus_oxc_transform'];
|
|
63
|
+
const CRATE_FILES = ['Cargo.toml', 'Cargo.lock', 'rust-toolchain.toml', 'src'];
|
|
64
|
+
|
|
65
|
+
function arg(name) {
|
|
66
|
+
const i = process.argv.indexOf(name);
|
|
67
|
+
if (i < 0 || !process.argv[i + 1]) throw new Error(`oxc-wasm: ${name} <value> is required`);
|
|
68
|
+
return path.resolve(process.argv[i + 1]);
|
|
69
|
+
}
|
|
70
|
+
const WORK = arg('--work');
|
|
71
|
+
const OUT = arg('--out');
|
|
72
|
+
|
|
73
|
+
const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
74
|
+
|
|
75
|
+
function run(cmd, args, opts = {}) {
|
|
76
|
+
const r = spawnSync(cmd, args, { stdio: opts.capture ? 'pipe' : 'inherit', encoding: 'utf8', ...opts });
|
|
77
|
+
if (r.status !== 0) throw new Error(`oxc-wasm: ${cmd} ${args.join(' ')} exited ${r.status}${r.stderr ? `\n${r.stderr}` : ''}`);
|
|
78
|
+
return r.stdout ?? '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function copyCrate(into) {
|
|
82
|
+
await fs.rm(into, { recursive: true, force: true });
|
|
83
|
+
await fs.mkdir(into, { recursive: true });
|
|
84
|
+
for (const entry of CRATE_FILES) await fs.cp(path.join(HERE, entry), path.join(into, entry), { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function toolchain(crateDir, text) {
|
|
88
|
+
const pinned = text.match(/^channel = "(.+)"$/m)?.[1];
|
|
89
|
+
if (!pinned) throw new Error('oxc-wasm: rust-toolchain.toml names no channel');
|
|
90
|
+
// rustup, when present, owns the pinned toolchain; otherwise the rustc on PATH must be it.
|
|
91
|
+
const which = spawnSync('rustup', ['which', '--toolchain', pinned, 'rustc'], { encoding: 'utf8', cwd: crateDir });
|
|
92
|
+
const binDir = which.status === 0 ? path.dirname(which.stdout.trim()) : null;
|
|
93
|
+
const env = { ...process.env, PATH: binDir ? `${binDir}:${process.env.PATH}` : process.env.PATH, RUSTUP_TOOLCHAIN: pinned };
|
|
94
|
+
const rustc = run('rustc', ['-vV'], { capture: true, env });
|
|
95
|
+
const release = rustc.match(/^release: (.+)$/m)?.[1];
|
|
96
|
+
if (release !== pinned) {
|
|
97
|
+
throw new Error(`oxc-wasm: needs rustc ${pinned} with ${TARGET} (rustup toolchain install ${pinned} --target ${TARGET}); found ${release}`);
|
|
98
|
+
}
|
|
99
|
+
const sysroot = run('rustc', ['--print', 'sysroot'], { capture: true, env }).trim();
|
|
100
|
+
if (!existsSync(path.join(sysroot, 'lib', 'rustlib', TARGET))) {
|
|
101
|
+
throw new Error(`oxc-wasm: rustc ${pinned} has no ${TARGET} target (rustup target add --toolchain ${pinned} ${TARGET})`);
|
|
102
|
+
}
|
|
103
|
+
// The compiler itself, not a rustup proxy: cargo is told to use exactly this.
|
|
104
|
+
const compiler = path.join(sysroot, 'bin', 'rustc');
|
|
105
|
+
if (run(compiler, ['-vV'], { capture: true }) !== rustc) throw new Error(`oxc-wasm: ${compiler} is not the rustc ${pinned} checked`);
|
|
106
|
+
return { rustc: rustc.trim(), compiler, env };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function wasmOpt() {
|
|
110
|
+
const archives = path.join(WORK, '_archives');
|
|
111
|
+
await fs.mkdir(archives, { recursive: true });
|
|
112
|
+
const archive = path.join(archives, path.basename(BINARYEN.url));
|
|
113
|
+
if (!existsSync(archive)) {
|
|
114
|
+
const res = await fetch(BINARYEN.url, { redirect: 'follow' });
|
|
115
|
+
if (!res.ok) throw new Error(`oxc-wasm: GET ${BINARYEN.url} -> ${res.status}`);
|
|
116
|
+
await fs.writeFile(archive, new Uint8Array(await res.arrayBuffer()));
|
|
117
|
+
}
|
|
118
|
+
const digest = sha256(await fs.readFile(archive));
|
|
119
|
+
if (digest !== BINARYEN.sha256) throw new Error(`oxc-wasm: ${BINARYEN.url} sha256 ${digest}, pinned ${BINARYEN.sha256}`);
|
|
120
|
+
const dir = path.join(WORK, 'binaryen');
|
|
121
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
122
|
+
await fs.mkdir(dir, { recursive: true });
|
|
123
|
+
run('tar', ['xzf', archive, '-C', dir]);
|
|
124
|
+
const bin = path.join(dir, `binaryen-${BINARYEN.version}`, 'bin', 'wasm-opt');
|
|
125
|
+
const version = run(bin, ['--version'], { capture: true }).trim();
|
|
126
|
+
if (!version.includes(BINARYEN.version)) throw new Error(`oxc-wasm: ${bin} reports ${version}`);
|
|
127
|
+
return { bin, version };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The import, export and memory sections of a wasm binary. */
|
|
131
|
+
function wasmFacts(bytes) {
|
|
132
|
+
let at = 8;
|
|
133
|
+
const u = () => {
|
|
134
|
+
let result = 0, shift = 0, byte;
|
|
135
|
+
do { byte = bytes[at++]; result |= (byte & 0x7f) << shift; shift += 7; } while (byte & 0x80);
|
|
136
|
+
return result >>> 0;
|
|
137
|
+
};
|
|
138
|
+
const str = () => { const n = u(); const s = new TextDecoder().decode(bytes.subarray(at, at + n)); at += n; return s; };
|
|
139
|
+
const facts = { imports: [], exports: [], memories: [] };
|
|
140
|
+
while (at < bytes.length) {
|
|
141
|
+
const id = bytes[at++];
|
|
142
|
+
const size = u();
|
|
143
|
+
const end = at + size;
|
|
144
|
+
if (id === 2) {
|
|
145
|
+
for (let i = u(); i > 0; i--) {
|
|
146
|
+
facts.imports.push(`${str()}.${str()}`);
|
|
147
|
+
const kind = bytes[at++];
|
|
148
|
+
if (kind === 0) u();
|
|
149
|
+
else if (kind === 1) { at++; const f = bytes[at++]; u(); if (f & 1) u(); }
|
|
150
|
+
else if (kind === 2) { const f = bytes[at++]; u(); if (f & 1) u(); }
|
|
151
|
+
else if (kind === 3) at += 2;
|
|
152
|
+
}
|
|
153
|
+
} else if (id === 5) {
|
|
154
|
+
for (let i = u(); i > 0; i--) {
|
|
155
|
+
const flags = bytes[at++];
|
|
156
|
+
const memory = { shared: (flags & 2) !== 0, initial: u() };
|
|
157
|
+
if (flags & 1) memory.maximum = u();
|
|
158
|
+
facts.memories.push(memory);
|
|
159
|
+
}
|
|
160
|
+
} else if (id === 7) {
|
|
161
|
+
for (let i = u(); i > 0; i--) { facts.exports.push(str()); at++; u(); }
|
|
162
|
+
}
|
|
163
|
+
at = end;
|
|
164
|
+
}
|
|
165
|
+
return facts;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── Main ────────────────────────────────────────────────────────────────
|
|
169
|
+
await fs.mkdir(WORK, { recursive: true });
|
|
170
|
+
await fs.mkdir(OUT, { recursive: true });
|
|
171
|
+
const crateDir = path.join(WORK, 'nimbus-oxc');
|
|
172
|
+
const inputs = await oxcWasmInputs();
|
|
173
|
+
await copyCrate(crateDir);
|
|
174
|
+
const toolchainText = await fs.readFile(path.join(crateDir, 'rust-toolchain.toml'), 'utf8');
|
|
175
|
+
const rust = toolchain(crateDir, toolchainText);
|
|
176
|
+
const optimizer = await wasmOpt();
|
|
177
|
+
|
|
178
|
+
const cargoHome = process.env.CARGO_HOME ?? path.join(process.env.HOME ?? '/', '.cargo');
|
|
179
|
+
// Cargo and rustc read settings from the environment that Cargo.toml and the
|
|
180
|
+
// flags below pin (RUSTC, RUSTDOC, CARGO_PROFILE_RELEASE_*, CARGO_BUILD_*,
|
|
181
|
+
// RUSTFLAGS, RUSTC_WRAPPER, …): the build sees none of them but where its
|
|
182
|
+
// caches live, and the compiler is the one toolchain() checked.
|
|
183
|
+
const KEPT = new Set(['CARGO_HOME', 'RUSTUP_HOME', 'RUSTUP_TOOLCHAIN']);
|
|
184
|
+
const OVERRIDES = /^(CARGO|RUST|RUSTC|RUSTDOC)(_|$)|^RUSTFLAGS$|^RUSTDOCFLAGS$/;
|
|
185
|
+
const ambient = Object.fromEntries(Object.entries(rust.env).filter(([name]) => KEPT.has(name) || !OVERRIDES.test(name)));
|
|
186
|
+
for (const name of ['RUSTC', 'RUSTDOC', 'RUSTFLAGS', 'RUSTC_WRAPPER', 'CARGO_PROFILE_RELEASE_OPT_LEVEL', 'CARGO_BUILD_TARGET']) {
|
|
187
|
+
if (name in ambient) throw new Error(`oxc-wasm: ${name} survived the environment filter`);
|
|
188
|
+
}
|
|
189
|
+
const env = {
|
|
190
|
+
...ambient,
|
|
191
|
+
RUSTC: rust.compiler,
|
|
192
|
+
CARGO_TARGET_DIR: path.join(WORK, 'target'),
|
|
193
|
+
CARGO_ENCODED_RUSTFLAGS: RUSTFLAGS.join('\x1f'),
|
|
194
|
+
// Machine paths out of panic messages without entering cargo's metadata
|
|
195
|
+
// hash (napi-wasm/rustc-remap.sh explains why not RUSTFLAGS).
|
|
196
|
+
RUSTC_WRAPPER: path.join(HERE, '..', 'napi-wasm', 'rustc-remap.sh'),
|
|
197
|
+
NIMBUS_REMAP_WORK: WORK,
|
|
198
|
+
NIMBUS_REMAP_CARGO_HOME: cargoHome,
|
|
199
|
+
SOURCE_DATE_EPOCH: '0',
|
|
200
|
+
};
|
|
201
|
+
const jobs = Number(process.env.NIMBUS_CARGO_JOBS ?? '4');
|
|
202
|
+
if (!Number.isInteger(jobs) || jobs < 1 || jobs > 12) throw new Error('oxc-wasm: NIMBUS_CARGO_JOBS must be an integer from 1 to 12');
|
|
203
|
+
const cargoArgs = ['build', '--release', '--locked', '--lib', '--target', TARGET, '-j', String(jobs)];
|
|
204
|
+
const runner = process.env.NIMBUS_CARGO_RUNNER;
|
|
205
|
+
if (runner) run(runner, ['cargo', ...cargoArgs], { cwd: crateDir, env });
|
|
206
|
+
else run('cargo', cargoArgs, { cwd: crateDir, env });
|
|
207
|
+
|
|
208
|
+
const linked = path.join(WORK, 'target', TARGET, 'release', 'nimbus_oxc.wasm');
|
|
209
|
+
const output = path.join(OUT, 'nimbus-oxc.wasm');
|
|
210
|
+
run(optimizer.bin, [linked, ...WASM_OPT_ARGS, '-o', output]);
|
|
211
|
+
const wasm = new Uint8Array(await fs.readFile(output));
|
|
212
|
+
const facts = wasmFacts(wasm);
|
|
213
|
+
if (facts.imports.length !== 0) throw new Error(`oxc-wasm: the module imports ${facts.imports.join(', ')}; it must import nothing`);
|
|
214
|
+
if (JSON.stringify([...facts.exports].sort()) !== JSON.stringify(EXPORTS)) {
|
|
215
|
+
throw new Error(`oxc-wasm: the module exports ${facts.exports.join(', ')}; the ABI is ${EXPORTS.join(', ')}`);
|
|
216
|
+
}
|
|
217
|
+
const [memory] = facts.memories;
|
|
218
|
+
if (facts.memories.length !== 1 || memory.shared) throw new Error('oxc-wasm: the module must define one unshared memory');
|
|
219
|
+
if (memory.initial > OXC_MAX_INITIAL_PAGES) {
|
|
220
|
+
throw new Error(`oxc-wasm: the module's memory starts at ${memory.initial} pages, over OXC_MAX_INITIAL_PAGES (${OXC_MAX_INITIAL_PAGES})`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const drifted = changedInputs(inputs, await oxcWasmInputs());
|
|
224
|
+
if (drifted.length) throw new Error(`oxc-wasm: inputs changed during the build: ${drifted.join(', ')}`);
|
|
225
|
+
const cargoToml = await fs.readFile(path.join(crateDir, 'Cargo.toml'), 'utf8');
|
|
226
|
+
const lock = await fs.readFile(path.join(crateDir, 'Cargo.lock'), 'utf8');
|
|
227
|
+
const provenance = {
|
|
228
|
+
artifact: 'nimbus-oxc',
|
|
229
|
+
oxc: lock.match(/\nname = "oxc"\nversion = "(.+)"/)?.[1],
|
|
230
|
+
target: TARGET,
|
|
231
|
+
source: {
|
|
232
|
+
crate: 'packages/worker/scripts/oxc-wasm',
|
|
233
|
+
manifestVersion: cargoToml.match(/^version = "(.+)"$/m)?.[1],
|
|
234
|
+
// Every input's sha256 (inputs.mjs); bundle-oxc-wasm.mjs stages this wasm
|
|
235
|
+
// only while they are the tree's.
|
|
236
|
+
inputs,
|
|
237
|
+
},
|
|
238
|
+
toolchain: rust.rustc,
|
|
239
|
+
cargo: {
|
|
240
|
+
command: `cargo ${cargoArgs.slice(0, -2).join(' ')}`,
|
|
241
|
+
rustflags: RUSTFLAGS,
|
|
242
|
+
rustcWrapper: 'napi-wasm/rustc-remap.sh (--remap-path-prefix <work>=/build <CARGO_HOME>=/cargo)',
|
|
243
|
+
},
|
|
244
|
+
wasmOpt: { binaryen: BINARYEN, version: optimizer.version, args: WASM_OPT_ARGS },
|
|
245
|
+
wasm: { imports: [], exports: EXPORTS, memoryInitialPages: memory.initial, stackBytes: WASM_STACK_BYTES },
|
|
246
|
+
outputs: { 'nimbus-oxc.wasm': { bytes: wasm.length, sha256: sha256(wasm) } },
|
|
247
|
+
};
|
|
248
|
+
await fs.writeFile(path.join(OUT, 'provenance.json'), JSON.stringify(provenance, null, 2) + '\n');
|
|
249
|
+
console.log(JSON.stringify(provenance.outputs, null, 2));
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//! `cargo run --release --example memory -- <file> [key=value ...]`: peak heap of one transform.
|
|
2
|
+
use std::alloc::{GlobalAlloc, Layout, System};
|
|
3
|
+
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
|
|
4
|
+
|
|
5
|
+
use oxc::allocator::Allocator;
|
|
6
|
+
|
|
7
|
+
struct Counting;
|
|
8
|
+
static LIVE: AtomicUsize = AtomicUsize::new(0);
|
|
9
|
+
static PEAK: AtomicUsize = AtomicUsize::new(0);
|
|
10
|
+
|
|
11
|
+
unsafe impl GlobalAlloc for Counting {
|
|
12
|
+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
13
|
+
let live = LIVE.fetch_add(layout.size(), Relaxed) + layout.size();
|
|
14
|
+
PEAK.fetch_max(live, Relaxed);
|
|
15
|
+
unsafe { System.alloc(layout) }
|
|
16
|
+
}
|
|
17
|
+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
|
18
|
+
LIVE.fetch_sub(layout.size(), Relaxed);
|
|
19
|
+
unsafe { System.dealloc(ptr, layout) }
|
|
20
|
+
}
|
|
21
|
+
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
|
22
|
+
let live = LIVE.fetch_add(new_size, Relaxed) + new_size;
|
|
23
|
+
PEAK.fetch_max(live, Relaxed);
|
|
24
|
+
LIVE.fetch_sub(layout.size(), Relaxed);
|
|
25
|
+
unsafe { System.realloc(ptr, layout, new_size) }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#[global_allocator]
|
|
30
|
+
static GLOBAL: Counting = Counting;
|
|
31
|
+
|
|
32
|
+
fn main() {
|
|
33
|
+
let mut args = std::env::args().skip(1);
|
|
34
|
+
let path = args.next().expect("a file");
|
|
35
|
+
let source = std::fs::read_to_string(&path).expect("readable file");
|
|
36
|
+
let wire: Vec<String> = args.flat_map(|a| a.splitn(2, '=').map(str::to_string).collect::<Vec<_>>()).collect();
|
|
37
|
+
let options = nimbus_oxc::options::Options::decode(&wire.join("\0")).expect("options");
|
|
38
|
+
let base = LIVE.load(Relaxed);
|
|
39
|
+
PEAK.store(base, Relaxed);
|
|
40
|
+
let allocator = Allocator::default();
|
|
41
|
+
let started = std::time::Instant::now();
|
|
42
|
+
let output = nimbus_oxc::transform(&allocator, &source, &options);
|
|
43
|
+
let ms = started.elapsed().as_secs_f64() * 1000.0;
|
|
44
|
+
let arena = allocator.capacity();
|
|
45
|
+
let mib = |b: usize| b as f64 / 1048576.0;
|
|
46
|
+
eprintln!(
|
|
47
|
+
"source {:.2} MiB, output {:.2} MiB, arena {:.2} MiB (used {:.2}), peak heap over base {:.2} MiB, {:.1} ms, errors {}",
|
|
48
|
+
mib(source.len()), mib(output.code.len()), mib(arena), mib(allocator.used_bytes()), mib(PEAK.load(Relaxed) - base), ms,
|
|
49
|
+
output.diagnostics.iter().filter(|d| d.error).count()
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//! `cargo run --example transform -- <file> [key=value ...]`: print one transform.
|
|
2
|
+
use oxc::allocator::Allocator;
|
|
3
|
+
|
|
4
|
+
fn main() {
|
|
5
|
+
let mut args = std::env::args().skip(1);
|
|
6
|
+
let path = args.next().expect("a file");
|
|
7
|
+
let source = std::fs::read_to_string(&path).expect("readable file");
|
|
8
|
+
// key=value; define=name=replacement
|
|
9
|
+
let wire: Vec<String> = args.flat_map(|a| a.splitn(if a.starts_with("define=") { 3 } else { 2 }, '=').map(str::to_string).collect::<Vec<_>>()).collect();
|
|
10
|
+
let options = nimbus_oxc::options::Options::decode(&wire.join("\0")).expect("options");
|
|
11
|
+
let allocator = Allocator::default();
|
|
12
|
+
let output = nimbus_oxc::transform(&allocator, &source, &options);
|
|
13
|
+
for d in &output.diagnostics {
|
|
14
|
+
eprintln!("{} {}:{}:{}: {}", if d.error { "ERROR" } else { "WARN" }, d.file, d.line, d.column, d.text);
|
|
15
|
+
}
|
|
16
|
+
print!("{}", output.code);
|
|
17
|
+
if let Some(map) = output.map {
|
|
18
|
+
eprintln!("MAP {map}");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* inputs.mjs — what the Oxc wasm is built from, as digests: build.mjs writes
|
|
3
|
+
* them into the staged provenance.json, and bundle-oxc-wasm.mjs refuses to
|
|
4
|
+
* stage a wasm whose recorded inputs are not the tree's (a source edited
|
|
5
|
+
* without a rebuild).
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { promises as fs } from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const CRATE = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
14
|
+
|
|
15
|
+
/** Files outside src/ that decide the output, relative to the crate. */
|
|
16
|
+
const FILES = ['Cargo.toml', 'Cargo.lock', 'rust-toolchain.toml', 'build.mjs', 'inputs.mjs', '../napi-wasm/rustc-remap.sh'];
|
|
17
|
+
|
|
18
|
+
/** `{ <path>: <sha256> }` for every input, src/ file by file, sorted by path. */
|
|
19
|
+
export async function oxcWasmInputs(crate = CRATE) {
|
|
20
|
+
const src = (await fs.readdir(path.join(crate, 'src'), { recursive: true, withFileTypes: true }))
|
|
21
|
+
.filter((entry) => entry.isFile())
|
|
22
|
+
.map((entry) => path.relative(crate, path.join(entry.parentPath, entry.name)).split(path.sep).join('/'));
|
|
23
|
+
const paths = [...FILES, ...src].sort();
|
|
24
|
+
const digests = await Promise.all(paths.map(async (file) => [file, sha256(await fs.readFile(path.join(crate, file)))]));
|
|
25
|
+
return Object.fromEntries(digests);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The inputs that differ between two records, as `<path>` lines. */
|
|
29
|
+
export function changedInputs(recorded, current) {
|
|
30
|
+
const paths = new Set([...Object.keys(recorded ?? {}), ...Object.keys(current)]);
|
|
31
|
+
return [...paths].sort().filter((file) => recorded?.[file] !== current[file]);
|
|
32
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
//! The wasm32 interface. The module imports nothing; JavaScript drives it
|
|
2
|
+
//! through four exports:
|
|
3
|
+
//!
|
|
4
|
+
//! 1. `nimbus_oxc_alloc(capacity)` returns a buffer, into which the caller
|
|
5
|
+
//! writes the UTF-8 source followed by the options' wire form (options.rs).
|
|
6
|
+
//! `nimbus_oxc_realloc(ptr, capacity, new_capacity)` grows it, for a source
|
|
7
|
+
//! whose UTF-8 is longer than the caller first guessed.
|
|
8
|
+
//! 2. `nimbus_oxc_transform(ptr, capacity, source_len, options_len)` consumes
|
|
9
|
+
//! that buffer and returns the address of nine `u32`s: status (0 ok, 1 the
|
|
10
|
+
//! module was refused, 2 the options were), then the address and length of
|
|
11
|
+
//! the code, of the source map JSON, and of the diagnostics, then the bytes
|
|
12
|
+
//! of AST arena the transform used and reserved.
|
|
13
|
+
//! 3. The caller copies what it needs, then calls `nimbus_oxc_release()`,
|
|
14
|
+
//! which frees the outputs. Nothing is kept between transforms, so the
|
|
15
|
+
//! arena a large module needed is reused for the next module's arena and
|
|
16
|
+
//! everything else.
|
|
17
|
+
//!
|
|
18
|
+
//! Diagnostics are seven fields per diagnostic: `E` or `W`, line, column,
|
|
19
|
+
//! length, file, line text, message. Each field is its UTF-8 byte length in
|
|
20
|
+
//! decimal, `:`, then its bytes, so a field may hold any character (a source
|
|
21
|
+
//! line can hold a NUL).
|
|
22
|
+
|
|
23
|
+
use std::alloc::{Layout, alloc, dealloc, realloc};
|
|
24
|
+
use std::fmt::Write;
|
|
25
|
+
|
|
26
|
+
use oxc::allocator::Allocator;
|
|
27
|
+
|
|
28
|
+
use crate::options::Options;
|
|
29
|
+
|
|
30
|
+
struct Result {
|
|
31
|
+
words: [u32; 9],
|
|
32
|
+
code: String,
|
|
33
|
+
map: String,
|
|
34
|
+
diagnostics: String,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static mut RESULT: Option<Result> = None;
|
|
38
|
+
|
|
39
|
+
#[unsafe(no_mangle)]
|
|
40
|
+
pub extern "C" fn nimbus_oxc_alloc(capacity: usize) -> *mut u8 {
|
|
41
|
+
// SAFETY: the size is nonzero and the layout's alignment is 1.
|
|
42
|
+
unsafe { alloc(Layout::from_size_align_unchecked(capacity.max(1), 1)) }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// # Safety
|
|
46
|
+
/// `ptr` came from `nimbus_oxc_alloc(capacity)` (or this function with that capacity).
|
|
47
|
+
#[unsafe(no_mangle)]
|
|
48
|
+
pub unsafe extern "C" fn nimbus_oxc_realloc(ptr: *mut u8, capacity: usize, new_capacity: usize) -> *mut u8 {
|
|
49
|
+
// SAFETY: as documented; the layout matches the allocation's.
|
|
50
|
+
unsafe { realloc(ptr, Layout::from_size_align_unchecked(capacity.max(1), 1), new_capacity.max(1)) }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// # Safety
|
|
54
|
+
/// `ptr` came from `nimbus_oxc_alloc(capacity)` and its first
|
|
55
|
+
/// `source_len + options_len` bytes are written.
|
|
56
|
+
#[unsafe(no_mangle)]
|
|
57
|
+
pub unsafe extern "C" fn nimbus_oxc_transform(
|
|
58
|
+
ptr: *mut u8,
|
|
59
|
+
capacity: usize,
|
|
60
|
+
source_len: usize,
|
|
61
|
+
options_len: usize,
|
|
62
|
+
) -> *const u32 {
|
|
63
|
+
// SAFETY: the caller wrote these bytes at `ptr`.
|
|
64
|
+
let bytes = unsafe { std::slice::from_raw_parts(ptr, source_len + options_len) };
|
|
65
|
+
let result = run(&bytes[..source_len], &bytes[source_len..]);
|
|
66
|
+
// SAFETY: allocated by `nimbus_oxc_alloc(capacity)`.
|
|
67
|
+
unsafe { dealloc(ptr, Layout::from_size_align_unchecked(capacity.max(1), 1)) };
|
|
68
|
+
// SAFETY: wasm32 here is single-threaded; nothing else holds a reference to RESULT.
|
|
69
|
+
unsafe {
|
|
70
|
+
let slot = &mut *std::ptr::addr_of_mut!(RESULT);
|
|
71
|
+
let result = slot.insert(result);
|
|
72
|
+
result.words[1] = result.code.as_ptr() as u32;
|
|
73
|
+
result.words[2] = result.code.len() as u32;
|
|
74
|
+
result.words[3] = result.map.as_ptr() as u32;
|
|
75
|
+
result.words[4] = result.map.len() as u32;
|
|
76
|
+
result.words[5] = result.diagnostics.as_ptr() as u32;
|
|
77
|
+
result.words[6] = result.diagnostics.len() as u32;
|
|
78
|
+
result.words.as_ptr()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#[unsafe(no_mangle)]
|
|
83
|
+
pub extern "C" fn nimbus_oxc_release() {
|
|
84
|
+
// SAFETY: as in `nimbus_oxc_transform`.
|
|
85
|
+
unsafe { *std::ptr::addr_of_mut!(RESULT) = None };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn run(source: &[u8], options: &[u8]) -> Result {
|
|
89
|
+
let refused = |status: u32, message: String| Result {
|
|
90
|
+
words: [status, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
91
|
+
code: String::new(),
|
|
92
|
+
map: String::new(),
|
|
93
|
+
diagnostics: diagnostic_fields(['E'.to_string(), "0".into(), "0".into(), "0".into(), "<stdin>".into(), String::new(), message]),
|
|
94
|
+
};
|
|
95
|
+
let Ok(source) = std::str::from_utf8(source) else {
|
|
96
|
+
return refused(1, "the source is not UTF-8".into());
|
|
97
|
+
};
|
|
98
|
+
let options = match std::str::from_utf8(options).map_err(|e| e.to_string()).and_then(Options::decode) {
|
|
99
|
+
Ok(options) => options,
|
|
100
|
+
Err(message) => return refused(2, message),
|
|
101
|
+
};
|
|
102
|
+
let allocator = Allocator::default();
|
|
103
|
+
let output = crate::transform(&allocator, source, &options);
|
|
104
|
+
let arena = [allocator.used_bytes() as u32, allocator.capacity() as u32];
|
|
105
|
+
drop(allocator);
|
|
106
|
+
let diagnostics = output
|
|
107
|
+
.diagnostics
|
|
108
|
+
.iter()
|
|
109
|
+
.map(|d| {
|
|
110
|
+
diagnostic_fields([
|
|
111
|
+
if d.error { "E" } else { "W" }.to_string(),
|
|
112
|
+
d.line.to_string(),
|
|
113
|
+
d.column.to_string(),
|
|
114
|
+
d.length.to_string(),
|
|
115
|
+
d.file.clone(),
|
|
116
|
+
d.line_text.clone(),
|
|
117
|
+
d.text.clone(),
|
|
118
|
+
])
|
|
119
|
+
})
|
|
120
|
+
.collect();
|
|
121
|
+
Result {
|
|
122
|
+
words: [u32::from(output.has_errors()), 0, 0, 0, 0, 0, 0, arena[0], arena[1]],
|
|
123
|
+
code: output.code,
|
|
124
|
+
map: output.map.unwrap_or_default(),
|
|
125
|
+
diagnostics,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// One diagnostic's fields, each as `<byte length>:<bytes>`.
|
|
130
|
+
fn diagnostic_fields(fields: [String; 7]) -> String {
|
|
131
|
+
let mut out = String::new();
|
|
132
|
+
for field in fields {
|
|
133
|
+
let _ = write!(out, "{}:{field}", field.len());
|
|
134
|
+
}
|
|
135
|
+
out
|
|
136
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//! Diagnostics in the shape esbuild reports them: text plus a location whose
|
|
2
|
+
//! line is 1-based and whose column is a 0-based byte offset into the line.
|
|
3
|
+
|
|
4
|
+
use oxc::diagnostics::{OxcDiagnostic, Severity};
|
|
5
|
+
|
|
6
|
+
pub struct Diagnostic {
|
|
7
|
+
pub error: bool,
|
|
8
|
+
pub text: String,
|
|
9
|
+
pub file: String,
|
|
10
|
+
/// 0 when the diagnostic has no location.
|
|
11
|
+
pub line: u32,
|
|
12
|
+
pub column: u32,
|
|
13
|
+
pub length: u32,
|
|
14
|
+
pub line_text: String,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
pub fn convert(source: &str, file: &str, diagnostics: Vec<OxcDiagnostic>) -> Vec<Diagnostic> {
|
|
18
|
+
diagnostics
|
|
19
|
+
.into_iter()
|
|
20
|
+
.filter(|d| matches!(d.severity, Severity::Error | Severity::Warning))
|
|
21
|
+
.map(|d| {
|
|
22
|
+
let mut text = d.message.to_string();
|
|
23
|
+
if let Some(help) = &d.help {
|
|
24
|
+
text.push_str(" (");
|
|
25
|
+
text.push_str(help);
|
|
26
|
+
text.push(')');
|
|
27
|
+
}
|
|
28
|
+
let mut diagnostic = Diagnostic {
|
|
29
|
+
error: d.severity == Severity::Error,
|
|
30
|
+
text,
|
|
31
|
+
file: file.to_string(),
|
|
32
|
+
line: 0,
|
|
33
|
+
column: 0,
|
|
34
|
+
length: 0,
|
|
35
|
+
line_text: String::new(),
|
|
36
|
+
};
|
|
37
|
+
if let Some(label) = d.labels.first() {
|
|
38
|
+
let offset = (label.offset() as usize).min(source.len());
|
|
39
|
+
let line_start = source[..offset].rfind(['\n', '\r']).map_or(0, |i| i + 1);
|
|
40
|
+
let line_end = source[offset..].find(['\n', '\r']).map_or(source.len(), |i| offset + i);
|
|
41
|
+
diagnostic.line = 1 + source[..line_start].bytes().filter(|&b| b == b'\n').count() as u32;
|
|
42
|
+
diagnostic.column = (offset - line_start) as u32;
|
|
43
|
+
diagnostic.length = label.span().size().min((line_end - offset) as u32);
|
|
44
|
+
diagnostic.line_text = source[line_start..line_end].to_string();
|
|
45
|
+
}
|
|
46
|
+
diagnostic
|
|
47
|
+
})
|
|
48
|
+
.collect()
|
|
49
|
+
}
|