@nubjs/nub-linux-x64 0.0.12 → 0.0.14

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.
@@ -1,317 +1,101 @@
1
- // Nub fast-path preload — Node 22.15+ (sync `module.registerHooks`).
1
+ // Nub compat-tier preload — Node 18.19–22.14, injected via `--import` (ESM).
2
2
  //
3
- // This file is the main-thread `--import` preload for ALL tiers. It owns only
4
- // the tier-specific wiring; every resolution + transpile primitive lives in
5
- // runtime/transform-core.mjs (shared verbatim with the compat-tier loader
6
- // worker, so the two can never drift). What stays here:
7
- // - watch-mode dependency IPC (main-thread only),
8
- // - the thin resolve/load hooks that delegate to the core,
9
- // - the main-thread CommonJS `require()` shim (Module._resolveFilename +
10
- // require.extensions), installed on BOTH tiers `module.register` hooks ESM
11
- // but not require, and `module.registerHooks`' require coverage is incomplete
12
- // before ~Node 24 (e.g. require-from-`.cts` on 22.15),
13
- // - clobbered-polyfill preloading + the Temporal lazy global,
14
- // - tier dispatch (registerHooks vs register).
15
-
3
+ // The FAST tier (Node 22.15+) is loaded separately, as a `--require` CommonJS
4
+ // preload (runtime/preload.cjs), so Node keeps its synchronous `Module.runMain`
5
+ // CJS entry path (top-level `executionAsyncId()===1`, sync exception origin,
6
+ // `require.main.id` `'.'`, `module.parent` `null`). The mere presence of an
7
+ // `--import` ESM preload forces eager ESM-loader init that routes even a CJS entry
8
+ // through the async ESM module-job (R1) so the fast tier must NOT use `--import`.
9
+ //
10
+ // THIS file stays the compat path: on 18.19–22.14, `module.registerHooks` does not
11
+ // exist and `require(esm)` is unreliable, so hooks run async in a dedicated loader
12
+ // worker via `module.register`, and the parser is preloaded via dynamic `import()`
13
+ // while we can still `await`. That async machinery is exactly why the compat tier
14
+ // keeps `--import` — its top-level `await` is accepted here (an `--import` ESM
15
+ // module may be async), and the < 22.15 floor has no equivalent sync surface.
16
+ //
17
+ // Resolution + transpile primitives come from runtime/transform-core.mjs; the
18
+ // non-tier-specific wiring (watch IPC, the CJS require() shim, clobbered-polyfill
19
+ // preloading, the Temporal lazy global) is shared verbatim with the fast tier via
20
+ // runtime/preload-common.cjs, so the two tiers can never drift.
21
+
22
+ // MUST be first: restores NODE_COMPILE_CACHE into process.env (R8) before
23
+ // transform-core.mjs's body evaluates, since transform-core reads it as the
24
+ // transpile-cache disable signal. ESM imports evaluate in source order, so this
25
+ // side-effecting import has to precede the transform-core import. See
26
+ // compile-cache-restore.mjs.
27
+ import "./compile-cache-restore.mjs";
16
28
  import module from "node:module";
17
- import { readdirSync } from "node:fs";
18
- import { fileURLToPath, pathToFileURL } from "node:url";
19
29
  import { createRequire } from "node:module";
20
- import { join, dirname, resolve as pathResolve, extname as pathExtname } from "node:path";
21
- import {
22
- TRANSPILE_EXTS, DATA_EXTS, TS_PARENT_EXTS,
23
- extname, isNodeModules, getTsconfigForDir, getPackageType,
24
- resolveSpec, resolveCjsPath, requireTargetIsEsm, loadTranspile, loadData,
25
- maybeSweepCache, setWatchHooks, ensureParser,
26
- } from "./transform-core.mjs";
30
+ import * as core from "./transform-core.mjs";
27
31
 
28
32
  const __require = createRequire(import.meta.url);
