Repository navigation
fix(e2e)!: load TypeScript with e2e's own oxc loader, so pnpm 11+ installs pass - #790
Conversation
🟢 e2e web: 91 passedAll 91 tests in 41 files
e2e 0.16.0 · 1m 49s · web · run artifacts |
🟢 e2e web agent: 33 passed34 agent steps · 26 replayed from cache · 20 model calls · 76.4k tokens (92% cached) · $0.0036 All 33 tests in 5 files
e2e 0.16.0 · 2m 40s · web · run artifacts |
There was a problem hiding this comment.
All reported issues were addressed across 44 files
Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.
Re-trigger cubic
🟢 e2e ios: 1 flaky, 51 passed, 1 skipped
|
| Test | Time | |
|---|---|---|
| tests/controls.e2e.ts · 1 flaky, 22 passed, 1 skipped | 4m 4s | |
| 🟢 | control inventory › a label query resolves a labeled text field | 11.9s |
| 🟢 | control inventory › a labeled field answers name, display value, value reads, fill, and clear | 20.5s |
| 🟢 | control inventory › a placeholder query and attribute reads find the field | 6.3s |
| 🟢 | control inventory › a secure field takes a fill and denies value reads | 12.2s |
| 🟢 | control inventory › press sends a character and Enter through the soft keyboard | 10.8s |
| 🟢 | control inventory › a field autofocuses on mount and focus() moves the focus | 13.9s |
| control inventory › toBeFocused follows the focus (2 failed attempts first) | 16.5s | |
| 🟢 | control inventory › a switch checks, unchecks, and reports its state | 17.9s |
| 🟢 | control inventory › checkbox and radio roles answer getByRole, and a checkbox toggles by tap | 12.5s |
| 🟢 | control inventory › checkbox and radio roles check, uncheck, and report their state | 15.4s |
| 🟢 | control inventory › a radio group reports the checked option | 10.5s |
| 🟢 | control inventory › buttons report disabled, enabled, and a state that arrives later | 9.7s |
| 🟢 | control inventory › a display: none element is absent rather than hidden, its visible twin attached | 10.4s |
| 🟢 | control inventory › a header answers its accessible name | 6.5s |
| 🟢 | control inventory › a tab row answers count, all, texts, filters, positions, and switching | 9.7s |
| 🟢 | control inventory › a list answers counts, list-form text, reads, and boxes in order | 11.3s |
| ⏭️ | control inventory › a row contains its texts (skipped: React Native flattens a plain View out of the accessibility tree on both platforms: XCTest and the Android helper report a View with a testID as a leaf beside its children, so filter({ has }) and a query scoped to it match nothing; only Sc…) | |
| 🟢 | control inventory › a long press lands as one | 10.2s |
| 🟢 | control inventory › pointer verbs land at the point asked for | 13.1s |
| 🟢 | control inventory › an ambiguous locator fails at once | 8.0s |
| 🟢 | control inventory › screenshot, restart, and clearState reopen the app on its home list | 15.7s |
| 🟢 | refusals › verbs a device never declares are refused before the engine | 3ms |
| 🟢 | refusals › keys the soft keyboard cannot send are refused by the engine | 406ms |
| 🟢 | refusals › a missing locator fails as not found once its timeout passes | 1.2s |
| 🟢 | tests/device-fixture.e2e.ts · 9 passed | 1m 31s |
| 🟢 | device fixture › setAppearance flips the color scheme the app reads | 12.9s |
| 🟢 | device fixture › setOrientation rotates the window the app measures | 9.0s |
| 🟢 | device fixture › the clipboard reads back what was written | 8.1s |
| 🟢 | device fixture › home leaves the app, openApp brings it back where it was | 8.6s |
| 🟢 | device fixture › closeApp then openApp relaunches on the home list | 10.5s |
| 🟢 | device fixture › foregroundApp after closeApp reports the closed session as APP_NOT_OPEN | 6.8s |
| 🟢 | device fixture › device.back pops the scenario | 8.7s |
| 🟢 | device fixture › setLocation and clearLocation change what the app reads | 15.2s |
| 🟢 | device fixture › enrollBiometrics and setBiometrics answer a biometric prompt | 10.9s |
| 🟢 | tests/device.e2e.ts · 2 passed | 58.7s |
| 🟢 | sequential onboarding echoes the exact values | 43.8s |
| 🟢 | onboarding rejects a malformed email before advancing | 14.9s |
| 🟢 | tests/flows-lists.e2e.ts · 7 passed | 4m 18s |
| 🟢 | infinite scroll reaches the target item | 31.8s |
| 🟢 | product catalog adds exactly two of the right variant | 12.8s |
| 🟢 | gestures: long-press, then swipe left | 11.2s |
| 🟢 | sticky chrome: accept below the fold, then continue | 12.8s |
| 🟢 | async states: load, pull to refresh, claim through the toast | 14.3s |
| 🟢 | huge virtualized list reaches row 512 | 2m 39s |
| 🟢 | web view coupon form applies the code shown on the page | 16.6s |
| 🟢 | tests/flows.e2e.ts · 7 passed | 1m 31s |
| 🟢 | text inputs unlock the submit button | 20.8s |
| 🟢 | filled text inputs report their values | 12.6s |
| 🟢 | modal flow confirms through the native alert | 11.8s |
| 🟢 | bottom tabs act inside the last tab | 9.4s |
| 🟢 | error recovery retries, then confirms the delete | 12.0s |
| 🟢 | choice controls place the exact order | 10.9s |
| 🟢 | debounced search selects the target once results arrive | 13.6s |
| 🟢 | tests/login-form.e2e.ts · 3 passed | 57.5s |
| 🟢 | login form › rejects a malformed email | 15.2s |
| 🟢 | login form › rejects an unknown account | 15.2s |
| 🟢 | login form › signs in and logs out | 27.1s |
| 🟢 | tests/smoke.e2e.ts · 1 passed | 15.2s |
| 🟢 | home lists the scenarios and opens one | 15.2s |
e2e 0.16.0 · 7m 23s · ios-simulator · run artifacts
🟢 e2e android: 1 flaky, 53 passed, 1 skipped
|
| Test | Time | |
|---|---|---|
| tests/device.e2e.ts · 1 flaky, 3 passed | 38.4s | |
| 🟢 | permission prompt › allowing the system dialog enables the microphone | 6.7s |
| 🟢 | permission prompt › a denied prompt recovers once the permission is granted outside | 12.3s |
| sequential onboarding echoes the exact values (1 failed attempt first) | 12.7s | |
| 🟢 | onboarding rejects a malformed email before advancing | 6.7s |
| ⏭️ | tests/controls.e2e.ts · 24 passed, 1 skipped | 2m 55s |
| 🟢 | control inventory › a label query resolves a labeled text field | 20.7s |
| 🟢 | control inventory › a labeled field answers name, display value, value reads, fill, and clear | 13.0s |
| 🟢 | control inventory › a placeholder query and attribute reads find the field | 5.2s |
| 🟢 | control inventory › a secure field takes a fill and denies value reads | 6.5s |
| 🟢 | control inventory › press sends a character and Enter through the soft keyboard | 8.6s |
| 🟢 | control inventory › a field autofocuses on mount and focus() moves the focus | 11.2s |
| 🟢 | control inventory › toBeFocused follows the focus | 10.1s |
| 🟢 | control inventory › a switch checks, unchecks, and reports its state | 9.5s |
| 🟢 | control inventory › checkbox and radio roles answer getByRole, and a checkbox toggles by tap | 7.0s |
| 🟢 | control inventory › checkbox and radio roles check, uncheck, and report their state | 11.9s |
| 🟢 | control inventory › a tab role answers getByRole | 3.6s |
| 🟢 | control inventory › a radio group reports the checked option | 8.9s |
| 🟢 | control inventory › buttons report disabled, enabled, and a state that arrives later | 7.6s |
| 🟢 | control inventory › a display: none element is absent rather than hidden, its visible twin attached | 6.2s |
| 🟢 | control inventory › a header answers its accessible name | 3.7s |
| 🟢 | control inventory › a tab row answers count, all, texts, filters, positions, and switching | 5.5s |
| 🟢 | control inventory › a list answers counts, list-form text, reads, and boxes in order | 5.0s |
| ⏭️ | control inventory › a row contains its texts (skipped: React Native flattens a plain View out of the accessibility tree on both platforms: XCTest and the Android helper report a View with a testID as a leaf beside its children, so filter({ has }) and a query scoped to it match nothing; only Sc…) | |
| 🟢 | control inventory › a long press lands as one | 7.0s |
| 🟢 | control inventory › a double tap lands inside the 300 ms window | 7.7s |
| 🟢 | control inventory › an ambiguous locator fails at once | 4.9s |
| 🟢 | control inventory › screenshot, restart, and clearState reopen the app on its home list | 8.4s |
| 🟢 | refusals › verbs a device never declares are refused before the engine | 2ms |
| 🟢 | refusals › keys the soft keyboard cannot send are refused by the engine | 1.3s |
| 🟢 | refusals › a missing locator fails as not found once its timeout passes | 1.1s |
| 🟢 | tests/device-fixture.e2e.ts · 8 passed | 1m 2s |
| 🟢 | device fixture › setOrientation rotates the window the app measures | 4.9s |
| 🟢 | device fixture › home leaves the app, openApp brings it back where it was | 5.5s |
| 🟢 | device fixture › foregroundApp sees the home screen after home() | 3.4s |
| 🟢 | device fixture › closeApp then openApp relaunches on the home list | 6.0s |
| 🟢 | device fixture › foregroundApp after closeApp reports the closed session as APP_NOT_OPEN | 3.9s |
| 🟢 | device fixture › device.back pops the scenario | 4.4s |
| 🟢 | device fixture › setNetwork and setAirplaneMode change what the app sees | 15.1s |
| 🟢 | device fixture › setLocation and clearLocation change what the app reads | 18.8s |
| 🟢 | tests/flows-lists.e2e.ts · 7 passed | 2m 13s |
| 🟢 | infinite scroll reaches the target item | 23.6s |
| 🟢 | product catalog adds exactly two of the right variant | 9.3s |
| 🟢 | gestures: long-press, then swipe left | 7.4s |
| 🟢 | gestures: double-tap completes the flow | 9.4s |
| 🟢 | sticky chrome: accept below the fold, then continue | 11.2s |
| 🟢 | async states: load, pull to refresh, claim through the toast | 11.4s |
| 🟢 | huge virtualized list reaches row 512 | 1m 1s |
| 🟢 | tests/flows.e2e.ts · 7 passed | 50.4s |
| 🟢 | text inputs unlock the submit button | 6.1s |
| 🟢 | filled text inputs report their values | 5.5s |
| 🟢 | modal flow confirms through the native alert | 7.8s |
| 🟢 | bottom tabs act inside the last tab | 6.3s |
| 🟢 | error recovery retries, then confirms the delete | 8.1s |
| 🟢 | choice controls place the exact order | 8.3s |
| 🟢 | debounced search selects the target once results arrive | 8.3s |
| 🟢 | tests/login-form.e2e.ts · 3 passed | 29.4s |
| 🟢 | login form › rejects a malformed email | 8.4s |
| 🟢 | login form › rejects an unknown account | 8.5s |
| 🟢 | login form › signs in and logs out | 12.5s |
| 🟢 | tests/smoke.e2e.ts · 1 passed | 5.1s |
| 🟢 | home lists the scenarios and opens one | 5.1s |
e2e 0.16.0 · 8m 28s · android-emulator · run artifacts
🟢 e2e android agent: 20 passed24 agent steps · 132 model calls · 839.5k tokens (83% cached) · $0.0549 All 20 tests in 1 file
e2e 0.16.0 · 10m 44s · android-emulator · run artifacts |
🟢 e2e ios agent: 1 flaky, 20 passed, 1 skipped25 agent steps · 139 model calls · 1.0M tokens (79% cached) · $0.0787
|
| Test | Agent | Time | |
|---|---|---|---|
| tests-agent/scenarios.e2e.ts · 1 flaky, 20 passed, 1 skipped | 25 steps · 139 calls | 13m 27s | |
| 🟢 | act completes Login Form | 1 step · 13 calls | 1m 17s |
| 🟢 | act completes Infinite Scroll List | 1 step · 3 calls | 54.1s |
| 🟢 | act completes Modal Flow | 1 step · 4 calls | 17.0s |
| 🟢 | act completes Bottom Tabs | 1 step · 3 calls | 12.6s |
| 🟢 | act completes Text Input Variations | 1 step · 3 calls | 26.4s |
| 🟢 | act completes Broken Accessibility | 2 steps · 7 calls | 37.5s |
| 🟢 | act completes Vision Only | 2 steps · 6 calls | 30.5s |
| act completes Gestures (1 failed attempt first) | 1 step · 7 calls | 38.3s | |
| 🟢 | act completes Async States | 1 step · 7 calls | 31.3s |
| 🟢 | act completes Debounced Search | 1 step · 4 calls | 20.6s |
| 🟢 | act completes Flattened Registration Form | 2 steps · 21 calls | 2m 13s |
| 🟢 | act completes Flattened Login | 2 steps · 13 calls | 1m 19s |
| 🟢 | act completes Sticky Chrome Target | 1 step · 7 calls | 34.7s |
| 🟢 | act completes WebView Accessibility | 1 step · 5 calls | 27.8s |
| 🟢 | act completes Permission Prompt | 1 step · 3 calls | 19.2s |
| 🟢 | act completes Photo Picker | 1 step · 4 calls | 18.8s |
| 🟢 | act completes Product Catalog | 1 step · 5 calls | 18.3s |
| 🟢 | act completes Error Recovery | 1 step · 6 calls | 18.5s |
| 🟢 | act completes Choice Controls | 1 step · 8 calls | 43.3s |
| 🟢 | act completes Stripe PaymentSheet | 1 step · 6 calls | 46.3s |
| 🟢 | act completes Apple Pay | 1 step · 4 calls | 23.7s |
| ⏭️ | act completes Apple Pay Billing Address (skipped: the CI simulator's Apple Pay sheet skips the billing-address step, so the scenario cannot be completed there) |
e2e 0.16.0 · 14m 52s · ios-simulator · run artifacts
|
[claude-opus-5-5] responding on behalf of okwasniewski For the human review:
|
okwasniewski
left a comment
There was a problem hiding this comment.
On 44a714f:
The new STACK_FRAME regex in collect/registry.ts:501 misses V8's anonymous async frame. When a shared helper in another file awaits something and then calls test(), V8 shows the test module as at async file:///.../a.test.ts:3:1, with no function name and no parens. The capture becomes async file:///.... It doesn't start with file:, so it isn't converted and never matches moduleFile. The test's source location then points at the helper instead of the test file. The old regex took only the trailing location, so main handles this frame. I reproduced the stack shape on Node 26. Allowing an optional async prefix fixes it: /^at (?:async )?(?:[^(]*? \()?(.+?):(\d+):(\d+)\)?$/. A captureSource case with a cross-file async helper would cover it.
reviewed 44a714f by Hermes on behalf of @okwasniewski
44a714f to
b8f4fec
Compare
There was a problem hiding this comment.
All reported issues were addressed across 21 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
[claude-opus-5-5] responding on behalf of okwasniewski Review round addressed (head 3ff7ada). For the human:
|
3ff7ada to
ff7533e
Compare
ff7533e to
aff3733
Compare
aff3733 to
a0f8df3
Compare
…ithout extensions, unreadable tsconfig, chained source maps, Windows require paths
…w Yarn PnP export targets
… check first, docs
There was a problem hiding this comment.
All reported issues were addressed across 22 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
Edge-case round done (b263fb8, d8c8129, 9b01e2e, 81e37dd, on top of the rebased a0f8df3). Each finding has a repro in
Package-manager matrix (verdaccio tarballs, main Follow-ups, not in this PR:
|
…reign infra errors, review tests
|
Update, 3752276: cubic's 6 threads fixed or answered (FILE: URLs, foreign infrastructure errors, changeset wording, |
Every new pnpm 11+ user's first
pnpm installafterpnpm dlx e2e initfails today:e2eloaded TypeScript throughtsx,tsxdepends onesbuild, andesbuildhas apostinstall. pnpm 11+ fails on any build script the project has not decided on. The root fix is that nothinge2einstalls runs an install script. Replaces the closed #712, which editedpnpm-workspace.yamlinstead.What changed
tsxis gone fromdependencies. e2e loads TypeScript itself with synchronousmodule.registerHookshooks (packages/e2e/src/config/esm-hooks.ts), compiles withoxc-transform(config/typescript.ts), and reads tsconfig withget-tsconfig(config/tsconfig.ts). None of the new dependencies has an install script; platform binaries come asoptionalDependencies.config/compiled-files.ts): one extension table (lang,format, the extension an import writes) drives what is compiled, its module format, the./x.jsswap, and implied extensions..ts .mts .tsx .jsxrun as ES modules,.ctsas CommonJS. A project file compiles with the nearest tsconfig.json; TypeScript in an installed package compiles with defaults../x.jsand./xtox.ts, a directory's package.jsonmainthen index, tsconfigpaths/baseUrlfor project files); a#import or package export whose target is missing (./x.js, or./xwith no extension) moves to the TypeScript file behind it, also when a lax resolver earlier in the chain returned the missing file. An import written in JavaScript gets plain Node resolution.require()specifiers are read as platform paths (C:\app\helperon Windows;config/specifiers.ts).loadreads source throughnextLoad, so other hooks in the chain still see the file.files: []+references, as Vite writes). One that cannot be read (anextendspackage not installed) is skipped with one warning per run (the runner hands the files it warned about to its workers, which warn only about others they reach). tsconfig and file lookups live in a view per module graph (e2e-graph): fresh for eache2e mcpsession's graph load, read once otherwise (collecting 50 files parses theextendschain once). Outside a graph, file lookups go to the disk each time, as Node's own resolution does.require = globalThis[Symbol.for("e2e.hooked-require.v1")](__filename);, arequirethe loader registers: therequireNode hands a CommonJS module an ES module imported skips resolve hooks before modules: fix sync hook short-circuit in require() in imported CJS nodejs/node#62920 (24.18.0, 26.2.0; not in 22), and on every version once amodule.registerloader is in the chain (Yarn PnP, tsx in NODE_OPTIONS). Itsresolveruns the loader's resolve hook, sincerequire.resolveskips hooks before module: run require.resolve through module.registerHooks() nodejs/node#62028 (24.15.0; not in 22). The statement joins the end of the last directive (found from oxc-parser's parse of the output), so the prologue and every mapped position stay put; with no directive it takes its own first line and the map gains one unmapped line.usingon Node 22, legacy decorators) come from e2e's own@oxc-project/runtime: thefrom "..."/require("...")specifiers oxc reports inhelpersUsedare rewritten (oxc 0.152 has no option for it), so a project's own@oxc-project/runtimeimport is untouched.export {};to CommonJS whose only imports were type-only, before any trailing comment; the loader finds it in oxc-parser's parse of the output and blanks it. Not reported upstream yet (crates/oxc_transformer/src/typescript/annotations.rs).file:line:coland the fix: auto-accessors (no oxc release lowers them), decorators withoutexperimentalDecorators, andimportdeclarations and valueexportdeclarations in.cts(export interface/type/declarepass, as tsc allows). The check parses only files whose text could hold them.--versionand--helprun without the native binding; without it, a run fails withTYPESCRIPT_COMPILER_UNAVAILABLE(exit 3) naming the@oxc-transform/@oxc-parserbindings for the platform and [BUG] Platform-specific optional dependencies not being included inpackage-lock.jsonwhen reinstalling withnode_modulespresent npm/cli#4828; any other load failure gives its reason.@jridgewell/remapping), sofile:linestill names the file on disk.--importconfig/register.ts, which registers the hooks; it andesm-hooks.tsload under Node's own type stripping, so they stay erasable TypeScript.tsxUsesSyncHooksand its version table, the tsx namespace andtsx-namespaceURL tagging, theNODE_OPTIONSinspection, and the diagnose hint for tsx's?namespace=CommonJS dead end. Config and tests now share the runner's module instances; theglobalThisslots stay for a project that loads its own copy of e2e.at async ...frames (a test file that awaits a helper declaring tests), and onlyfile:URLs are decoded (collect/registry.ts); main throws on a%in a file name and loses the location at a#.util.getCallSitesdrops async frames andError.prepareStackTracegets no call sites while source maps are on, so the text stays the source.import typethat stays in the output (a decorated member's type underemitDecoratorMetadata, a re-exported type) fails at link time as before, but the error now names the importing line andimport type.^22.22.3 || >=24.8.0for every published package (was>=22.12.0), as an explicit release-range model ininternal/node-version.ts.registerHooksdoes not exist before 22.15, and before 22.22.3 / 24.8.0 a CommonJS file an ES module imported cannotrequire()an ES module the hooks load (esm: populate separate cache for require(esm) in imported CJS nodejs/node#59679). The CLI refuses older versions naming the ones to upgrade to, and the Bun and Deno runtimes (Bun has noregisterHooks; Deno has it but cannot load oxc's addon from a hook);registerLoaderrefuses them too (NODE_UNSUPPORTED). No asyncmodule.registerfallback.pnpm check:install-scripts(inpnpm check) walks the installed tree of every published package and names the chain to anypreinstall/install/postinstallorbinding.gyp(on main:e2e > tsx@4.23.14 > esbuild@0.28.2). A newinstallCI job runsscripts/install-smoke.tswith pnpm 11 and 12: packse2e,@e2e-dev/web,@e2e-dev/mobile, installs them into a fresh project, installs again from the lockfile, runs a TypeScript test, and fails on a non-zero exit,ERR_PNPM_IGNORED_BUILDS, or a writtenpnpm-workspace.yaml(on main:pnpm installexits 1).pnpm testruns on 22, 24, 26 (check-latest) and the floors 22.22.3 and 24.8.0. Vitest externalizes the workspace packages'dist/so integration tests load them natively, through e2e's hooks.flowchart LR A["import / require"] --> W{written in a compiled file?} W -- no --> N[Node resolution] W -- "yes: ./x.js, ./x, ./dir, tsconfig alias" --> P[TypeScript file, from the graph's view] W -- "yes: # import / package export" --> X[Node resolution, retried on the x.js to x.ts swap] P --> N N --> F["format from the extension table; e2e-graph query"] X --> F F --> L{load hook} L -- "TypeScript, JSX" --> O["nextLoad source, oxc-transform, inline source map"] L -- "JSON, no type attribute" --> J["nextLoad as json, export default JSON.parse"] L -- else --> D[nextLoad]Parity with tsx
Each row has a test:
tests/integration/typescript-loader.test.tsruns the built CLI on a fixture with all of them, in an ESM package and in a package without"type": "module", with--workers 2;tests/unit/esm-hooks.test.tsandtests/unit/typescript.test.tscover the rules one by one."type": "module""type"(Next.js-style CJS scope).mts.ctswithimport type,import x = require(),export =.ctswith ESMimportstatements.tsxand.jsx(tsconfigjsx/jsxFactory)const enum, namespaces, parameter propertiesexperimentalDecorators(+emitDecoratorMetadata)file:line:col, see belowaccessor x = 1)file:line:col, see below.ctswithexport interface/type/declareandmodule.exportsexports/importstarget without extension (./src/*)extendspackage not installedmodule.registerloader in the chain),.ctsimport typepaths+baseUrl./xand./x.jsimportingx.ts, directory index.tssource#subpath imports, including one mapped to./x.jswithx.tson disk.ctsrequiring a sibling.ts(extensionless,.cjs, tsconfig alias) and a decorator helperwith { type: 'json' }.cjshelperrequire()ing project TypeScript.ctswith a hashbang, a header comment, and'use strict'; stack line of a throw.tswritten withmodule.exports, required by a.cjsfilefile:line:coland test source in report and failure textfails.e2e.ts:7:19, test at5:150% off #1.e2e.tsCOLLECTION_ERROR: URI malformed(50%off), empty source file (#)7:19and5:1e2e-graph), tsconfig alias in the graphe2e mcp,e2e exploremcp-server.test.ts,explore.test.ts)pathToFileURL/fileURLToPathonly, no platform branchesBehavior changes, all in
docs/reference/config.mdxand the changeset:importstatements intorequirecalls, so a.ctsfile usesimport x = require(). TypeScript'sverbatimModuleSyntaxand Node's own type stripping draw the same line.experimentalDecorators, and auto-accessors, are refused with theirfile:line:coland the fix.bun --bun e2eanddeno run npm:e2eexit 2 naming the fix. Bun and Deno still install it, andbunx e2eruns it on Node.js..ctsfile on Node.js 24.8.0 to 24.11.0: Node rejects the sourceless CommonJS result PnP's loader returns before e2e's hook sees it (fixed in 24.11.1). Documented indocs/reference/cli.mdx..tsis an ES module, including one a.cjsfile requires; CommonJS TypeScript goes in.cts.pathsthere).Verified
Ran it locally: yes
Fresh user, pnpm 12.6.0. Every published package packed (
pnpm pack) and served from a local verdaccio per build (main on 4410, this branch on 4411;e2eand@e2e-dev/*never proxy to npmjs, everything else does), fresh/tmpdirs, fresh store,minimumReleaseAge0 so a seconds-old local publish is not what pnpm writes a workspace file for, example test against a static site on 4420.pnpm dlx e2e init --yespnpm installERR_PNPM_IGNORED_BUILDS(esbuild@0.28.2)pnpm install --frozen-lockfileERR_PNPM_IGNORED_BUILDSpnpm-workspace.yamlallowBuilds: esbuild: set this to true or falseesbuild/tsxinpnpm-lock.yamlpnpm exec e2e run(example test)pnpm 12.9.0 on this branch: the same five steps, all exit 0, no
pnpm-workspace.yaml.npm 11.17 (
npx e2e init --yes,npm install,npm ci,npx e2e run) and bun 1.4.2 (bunx e2e init --yes,bun install,bun install --frozen-lockfile,bunx e2e run) on this branch: every step exit 0, example test passes.Node floor. The same three files on each version (
hooks.mjsregisters pass-throughregisterHooks,main.mjsimportsreq.cjs, which requiresesm.mjs):SyntaxError: The requested module 'node:module' does not provide an export named 'registerHooks'TypeError: Cannot read properties of undefined (reading 'exports')atnode:internal/modules/esm/translators(cjsCache.get(job.url).exports)answer 42The e2e unit and integration suites pass on 22.22.3, 24.8.0, 24.17.0 (the compiled-CommonJS
requirepath), and 24.19.0; the loader's own tests also on 24.11.0 and 26.3.0; the parity fixture through the built CLI (--workers 2, ESM and CJS scope) also on 26.10.0. On 24.7.0 the CLI stops withe2e requires Node.js 22.22.3 or newer on Node.js 22, or 24.8.0 or newer; this is Node.js 24.7.0. ....Install smoke.
node scripts/install-smoke.ts 11and... 12on this branch: install, frozen install, and the TypeScript test all exit 0. On main,pnpm installexits 1.Package managers. Every published package packed and served from a local verdaccio on 4411 (
e2eand@e2e-dev/*never proxy to npmjs), main ise2e@0.16.0from npmjs. A fresh/tmpproject per row:init --yesthrough the manager's own runner for npm and pnpm, a copy of that npm-initialized project for the rest; then a TS-heavy fixture (tsconfigpaths, a.ctsthat requires./plain, a legacy decorator and an enum, JSX, a#shared/*import map naming./shared/src/*with no extension); install, frozen install, the example test against a static site on 4420, and the fixture. Node 24.19.Cannot find module './plain'from the.cts)--omit=optionalTYPESCRIPT_COMPILER_UNAVAILABLEnaming the darwin-arm64 oxc bindings and npm/cli#4828, exit 3;e2e --versionworksERR_PNPM_IGNORED_BUILDS/ sameERR_PNPM_IGNORED_BUILDS/ sameexports: {"./*": "./src/*"})ERR_PNPM_IGNORED_BUILDS/ samenodeLinker: node-modulesbunx e2e run(Node.js)bun --bun e2e run(Bun runtime)Cannot find package 'tsx:'e2e runs on Node.js: Bun 1.4.2 reports Node.js 26.3.0 but has no module.registerHooks ...deno run -A npm:e2e runLoading unprepared module ...?tsx-namespace=e2ee2e runs on Node.js, not Deno 2.9.4: run the CLI with Node.js (npx e2e).Suites. testbed: 71 passed, 1 skipped. Web benchmark
tests/: 91 passed. Web benchmarktests-agent/withCI=1 --strict-cacheand no model key: identical per-test verdicts and counters on main and this branch (26 replayed, 2 handed off, 6 missed, 2 model calls refused for the missing key), so the branch moves no replay key. Mobile benchmark left to CI. Every edge case from review has a repro in/tmp/edge-reprosand a committed regression test.Timings. Config load and collect in a fresh process per round (
loadConfigModule+resolveConfig+collectfromdist), alternating rounds against main, medians.Config load is 10 to 14 ms faster and CPU about 18% lower (no tsx registration, no esbuild service). Collect is 4 to 5 ms slower: the syntax check parses files whose text could hold a refused construct, and oxc-parser loads on first use.