Skip to content

feat: support tsx as a fallback loader for TypeScript and JSX configs#4796

Merged
alexander-akait merged 3 commits into
mainfrom
claude/webpack-cli-pr-4790-29d4e7
Jul 3, 2026
Merged

feat: support tsx as a fallback loader for TypeScript and JSX configs#4796
alexander-akait merged 3 commits into
mainfrom
claude/webpack-cli-pr-4790-29d4e7

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

Adds tsx as a fallback loader for TypeScript and JSX configuration files (.ts, .tsx, .cts, .mts, .jsx), used when none of the loaders known to interpret (ts-node, sucrase, @babel/register, esbuild-register, @swc/register) are installed.

Closes #4790

The approach in #4790 — appending "tsx/cjs" to interpret's loader tables — cannot work: rechoir resolves loader modules with the legacy resolve@1.x package, which does not support package exports maps — and tsx/cjs exists only in tsx's exports map (there is no physical cjs.js file). rechoir.prepare() therefore always fails to resolve the real package (Cannot find module 'tsx/cjs'); the tests in #4790 only passed because they committed a hand-written fake tsx stub into a fixture node_modules.

Implementation

  • The interpret/rechoir flow is untouched and its loaders keep priority.
  • When rechoir.prepare() fails for a config with a tsx-loadable extension, the CLI registers tsx itself via createRequire(configFilePath)("tsx/cjs") — Node's own resolver, which understands exports maps and resolves from the user's project. Requiring tsx/cjs installs its require hook, then config loading proceeds as usual.
  • .mts, which has no interpret entry at all, gets a best-effort tsx registration; when tsx is missing, the error output is identical to main today (the original import() failure is reported).
  • When nothing works, the tsx resolution failure is appended to the existing loader failure list before Please install one of them (first line only, to avoid the noisy require stack).

The tsx registration has no dependency on interpret/rechoir, so it survives their planned future removal unchanged.

Test plan

  • New fixture test/build/config-format/typescript-tsx/ exercises the real tsx package (added as a root devDependency) for .ts, .mts and .jsx configs. Because the repository has working interpret loaders installed, the test's beforeAll generates throwing stubs for all of them into the fixture's node_modules (already gitignored — nothing fake is committed), so the assertions can only pass through tsx.
  • Negative paths verified manually: with tsx also unavailable, .ts exits 2 listing all loader failures including tsx; .mts reports the original import error exactly as before.
  • All 17 config-format suites (37 tests) and all 69 config/extends suites (152 tests) pass; tsc --build, ESLint, Prettier and cspell are clean.
  • Changeset included (webpack-cli: minor).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Hv8piVkEGYdWzWoWCtMcif

claude added 2 commits July 3, 2026 20:41
Register `tsx/cjs` manually when the loaders known to `interpret` are
unavailable. It cannot go through `interpret`/`rechoir`: the `tsx/cjs`
entry point exists only in the package's `exports` map, which
`rechoir`'s resolver (legacy `resolve@1.x`) does not support, so it is
resolved with `createRequire` from the configuration file's location
and required directly, installing its require hook.

The loaders listed in `interpret` keep priority; `tsx` is tried only
after all of them have failed. For `.mts`, which has no `interpret`
entry at all, `tsx` is registered best-effort and the original error
reporting is preserved when it is missing. When nothing works, the
`tsx` resolution failure is appended to the existing loader failure
list before "Please install one of them".

The fixture shadows the `interpret` loaders with generated throwing
stubs (kept out of the repository — `test/**/node_modules` is already
ignored) so the tests exercise the real `tsx` package from the root
dev dependencies for both `.ts` and `.mts` configurations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hv8piVkEGYdWzWoWCtMcif
`tsx/cjs` also installs a require hook for `.jsx` (esbuild transform,
including `@jsx` pragma support), so offer it as a fallback there too
when the `interpret` loaders (`@babel/register`, `sucrase`) are not
installed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hv8piVkEGYdWzWoWCtMcif
Copilot AI review requested due to automatic review settings July 3, 2026 20:58
@changeset-bot

changeset-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 58a215d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
webpack-cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@linux-foundation-easycla

linux-foundation-easycla Bot commented Jul 3, 2026

Copy link
Copy Markdown

CLA Not Signed

One or more co-authors of this pull request were not found. You must specify co-authors in commit message trailer via:

Co-authored-by: name <email>

