node:module: stripTypeScriptTypes strip mode + StrippedTypeScript compile cache (+3 tests) - #35517
Draft
cirospaciari wants to merge 15 commits into
Draft
cirospaciari wants to merge 15 commits into
cirospaciari wants to merge 15 commits into
Conversation
…or messages The merge of claude/node-v26-permission-wave2 dropped the reject_bad_negations field from the ParseOptions initializer in Arguments.rs (added by the cli negation-errors commit), breaking the build, and resolved three node_fs.rs call sites back to the pre-parity generic 'path must be a string' errors, orphaning PathOrFdExt::from_js_required. Restores the field and the from_js_required calls; deletes BUFFER_EXPECTED_TYPES, superseded by throw_invalid_argument_type_list at its only former call site.
…strip mode Node v26's stripTypeScriptTypes (amaro/swc_ts_fast_strip) blanks type-only syntax in place so line/column positions match the input; the transform mode and sourceMap option were removed upstream (nodejs/node#61803). Bun's transpiler re-prints from the AST and cannot preserve positions, so strip mode is a dedicated pass: - the lexer captures the token stream when track_tokens is set (same pattern as track_comments; snapshot/restore truncates it) - the parser records the byte span of each type-only construct while skipping it (P::ts_strip, recording sites across parse_*.rs, each behind an Option check that is None for every normal parse) - js_parser/ts_strip.rs ports swc_ts_fast_strip's post-pass: whitespace substitution preserving newlines and character widths, ASI-protection semicolons, generic-arrow rewrites, and the unsupported-syntax rejections (enum, namespace, parameter properties, import =, export =, angle-bracket assertions, grouping-changing casts) with amaro's messages module.stripTypeScriptTypes validates like lib/internal/modules/ typescript.js (ERR_INVALID_ARG_TYPE/VALUE order, once-per-process ExperimentalWarning, sourceURL suffix) in internal/shared.ts and calls the native pass; parse errors map to ERR_INVALID_TYPESCRIPT_SYNTAX and unsupported constructs to ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, both SyntaxError like Node. process.config.variables.node_use_amaro now reports true, matching a Node build with amaro, which un-skips the upstream tests gated on it. NODE_COMPILE_CACHE learns Node's StrippedTypeScript entry type: TypeScript modules record a transpilation-cache entry keyed by the raw source next to their CommonJS/ESM code-cache entry, with Node's log lines (saving/ retrieving transpile cache, writing/skip persisting) and on-disk validation; the code-cache accepted line now matches Node's V8-prefixed wording. Vendored: test-module-strip-types, test-util-getcallsites, test-compile-cache-typescript-{commonjs,esm,strip-sourcemaps}.
The 'Merge origin/claude/node-v26-combined-34660' merge (de14ef6) resolved src/runtime/cli/Arguments.rs and src/js/node/util.ts wholesale to the pre-merge side, silently reverting the combined branch's work: - Arguments.rs lost the -e/-p/--print value-binding rewrite (including every 'eval.provided = true' write, whose consumer in mod.rs survived), so 'bun -e code' printed the help text and exited 0 — breaking every subprocess spawned with -e. Also lost: --check/-c, --input-type, --inspect-port/--debug-port parsing, NODE_OPTIONS validation, and the --no-<flag> negation errors. Restored via a proper 3-way merge against the original merge base, keeping the later permission-wave2 hunks. - util.ts lost util.diff() (its myers_diff backend survived unreferenced). readline.ts was also resolved to one side, but its dropped hunks are superseded by the newer promises rework already on this branch; no change needed there.
…ct classes - drop the binary-grouping rejection: the swc_ts_fast_strip revision in Node v26.3.0's amaro predates it, so no input triggers it there - blank the 'abstract' keyword of abstract class statements - leave 'export as namespace ns;' verbatim (no swc visitor erases it) - un-vendor test-util-getcallsites (needs >1 call-site frame at module top level; Bun's CJS loader is native so only the file frame exists) and test-compile-cache-typescript-strip-sourcemaps (asserts CommonJS classification for an import-less .ts entry; Bun classifies it ESM)
Collaborator
Contributor
|
Found 3 issues this PR may fix:
🤖 Generated with Claude Code |
Contributor
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
…aude/node-strip-types # Conflicts: # src/js_parser/lexer.rs # src/js_parser/parser.rs # src/jsc/NodeCompileCache.rs # src/jsc/bindings/ErrorCode.ts # src/runtime/cli/Arguments.rs
…-damage in Rust
- src/jsc/bindings/BunHeapProfiler.h: restore header required by
$newCppFunction("BunHeapProfiler.cpp", ...) codegen (GeneratedJS2Native.h
#include).
- src/jsc/web_worker.rs: deref parent VM directly; parent_ref local was
removed.
- src/runtime/node/node_module_binding.rs: add use_define_for_class_fields
to ParseOptions initializer.
- src/runtime/node/path.rs: make resolve_{posix,windows}_t pub(crate) so
permission.rs can call them.
- src/clap/lib.rs: make Diagnostic fields pub so bun_runtime can read them.
- src/runtime/cli/run_command.rs: exec_check back to pub(crate)
(unreachable_pub).
- src/runtime/permission.rs: restore bun_threading::RwLock and
bun_core::env_var::NODE_OPTIONS (merge damage reverted them to disallowed
std::sync::RwLock / std::env::var).
- src/runtime/jsc_hooks.rs, src/runtime/timer/Timer.rs: keep // SAFETY:
adjacent to its unsafe block.
- src/jsc/BunHeapProfiler.rs: then_some over then(|| ...).
…aude/node-strip-types # Conflicts: # src/jsc/bindings/BunHeapProfiler.h # src/jsc/web_worker.rs # src/runtime/cli/run_command.rs
…aude/node-strip-types
Collaborator
|
Cross-reference: this resolves #25058 ( |
This was referenced Aug 21, 2026
This was referenced Sep 2, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
module.stripTypeScriptTypes(code[, options])with Node v26 semantics, reportsprocess.config.variables.node_use_amaro: true, and adds Node'sStrippedTypeScripttranspilation-cache entry type to the NODE_COMPILE_CACHE emulation. Vendors 3 upstream tests this unlocks (all previously skipped on thenode_use_amarogate).Why strip mode is not a transpiler wrapper
Node v26.0.0 removed the
transformmode andsourceMapoption (nodejs/node#61803); the only remaining mode,'strip', replaces type syntax in place with whitespace so line/column positions in the output equal the input:Bun's transpiler re-prints from the AST, which cannot preserve positions, so strip mode is a dedicated pass ported from amaro's
swc_ts_fast_strip(the exact library Node embeds):js_parser/lexer.rs): captures the token stream whentrack_tokensis set — same pattern as the existingtrack_comments, truncated on backtracking via the snapshot lengths.P::ts_strip; ~30 recording sites acrossparse_*.rs). Every site is behind anOptionnull-check that isNonefor every normal parse; erased statements are recorded at theS::TypeScriptconstruction sites the parser already has.js_parser/ts_strip.rs, new): applies swc's algorithm over spans + tokens — whitespace substitution preserving newlines and UTF-8 character widths (U+00A0/U+2002 for multi-byte), ASI-protection semicolons (let x = 1⏎type A = string⏎(f)()gets a;written into the blank), the generic-arrow<→(rewrites, andERR_UNSUPPORTED_TYPESCRIPT_SYNTAXrejections (enum, namespace, parameter properties,import =,export =,<T>exprassertions, identifier-namedmodule) with amaro's exact messages. Erased ambient (declare) containers suppress inner rejections by span containment, mirroring swc's unvisited subtrees.Validation lives in
internal/shared.tsand matcheslib/internal/modules/typescript.jscheck-for-check: argument order,ERR_INVALID_ARG_TYPE/ERR_INVALID_ARG_VALUE, once-per-process ExperimentalWarning,sourceURLsuffix, and thefilename:line\n<snippet>stack decoration. Parse errors throwERR_INVALID_TYPESCRIPT_SYNTAX; both codes areSyntaxErrorsubclasses like Node.Verified against the oracle: a 156-case differential corpus (every construct class: annotations, generics, as/satisfies/const-assertions, class modifiers/overloads/index signatures, import/export type specifiers, ASI hazards, multi-byte identifiers, comments, hashbang) runs byte-identical to
nodev26.3.0 on 153 cases. The 3 divergences are: parse-error message text comes from Bun's parser (code/class match; 2 cases), andclass C { override m() {} }withoutextendsis an swc-parser-only strictness error Bun's parser accepts (1 case).node_use_amaro
Node builds with amaro report
process.config.variables.node_use_amaro: true; six upstream tests skip on it. With the API implemented the flag now reports true. No other vendored test reads the flag (checked the whole vendored tree).StrippedTypeScript compile-cache entries
Node keeps a second NODE_COMPILE_CACHE entry per TypeScript file: the transpilation cache (
CachedCodeType::kStrippedTypeScript), keyed by the raw source.NodeCompileCache.rsnow models entry types as an enum (CommonJs/Esm/StrippedTypeScript, discriminants preserve the oldis_cjskey salt so existing cache dirs stay valid) and records a transpilation entry when a TS module is transpiled, with Node's log lines (saving transpilation cache…,retrieving transpile cache… success,writing cache for StrippedTypeScript… success,skip persisting… because cache was the same) and full on-disk validation. The stored transpiled text is validated on reload but not yet fed back to the loader (Bun's own runtime transpiler cache already skips re-transpiling); noted in a comment. The code-cache accepted line now saysV8 code cache for…matching Node's wording (compile_cache.cc:318) — the looser regex in the previously-vendored compile-cache tests still matches, verified by re-running all 14 of them.Vendored tests (byte-verbatim, all run their bodies)
test-module-strip-types.js— 6/6 subtests pass (skips as "Requires Amaro" on unfixed builds)test-compile-cache-typescript-commonjs.js— .ts/.cts/.mts loads with both cache-entry kinds across two runstest-compile-cache-typescript-esm.jsPlus
test/js/node/module/strip-typescript-types.test.ts(15 tests, every expected string captured from Node v26.3.0; fails on unfixed builds — the export doesn't exist).Evaluated and not vendored (with reasons)
test-util-getcallsites— needsgetCallSites().length > 1at module top level; Bun's CJS loader is native so only the file frame exists (Node's JS loader frames pad the stack).test-compile-cache-typescript-strip-sourcemaps— asserts CommonJS classification for an import-less.tsentry; Bun classifies it ESM. Changing Bun's module-type default is not on the table for a compat test.test-inspector-strip-types— needs the inspector work that lives onclaude/node-v26-combined-34719; hangs on this base.test-node-output-eval.mjs— snapshot-asserts strip-only execution semantics (-p 'enum Foo{}'must fail with the strip error); Bun executes TS with transform semantics by design.Carried repairs
The first two commits are cherry-picks of the base-branch merge-damage repairs (
reject_bad_negationsfield and the-e/-peval binding —bun -e codeprinted help and exited 0 on the current base, breaking every subprocess test). They already exist on sibling session branches and will drop out when the base is fixed.Known pre-existing on this base (not this PR):
transpiler.test.js"deeply nested unary operators" fails becausebun -e '- - - …'(code starting with-) exits with the wrong code after the -e/-p rewrite; reproduces on the base + repairs without this PR's changes.