Skip to content

fix(config): support tsconfig extends array form - #2054

Merged
james-elicx merged 1 commit into
cloudflare:mainfrom
Xplod13:fix/tsconfig-extends-array
Jun 15, 2026
Merged

fix(config): support tsconfig extends array form#2054
james-elicx merged 1 commit into
cloudflare:mainfrom
Xplod13:fix/tsconfig-extends-array

Conversation

@Xplod13

@Xplod13 Xplod13 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Support the TypeScript 5.0+ array form of extends ("extends": ["./a.json", "./b.json"]) when loading tsconfig path aliases.
  • Previously only the string form was handled, so array-form extends was silently ignored and inherited paths/baseUrl were dropped.

Root Cause

Two sites resolved extends as a string only:

  • config/tsconfig-paths.ts: const extendsList = typeof parsed.extends === "string" ? [parsed.extends] : [];
  • index.ts: if (typeof parsed.extends === "string") { … }

When a project used extends: [...] (common in monorepos with shared base configs), both produced an empty parent list, so path aliases / baseUrl defined in the extended configs were never loaded — and next.config.ts imports through those aliases failed at config-load time.

The fix normalizes extends (string | string[] | undefined) into an ordered list at both sites and iterates the parents, merging so that later array entries override earlier ones and the child config overrides all parents — matching Next.js' loadTsConfigFile. The shared seen set is threaded through every entry so cyclic extends can't loop, and each parent's aliases continue to resolve relative to its own directory. Non-string array entries are filtered out; the single-string form is unchanged.

References

Verification

  • CI=true pnpm test tests/tsconfig-paths-config-loader.test.ts — new cases (ported from Next.js): array-form extends resolves inherited paths; later array entries override earlier; child overrides array-extended paths. Red before, green after; single-string regression and child-override tests still pass (12 passed).
  • CI=true pnpm test tests/tsconfig-path-alias-build.test.ts tests/tsconfig-paths-vite8.test.ts — the index.ts call-site suites pass (no regression).
  • CI=true pnpm test — full battery green (only the pre-existing env flakes deploy.test.ts > resolveWranglerBin and oxlint-prefer-shared-utils, reproduced identically on origin/main).
  • CI=true npx vp check — clean on all changed files.

## Summary
- Handle the `extends` array form (TypeScript 5.0+) when resolving
  tsconfig/jsconfig path aliases and `baseUrl` for `next.config.ts` loading.
- Normalize `extends` (string | string[] | undefined) into an ordered list at
  both loader sites; iterate parents so later entries override earlier ones.
- Add array-extends regression tests (single parent, multi-parent precedence,
  child override).

## Root Cause
Both tsconfig loaders only handled the string form of `extends`
(`typeof parsed.extends === "string"`). When `extends` was an array
(`["./a.json", "./b.json"]`), it was silently dropped, so inherited
`compilerOptions.paths`/`baseUrl` were lost and path-aliased imports in
`next.config.ts` failed to resolve. Next.js normalizes via
`Array.isArray(config.extends) ? config.extends : [config.extends]` and merges
parents in order; this change mirrors that behavior at both vinext sites
(packages/vinext/src/config/tsconfig-paths.ts and packages/vinext/src/index.ts).

## References
- Fixes cloudflare#1974
- Next.js parity: packages/next/src/build/next-config-ts/transpile-config.ts (loadTsConfigFile)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Jun 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2054
npm i https://pkg.pr.new/vinext@2054

commit: c2a9196

@james-elicx

Copy link
Copy Markdown
Member

/bigbonk review for issues

@ask-bonk ask-bonk 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.

Review: tsconfig array-form extends

The fix is correct and the merge semantics match Next.js parity. I verified against the canary source packages/next/src/build/next-config-ts/transpile-config.ts:

  • Parents merged in order via { ...mergedOptions, ...parentOptions } → later array entries override earlier ones ✅
  • paths: currentOptions.paths ?? mergedOptions.paths → child overrides parents ✅
  • baseUrl handling in tsconfig-paths.ts (parent.baseUrl ?? resolution.baseUrl) correctly lets a later parent's baseUrl win while falling back across entries ✅

The shared seen set is threaded correctly through both recursion sites so cyclic extends can't loop, and each parent resolves relative to its own directory. I ran tests/tsconfig-paths-config-loader.test.ts (12 passed) and tests/tsconfig-paths-vite8.test.ts (7 passed) — both green.

One gap worth addressing

The PR fixes two separate, duplicated implementations (config/tsconfig-paths.ts and index.ts::loadTsconfigPathAliases), but the new tests only exercise tsconfig-paths.ts via loadTsconfigPathAliasesForRoot. The index.ts site has no test coverage for the array form — the only extends test touching that path (tsconfig-paths-vite8.test.ts:122) still uses the string form (extends: "./tsconfig.base.json"). So the index.ts half of this fix is untested. Adding an array-form case to tsconfig-paths-vite8.test.ts (or the alias-build suite) would lock in the behavior at that call site too.

Pre-existing (not blocking)

This is the third+ copy of resolveTsconfigExtends / resolveTsconfigPathCandidate / the new normalize*Extends helpers across index.ts and config/tsconfig-paths.ts. The duplication predates this PR, but it's exactly the kind of divergence-prone copy AGENTS.md warns about — a future cleanup could consolidate these into the shared config/tsconfig-paths.ts module and have index.ts import them.

Nice work porting the Next.js test cases and linking them.

const extendedPath = resolveTsconfigExtends(normalizedPath, extendsSpecifier);
if (extendedPath) {
aliases = loadTsconfigPathAliases(extendedPath, projectRoot, seen);
aliases = { ...aliases, ...loadTsconfigPathAliases(extendedPath, projectRoot, seen) };

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.

This array-form extends path in index.ts is the second site fixed, but it has no test coverage for the array form — tests/tsconfig-paths-vite8.test.ts:122 (the only extends test exercising loadTsconfigPathAliases) still uses the string form. Consider adding an array-form case to that suite so the index.ts half of this fix is locked in, not just the config/tsconfig-paths.ts half.

@ask-bonk

ask-bonk Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review posted. Summary of my findings on PR #2054:

Verdict: Correct fix, with one test-coverage gap.

What's good:

  • The array-form extends handling matches Next.js parity exactly (verified against canary transpile-config.ts): later array entries override earlier, child overrides parents, baseUrl fallback chains correctly.
  • The shared seen set is threaded through both recursion sites, so cyclic extends can't loop.
  • Non-string array entries are filtered; single-string form is unchanged.
  • Tests I ran pass: tsconfig-paths-config-loader.test.ts (12) and tsconfig-paths-vite8.test.ts (7).

Gap flagged (inline comment):

  • The PR fixes two duplicated implementations, but the new tests only cover config/tsconfig-paths.ts. The index.ts::loadTsconfigPathAliases site has no array-form test coverage — its only extends test still uses the string form. Suggested adding an array-form case to lock that half in.

Pre-existing (non-blocking):

  • The resolveTsconfigExtends / resolveTsconfigPathCandidate / normalize*Extends helpers are duplicated across the two files — a divergence risk worth a future consolidation, but the PR didn't introduce it.

github run

@james-elicx
james-elicx merged commit 157cf7a into cloudflare:main Jun 15, 2026
45 checks passed
@Xplod13
Xplod13 deleted the fix/tsconfig-extends-array branch June 16, 2026 17:09
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.

Config: tsconfig extends array form ignored — path aliases dropped when loading next.config.ts

2 participants