Supported Co-authored-by: formats include:

  1. Anything <id+login@users.noreply.github.com> - it will locate your GitHub user by id part.
  2. Anything <login@users.noreply.github.com> - it will locate your GitHub user by login part.
  3. Anything <public-email> - it will locate your GitHub user by public-email part. Note that this email must be made public on Github.
  4. Anything <other-email> - it will locate your GitHub user by other-email part but only if that email was used before for any other CLA as a main commit author.
  5. login <any-valid-email> - it will locate your GitHub user by login part, note that login part must be at least 3 characters long.

Alternatively, if the co-author should not be included, remove the Co-authored-by: line from the commit message.

Please update your commit message(s) by doing git commit --amend and then git push [--force] and then request re-running CLA check via commenting on this pull request:

/easycla

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.74510% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.30%. Comparing base (3cd7423) to head (58a215d).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
packages/webpack-cli/src/webpack-cli.ts 62.74% 19 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4796      +/-   ##
==========================================
- Coverage   93.43%   93.30%   -0.14%     
==========================================
  Files          14       14              
  Lines        5420     5463      +43     
  Branches      788      792       +4     
==========================================
+ Hits         5064     5097      +33     
- Misses        354      364      +10     
  Partials        2        2              
Files with missing lines Coverage Δ
packages/webpack-cli/src/webpack-cli.ts 96.71% <62.74%> (-0.25%) ⬇️

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3cd7423...58a215d. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a tsx-based fallback loader path for TypeScript/JSX webpack config files when interpret/rechoir can’t register any of their known loaders, and introduces a fixture that validates the fallback against the real tsx package.

Changes:

  • Add tsx/cjs registration (via createRequire(configFilePath)) as a fallback when rechoir.prepare() fails for TS/JSX config extensions, and as best-effort support for .mts.
  • Add a new test/build/config-format/typescript-tsx/ fixture + Jest suite that disables interpret loaders via throwing stubs to force exercising the tsx fallback.
  • Add tsx as a root devDependency and include a changeset entry for the feature.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/webpack-cli/src/webpack-cli.ts Implements the tsx fallback loader registration and integrates it into the config loading flow.
test/build/config-format/typescript-tsx/typescript.test.mjs New integration tests forcing the tsx fallback by shadowing interpret loaders with throwing stubs.
test/build/config-format/typescript-tsx/webpack.config.ts TS config fixture consumed by the new test.
test/build/config-format/typescript-tsx/webpack.config.mts MTS config fixture consumed by the new test.
test/build/config-format/typescript-tsx/webpack.config.jsx JSX config fixture consumed by the new test.
test/build/config-format/typescript-tsx/main.ts Minimal entry file for the build output assertions.
test/build/config-format/typescript-tsx/package.json Fixture-local package settings (CommonJS package type + engine constraint).
package.json Adds tsx devDependency for running the new fixture against the real package.
package-lock.json Locks tsx (and its deps) into the dependency graph.
eslint.config.mjs Ignores the JSX fixture config file for ESLint parsing.
.changeset/tsx-config-loader.md Declares a minor bump for webpack-cli describing the new fallback behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/webpack-cli/src/webpack-cli.ts Outdated
Comment on lines +2916 to +2918
} catch (err) {
return err as Error;
}
Comment on lines +64 to +68
// TypeScript and JSX configuration files that `tsx` can load through its
// CommonJS require hook (`tsx/cjs`). Used as a fallback when the loaders
// listed in `interpret` are unavailable (for `.mts` there is no `interpret`
// entry at all, so `tsx` is the only fallback).
const TSX_LOADABLE_EXTENSIONS = new Set([".ts", ".tsx", ".cts", ".mts", ".jsx"]);
- Normalize non-Error throws in the tsx registration so the
  error-reporting path can rely on `.message`.
- Fix the pre-existing "Unable load" typo ("Unable to load").
- Cover `.cts` (special-cased extension mapping) and `.tsx` (types and
  JSX together) in the fixture, alongside `.ts`, `.mts` and `.jsx`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hv8piVkEGYdWzWoWCtMcif
@alexander-akait alexander-akait merged commit 9a5fef9 into main Jul 3, 2026
16 of 19 checks passed
@alexander-akait alexander-akait deleted the claude/webpack-cli-pr-4790-29d4e7 branch July 3, 2026 21:30
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.

3 participants