@nubjs/nub-win32-arm64 0.0.13 → 0.0.15
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/bin/nub.exe +0 -0
- package/package.json +1 -1
- package/runtime/addons/nub-native.node +0 -0
- package/runtime/polyfills.cjs +178 -0
- package/runtime/preload-async-hooks.mjs +5 -6
- package/runtime/preload-common.cjs +548 -0
- package/runtime/preload.cjs +277 -0
- package/runtime/preload.mjs +22 -11
- package/runtime/transform-core.mjs +98 -199
- package/runtime/version.mjs +1 -1
- package/runtime/worker-polyfill.mjs +15 -2
- package/runtime/node_modules/@oxc-parser/binding-win32-arm64-msvc/README.md +0 -3
- package/runtime/node_modules/@oxc-parser/binding-win32-arm64-msvc/package.json +0 -39
- package/runtime/node_modules/@oxc-parser/binding-win32-arm64-msvc/parser.win32-arm64-msvc.node +0 -0
- package/runtime/node_modules/@oxc-parser/binding-win32-x64-msvc/README.md +0 -3
- package/runtime/node_modules/@oxc-parser/binding-win32-x64-msvc/package.json +0 -39
- package/runtime/node_modules/@oxc-parser/binding-win32-x64-msvc/parser.win32-x64-msvc.node +0 -0
- package/runtime/node_modules/@oxc-project/types/LICENSE +0 -22
- package/runtime/node_modules/@oxc-project/types/README.md +0 -3
- package/runtime/node_modules/@oxc-project/types/package.json +0 -26
- package/runtime/node_modules/@oxc-project/types/types.d.ts +0 -1912
- package/runtime/node_modules/@oxc-transform/binding-win32-arm64-msvc/README.md +0 -3
- package/runtime/node_modules/@oxc-transform/binding-win32-arm64-msvc/package.json +0 -41
- package/runtime/node_modules/@oxc-transform/binding-win32-arm64-msvc/transform.win32-arm64-msvc.node +0 -0
- package/runtime/node_modules/@oxc-transform/binding-win32-x64-msvc/README.md +0 -3
- package/runtime/node_modules/@oxc-transform/binding-win32-x64-msvc/package.json +0 -41
- package/runtime/node_modules/@oxc-transform/binding-win32-x64-msvc/transform.win32-x64-msvc.node +0 -0
- package/runtime/node_modules/oxc-parser/LICENSE +0 -22
- package/runtime/node_modules/oxc-parser/README.md +0 -167
- package/runtime/node_modules/oxc-parser/package.json +0 -153
- package/runtime/node_modules/oxc-parser/src-js/bindings.js +0 -601
- package/runtime/node_modules/oxc-parser/src-js/generated/constants.js +0 -105
- package/runtime/node_modules/oxc-parser/src-js/generated/deserialize/js.js +0 -5862
- package/runtime/node_modules/oxc-parser/src-js/generated/deserialize/js_range.js +0 -6403
- package/runtime/node_modules/oxc-parser/src-js/generated/deserialize/ts.js +0 -6154
- package/runtime/node_modules/oxc-parser/src-js/generated/deserialize/ts_range.js +0 -6723
- package/runtime/node_modules/oxc-parser/src-js/generated/lazy/constructors.js +0 -13875
- package/runtime/node_modules/oxc-parser/src-js/generated/lazy/type_ids.js +0 -191
- package/runtime/node_modules/oxc-parser/src-js/generated/lazy/walk.js +0 -5810
- package/runtime/node_modules/oxc-parser/src-js/generated/visit/keys.js +0 -220
- package/runtime/node_modules/oxc-parser/src-js/generated/visit/type_ids.js +0 -177
- package/runtime/node_modules/oxc-parser/src-js/generated/visit/visitor.d.ts +0 -387
- package/runtime/node_modules/oxc-parser/src-js/generated/visit/walk.js +0 -2455
- package/runtime/node_modules/oxc-parser/src-js/index.d.ts +0 -312
- package/runtime/node_modules/oxc-parser/src-js/index.js +0 -108
- package/runtime/node_modules/oxc-parser/src-js/raw-transfer/common.js +0 -301
- package/runtime/node_modules/oxc-parser/src-js/raw-transfer/eager.js +0 -255
- package/runtime/node_modules/oxc-parser/src-js/raw-transfer/lazy-common.js +0 -11
- package/runtime/node_modules/oxc-parser/src-js/raw-transfer/lazy.js +0 -162
- package/runtime/node_modules/oxc-parser/src-js/raw-transfer/node-array.js +0 -365
- package/runtime/node_modules/oxc-parser/src-js/raw-transfer/supported.js +0 -52
- package/runtime/node_modules/oxc-parser/src-js/raw-transfer/visitor.js +0 -127
- package/runtime/node_modules/oxc-parser/src-js/visit/index.js +0 -41
- package/runtime/node_modules/oxc-parser/src-js/visit/visitor.js +0 -405
- package/runtime/node_modules/oxc-parser/src-js/wasm.js +0 -13
- package/runtime/node_modules/oxc-parser/src-js/webcontainer-fallback.cjs +0 -21
- package/runtime/node_modules/oxc-parser/src-js/wrap.js +0 -57
- package/runtime/node_modules/oxc-transform/LICENSE +0 -22
- package/runtime/node_modules/oxc-transform/README.md +0 -84
- package/runtime/node_modules/oxc-transform/browser.js +0 -1
- package/runtime/node_modules/oxc-transform/index.d.ts +0 -658
- package/runtime/node_modules/oxc-transform/index.js +0 -598
- package/runtime/node_modules/oxc-transform/package.json +0 -114
- package/runtime/node_modules/oxc-transform/webcontainer-fallback.cjs +0 -21
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
// Shared preload machinery for BOTH tiers — CommonJS, zero top-level await.
|
|
2
|
+
//
|
|
3
|
+
// The fast tier (Node 22.15+) loads this from a `--require` CJS preload
|
|
4
|
+
// (preload.cjs) so Node keeps its synchronous `Module.runMain` CJS entry path
|
|
5
|
+
// (top-level `executionAsyncId()===1`, sync exception origin, `require.main.id`
|
|
6
|
+
// `'.'`, `module.parent` `null`) — all of which the old `--import` ESM preload
|
|
7
|
+
// broke by forcing eager ESM-loader init that routed even a CJS entry through the
|
|
8
|
+
// async ESM module-job (R1). The compat tier (18.19–22.14) loads this from its
|
|
9
|
+
// async `--import` preload.mjs and reuses the same hook/require/watch/Temporal
|
|
10
|
+
// logic; only hook REGISTRATION differs (sync `module.registerHooks` on the fast
|
|
11
|
+
// tier vs async `module.register` loader worker on compat), which each entry owns.
|
|
12
|
+
//
|
|
13
|
+
// EVERYTHING here is synchronous and import-of-transform-core is a plain
|
|
14
|
+
// `require()` — transform-core.mjs has no top-level await and is require(esm)-able
|
|
15
|
+
// on the fast tier; the compat entry passes its already-imported core bindings in
|
|
16
|
+
// (it imported them as ESM), so this module never require()s the core there.
|
|
17
|
+
|
|
18
|
+
const module_ = require("node:module");
|
|
19
|
+
const { readdirSync } = require("node:fs");
|
|
20
|
+
const { fileURLToPath, pathToFileURL } = require("node:url");
|
|
21
|
+
const { join, dirname, extname: pathExtname } = require("node:path");
|
|
22
|
+
|
|
23
|
+
// ── Watch-mode dependency reporting (main thread only) ──────────────
|
|
24
|
+
// Under `nub watch`, Node's FilesWatcher only watches files in the import graph;
|
|
25
|
+
// config files (tsconfig.json, package.json) and `.env*` are NOT in any graph, so
|
|
26
|
+
// an edit to them otherwise goes stale. Node accepts incremental
|
|
27
|
+
// `process.send({'watch:require': [...]})` over its WATCH_REPORT_DEPENDENCIES IPC
|
|
28
|
+
// at ANY point in the child's life (it adds each path to the watch set), so we
|
|
29
|
+
// report config paths AS the core loader discovers them. The reporters are
|
|
30
|
+
// injected into the core via setWatchHooks so getTsconfigForDir / getPackageType
|
|
31
|
+
// self-report. The flush is coalesced via setImmediate.
|
|
32
|
+
function installWatchReporting(core) {
|
|
33
|
+
const WATCH_REPORTING =
|
|
34
|
+
process.env.WATCH_REPORT_DEPENDENCIES === "1" && typeof process.send === "function";
|
|
35
|
+
const watchReported = new Set();
|
|
36
|
+
const watchPending = [];
|
|
37
|
+
let watchFlushScheduled = false;
|
|
38
|
+
function flushWatchDeps() {
|
|
39
|
+
watchFlushScheduled = false;
|
|
40
|
+
if (watchPending.length === 0) return;
|
|
41
|
+
const batch = watchPending.splice(0, watchPending.length);
|
|
42
|
+
try { process.send({ "watch:require": batch }); } catch {}
|
|
43
|
+
}
|
|
44
|
+
function reportWatchDep(path) {
|
|
45
|
+
if (!WATCH_REPORTING || !path || watchReported.has(path)) return;
|
|
46
|
+
watchReported.add(path);
|
|
47
|
+
watchPending.push(path);
|
|
48
|
+
if (!watchFlushScheduled) {
|
|
49
|
+
watchFlushScheduled = true;
|
|
50
|
+
// A scheduled immediate is drained before the loop would exit, so even a
|
|
51
|
+
// script that finishes synchronously flushes its deps. (Don't unref: an
|
|
52
|
+
// unref'd immediate is skipped on a synchronous exit, dropping the report.)
|
|
53
|
+
setImmediate(flushWatchDeps);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Report a directory's `.env*` files (the natural watch targets). Scanned once
|
|
57
|
+
// per directory, lazily.
|
|
58
|
+
const watchEnvScannedDirs = new Set();
|
|
59
|
+
function reportEnvFilesIn(dir) {
|
|
60
|
+
if (!WATCH_REPORTING || watchEnvScannedDirs.has(dir)) return;
|
|
61
|
+
watchEnvScannedDirs.add(dir);
|
|
62
|
+
let entries;
|
|
63
|
+
try { entries = readdirSync(dir); } catch { return; }
|
|
64
|
+
for (const name of entries) {
|
|
65
|
+
if (name === ".env" || name.startsWith(".env.")) reportWatchDep(join(dir, name));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
core.setWatchHooks({ reportDep: reportWatchDep, reportEnvDir: reportEnvFilesIn });
|
|
69
|
+
return WATCH_REPORTING;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── Resolve / load hooks (sync `module.registerHooks` shape) ────────
|
|
73
|
+
// Returns `{ resolve, load }` closing over `core` + the watch flag. The compat
|
|
74
|
+
// tier does NOT use these (its hooks run async in the loader worker via
|
|
75
|
+
// preload-async-hooks.mjs); only the fast tier's `module.registerHooks` does.
|
|
76
|
+
|
|
77
|
+
// True once USER code registers its own `module.registerHooks` (a ts-node/tsx-style
|
|
78
|
+
// transpiler). nub registers exactly one hook set from the preload (the FIRST call
|
|
79
|
+
// after the wrap below); every later call is the user's. This lets the load hook
|
|
80
|
+
// tell apart a bare `'typescript'` format that a USER resolve hook set (defer — the
|
|
81
|
+
// user's own load hook will transpile) from the bare `'typescript'` that Node's
|
|
82
|
+
// NATIVE CJS loader assigns to a `.ts` entry/require in a package with no explicit
|
|
83
|
+
// `type` (transpile — there is no user hook to do it, and Node's strip-only mode
|
|
84
|
+
// can't handle enums/namespaces). See makeHooks().load.
|
|
85
|
+
let __userHooksRegistered = false;
|
|
86
|
+
function installUserHookDetector() {
|
|
87
|
+
if (typeof module_.registerHooks !== "function") return;
|
|
88
|
+
const orig = module_.registerHooks;
|
|
89
|
+
if (orig.__nubWrapped) return;
|
|
90
|
+
let seen = 0;
|
|
91
|
+
const wrapped = function (...args) {
|
|
92
|
+
// Call #1 is nub's own preload registration; #2+ are user hooks.
|
|
93
|
+
if (seen >= 1) __userHooksRegistered = true;
|
|
94
|
+
seen += 1;
|
|
95
|
+
return orig.apply(this, args);
|
|
96
|
+
};
|
|
97
|
+
wrapped.__nubWrapped = true;
|
|
98
|
+
try { module_.registerHooks = wrapped; } catch {}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function makeHooks(core, watchReporting) {
|
|
102
|
+
installUserHookDetector();
|
|
103
|
+
|
|
104
|
+
function resolve(specifier, context, nextResolve) {
|
|
105
|
+
const r = core.resolveSpec(specifier, context.parentURL);
|
|
106
|
+
return r ?? nextResolve(specifier, context);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function load(url, context, nextLoad) {
|
|
110
|
+
const ext = core.extname(url);
|
|
111
|
+
|
|
112
|
+
// Watch mode: surface this file's nearest config files (tsconfig.json,
|
|
113
|
+
// package.json) + sibling `.env*` so edits to them restart the run. Done for
|
|
114
|
+
// every user file (not just transpiled ones) — getTsconfigForDir/
|
|
115
|
+
// getPackageType self-report via the injected watch hooks.
|
|
116
|
+
if (watchReporting && url.startsWith("file:") && !core.isNodeModules(url)) {
|
|
117
|
+
try {
|
|
118
|
+
const dir = dirname(fileURLToPath(url));
|
|
119
|
+
core.getTsconfigForDir(dir);
|
|
120
|
+
core.getPackageType(dir);
|
|
121
|
+
} catch {}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// A USER resolve hook (a ts-node/tsx-style transpiler registered AFTER nub's
|
|
125
|
+
// own preload hook) claimed this file with the bare 'typescript' format: defer
|
|
126
|
+
// to the user's own load chain. The discriminator is `__userHooksRegistered`,
|
|
127
|
+
// NOT the bare format alone — Node's NATIVE CJS loader ALSO emits the bare
|
|
128
|
+
// string 'typescript' for a `.ts` entry/require whose nearest package.json has
|
|
129
|
+
// no explicit `type` (cjs/loader.js getFormatOfExtensionlessFile, lines ~1986),
|
|
130
|
+
// and in that native case nub MUST transpile (Node's strip-only mode can't
|
|
131
|
+
// handle enums/namespaces). So we only step aside when a user hook is present:
|
|
132
|
+
// nub registers exactly one hook set from the preload, the user registers theirs
|
|
133
|
+
// later, and registering theirs OUTERMOST (LIFO) means their load hook wraps
|
|
134
|
+
// nub's — it sets format='typescript', calls nextLoad into nub, and (without this
|
|
135
|
+
// guard) nub would transpile with oxc — a type-stripper, not a module-format
|
|
136
|
+
// transformer — leaving `export {}` verbatim and, for a `type:commonjs` package,
|
|
137
|
+
// handing Node format='commonjs' + ESM source = invalid CJS. Stepping aside lets
|
|
138
|
+
// nub fall through to Node's native load, returning raw TS source back up to the
|
|
139
|
+
// user's outer hook, which does the real ESM->CJS conversion, matching Node.
|
|
140
|
+
// Native 'module-typescript'/'commonjs-typescript' formats still fall through to
|
|
141
|
+
// nub's transpile below, so normal augmentation is unchanged.
|
|
142
|
+
if (__userHooksRegistered && context && context.format === "typescript") {
|
|
143
|
+
return nextLoad(url, context);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// R12: never transpile `.ts`/`.tsx`/… inside node_modules. Node itself throws
|
|
147
|
+
// ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING for TS under node_modules; if
|
|
148
|
+
// nub transpiled it instead, that native error would never surface and nub
|
|
149
|
+
// would be MORE permissive than Node. Fall through to `nextLoad` so Node's own
|
|
150
|
+
// handling (and its error) applies. (The TS-parent extensionless resolution in
|
|
151
|
+
// the resolve hook is intended and stays — only this load-time transpile is
|
|
152
|
+
// gated.)
|
|
153
|
+
if (core.TRANSPILE_EXTS.has(ext) && !core.isNodeModules(url)) {
|
|
154
|
+
return core.loadTranspile(url, ext);
|
|
155
|
+
}
|
|
156
|
+
if (ext in core.DATA_EXTS) return core.loadData(url, ext);
|
|
157
|
+
|
|
158
|
+
const r = nextLoad(url, context);
|
|
159
|
+
// nub's sync `module.registerHooks` load hook forces the synchronous
|
|
160
|
+
// module-job (ModuleJobSync.syncLink -> loadAndTranslateForImportInRequiredESM),
|
|
161
|
+
// which cannot async-fetch source. When a user `--experimental-loader` resolve
|
|
162
|
+
// hook sets `format` without a `source` (a pattern vanilla Node tolerates on its
|
|
163
|
+
// async load path by fetching the source itself), the default load returns
|
|
164
|
+
// source:null and Node's assertBufferSource throws ERR_INVALID_RETURN_PROPERTY_VALUE.
|
|
165
|
+
// Backfill the source from disk so the sync path matches Node — without touching
|
|
166
|
+
// nub's own resolve/transpile hooks.
|
|
167
|
+
//
|
|
168
|
+
// EXCEPTION — format 'commonjs' (and 'builtin') MUST keep source:null. For those
|
|
169
|
+
// formats Node's ESM loader deliberately returns no source and hands the module
|
|
170
|
+
// off to the NATIVE CommonJS loader (Module._load), where `require()` uses CJS
|
|
171
|
+
// resolution. A CJS `.js` ENTRY, when a user `--experimental-loader` is active, is
|
|
172
|
+
// routed through the ESM loader for format detection but still loads as CJS this
|
|
173
|
+
// way. If we backfilled its source, the ESM loader would instead translate it via
|
|
174
|
+
// its CommonJS-to-ESM wrapper, routing every inner `require()` through the ESM
|
|
175
|
+
// resolve hook — so `require('assert')` would hand the bare 'assert' specifier to
|
|
176
|
+
// the user's resolve hook and crash with ERR_INVALID_RETURN_PROPERTY_VALUE (the
|
|
177
|
+
// shadow-realm/custom-loaders corpus failure). Only ESM-shaped formats ('module',
|
|
178
|
+
// 'json', 'wasm', …) genuinely need the source on the sync path.
|
|
179
|
+
if (
|
|
180
|
+
r && r.source == null && r.format &&
|
|
181
|
+
r.format !== "commonjs" && r.format !== "builtin" &&
|
|
182
|
+
typeof url === "string" && url.startsWith("file:")
|
|
183
|
+
) {
|
|
184
|
+
try {
|
|
185
|
+
const { readFileSync } = require("node:fs");
|
|
186
|
+
return { ...r, source: readFileSync(fileURLToPath(url)) };
|
|
187
|
+
} catch { /* fall through with the original result */ }
|
|
188
|
+
}
|
|
189
|
+
return r;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { resolve, load };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ── CommonJS require() augmentation (BOTH tiers) ────────────────────
|
|
196
|
+
// `module.registerHooks`' CJS-`require()` coverage is INCOMPLETE before ~Node 24:
|
|
197
|
+
// on Node 22.15 a `require()` from a `.cts` parent (which Node loads via the ESM
|
|
198
|
+
// translator's special-require) hits native Module._resolveFilename with no
|
|
199
|
+
// tsconfig/extensionless handling — a `require('@alias')` or `require('./x')` of a
|
|
200
|
+
// `.ts` target throws MODULE_NOT_FOUND, while the same code works on Node 26 (where
|
|
201
|
+
// registerHooks does cover it) and on the fast `import` path. On the compat tier
|
|
202
|
+
// (18.19–22.14) the only hook surface is `module.register`, which intercepts the
|
|
203
|
+
// ESM loader ONLY — so `require()` is entirely unaugmented there. Both gaps have
|
|
204
|
+
// the same closure: install this main-thread CJS shim, reusing the core's canonical
|
|
205
|
+
// resolveCjsPath / loadTranspile (no drift). It tries nub's resolution first and
|
|
206
|
+
// FALLS THROUGH to native on a miss, so it is a safe no-op on the versions where
|
|
207
|
+
// registerHooks already covers require (Node 24+/26). Mechanism stays within the
|
|
208
|
+
// augmenter rules: exactly what `--require`-installing the ts-node / tsx CJS shim
|
|
209
|
+
// has always done.
|
|
210
|
+
//
|
|
211
|
+
// This error is surfaced ONLY on Node versions without native require(esm)
|
|
212
|
+
// (< 20.19 / 22.0–22.11), where require() of an ES module genuinely cannot work.
|
|
213
|
+
// On every require(esm)-capable Node, Node loads the ES module itself and this is
|
|
214
|
+
// never reached. The message is user-facing: no internal mechanism names.
|
|
215
|
+
function requireEsmError(filename) {
|
|
216
|
+
const err = new Error(
|
|
217
|
+
`Cannot require() this file — it is an ES module.\n` +
|
|
218
|
+
` ${filename}\n` +
|
|
219
|
+
`It uses \`import\`/\`export\`, so it loads as an ES module, and this version of ` +
|
|
220
|
+
`Node can't require() an ES module. Load it with \`import(...)\` instead, rename ` +
|
|
221
|
+
`it to .cts for a CommonJS module, or upgrade Node.`,
|
|
222
|
+
);
|
|
223
|
+
err.code = "ERR_REQUIRE_ESM";
|
|
224
|
+
return err;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// `withClassicTranspile` — also install the `require.extensions` (classic CommonJS
|
|
228
|
+
// loader) transpile hook. Needed ONLY on Node WITHOUT native require(esm)
|
|
229
|
+
// (< 20.19 / 22.0–22.11): there, `module.register`'s ESM-loader hooks can't reach a
|
|
230
|
+
// `require()`, AND an ES module simply can't be require()d, so we transpile CJS
|
|
231
|
+
// content classically and surface a clean error for ESM content. On require(esm)-
|
|
232
|
+
// capable Node we DON'T install it — registering `require.extensions['.ts']` would
|
|
233
|
+
// shadow Node's own native require(esm) of ES-module `.ts` files (breaking
|
|
234
|
+
// `require("./esm.ts")`), and the resolve shim below plus the tier's load hook
|
|
235
|
+
// already cover resolution + transpile.
|
|
236
|
+
function installCjsRequireHooks(core, withClassicTranspile) {
|
|
237
|
+
const origResolveFilename = module_._resolveFilename;
|
|
238
|
+
module_._resolveFilename = function (request, parent, isMain, options) {
|
|
239
|
+
let resolved = null;
|
|
240
|
+
try {
|
|
241
|
+
const parentPath = parent && typeof parent.filename === "string" ? parent.filename : null;
|
|
242
|
+
resolved = core.resolveCjsPath(request, parentPath);
|
|
243
|
+
} catch { /* fall through to Node */ }
|
|
244
|
+
if (resolved) {
|
|
245
|
+
if (withClassicTranspile && core.requireTargetIsEsm(resolved, pathExtname(resolved))) {
|
|
246
|
+
throw requireEsmError(resolved);
|
|
247
|
+
}
|
|
248
|
+
return resolved;
|
|
249
|
+
}
|
|
250
|
+
return origResolveFilename.call(this, request, parent, isMain, options);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
if (!withClassicTranspile) return;
|
|
254
|
+
|
|
255
|
+
// require.extensions: transpile via the SAME loadTranspile the load hook uses —
|
|
256
|
+
// target:'es2022' lowering (`using`), tsconfig, source maps, the Stage-3
|
|
257
|
+
// decorator guard, and module-format detection are all identical to the fast
|
|
258
|
+
// tier. The path is already a real TS file (Module._resolveFilename ran first).
|
|
259
|
+
// A module-format source can't be _compile'd as CJS — same clean error as above.
|
|
260
|
+
const transpileExtension = (mod, filename) => {
|
|
261
|
+
const { source, format } = core.loadTranspile(pathToFileURL(filename).href, pathExtname(filename));
|
|
262
|
+
if (format === "module") throw requireEsmError(filename);
|
|
263
|
+
mod._compile(source, filename);
|
|
264
|
+
};
|
|
265
|
+
for (const ext of [".ts", ".cts", ".mts", ".tsx", ".jsx"]) {
|
|
266
|
+
module_._extensions[ext] = transpileExtension;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ── Clobbered-polyfill preloading + Temporal lazy global ────────────
|
|
271
|
+
// Packages in the core's CLOBBER_MAP can't be imported after hooks register
|
|
272
|
+
// because the resolve hook returns a synthetic module instead of the real package.
|
|
273
|
+
// Load them here via CJS require (not yet hooked) and return them so the polyfill
|
|
274
|
+
// installer can stash them. Temporal is the exception (A37): the polyfill is ~18ms
|
|
275
|
+
// to load and most scripts never touch it, so we only RESOLVE its path now (cheap)
|
|
276
|
+
// and defer the load to a lazy global getter. Requiring it later by absolute path
|
|
277
|
+
// bypasses the CLOBBER_MAP resolve-hook entry, which keys on the specifier.
|
|
278
|
+
function preloadPolyfillPackages(reqFromRuntime) {
|
|
279
|
+
const preloaded = {};
|
|
280
|
+
// Feature-detect before requiring (A39): URLPattern is native on Node 24+, so
|
|
281
|
+
// skip loading the polyfill there. On 22.x it's absent → load it.
|
|
282
|
+
if (typeof globalThis.URLPattern === "undefined") {
|
|
283
|
+
try { preloaded.urlpattern = reqFromRuntime("urlpattern-polyfill"); } catch {}
|
|
284
|
+
}
|
|
285
|
+
// Float16Array: native on Node 24+, absent on the 22.x floor.
|
|
286
|
+
if (typeof globalThis.Float16Array === "undefined") {
|
|
287
|
+
try { preloaded.float16 = reqFromRuntime("@petamoriken/float16"); } catch {}
|
|
288
|
+
}
|
|
289
|
+
return preloaded;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Install the lazy `globalThis.Temporal` getter. The polyfill is loaded — and even
|
|
293
|
+
// RESOLVED — only on first access. CRITICAL ordering note (regexp one-off): the
|
|
294
|
+
// `require.resolve("@js-temporal/polyfill")` is deferred INTO the getter, NOT run at
|
|
295
|
+
// preload top level. An unconditional resolve at startup mutates the legacy
|
|
296
|
+
// `RegExp.$_` static (the resolved node_modules path matches an internal regex), so
|
|
297
|
+
// a program inspecting `RegExp.$_` on its first line would otherwise see a leaked
|
|
298
|
+
// path (test-startup-empty-regexp-statics). Deferring the resolve keeps `RegExp.$_`
|
|
299
|
+
// empty at user-code start; the cost is paid only by a program that touches Temporal.
|
|
300
|
+
function installTemporalLazyGlobal(reqFromRuntime) {
|
|
301
|
+
if (typeof globalThis.Temporal !== "undefined") return;
|
|
302
|
+
|
|
303
|
+
const defineTemporal = (value) =>
|
|
304
|
+
Object.defineProperty(globalThis, "Temporal", {
|
|
305
|
+
value,
|
|
306
|
+
configurable: true,
|
|
307
|
+
writable: true,
|
|
308
|
+
enumerable: false,
|
|
309
|
+
});
|
|
310
|
+
Object.defineProperty(globalThis, "Temporal", {
|
|
311
|
+
configurable: true,
|
|
312
|
+
enumerable: false,
|
|
313
|
+
get() {
|
|
314
|
+
let temporalPath;
|
|
315
|
+
try { temporalPath = reqFromRuntime.resolve("@js-temporal/polyfill"); } catch {}
|
|
316
|
+
if (!temporalPath) return undefined;
|
|
317
|
+
const polyfill = reqFromRuntime(temporalPath);
|
|
318
|
+
// @js-temporal/polyfill exports `toTemporalInstant` as a function but does
|
|
319
|
+
// NOT auto-install it on Date.prototype (you assign it yourself). Install it
|
|
320
|
+
// here so that on the floor (no native Temporal) `date.toTemporalInstant()`
|
|
321
|
+
// AND the package clobber's re-export of `Date.prototype.toTemporalInstant`
|
|
322
|
+
// both work — matching native Node. Guarded so we never replace a native
|
|
323
|
+
// implementation on a runtime that ships Temporal.
|
|
324
|
+
if (
|
|
325
|
+
typeof Date.prototype.toTemporalInstant !== "function" &&
|
|
326
|
+
typeof polyfill.toTemporalInstant === "function"
|
|
327
|
+
) {
|
|
328
|
+
Object.defineProperty(Date.prototype, "toTemporalInstant", {
|
|
329
|
+
value: polyfill.toTemporalInstant,
|
|
330
|
+
configurable: true,
|
|
331
|
+
writable: true,
|
|
332
|
+
enumerable: false,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const T = polyfill.Temporal;
|
|
336
|
+
defineTemporal(T);
|
|
337
|
+
return T;
|
|
338
|
+
},
|
|
339
|
+
set: defineTemporal,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ── Compile-cache handling (R8) ─────────────────────────────────────
|
|
344
|
+
// nub injects its preload chain via `--require`, which Node loads at bootstrap.
|
|
345
|
+
// If the user set NODE_COMPILE_CACHE, Node would enable the V8 code cache BEFORE
|
|
346
|
+
// this preload runs and cache every module the chain pulls in (preload.cjs,
|
|
347
|
+
// transform-core.mjs, this file, polyfills.cjs, …) into the USER's dir — so a
|
|
348
|
+
// program reading `fs.readdirSync(NODE_COMPILE_CACHE)` would see ~9 nub entries,
|
|
349
|
+
// not its own 1 (program-observable; R8). spawn.rs prevents that by STRIPPING
|
|
350
|
+
// NODE_COMPILE_CACHE from the child env (bootstrap caches nothing) and stashing
|
|
351
|
+
// the original value in a sentinel file keyed on nub's PID — which is THIS child's
|
|
352
|
+
// `process.ppid` (nub is our direct parent). The dir travels via a sentinel file,
|
|
353
|
+
// never a NUB_* env var (brand boundary).
|
|
354
|
+
//
|
|
355
|
+
// Two preload steps consume it:
|
|
356
|
+
// 1. restoreCompileCacheEnv() runs EARLY, before transform-core.mjs is required,
|
|
357
|
+
// to put the original value BACK into process.env.NODE_COMPILE_CACHE. That
|
|
358
|
+
// matters because (a) transform-core reads `NODE_COMPILE_CACHE === "0"` as
|
|
359
|
+
// nub's transpile-cache disable signal, and (b) user code may read the env.
|
|
360
|
+
// Restoring it in JS does NOT re-trigger Node's V8 compile cache (Node
|
|
361
|
+
// configures that once at bootstrap from the now-stripped env), so the
|
|
362
|
+
// preload chain stays uncached. It also DELETES the sentinel (consume-once,
|
|
363
|
+
// so a recycled PID can't read stale state and the file never leaks).
|
|
364
|
+
// 2. reenableUserCompileCache() runs LAST, after all nub modules are loaded
|
|
365
|
+
// uncached and right before user code, and calls
|
|
366
|
+
// `module.enableCompileCache(dir)` for a real dir so the user's OWN modules
|
|
367
|
+
// cache as they always did. A value of "0" is nub's disable sentinel (Node
|
|
368
|
+
// treats "0" as a literal dir named 0, but nub honors it as "no caching"),
|
|
369
|
+
// so we skip enabling there.
|
|
370
|
+
// Best-effort throughout: a missing/unreadable sentinel or an enableCompileCache
|
|
371
|
+
// failure just means no user compile cache — strictly safer than the old pollution.
|
|
372
|
+
// `os.tmpdir()` without requiring `node:os`. Requiring os at preload pulls
|
|
373
|
+
// `Internal Binding os` + `NativeModule os` into process.moduleLoadList on EVERY
|
|
374
|
+
// startup (test-bootstrap-modules observes this) even though almost no run touches
|
|
375
|
+
// the compile-cache sentinel. This replica mirrors Node's libuv/os.tmpdir() env
|
|
376
|
+
// resolution (POSIX: TMPDIR→TMP→TEMP→/tmp; Win32: TEMP→TMP→SystemRoot/windir+\temp),
|
|
377
|
+
// trailing-separator-stripped, which is also what Rust's env::temp_dir() (the side
|
|
378
|
+
// that WRITES the sentinel in spawn.rs) resolves to — so both ends agree.
|
|
379
|
+
function tmpdirNoOs() {
|
|
380
|
+
const env = process.env;
|
|
381
|
+
if (process.platform === "win32") {
|
|
382
|
+
let dir = env.TEMP || env.TMP || ((env.SystemRoot || env.windir || "") + "\\temp");
|
|
383
|
+
if (dir.length > 1 && dir.endsWith("\\") && !dir.endsWith(":\\")) dir = dir.slice(0, -1);
|
|
384
|
+
return dir;
|
|
385
|
+
}
|
|
386
|
+
let dir = env.TMPDIR || env.TMP || env.TEMP || "/tmp";
|
|
387
|
+
if (dir.length > 1 && dir.endsWith("/")) dir = dir.slice(0, -1);
|
|
388
|
+
return dir;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function compileCacheSentinelPath() {
|
|
392
|
+
return join(tmpdirNoOs(), `nub-ccache-${process.ppid}`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function restoreCompileCacheEnv() {
|
|
396
|
+
try {
|
|
397
|
+
const { readFileSync, rmSync } = require("node:fs");
|
|
398
|
+
const value = readFileSync(compileCacheSentinelPath(), "utf8");
|
|
399
|
+
try { rmSync(compileCacheSentinelPath()); } catch {}
|
|
400
|
+
if (value) process.env.NODE_COMPILE_CACHE = value;
|
|
401
|
+
} catch { /* no sentinel: env was never set, or already consumed */ }
|
|
402
|
+
// Propagate the R8 strip to node grandchildren the user spawns directly (plain
|
|
403
|
+
// node inheriting nub's --require preload + a live NODE_COMPILE_CACHE → it would
|
|
404
|
+
// cache nub's preload chain into the user's dir). The wrap MUST preserve each
|
|
405
|
+
// function's own symbols (esp. [util.promisify.custom]) — dropping them broke
|
|
406
|
+
// util.promisify(child_process.*) + abort/sync-io behavior. See wrapSpawnLike.
|
|
407
|
+
try { armChildProcessCompileCacheWrap(); } catch {}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Arm the child_process compile-cache wrap WITHOUT eagerly requiring child_process.
|
|
411
|
+
//
|
|
412
|
+
// Eagerly `require("node:child_process")` at preload time pulls ~40 builtins into
|
|
413
|
+
// process.moduleLoadList on EVERY startup — net, dgram, the entire streams tree,
|
|
414
|
+
// spawn_sync/tty_wrap/pipe_wrap/tcp_wrap, os, vm, etc. (test-bootstrap-modules
|
|
415
|
+
// observes the exact list; child_process is the dominant extra-builtin source).
|
|
416
|
+
// A program that never spawns a child shouldn't pay that cost — and Node's own
|
|
417
|
+
// startup never loads child_process.
|
|
418
|
+
//
|
|
419
|
+
// So we intercept `Module._load` and apply the wrap to the child_process module the
|
|
420
|
+
// FIRST time USER code requires it (`require('child_process')` /
|
|
421
|
+
// `require('node:child_process')`), patching the returned singleton before handing
|
|
422
|
+
// it back. After patching once we restore the original `_load`, so steady-state
|
|
423
|
+
// require() has zero added overhead. If the user never requires child_process, the
|
|
424
|
+
// module is never loaded and the builtins stay out of the load list — matching Node.
|
|
425
|
+
let __cpWrapArmed = false;
|
|
426
|
+
function armChildProcessCompileCacheWrap() {
|
|
427
|
+
if (__cpWrapArmed || __cpWrapped) return;
|
|
428
|
+
__cpWrapArmed = true;
|
|
429
|
+
if (typeof module_._load !== "function") return;
|
|
430
|
+
const origLoad = module_._load;
|
|
431
|
+
module_._load = function (request, parent, isMain) {
|
|
432
|
+
const exports = origLoad.call(this, request, parent, isMain);
|
|
433
|
+
if (request === "child_process" || request === "node:child_process") {
|
|
434
|
+
module_._load = origLoad; // restore: one-shot, no steady-state overhead
|
|
435
|
+
try { wrapChildProcessCompileCache(exports); } catch {}
|
|
436
|
+
}
|
|
437
|
+
return exports;
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Monkey-patch child_process so node-targeted children the USER spawns with an
|
|
442
|
+
// explicit live NODE_COMPILE_CACHE get the SAME R8 treatment spawn.rs gives nub's
|
|
443
|
+
// own children: strip NODE_COMPILE_CACHE from the child env (so Node's bootstrap
|
|
444
|
+
// caches nothing of nub's inherited preload chain) and stash the original dir in a
|
|
445
|
+
// PID-keyed sentinel file the grandchild's restoreCompileCacheEnv() reads back via
|
|
446
|
+
// `process.ppid` to re-enable caching for the USER's own modules post-bootstrap.
|
|
447
|
+
// Brand rule: the dir travels via a sentinel file, never a NUB_* env var.
|
|
448
|
+
// `cp` is the already-loaded child_process exports object, passed in by the lazy
|
|
449
|
+
// `_load` interceptor so we never require it ourselves (which would defeat the
|
|
450
|
+
// deferral).
|
|
451
|
+
let __cpWrapped = false;
|
|
452
|
+
function wrapChildProcessCompileCache(cp) {
|
|
453
|
+
if (__cpWrapped || !cp) return;
|
|
454
|
+
__cpWrapped = true;
|
|
455
|
+
const { writeFileSync } = require("node:fs");
|
|
456
|
+
const { basename } = require("node:path");
|
|
457
|
+
|
|
458
|
+
const isNodeTarget = (command) => {
|
|
459
|
+
if (typeof command !== "string" || command.length === 0) return false;
|
|
460
|
+
if (command === process.execPath) return true;
|
|
461
|
+
const base = basename(command).toLowerCase();
|
|
462
|
+
return base === "node" || base === "node.exe";
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// Returns a possibly-rewritten options object with NODE_COMPILE_CACHE stripped
|
|
466
|
+
// from its env, after writing the sentinel keyed on THIS process's pid (= the
|
|
467
|
+
// grandchild's process.ppid). No-op unless the child env explicitly carries a
|
|
468
|
+
// live (non-empty, != "0") NODE_COMPILE_CACHE — an inherited (undefined env)
|
|
469
|
+
// value was already stripped from this process by nub, so nothing to do there.
|
|
470
|
+
const stripFromOptions = (options) => {
|
|
471
|
+
if (!options || typeof options !== "object") return options;
|
|
472
|
+
const env = options.env;
|
|
473
|
+
if (!env || typeof env !== "object") return options;
|
|
474
|
+
const dir = env.NODE_COMPILE_CACHE;
|
|
475
|
+
if (!dir || dir === "0") return options;
|
|
476
|
+
try {
|
|
477
|
+
writeFileSync(join(tmpdirNoOs(), `nub-ccache-${process.pid}`), String(dir));
|
|
478
|
+
} catch { return options; }
|
|
479
|
+
const newEnv = { ...env };
|
|
480
|
+
delete newEnv.NODE_COMPILE_CACHE;
|
|
481
|
+
return { ...options, env: newEnv };
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// For (command, args?, options?) signatures the options object is the last arg
|
|
485
|
+
// that is a non-array object; args is an optional array in between. Rewrites the
|
|
486
|
+
// call in place and dispatches to the original.
|
|
487
|
+
// Copy `orig`'s OWN symbols onto `wrapped` — crucially [util.promisify.custom],
|
|
488
|
+
// which Node sets on execFile/exec so `util.promisify(execFile)` returns a
|
|
489
|
+
// {stdout,stderr} promise. A bare wrapper without it silently changes promisify's
|
|
490
|
+
// result shape (broke test-child-process-promisified / -abortController /
|
|
491
|
+
// util-promisify-custom-names / sync-io-option / test-output-abort).
|
|
492
|
+
const preserveSymbols = (wrapped, orig) => {
|
|
493
|
+
for (const s of Object.getOwnPropertySymbols(orig)) {
|
|
494
|
+
try { wrapped[s] = orig[s]; } catch { /* read-only symbol: skip */ }
|
|
495
|
+
}
|
|
496
|
+
return wrapped;
|
|
497
|
+
};
|
|
498
|
+
const wrapSpawnLike = (orig) => preserveSymbols(function (command, ...rest) {
|
|
499
|
+
if (isNodeTarget(command)) {
|
|
500
|
+
let optIdx = -1;
|
|
501
|
+
for (let i = rest.length - 1; i >= 0; i--) {
|
|
502
|
+
const a = rest[i];
|
|
503
|
+
if (a && typeof a === "object" && !Array.isArray(a)) { optIdx = i; break; }
|
|
504
|
+
if (typeof a === "function") continue; // execFile callback
|
|
505
|
+
if (Array.isArray(a)) break; // args array — no options object present
|
|
506
|
+
}
|
|
507
|
+
if (optIdx >= 0) rest[optIdx] = stripFromOptions(rest[optIdx]);
|
|
508
|
+
}
|
|
509
|
+
return orig.call(this, command, ...rest);
|
|
510
|
+
}, orig);
|
|
511
|
+
|
|
512
|
+
cp.spawn = wrapSpawnLike(cp.spawn);
|
|
513
|
+
cp.spawnSync = wrapSpawnLike(cp.spawnSync);
|
|
514
|
+
cp.execFile = wrapSpawnLike(cp.execFile);
|
|
515
|
+
cp.execFileSync = wrapSpawnLike(cp.execFileSync);
|
|
516
|
+
|
|
517
|
+
// fork() always runs `process.execPath`, so it is always a node target. Its
|
|
518
|
+
// signature is (modulePath, args?, options?); reuse the same options rewrite.
|
|
519
|
+
const origFork = cp.fork;
|
|
520
|
+
cp.fork = function (modulePath, ...rest) {
|
|
521
|
+
let optIdx = -1;
|
|
522
|
+
for (let i = rest.length - 1; i >= 0; i--) {
|
|
523
|
+
const a = rest[i];
|
|
524
|
+
if (a && typeof a === "object" && !Array.isArray(a)) { optIdx = i; break; }
|
|
525
|
+
if (Array.isArray(a)) break;
|
|
526
|
+
}
|
|
527
|
+
if (optIdx >= 0) rest[optIdx] = stripFromOptions(rest[optIdx]);
|
|
528
|
+
return origFork.call(this, modulePath, ...rest);
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function reenableUserCompileCache() {
|
|
533
|
+
const dir = process.env.NODE_COMPILE_CACHE;
|
|
534
|
+
// "0" is nub's disable signal (see transform-core); anything else is the user's
|
|
535
|
+
// real cache dir, which we re-point Node's compile cache at for THEIR modules.
|
|
536
|
+
if (!dir || dir === "0") return;
|
|
537
|
+
try { module_.enableCompileCache(dir); } catch {}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
module.exports = {
|
|
541
|
+
installWatchReporting,
|
|
542
|
+
makeHooks,
|
|
543
|
+
installCjsRequireHooks,
|
|
544
|
+
preloadPolyfillPackages,
|
|
545
|
+
installTemporalLazyGlobal,
|
|
546
|
+
restoreCompileCacheEnv,
|
|
547
|
+
reenableUserCompileCache,
|
|
548
|
+
};
|