Skip to content

fix(build): forward pageExtensions to Vite resolve.extensions#1649

Merged
james-elicx merged 1 commit into
mainfrom
fix/issue-1502-page-extensions
May 28, 2026
Merged

fix(build): forward pageExtensions to Vite resolve.extensions#1649
james-elicx merged 1 commit into
mainfrom
fix/issue-1502-page-extensions

Conversation

@james-elicx
Copy link
Copy Markdown
Member

Summary

Fixes #1502.

When next.config.{js,ts} configured pageExtensions with values beyond Vite's defaults (for example ["platform.tsx", "tsx", "mdx"]), extensionless imports of files with those extensions failed to resolve and the build crashed with Custom deploy script failed: undefined (1).

This change mirrors Next.js / Turbopack resolveExtensions semantics: the user-configured pageExtensions are prepended (each prefixed with .) to Vite's default extension list, preserving the user's priority — so .web.tsx resolves before .tsx and .platform.tsx resolves before .tsx, matching how Turbopack/webpack pick extension priority in Next.js.

The page scanner already accepted pageExtensions; this PR fills in the remaining gap on the Vite resolver side.

Ported from Next.js: test/e2e/app-dir/resolve-extensions/.

Test plan

  • vp test run tests/page-extensions-resolve.test.ts (new tests cover the helper and verify the Vite config wiring)
  • vp test run tests/file-matcher.test.ts tests/page-extensions-routing.test.ts tests/build-optimization.test.ts (no regressions)
  • vp test run tests/next-config.test.ts tests/next-config-extensions.test.ts (config tests still pass)
  • vp check tests/page-extensions-resolve.test.ts packages/vinext/src/routing/file-matcher.ts packages/vinext/src/index.ts
  • CI Vitest + Playwright E2E green

When the user configured `pageExtensions` with values beyond Vite's
defaults (e.g. `["platform.tsx", "tsx", "mdx"]`), extensionless imports
of files with those extensions failed to resolve and the build crashed
with `Custom deploy script failed: undefined (1)`.

Mirror Next.js / Turbopack `resolveExtensions` semantics: prepend the
configured `pageExtensions` (each prefixed with `.`) to Vite's default
extension list, preserving the user's priority so e.g. `.web.tsx`
resolves before `.tsx`.

Ported from Next.js: test/e2e/app-dir/resolve-extensions/

Refs: #1502
@james-elicx
Copy link
Copy Markdown
Member Author

/bigbonk review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 28, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1649

commit: 5a48bac

@github-actions
Copy link
Copy Markdown

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
web preview production

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-scoped fix. The helper function is correct, the tests are thorough, and the integration point in index.ts is minimal.

A few notes from the review:

Config merge semantics are correct. I verified Vite's resolveEnvironmentResolveOptions uses mergeWithDefaults (not mergeConfig), which means the plugin's resolve.extensions array replaces Vite's defaults wholesale rather than being concatenated. This is why buildViteResolveExtensions must manually include Vite's defaults as its second parameter — that design is correct.

Minor consideration (non-blocking): If the user explicitly sets resolve.extensions in their vite.config.ts (or another plugin does), Vite's runConfigHook uses mergeConfig between plugin config hook returns, which concatenates arrays. So the final resolved list could contain duplicates — e.g., if the user wrote resolve: { extensions: ['.tsx'] }, the resolved array would have .tsx twice. This is harmless (Vite tries them in order, first match wins, and deduplication isn't needed for correctness), but worth noting for anyone reading the code later. Not worth complicating the implementation for.

normalizePageExtensions edge case works correctly. The regex /^\.+/ strips leading dots before the function re-adds a single . prefix in buildViteResolveExtensions. Input like ".platform.tsx" correctly normalizes to "platform.tsx"".platform.tsx". The test at line 58-62 covers this.

Tests are solid. Good coverage of the priority ordering, deduplication, multi-segment extensions, and the integration test that exercises the actual plugin config() hook with a tmpdir fixture.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 28, 2026

Approved PR #1649. Here's a summary of the review:

Verdict: Approved

The fix is clean and correct. buildViteResolveExtensions properly prepends user-configured pageExtensions (dot-prefixed) ahead of Vite's defaults, deduplicates, and returns the merged list. The integration point in index.ts is a single line addition. Key findings:

  • Config merge semantics are sound. Vite's resolveEnvironmentResolveOptions uses mergeWithDefaults (not mergeConfig), so the plugin's array fully replaces Vite's defaults — which is why the helper must manually include Vite defaults in its fallback parameter. This is correct.
  • Edge cases handled. normalizePageExtensions strips leading dots via /^\.+/ before buildViteResolveExtensions re-adds a single . prefix, so inputs like ".platform.tsx" round-trip correctly.
  • Tests are thorough. Good coverage of priority ordering, deduplication, multi-segment extensions (.platform.tsx, .web.tsx), MDX, and a full plugin integration test with a tmpdir fixture.

github run

@james-elicx james-elicx marked this pull request as ready for review May 28, 2026 13:31
@james-elicx james-elicx merged commit bdc6fe0 into main May 28, 2026
38 checks passed
@james-elicx james-elicx deleted the fix/issue-1502-page-extensions branch May 28, 2026 15:04
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.

Build: pageExtensions / custom resolveExtensions cause build failure

1 participant