Files
49e3ef6269 perf(cli): let tests resolve core modules individually (#10917)
* perf(cli): let tests resolve core modules individually, and stop two files importing the whole package

Importing from the core package root pulls in its entire export graph — a bit
over six hundred modules — however little of it a file actually uses. In a
release run the cli workspace spent 2223s collecting modules against 1372s
running tests, and a file that imports the package root costs about 11.5s
before its first assertion where one importing a single module costs about 2s.

cli's tsconfig already maps a wildcard subpath onto core's sources, so esbuild
resolves per-module imports when it bundles. Vitest does not read tsconfig
paths, and the alias list that stands in for them named only four subpaths, so
those imports did not resolve under test at all. This adds the wildcard there.

Expressing the alias list as an ordered array is what allows a pattern entry.
The package root has to become an exact match in the process: as a string it
would also match everything beneath it and rewrite each subpath into a path
under index.ts.

Two files move to per-module imports as a first check that the mapping holds
end to end. Both were picked because nothing that depends on them replaces the
core package with a mock factory — where a test does that, the mock stops
intercepting once the code under test imports the module directly, so those
call sites need their mocks moved in the same change and are left alone here.

* fix(cli): restore the named core subpaths the previous commit dropped

The previous commit was assembled from a working copy that predated main by
several weeks, so it silently reverted this file to that older state. Four
named core subpaths added since — envVarResolver, noFollowOpen,
subSessionConstants and toolWriteOrigin — disappeared with it, and the new
wildcard then claimed those specifiers and pointed them at files that do not
exist. 257 test files failed to load as a result.

All eight named subpaths are restored and kept ahead of the wildcard, with a
comment saying why that order matters and what a contributor adding a ninth
has to do. None of the eight can be derived from its specifier, so none of
them can be folded into the pattern.

The two migrated source files are rebuilt on their current contents for the
same reason; one of them had also been reverted by a line.

* fix(cli): name the migrated core imports so plain Node resolves them

The tipHistory / RemoteInputWatcher migration used .js-suffixed subpath
specifiers that match no entry in packages/core/package.json exports, so
the built-but-unbundled CLI (npm start / build-and-start, whose tsc dist
keeps specifiers) crashed at module load with ERR_PACKAGE_PATH_NOT_EXPORTED
while typecheck (tsconfig paths), unit tests (vitest wildcard alias) and the
bundle (esbuild paths) all bypassed exports and stayed green. Switch the two
files to named subpaths (storage, atomicFileWrite, debugLogger), following
the convention of the eight existing entries, and add the matching exports
entries and vitest aliases.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtlz4ubdgq

* test(scripts): guard core subpath exports resolution under plain Node

Every core subpath specifier statically imported from packages/cli/src must
resolve through packages/core/package.json exports in a real child node
process — no vitest aliases, no tsconfig paths. Without the matching exports
entries the built-but-unbundled CLI dies with ERR_PACKAGE_PATH_NOT_EXPORTED
while every gate that bypasses exports stays green; this guard goes red the
moment an entry is removed.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtlz4ubdgq

* fix(cli): name every core subpath import in cli tsconfig paths

The named subpaths cli sources import from @qwen-code/qwen-code-core had
no named `paths` entries, so the wildcard composed nonexistent files and
esbuild (bundle) plus the dev loader chain fell back to the exports map,
loading packages/core/dist copies while every package-root import loads
packages/core/src — two instances of barrel-exported, stateful modules
(debugLogger, storage, atomicFileWrite, envVarResolver, toolWriteOrigin,
memoryScopes) in one process. Add the six missing named entries beside the
existing ones so bundle and dev resolve all subpaths into the core src
tree, consistent with the vitest alias list.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtme4yqxhl

* test(scripts): pin subpath exports targets and guard bundle resolution

Complete the resolution guard and harden its probe:

- Cover every core subpath statically imported from packages/cli/src
  (adds toolWriteOrigin and memoryScopes) plus the subpaths npm start
  reaches through @qwen-code/acp-bridge (subSessionConstants, goalWire,
  transcriptRecords), instead of the previous five specifiers.
- Pin each specifier to its expected dist target: assert the resolved
  URL equals the pinned path and the target file exists, so a typo'd or
  redirected exports target fails the guard (import.meta.resolve alone
  accepts both). This makes a built core dist a prerequisite, which
  vitest-global-setup already fail-fasts on.
- Add a bundle-resolution guard: esbuild-bundle every cli/src core
  subpath under packages/cli/tsconfig.json and assert no input comes
  from packages/core/dist, pinning the tsconfig paths entries.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtme4yqxhl

* fix: cover goalWire in the core-subpath guard and make it OS-independent

- Add goalWire to the bundle arm and named tsconfig paths entries in
  packages/cli and packages/acp-bridge: acp-bridge's transcript-replay
  imports @qwen-code/qwen-code-core/goalWire, and without a named entry
  the wildcard resolved to a nonexistent ../core/src/goalWire and fell
  back to the exports map, bundling packages/core/dist/src/goals/
  goal-wire.js next to src-resolved core modules — the module-identity
  split #10908's Known risks name. Verified with an esbuild metafile
  probe: dist input before the paths entry, src input after.
- Normalize esbuild metafile input keys to forward slashes before the
  dist-leak filter and src-target assertions so the guard behaves the
  same on Windows runners, where esbuild emits backslash separators.
- Correct the header: this lane's vitest config does not wire
  scripts/vitest-global-setup.js (it is a globalSetup only in the
  packages/core and packages/cli configs, and its DIST_PREREQUISITES
  has no key covering this lane), so a missing dist surfaces as the
  existence assertion naming the absent file.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtmourmvi4

* test: guard acp-bridge-routed core subpaths in the bundle arm

The bundle arm seeded goalWire into the cli-routed buildSync case, which
passes an explicit packages/cli/tsconfig.json — but the shipped bundle
resolves that import under packages/acp-bridge/tsconfig.json (mainBuild in
esbuild.config.js carries no tsconfig option, so esbuild discovers the
nearest tsconfig per importing file, and goalWire is imported only from
packages/acp-bridge/src/transcript-replay.ts). The acp-bridge goalWire
paths entry was therefore guarded by no arm, and transcriptRecords /
subSessionConstants were probed by none at all: removing the acp-bridge
goalWire entry left the test green while a production-shaped build pulled
packages/core/dist inputs.

Add an acp-bridge-routed buildSync case — no tsconfig option, resolveDir
packages/acp-bridge/src, seeding goalWire, transcriptRecords,
subSessionConstants and noFollowOpen — asserting no input lands under
packages/core/dist and each expected core src target is present. With the
new arm, removing the acp-bridge goalWire entry goes red (4 dist leaks)
where previously nothing did.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtmxfessij

* fix(cli): guard the conversationsRuntimeMarker subpath route

R6-1: @qwen-code/qwen-code-core/conversationsRuntimeMarker is
statically imported from packages/cli/src (config/shared-env-keys.ts,
serve/run-qwen-serve.ts) but carried no named entry in
packages/cli/tsconfig.json paths and was seeded into neither guard
map. The wildcard composed a nonexistent ../core/src target and fell
back to the exports map, so the cli-routed bundle loaded a
packages/core/dist copy next to the core src copy while every guard
arm stayed green. Add the named paths entry beside the ones this PR
already adds and seed both guard maps with the specifier.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtngpuscjl

* fix(sdk): guard the sdk-routed core subpath resolution

R6-2: the cli bundle reaches sdk sources through the cli tsconfig
@qwen-code/sdk/* mapping (ui/utils/export/export-transcript-document.ts
imports @qwen-code/sdk/daemon/transcript, which re-exports from
daemon/ui/chat-record-transcript.ts), and chat-record-transcript.ts
imports @qwen-code/qwen-code-core/transcriptRecords. With no paths in
packages/sdk-typescript/tsconfig.json, mainBuild's per-importing-file
tsconfig discovery found no mapping and fell back to the exports map,
bundling a packages/core/dist copy while the guard suite stayed green.
Add the named entry to the sdk tsconfig, mirroring acp-bridge, and a
third guard arm probing the sdk route.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtngpuscjl

* fix(sdk): reference core from the sdk composite reference project

The paths entry added for the sdk-routed guard resolves
@qwen-code/qwen-code-core/transcriptRecords to the core source file,
which in the composite tsc --build graph belongs to the core project.
Without a project reference the cli build failed with TS6059/TS6307;
declare the core reference, mirroring packages/acp-bridge/tsconfig.json.
Verified with npm run build in packages/cli and npm run typecheck in
packages/sdk-typescript.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtngpuscjl

* fix(sdk): keep the declaration build on the exports map

The paths entry added to tsconfig.json for the bundle's esbuild
discovery is inherited by tsconfig.build.json; in that plain
declaration build it pulled core sources into the program, re-rooted
the inferred rootDir above the package, and nested every emitted .d.ts
under dist/sdk-typescript/src/, dropping dist/daemon/index.d.ts that
web-shell imports (TS7016). Reset paths in tsconfig.build.json so the
declaration build resolves core through its exports map. Verified with
npm run build in packages/sdk-typescript: dist/daemon/index.d.ts
restored and the daemon browser bundle byte-identical (236252 bytes,
its pre-existing warning threshold breach is unchanged).

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtngpuscjl

* fix(scripts): route core subpaths to src in the dev loader

The dev loader intercepted only the exact package root, so a named core
subpath fell through to the package's exports map and loaded
packages/core/dist while every package-root import loaded
packages/core/src. One dev process then held two instances of the same
module: Config binds the debug session on the src copy
(setDebugLogSession(this)), so RemoteInputWatcher's dist-copy
REMOTE_INPUT logger read an empty session and every debugLogger(...) call
there silently no-oped with QWEN_DEBUG_LOG_FILE enabled. Storage split
its static state the same way.

Derive the interception map from the core exports map so every named
subpath short-circuits to its packages/core/src file and stays covered as
subpaths are added. The exports entries (built-but-unbundled CLI) and the
tsconfig paths entries (bundle lane) are untouched.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtow5pgslz

---------

Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-09-06 10:20:05 +00:00

14 lines
512 B
JSON

{
"extends": "./tsconfig.build.json",
"compilerOptions": {
"composite": true,
"rootDir": "./src",
"tsBuildInfoFile": "./dist/tsconfig.reference.tsbuildinfo"
},
// The paths entry inherited from tsconfig.json resolves core subpath imports
// to core sources; in a composite tsc --build graph that file belongs to
// the core project, so it must be referenced here (mirrors
// packages/acp-bridge/tsconfig.json) or the build fails TS6059/TS6307.
"references": [{ "path": "../core" }]
}