29
-
30
- // ── Watch-mode dependency reporting ─────────────────────────────────
31
- // Under `nub watch`, Node's FilesWatcher only watches files in the import graph;
32
- // config files (tsconfig.json, package.json) and `.env*` are NOT in any graph,
33
- // so an edit to them otherwise goes stale. Node accepts incremental
34
- // `process.send({'watch:require': [...]})` over its WATCH_REPORT_DEPENDENCIES
35
- // IPC at ANY point in the child's life (it adds each path to the watch set), so
36
- // we report config paths AS the core loader discovers them. The reporters are
37
- // injected into the core via setWatchHooks (below) so getTsconfigForDir /
38
- // getPackageType self-report. The flush is coalesced via setImmediate.
39
- const WATCH_REPORTING = process.env.WATCH_REPORT_DEPENDENCIES === "1" && typeof process.send === "function";
40
- const watchReported = new Set();
41
- const watchPending = [];
42
- let watchFlushScheduled = false;
43
- function flushWatchDeps() {
44
- watchFlushScheduled = false;
45
- if (watchPending.length === 0) return;
46
- const batch = watchPending.splice(0, watchPending.length);
47
- try { process.send({ "watch:require": batch }); } catch {}
48
- }
49
- function reportWatchDep(path) {
50
- if (!WATCH_REPORTING || !path || watchReported.has(path)) return;
51
- watchReported.add(path);
52
- watchPending.push(path);
53
- if (!watchFlushScheduled) {
54
- watchFlushScheduled = true;
55
- // A scheduled immediate is drained before the loop would exit, so even a
56
- // script that finishes synchronously flushes its deps. (Don't unref: an
57
- // unref'd immediate is skipped on a synchronous exit, dropping the report.)
58
- setImmediate(flushWatchDeps);
59
- }
60
- }
61
- // Report a directory's `.env*` files (the natural watch targets). Scanned once
62
- // per directory, lazily.
63
- const watchEnvScannedDirs = new Set();
64
- function reportEnvFilesIn(dir) {
65
- if (!WATCH_REPORTING || watchEnvScannedDirs.has(dir)) return;
66
- watchEnvScannedDirs.add(dir);
67
- let entries;
68
- try { entries = readdirSync(dir); } catch { return; }
69
- for (const name of entries) {
70
- if (name === ".env" || name.startsWith(".env.")) reportWatchDep(join(dir, name));
71
- }
72
- }
73
- setWatchHooks({ reportDep: reportWatchDep, reportEnvDir: reportEnvFilesIn });
74
-
75
- // Best-effort bounded-cache eviction (main thread only; the core guards on it).
76
- maybeSweepCache();
77
-
78
- // ── Resolve hook ────────────────────────────────────────────────────
79
- function resolve(specifier, context, nextResolve) {
80
- const r = resolveSpec(specifier, context.parentURL);
81
- return r ?? nextResolve(specifier, context);
82
- }
83
-
84
- // ── Load hook ───────────────────────────────────────────────────────
85
- function load(url, context, nextLoad) {
86
- const ext = extname(url);
87
-
88
- // Watch mode: surface this file's nearest config files (tsconfig.json,
89
- // package.json) + sibling `.env*` so edits to them restart the run. Done for
90
- // every user file (not just transpiled ones) — getTsconfigForDir/getPackageType
91
- // self-report via the injected watch hooks.
92
- if (WATCH_REPORTING && url.startsWith("file:") && !isNodeModules(url)) {
93
- try {
94
- const dir = dirname(fileURLToPath(url));
95
- getTsconfigForDir(dir);
96
- getPackageType(dir);
97
- } catch {}
98
- }
99
-
100
- if (TRANSPILE_EXTS.has(ext)) return loadTranspile(url, ext);
101
- if (ext in DATA_EXTS) return loadData(url, ext);
102
-
103
- return nextLoad(url, context);
104
- }
105
-
106
- // ── CommonJS require() augmentation (BOTH tiers) ────────────────────
107
- // `module.registerHooks`' CJS-`require()` coverage is INCOMPLETE before ~Node 24:
108
- // on Node 22.15 a `require()` from a `.cts` parent (which Node loads via the ESM
109
- // translator's special-require) hits native Module._resolveFilename with no
110
- // tsconfig/extensionless handling — a `require('@alias')` or `require('./x')` of a
111
- // `.ts` target throws MODULE_NOT_FOUND, while the same code works on Node 26 (where
112
- // registerHooks does cover it) and on the fast `import` path. On the compat tier
113
- // (18.19–22.14) the only hook surface is `module.register`, which intercepts the
114
- // ESM loader ONLY — so `require()` is entirely unaugmented there. Both gaps have
115
- // the same closure: install this main-thread CJS shim, reusing the core's canonical
116
- // resolveCjsPath / loadTranspile (no drift). It tries nub's resolution first and
117
- // FALLS THROUGH to native on a miss, so it is a safe no-op on the versions where
118
- // registerHooks already covers require (Node 24+/26). Mechanism stays within the
119
- // augmenter rules: exactly what `--require`-installing the ts-node / tsx CJS shim
120
- // has always done. So it runs on the fast tier too — registerHooks is necessary
121
- // but, below ~Node 24, not sufficient for require().
122
- // This error is surfaced ONLY on Node versions without native require(esm)
123
- // (< 20.19 / 22.0–22.11), where require() of an ES module genuinely cannot work.
124
- // On every require(esm)-capable Node, Node loads the ES module itself and this is
125
- // never reached. The message is user-facing: no internal mechanism names.
126
- function requireEsmError(filename) {
127
- const err = new Error(
128
- `Cannot require() this file — it is an ES module.\n` +
129
- ` ${filename}\n` +
130
- `It uses \`import\`/\`export\`, so it loads as an ES module, and this version of ` +
131
- `Node can't require() an ES module. Load it with \`import(...)\` instead, rename ` +
132
- `it to .cts for a CommonJS module, or upgrade Node.`,
133
- );
134
- err.code = "ERR_REQUIRE_ESM";
135
- return err;
136
- }
137
-
138
- // `withClassicTranspile` — also install the `require.extensions` (classic CommonJS
139
- // loader) transpile hook. This is needed ONLY on Node WITHOUT native require(esm)
140
- // (< 20.19 / 22.0–22.11): there, `module.register`'s ESM-loader hooks can't reach a
141
- // `require()`, AND an ES module simply can't be require()d, so we transpile CJS
142
- // content classically and surface a clean error for ESM content. On require(esm)-
143
- // capable Node we DON'T install it — registering `require.extensions['.ts']` would
144
- // shadow Node's own native require(esm) of ES-module `.ts` files (breaking
145
- // `require("./esm.ts")`), and the resolve shim below plus the tier's load hook
146
- // already cover resolution + transpile.
147
- function installCjsRequireHooks(withClassicTranspile) {
148
- // Module._resolveFilename: try nub's resolution first (tsconfig paths,
149
- // extensionless .ts, .js→.ts). If the target is an ES-module TS file AND this
150
- // Node can't require(esm), throw a clean error HERE — before Node's special-
151
- // require (used for require() from a CJS module loaded via the ESM translator)
152
- // crashes with an opaque `cjsCache.get(...)` TypeError. On require(esm)-capable
153
- // Node, return the path and let Node load the ES module itself. On miss/own
154
- // error, fall through to Node (augmentation never masks Node's diagnostics).
155
- const origResolveFilename = module._resolveFilename;
156
- module._resolveFilename = function (request, parent, isMain, options) {
157
- let resolved = null;
158
- try {
159
- const parentPath = parent && typeof parent.filename === "string" ? parent.filename : null;
160
- resolved = resolveCjsPath(request, parentPath);
161
- } catch { /* fall through to Node */ }
162
- if (resolved) {
163
- if (withClassicTranspile && requireTargetIsEsm(resolved, pathExtname(resolved))) {
164
- throw requireEsmError(resolved);
165
- }
166
- return resolved;
167
- }
168
- return origResolveFilename.call(this, request, parent, isMain, options);
169
- };
170
-
171
- if (!withClassicTranspile) return;
172
-
173
- // require.extensions: transpile via the SAME loadTranspile the load hook uses —
174
- // target:'es2022' lowering (`using`), tsconfig, source maps, the Stage-3
175
- // decorator guard, and module-format detection are all identical to the fast
176
- // tier. The path is already a real TS file (Module._resolveFilename ran first).
177
- // A module-format source can't be _compile'd as CJS — same clean error as above.
178
- const transpileExtension = (mod, filename) => {
179
- const { source, format } = loadTranspile(pathToFileURL(filename).href, pathExtname(filename));
180
- if (format === "module") throw requireEsmError(filename);
181
- mod._compile(source, filename);
182
- };
183
- for (const ext of [".ts", ".cts", ".mts", ".tsx", ".jsx"]) {
184
- module._extensions[ext] = transpileExtension;
185
- }
186
- }
187
-
188
- // ── Pre-load clobbered polyfill packages BEFORE hooks register ──────
189
- // Packages in the core's CLOBBER_MAP can't be imported after hooks register
190
- // because the resolve hook returns a synthetic module instead of the real
191
- // package. Load them here via CJS require (not yet hooked) and stash them.
192
- // Temporal is the exception (A37): the polyfill is ~18ms to load and most scripts
193
- // never touch it, so we only RESOLVE its path now (cheap) and defer the load to a
194
- // lazy global getter (below). Requiring it later by absolute path bypasses the
195
- // CLOBBER_MAP resolve-hook entry, which keys on the "@js-temporal/polyfill"
196
- // specifier — not the resolved path.
197
- const __preloadedPolyfills = {};
198
- // Feature-detect before requiring (A39): URLPattern is native on Node 24+, so
199
- // skip loading the polyfill there. On 22.x it's absent → load it.
200
- if (typeof globalThis.URLPattern === "undefined") {
201
- try { __preloadedPolyfills.urlpattern = __require("urlpattern-polyfill"); } catch {}
202
- }
203
- // Float16Array: native on Node 24+, absent on the 22.x floor.
204
- if (typeof globalThis.Float16Array === "undefined") {
205
- try { __preloadedPolyfills.float16 = __require("@petamoriken/float16"); } catch {}
206
- }
207
- let __temporalPath;
208
- try { __temporalPath = __require.resolve("@js-temporal/polyfill"); } catch {}
209
-
210
- // ── Tier dispatch ───────────────────────────────────────────────────
211
- // Three tiers per wiki/research/supported-node-versions.md:
212
- // - >= 22.15.0: sync module.registerHooks (fast path, in-thread)
213
- // - >= 18.19.0 < 22.15: async module.register (loader worker) + main-thread
214
- // CJS require() shim (module.register is ESM-only)
215
- // - < 18.19.0: unsupported (Rust spawn path hard-errors; defensive log)
216
- //
217
- // No user-visible distinction between the two augmentation tiers — both deliver
218
- // the identical feature surface, silently. The compat-mode notice was dropped
219
- // 2026-05-29 (it labeled a purely internal mechanism choice).
220
- const __nodeVersionParts = process.versions.node.split(".").map((n) => parseInt(n, 10));
221
- const __nodeMajor = __nodeVersionParts[0] || 0;
222
- const __nodeMinor = __nodeVersionParts[1] || 0;
223
- const __isAugmentedTier = __nodeMajor > 22 || (__nodeMajor === 22 && __nodeMinor >= 15);
224
- const __isCompatTier = !__isAugmentedTier && (__nodeMajor > 18 || (__nodeMajor === 18 && __nodeMinor >= 19));
225
- // Native TypeScript support (`process.features.typescript` is "strip"/"transform"
226
- // — Node 22.18+/24+). Where present, Node loads `.ts` from require() itself,
227
- // INCLUDING ES-module `.ts` via native require(esm); registering the classic
228
- // `require.extensions['.ts']` shim there would shadow that and break
229
- // `require("./esm.ts")`. Where absent (≤ 22.17 / the whole compat tier), Node can't
230
- // load a required `.ts` on its own, so the shim is required — it transpiles CJS
231
- // content and surfaces a clean error for ES-module content (which require() can't
232
- // load there anyway). Feature-detected, not version-gated.
33
+ const common = __require("./preload-common.cjs");
34
+ const { installSyncPolyfills } = __require("./polyfills.cjs");
35
+
36
+ // ── Tier detection ──────────────────────────────────────────────────
37
+ // This `.mjs` preload should only ever be `--import`ed for the compat tier (the
38
+ // Rust spawn path chooses `--require preload.cjs` for 22.15+). But guard anyway: if
39
+ // someone `--import`s it directly on an unsupported Node, emit a clear message and
40
+ // skip hook registration rather than throw (throwing breaks user-invoked --import
41
+ // flows). The fast-tier branch is intentionally absent here 22.15+ goes through
42
+ // preload.cjs.
43
+ const [__major = 0, __minor = 0] = process.versions.node.split(".").map((n) => parseInt(n, 10));
44
+ const __isCompatTier = __major > 18 || (__major === 18 && __minor >= 19);
45
+ const __isFastTier = __major > 22 || (__major === 22 && __minor >= 15);
46
+
47
+ // Native TypeScript support (`process.features.typescript`). Where absent (the
48
+ // whole compat tier ≤ 22.17), Node can't load a required `.ts` on its own, so the
49
+ // classic require.extensions transpile shim is installed; where present it's
50
+ // skipped so Node's native require() of `.ts` isn't shadowed.
233
51
  const __hasNativeTs = !!process.features?.typescript;
