Skip to content

Defer to native import-text on Node 26.5+ - #395

Merged
colinhacks merged 1 commit into
mainfrom
unflag-import-text
Jul 9, 2026
Merged

Defer to native import-text on Node 26.5+#395
colinhacks merged 1 commit into
mainfrom
unflag-import-text

Conversation

@colinhacks

Copy link
Copy Markdown
Contributor

Node 26.5.0 added --experimental-import-text (nodejs/node#62300) for import x from './f' with { type: "text" }. nub already polyfills text imports; this defers to native textStrategy where it exists.

  • feature_matrix: import-text Unflag [26.5.0, ∞) → inject the flag; stripped below the floor.
  • preload-common.cjs: feature-detect via allowedNodeEnvironmentFlags; 26.5+ nextLoads to native, else polyfills.
  • docs: "polyfilled below 26.5, native above", floor 18.20.

Byte-parity vs node --experimental-import-text on 26.5.0; polyfill on 26.4.0/18.20.4.

Node 26.5.0 added `--experimental-import-text` (nodejs/node#62300), which
gates `import x from './f' with { type: 'text' }`. nub already provides text
imports on every version that parses the `with` syntax (Node 18.20+) via its
loader polyfill (loadTextImport). Per the additive contract, defer to Node's
own textStrategy where it now exists and keep the polyfill only below it.

- feature_matrix: import-text Unflag row [26.5.0, inf) so nub injects
  --experimental-import-text (open-ended; never default-on through Node 27).
  Below the floor the flag is not injected and is stripped from an inherited
  NODE_OPTIONS (it is a "bad option" there).
- preload-common.cjs (fast tier): feature-detect native support via
  process.allowedNodeEnvironmentFlags; on 26.5+ nextLoad to native, else
  loadTextImport. preload-async-hooks.mjs (compat tier) always polyfills --
  that tier tops out at Node 22.14, below 26.5.
- docs: Modern APIs row now reads "polyfilled below Node 26.5, native above",
  floored at Node 18.20 (the `with`-syntax parse floor).

Verified byte-for-byte parity against `node --experimental-import-text` on
26.5.0; polyfill path on 26.4.0/18.20.4; SyntaxError on 18.19.1.

Claude-Session: https://claude.ai/code/session_01BgByKuKHWvYLhy6NSgdMkh
Copilot AI review requested due to automatic review settings July 9, 2026 15:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nub Ready Ready Preview, Comment Jul 9, 2026 4:00pm

Request Review

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — the fast-tier load hook now defers import … with { type: "text" } to Node's native --experimental-import-text on 26.5+, keeping the polyfill below.

  • Add import-text to the feature matrix — a single open-ended Unflag("--experimental-import-text") band [26.5.0, ∞) in feature_matrix.rs, slotting into the existing derived-floor machinery with no special-casing.
  • Fast-tier native deferralpreload-common.cjs feature-detects native support via process.allowedNodeEnvironmentFlags (NATIVE_IMPORT_TEXT) and steps aside to nextLoad on 26.5+, else polyfills via core.loadTextImport.
  • Compat-tier commentpreload-async-hooks.mjs documents that its tier tops out at 22.14, so native import-text is never reachable there.
  • Testsflags.rs and feature_matrix.rs cover inject at/above 26.5, the --no-experimental-import-text opt-out, NODE_OPTIONS stripping below the floor, and unflag_floor == 26.5.0.
  • Docs — a new runtime-table row plus prose ("polyfilled below Node 26.5, native above", floor 18.20).

The change is clean, self-consistent, and well-scoped. Verified independently:

  • The Node fact is grounded — --experimental-import-text landed in nodejs/node#62300 (semver-minor, experimental, flag-gated), matching the 26.5.0 placement in the matrix and evidence string.
  • The fast-tier deferral is correct: nub's resolve hook returns { url, shortCircuit } with no format, so nextLoad(url, context) reaches Node's default load with the type: "text" attribute intact and native textStrategy engages.
  • The compat-tier claim holds: that tier is 18.19–22.14, and the forced-async-tier path (node_hook_compose_broken) is bounded to 22.15–24.11.0 — both below 26.5 — so preload-async-hooks.mjs never runs on a native-capable Node.
  • No regression: deferring to nextLoad is strictly more permissive than the prior unconditional short-circuit, so a user load hook chained after nub's still observes type: "text" on 26.5+.

Pullfrog  | View workflow run | Using Claude Opus𝕏

@colinhacks
colinhacks merged commit 2407881 into main Jul 9, 2026
57 of 59 checks passed
colinhacks added a commit that referenced this pull request Jul 9, 2026
The salvaged test comments referenced the env-signal design from the closed
#396 branch (__NUB_NATIVE_IMPORT_TEXT); the merged #395 mechanism is the
NATIVE_IMPORT_TEXT feature-detect const in preload-common.cjs. Comments only.

Claude-Session: https://claude.ai/code/session_01Gp4tAn3Y66MWpAdGZm9VmL
colinhacks added a commit that referenced this pull request Jul 9, 2026
… 26.5 native) (#397)

* test: pin cross-tier import-text behavior (22.13 compat / 24.4 fast / 26.5 native)

Claude-Session: https://claude.ai/code/session_0144KrmfsBDfzFZnHAMaH6E3

* test: fix doc-comment references to the merged native-defer mechanism

The salvaged test comments referenced the env-signal design from the closed
#396 branch (__NUB_NATIVE_IMPORT_TEXT); the merged #395 mechanism is the
NATIVE_IMPORT_TEXT feature-detect const in preload-common.cjs. Comments only.

Claude-Session: https://claude.ai/code/session_01Gp4tAn3Y66MWpAdGZm9VmL
colinhacks added a commit that referenced this pull request Jul 10, 2026
…d set (#398)

nub injects `--experimental-*` flags to unflag Node features early, with
several open-ended version bands on the premise that once a flag exists Node
accepts it forever. That premise is false: Node keeps some unflagged flags as
no-ops but hard-removes others (`--experimental-policy`,
`--experimental-network-imports`; `--experimental-permission` was deleted at
24.0 when it stabilized to `--permission`). Injecting a removed flag is a
`node: bad option` startup abort, so an open-ended band would crash nub on a
future Node that drops the flag.

Probe the target Node's `process.allowedNodeEnvironmentFlags` once, cache it per
(path, mtime), and intersect it with the version-band inject set in
`compute_inject_flags` (Stage 4). A flag the running Node does not accept is
dropped, so injection self-corrects with no nub release when Node removes a
flag. Probe unavailable falls back to the prior version-band behavior. The
probe clears NODE_OPTIONS so an inherited preload can't pollute or side-effect
it.

A host-Node invariant test asserts the intersection drops nothing on a current,
supported Node (nub only injects env-allowed flags).

Refs #395.
@colinhacks

Copy link
Copy Markdown
Contributor Author

Shipped in v0.4.6: https://github.com/nubjs/nub/releases/tag/v0.4.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants