Commit Graph
2967 Commits
Author SHA1 Message Date
Malte UblandClaude Opus 5.5 1a17bd3405 Merge branch 'opt/npm-polyfill-shims' into opt/npm-esm-class-hoist
Renumbers this branch's npm case 2572 to 2588, after the renumbered
polyfill cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 21:44:23 -07:00
Malte UblandClaude Opus 5.5 1b282f9c80 Merge branch 'opt/npm-cjs-exports' into opt/npm-polyfill-shims
Renumbers this branch's npm cases 2569-2571 to 2585-2587: #1411 now uses
2571.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 21:44:08 -07:00
Malte UblandClaude Opus 5.5 dde27c7e21 Merge branch 'opt/npm-lazy-loaders' into opt/npm-cjs-exports
Renumbers this branch's npm cases 2567-2568 to 2583-2584: #1422 now uses
2567.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 21:43:36 -07:00
Malte UblandClaude Opus 5.5 6fc85e848d Merge branch 'opt/npm-esm-wrappers' into opt/npm-lazy-loaders
Renumbers this branch's npm cases 2564-2566 to 2580-2582: #1411 now uses
2564-2566.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 21:42:59 -07:00
Malte Ubl a77c3edd47 Merge remote-tracking branch 'origin/opt/npm-bundler-interop' into opt/npm-esm-wrappers
# Conflicts:
#	packages/compiler/src/frontend/npm-static-bundled-chunks.ts
#	tests/harness/npm-cases.ts
#	tests/harness/npm-static.test.ts
2026-10-19 21:42:14 -07:00
Malte UblandClaude Opus 5.5 6f2a7ce814 Share the initializer and top-level binding shapes with the class hoisting
The self-hosted compiler needs the same record type on both sides of the
call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:52:10 -07:00
Malte UblandClaude Opus 5.5 3d8c7259a0 Hoist lazily initialized ES module classes and follow on-demand workspace imports
A class a bundled ES module's __esm initializer stores into a module
binding (`Multi = class extends import_stream.Readable { … }`) becomes a
class declaration at the same text position: the initializer's body splits
around it into run-once pieces, so a class over a runtime-provided base no
longer has to exist as a stored class value. Only definitions whose
evaluation runs no code (no static fields, static blocks or computed names;
heritage absent, a builtin module member, or a hoisted class), declared
once, assigned only there and read only inside functions after the
assignment move.

npm selection now also scans the program modules a literal import()
reaches, with their static graphs: a workspace package loaded on demand
had its npm dependencies refused as "not part of the compiled module
graph".

`new import_x.C()` respelled `new require("x").C()`, which constructs the
require call; the interop binding now becomes `(require("x"))` there.

util.inherits onto a runtime-provided class (stream.Readable, the from2
and multistream shape) fences where it is written instead of failing in
Node's argument validation at run time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:33:19 -07:00
Chris Tate 1af7a862ce Keep stored cycle functions and self-referencing helper copies refused
Two fixes for the CommonJS cycle check and the bundled-chunk splitter:
- During a cycle's initialization, a read of a partner's exported
  function is safe only where the value neither runs nor stays: an
  expression statement, typeof, void, !, an equality operand, or the test
  of if, while, do and ?:. A const alias (`const f = binding.fn`), an
  object or array literal, a return, or an argument can be called before
  the cycle settles, so these keep SC1016 (Node throws when the function
  reads a binding in its temporal dead zone). A top-level const that only
  publishes the function in an export statement, as the static rewrite
  spells an export-table re-export, stays a read.
- A copied helper whose initializer is a named function expression
  reaches itself through its own name. Each chunk gets its own copy, so
  that name must also be used only as a callee; otherwise the chunk keeps
  the package refused with the existing reason.
2026-10-19 22:05:25 -05:00
Chris Tate d07188d250 Merge base 2026-10-19 22:00:59 -05:00
Malte UblandClaude Opus 5.5 c0e2e69a21 Record order baselines for the alias cases and corpus programs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:54:12 -07:00
Malte UblandClaude Opus 5.5 3a9c13a244 Format the alias tests and drop the merged duplicate moduleNs case
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:53:03 -07:00
Malte Ubl 6a61eced36 Merge remote-tracking branch 'origin/opt/npm-cjs-exports' into opt/npm-polyfill-shims 2026-10-19 19:52:09 -07:00
Malte UblandClaude Opus 5.5 2efa5d9411 Fold global detection typeof tests and follow var builtin aliases
A condition built from typeof tests whose answers are static (an
undeclared global is "undefined", the global object and process are
"object") folds, so the untaken arm of `typeof window < "u" ? window : …`
never lowers, and a detection chain used as a receiver resolves to the
global object it selects. JavaScript files no longer report a name the
file feature-tests with typeof as undeclared. `typeof global.setTimeout`
answers "function" instead of the identity token's "string".

`var pna = process` (process-nextick-args once it compiles as process)
and `var Transform = require("stream").Transform` alias like their const
spellings when nothing reassigns them; before, the first silently lost
process's members.

Also reads symbol declarations directly in the CommonJS accessor
snapshot path, after the checker helper's removal on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:52:05 -07:00
Malte UblandClaude Opus 5.5 f80e4c1a09 Compile polyfill packages that are Node builtins as the builtins
isarray, safe-buffer, process-nextick-args, inherits and util-deprecate
evaluate to a Node builtin on Node 24 at their checked major versions, so
a require of one compiles as that builtin expression at the require's
position: from an opted-in package's files (the installed version where
Node's resolution finds it), from the program's own CommonJS files, and
through an esbuild __commonJS chunk whose pnpm path key names the package
and its version. The package's files never join the program.

readable-stream 2.x and 4.x compile as node:stream, the package's own
READABLE_STREAM=disable behavior (a documented divergence). A use of the
entry's export picks the builtin module (member reads of the carried
classes) or stream.Readable (value uses); a mixture keeps the package.
3.x stays as is: its disable branch throws on Node 24.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:52:05 -07:00
Malte UblandClaude Opus 5.5 c1a9341739 Name the module truthiness corpus program instead of numbering it
Its number collided with another open change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:51:39 -07:00
Malte Ubl 28dc1b25a6 Merge branch 'opt/npm-lazy-loaders' into opt/npm-cjs-exports 2026-10-19 19:51:39 -07:00
Malte UblandClaude Opus 5.5 fa012f5da3 Name the corpus programs instead of numbering them
The numbers collided with other open changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:51:31 -07:00
Malte Ubl ea14ae9bae Merge remote-tracking branch 'origin/opt/native-undici' into opt/npm-polyfill-shims
# Conflicts:
#	packages/compiler/src/frontend/lowering/lower-modules.ts
#	tests/harness/npm.test.ts
2026-10-19 19:20:10 -07:00
Malte UblandClaude Opus 5.5 2b7c892af3 Record order baselines for the new cases and corpus program
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:29:54 -07:00
Malte UblandClaude Opus 5.5 3723e05417 Resolve bundled chunks that re-export a builtin to the builtin
A chunk whose whole body is `module.exports = require("<builtin>")`
(readable-stream's internal/streams/stream.js) is the builtin module:
its calls become that require, and no chunk module is emitted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:28:45 -07:00
Malte UblandClaude Opus 5.5 5670bc182e Respell members of a function export object; name conditional exports
A CommonJS module whose export object is one of its own top-level
functions (semver 5/6's `exports = module.exports = SemVer`) reaches
its members through that function after the statement: member reads
and writes through module.exports (and, after the rebinding, every
`exports` reference) become members of the function, the expando form
both resolvers model, and the rebinding itself drops. Constructor
functions exported whole with members attached afterwards no longer
lose those members at runtime.

Replacing module.exports or rebinding exports inside a branch (an
environment-dependent export object, readable-stream 2.x) now names the
construct instead of the generic assignment fence, and a required
builtin module object is truthy in the LLVM backend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:21:52 -07:00
Malte Ubl 8e41f7678a Merge remote-tracking branch 'origin/main' into opt/native-undici 2026-10-19 18:11:35 -07:00
Chris Tate 187886d2da Read symbol declarations directly in code added after the checker helpers were removed (#1426) 2026-10-19 20:11:00 -05:00
Malte Ubl f5f8c4821c Merge remote-tracking branch 'origin/main' into opt/native-undici
# Conflicts:
#	packages/compiler/src/frontend/lowering/builtins/timers.ts
#	packages/compiler/src/frontend/type-mapper.ts
2026-10-19 18:08:01 -07:00
Chris Tate 76dda23d8b Read symbol declarations directly in code added after the checker helpers were removed
#1399 removed CheckerFacade.declarationsOf and valueDeclarationOf. #1407 and
#1351 merged after it with calls to them, so main does not type-check. Read
symbol.declarations and symbol.valueDeclaration instead, as the rest of the
compiler does.
2026-10-19 20:07:39 -05:00
Chris Tate bb6270efc1 Fix silent divergences: constructor returns, arr && arr[0], ES5 class names, builtin function values, uninitialized let writes, module-export this, 1 ** Infinity (#1351) 2026-10-19 19:56:36 -05:00
Chris Tate 950ec9ee85 Give prototype name stamps their own copy of the name
A construction through a plain function stamps the prototype with the
name of the function in its `constructor` property, for util.inspect.
The stamp pointed at the function's name. A bound function's name
("bound " and the target's name) is a string the bound function owns,
so after the `constructor` property was replaced and the bound function
released, inspecting an instance read freed memory. The stamp now holds
a copy of the name, freed with the stamp at exit.

The corpus program constructs through functions whose prototype's
`constructor` is a bound function (and a bound function of one), then
replaces the property and inspects the instances.
2026-10-19 19:51:44 -05:00
Chris Tate 4770b3ffa7 Fix pi's small compile gaps (#1348) 2026-10-19 19:50:07 -05:00
Chris Tate adb879f678 Compile the real jose root entry: string durations, chained builders and navigator (#1407) 2026-10-19 19:45:13 -05:00
Chris Tate 2edc7a2c2a Read declarations from symbols and signatures (#1399) 2026-10-19 19:33:42 -05:00
Chris Tate 85d1c423d3 Read generic class narrowings through every class they can hold (#1419) 2026-10-19 19:32:55 -05:00
Malte UblandClaude Opus 5.5 1c3a405277 Expect the lazy-loader refusal for a namespace kept by a loader
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:49 -07:00
Malte UblandClaude Opus 5.5 13fa2c9505 Document built-in packages and unloaded optional dependencies
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:38 -07:00
Malte UblandClaude Opus 5.5 a6003ec011 Implement undici 5.x and 7.x as a built-in package
A TypeScript implementation over node:net, node:tls and node:events:
Dispatcher, Client (HTTP/1.1 with keep-alive), Pool, Agent,
buildConnector, request(), errors, the global dispatcher, fetch, Headers
and the diagnostics_channel publications, with per-version behavior.
Differential npm cases run the vendored real undici under Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:38 -07:00
Malte UblandClaude Opus 5.5 c59457b0dc Map npm packages to built-in implementations and mark optional dependencies unloaded
package-shims.ts registers implementation modules shipped with the
compiler per package and major version (chosen by the importer's
installed version) and reads the project's never-loaded packages from
"scriptc": { "packages": { "<name>": "unloaded" } }. Module edges go
through resolveProjectImport; the npm resolution, the npm-edge walk and
the --npm-static set skip such packages; the tsgo host reads a shimmed
package's index.d.ts as a re-export of its implementation; an unloaded
package's require() throws Node's not-installed MODULE_NOT_FOUND.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:38 -07:00
Malte UblandClaude Opus 5.5 1e38f02a3d Map @types/node 20's class-declared Timeout and Immediate handles
@types/node 20 and older declare NodeJS.Timeout and NodeJS.Immediate as
classes; 22+ (and scriptc's own declarations) declare interfaces. Accept
both as the native timer handle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:37 -07:00
Malte UblandClaude Opus 5.5 4993060d16 Convert string | URL unions to strings through the URL's href
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:37 -07:00
Malte UblandClaude Opus 5.5 b8dd7d2182 Throw MODULE_NOT_FOUND with its code from statement-level requires
A statement-level require() of a package nothing installed threw a plain
Error; the expression form already carried Node's MODULE_NOT_FOUND code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:31:36 -07:00
Chris Tate 4f7539103c Keep a lowered ES module namespace unobservable through calls that pass it as this
A call through a bundled ES module's namespace (`ns.fn()`, `(ns.fn)()`,
a tagged template) passes the namespace as `this`. Node's namespace is
the __toCommonJS object (`__esModule` first and non-enumerable,
non-configurable getters); the lowered object of getters differs. The
namespace lowers only when every such call targets a top-level function,
never reassigned, that reads no `this` (an arrow inside it included);
otherwise the package stays refused.
2026-10-19 19:10:11 -05:00
Chris Tate c18a4a790a Resolve chunk references by scope, keep shared functions out of chunk copies, and treat indirect calls as calls in cycles
Three fixes for the bundled-chunk splitter and the CommonJS cycle check:
- A reference counts as local only when a scope between it and the chunk
  factory (or the copied helper) declares its name. A nested function,
  block, loop or catch binding with the name of a top-level helper no
  longer hides the chunk's read of that helper. Before, the helper was not
  copied and the chunk failed at runtime on the unresolved reference.
- A top-level function is copied into a chunk only when the bundle calls
  it and uses it in no other way. Each chunk gets its own copy, so a
  function that is exported, compared, constructed, or given properties
  would lose its shared identity. Such a chunk now keeps the package
  refused.
- During a cycle's initialization, a read of a partner's exported
  function is safe only as a plain value read. A call through
  esbuild's `(0, binding.fn)()` spelling, `new`, a tagged template, a
  member of the function, or passing it to a call can run the function,
  so these uses keep SC1016 (Node throws when the function reads a binding
  in its temporal dead zone).
2026-10-19 19:07:43 -05:00
Chris Tate 31475715a0 Compare strings in UTF-16 code-unit order (#1424) 2026-10-19 19:04:23 -05:00
Malte UblandClaude Opus 5.5 02dba55958 Record order baselines for the new cases and corpus programs; format
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:55:55 -07:00
Malte UblandClaude Opus 5.5 bc32d97339 Compile build-utils' bundled code: star re-exports, aliases, three lowering fixes
- A star re-export of a lazily initialized module into the bundle's
  exports (__reExport(main, (init_x(), __toCommonJS(x_exports))))
  initializes the module at the statement and adds its names to the
  export table after the explicit ones, without default.
- The rewrite keeps the original's lexer answer whenever its export
  table holds accessors (Node's lexer stops at the first one).
- An npm alias directory of a selected package ("x-updated":
  "npm:x@6.3.0") resolves to that package's JavaScript in both resolvers.
- "use strict" code may read arguments in functions with parameters.
- A comma expression ending in null/undefined coerces its result.
- A void conditional return lowers as control flow.
- A detached method of a local class drops its empty capture list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:52:01 -07:00
Chris Tate 68f930bf53 Keep the global object in type assertions of globalThis (#1423) 2026-10-19 18:42:22 -05:00
Chris Tate 53af3dc930 Compare strings in UTF-16 code-unit order
Relational string operators (`<`, `<=`, `>`, `>=`) compared the UTF-8
bytes, which is code-point order. Node compares UTF-16 code units, so the
results differed for characters above U+FFFF against U+E000..U+FFFF and
for lone surrogates against surrogate pairs. The default `sort()`
comparator, `util.inspect` with `sorted: true` and dynamic comparisons
already used UTF-16 order; the default comparator of a dynamic array's
`sort()` did not.

Typed comparisons and the dynamic array sort now use `scr_str_cmp_u16`,
and the IR's `strCmp` node no longer has a `utf16` flag. The two orders
agree unless both of the first differing bytes are at least 0xED, so:

- the inline wrapper decides differing first words, or last words of
  equal lengths up to 16 bytes, in byte order when neither word has a
  byte of at least 0xED (a SWAR test). Other differing words, and the
  first bytes of shorter strings, decide by their first differing byte:
  in byte order when one of the two is below 0xED, and by plane when both
  are lead bytes from 0xEE to 0xF4 (a supplementary character orders
  below U+E000..U+FFFF);
- otherwise a proven-ASCII string on either side compares with `memcmp`
  (`scr_str_cmp`), and other pairs call `scr_str_cmp_u16_bytes`. It finds
  the first differing byte with words up to byte 32, then with 32-byte
  SSE2 or NEON blocks where the target has them. The call is marked as
  only reading its arguments, so repeated comparisons of one pair merge.
- `scr_str_cmp_u16` itself takes the same `memcmp` path for proven-ASCII
  strings.

The native compiler sorts record fields with `<` and checks the order
with `sort()`, so it reported SC9001 for 5888-inspect-sorted.ts, whose
object has the keys "\u{1F600}" and "\uffff". 5888 runs in the
native-frontend self-host check. Adds corpus 6023.
2026-10-19 18:10:54 -05:00
Chris Tate 899375b9fe Accept optional checked-dynamic parameters in generic method instances (#1421) 2026-10-19 18:05:41 -05:00
Malte UblandClaude Opus 5.5 f7f239ad53 Lower memoized lazy loaders of bundled ES-module namespaces
A loader `L = () => M ??= NS` over a lazily initialized module's
namespace (optionally through __toESM) is respelled at every `L().name`
as the module's initializer call followed by the export's live read;
loaders whose result escapes as a value stay refused. The canonical
export table keeps a getter it cannot chase as a live accessor, so a
lazy re-export still initializes its module at the first read. An
ES-module importer copies those accessors once when it links the
CommonJS module, in the lexer's order, exactly like Node's loader;
require() keeps reading them live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 15:55:37 -07:00
Malte UblandClaude Opus 5.5 6255c9351b Keep the global object in type assertions of globalThis
`globalThis as { [KEY]?: State }` is the usual way for several modules
to share process-wide state. globalThis lowers to a checked-dynamic
value, and asserting a checked-dynamic value to a record or array type
took the checked conversion, which builds a new native value. A write
through the asserted value went to that copy, so other modules never
saw it; string-keyed and index-signature views threw "expected object".

- An assertion of the global object (globalThis, global, also through
  `as unknown as`) keeps the checked-dynamic value when the conversion
  would copy, so member reads and writes reach the global object.
- File-scope and local bindings of such an assertion hold the global
  object (collectGlobals, lowerVarDecl), so an exported
  `const state = globalThis as ...` works across modules.
- The divergence scan no longer reports these assertions as SC6001
  width copies.
- Runtime: one global-object box per thread (scr_global_object), which
  globalThis, global and globalThis.globalThis all answer, and direct
  `globalThis[symbol]` accesses use that box's symbol-keyed properties,
  so every spelling of the global object sees one property.

Other checked-dynamic values keep the checked conversion: a native
value held as unknown unwraps by identity there, and a checked-dynamic
tree is copied at the documented boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 15:52:52 -07:00
Chris Tate ca01eabd12 Read generic class narrowings through every class they can hold
In a generic function, `node instanceof Identifier || node instanceof
PrivateIdentifier` narrows `node` to `T & PrivateIdentifier` when
Identifier has every member PrivateIdentifier has: TypeScript reduces the
union of the two intersections by structural subtyping. The read was
lowered as a downcast to PrivateIdentifier, and the narrowing guard
aborted when it held an Identifier. tsc-ts's NodeFactory.cloneNode has
this pattern, so declaration emit aborted.

When the checker types a read of a type-parameter intersection as a
subclass, and another class under the value's class is assignable to that
subclass without extending it, the read is now a class union over those
classes, and member reads and method calls on it dispatch over the union.
When this instantiation's class is unrelated to the narrowed class but
assignable to it (`T = Identifier`), member calls use the instantiation's
class. A union method helper whose body lowering is refused is no longer
left registered without a body.
2026-10-19 17:25:49 -05:00
Chris Tate c04f770104 Flatten the last value a take delivers in full (#1390) 2026-10-19 17:20:54 -05:00