234
52
 
235
- if (__isAugmentedTier) {
236
- // Fast path: sync, in-thread hooks. Same realm as user code; covers `import` and
237
- // (Node 24+) `require`. registerHooks' require RESOLUTION is incomplete on
238
- // 22.15–24, so also install the main-thread resolve shim. Install the classic
239
- // require.extensions transpile shim only on 22.15–22.17 (no native `.ts`); on
240
- // 22.18+/24+ skip it so Node's native require() of `.ts` — incl. ES modules —
241
- // isn't shadowed (see installCjsRequireHooks).
53
+ // Watch reporting + the Temporal lazy global are tier-independent.
54
+ common.installWatchReporting(core);
55
+
56
+ if (__isFastTier) {
57
+ // Defensive only the Rust path uses preload.cjs for 22.15+. If reached, the
58
+ // sync registerHooks API is available; register synchronously to stay correct.
59
+ const { resolve, load } = common.makeHooks(core, process.env.WATCH_REPORT_DEPENDENCIES === "1");
242
60
  module.registerHooks({ resolve, load });
243
- installCjsRequireHooks(!__hasNativeTs);
61
+ common.installCjsRequireHooks(core, !__hasNativeTs);
244
62
  } else if (__isCompatTier) {
245
63
  // Compat path: ESM `import` hooks run in a dedicated loader worker thread.
246
64
  module.register("./preload-async-hooks.mjs", import.meta.url);
247
65
  // The main-thread CommonJS require() shim's format detection is synchronous and
248
- // reaches down to Node 18.19, where `require("oxc-parser")` (ESM-only) fails —
249
- // so preload the parser via dynamic import now, while we can still await.
250
- await ensureParser();
66
+ // reaches down to Node 18.19, where `require("oxc-parser")` (ESM-only) fails — so
67
+ // preload the parser via dynamic import now, while we can still await.
68
+ await core.ensureParser();
251
69
  // module.register() is ESM-loader-only; augment CommonJS require() on the main
252
70
  // thread too. The compat tier never has native `.ts`, so it always installs the
253
71
  // classic transpile shim (CJS transpile + clean error for ES-module require()).
254
- installCjsRequireHooks(!__hasNativeTs);
72
+ common.installCjsRequireHooks(core, !__hasNativeTs);
255
73
  } else {
256
- // Defensive: Rust spawn path hard-errors on < 18.19 before reaching here. If
257
- // invoked directly via `node --import`, emit a clear message and skip hook
258
- // registration rather than throw (throwing breaks user-invoked --import flows).
259
74
  process.stderr.write(
260
75
  `Nub requires Node 18.19 or newer for runtime augmentation; got ${process.versions.node}. Preload is inactive.\n`,
261
76
  );
262
77
  }
263
78
 
264
- // ── Polyfill preloads (tier-independent) ────────────────────────────
265
- // Polyfills install identically on both augmented and compat paths.
266
- globalThis.__nubPreloaded = __preloadedPolyfills;
267
- await import("./polyfills.mjs");
268
- delete globalThis.__nubPreloaded;
269
-
270
- // ── Temporal: lazy global (A37) ─────────────────────────────────────
271
- // Temporal ships in no supported Node, so nub always provides it — but the
272
- // polyfill is ~18ms to load and rarely used, so install a lazy getter that loads
273
- // it (by resolved path, bypassing the clobber) on first access. The CLOBBER
274
- // re-export of `import "@js-temporal/polyfill"` reads globalThis.Temporal, so a
275
- // user importing the package triggers this same one-time load. A plain assignment
276
- // (`globalThis.Temporal = …`) replaces the accessor with the value.
277
- if (typeof globalThis.Temporal === "undefined" && __temporalPath) {
278
- const defineTemporal = (value) =>
279
- Object.defineProperty(globalThis, "Temporal", {
280
- value,
281
- configurable: true,
282
- writable: true,
283
- enumerable: false,
284
- });
285
- Object.defineProperty(globalThis, "Temporal", {
286
- configurable: true,
287
- enumerable: false,
288
- get() {
289
- const polyfill = __require(__temporalPath);
290
- // @js-temporal/polyfill exports `toTemporalInstant` as a function but does
291
- // NOT auto-install it on Date.prototype (you assign it yourself). Install it
292
- // here so that on the floor (no native Temporal) `date.toTemporalInstant()`
293
- // AND the package clobber's re-export of `Date.prototype.toTemporalInstant`
294
- // both work — matching native Node. Guarded so we never replace a native
295
- // implementation on a runtime that ships Temporal.
296
- if (
297
- typeof Date.prototype.toTemporalInstant !== "function" &&
298
- typeof polyfill.toTemporalInstant === "function"
299
- ) {
300
- Object.defineProperty(Date.prototype, "toTemporalInstant", {
301
- value: polyfill.toTemporalInstant,
302
- configurable: true,
303
- writable: true,
304
- enumerable: false,
305
- });
306
- }
307
- const T = polyfill.Temporal;
308
- defineTemporal(T);
309
- return T;
310
- },
311
- set: defineTemporal,
312
- });
79
+ // ── Clobbered-polyfill preloading + polyfills ───────────────────────
80
+ // Require the clobbered polyfill packages before the resolve hook would intercept
81
+ // them, then install the sync globals (shared with the fast tier) and the two ESM
82
+ // side-effect modules. On the compat tier `require(esm)` of the worker/navigator
83
+ // modules is unreliable, so they load via dynamic `import()` here.
84
+ const __preloadedPolyfills = common.preloadPolyfillPackages(__require);
85
+ installSyncPolyfills(__preloadedPolyfills);
86
+ if (typeof globalThis.navigator?.locks === "undefined") {
87
+ await import("./navigator-locks.mjs");
313
88
  }
89
+ await import("./worker-polyfill.mjs");
314
90
 
315
- // Watch-mode dependency reporting is incremental — see reportWatchDep + the
316
- // injected core hooks. Config deps are reported as the loader discovers each user
317
- // file, plus their `.env*` siblings.
91
+ // ── Temporal: lazy global (A37) ─────────────────────────────────────
92
+ common.installTemporalLazyGlobal(__require);
93
+
94
+ // ── Compile-cache: re-enable for the USER's modules (R8) ────────────
95
+ // spawn.rs strips NODE_COMPILE_CACHE for every augmented spawn (both tiers), so
96
+ // nub's preload chain is never cached into the user's dir. Re-point the cache at
97
+ // the user's original dir (via the PID-keyed sentinel) so their own modules still
98
+ // cache. `module.enableCompileCache` only exists on Node 22.8+, so on most of the
99
+ // compat tier (< 22.8) this is a safe no-op; it benefits 22.8–22.14 users who set
100
+ // NODE_COMPILE_CACHE. See reenableUserCompileCache for the full rationale.
101
+ common.reenableUserCompileCache();