Skip to content

fix!: preserve default extension filter when include is customized#100

Merged
chenjiahan merged 3 commits intomainfrom
chenjiahan/fix-default-extension-filter
Apr 8, 2026
Merged

fix!: preserve default extension filter when include is customized#100
chenjiahan merged 3 commits intomainfrom
chenjiahan/fix-default-extension-filter

Conversation

@chenjiahan
Copy link
Copy Markdown
Member

@chenjiahan chenjiahan commented Apr 8, 2026

Summary

When users override include with a directory such as src, the plugin currently drops its default script-extension guard because that guard lives on include itself. That makes the React Refresh loader eligible for non-script files imported from the same directory, so CSS and other assets can be instrumented unexpectedly.

This PR moves the default script extension matcher to test, leaves include unset by default, updates the option docs, and adds a regression test that verifies CSS is still excluded when include points at a directory.

Related Links

@chenjiahan chenjiahan changed the title fix: preserve default extension filter when include is customized fix!: preserve default extension filter when include is customized Apr 8, 2026
@chenjiahan chenjiahan marked this pull request as ready for review April 8, 2026 03:21
Copilot AI review requested due to automatic review settings April 8, 2026 03:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an option interaction in @rspack/plugin-react-refresh where overriding include with a directory unintentionally removed the default “script extensions only” guard, allowing non-script assets (e.g., CSS) to be processed by the React Refresh loader.

Changes:

  • Move the default script-extension matcher to test and leave include unset by default.
  • Update option documentation in src/options.ts and README.md to reflect the new defaults.
  • Add a regression test fixture importing CSS and a test ensuring CSS isn’t instrumented even when include targets a directory.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/test.spec.ts Adds CSS handling to the test harness and a regression test for directory-based include.
test/fixtures/include/style.css New CSS asset used to validate exclusion from React Refresh instrumentation.
test/fixtures/include/index.js Imports the CSS asset to ensure it participates in the build graph.
src/options.ts Updates defaults so the extension filter lives on test instead of include.
README.md Updates documented defaults for test and include.
Comments suppressed due to low confidence (2)

test/test.spec.ts:35

  • readOutput() returns an empty string when an output file is missing, and it’s now used for required bundles (react-refresh.js/fixture.js/runtime.js/vendor.js). This can cause multiple existing assertions (e.g. expect(vendor).not.toContain(...)) to pass even if the bundle wasn’t emitted, and it can also mask stale files if dist/ isn’t cleaned between compiles. Consider keeping strict reads (throw/fail) for required outputs and only using a safe/optional read for the CSS output, and/or remove dist/ (or read from stats assets) before each compilation to ensure outputs are from the current run.
const readOutput = (fixturePath: string, file: string) =>
  fs.existsSync(path.join(fixturePath, 'dist', file))
    ? fs.readFileSync(path.join(fixturePath, 'dist', file), 'utf-8')
    : '';

const compileWithReactRefresh = (
  fixturePath: string,
  refreshOptions: PluginOptions,
): Promise<CompilationResult> =>
  new Promise((resolve, reject) => {

src/options.ts:105

  • normalizeOptions() no longer sets a default for include, but the function still returns options as NormalizedPluginOptions where NormalizedPluginOptions = Required<PluginOptions>. This makes the normalized type claim include can’t be undefined, even though it can be when callers omit it. Please adjust the normalized type (or the normalization logic) so include is correctly typed as possibly undefined, and avoid relying on non-null assertions for include downstream.
export function normalizeOptions(
  options: PluginOptions,
): NormalizedPluginOptions {
  d(options, 'test', /\.(?:js|jsx|mjs|cjs|ts|tsx|mts|cts)$/);
  d(options, 'exclude', /node_modules/i);
  d(options, 'library');
  d(options, 'forceEnable', false);
  d(options, 'injectLoader', true);
  d(options, 'injectEntry', true);
  d(options, 'reloadOnRuntimeErrors', false);
  d(options, 'reactRefreshLoader', 'builtin:react-refresh-loader');
  return options as NormalizedPluginOptions;

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d7661eb96

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chenjiahan chenjiahan merged commit 3802d58 into main Apr 8, 2026
8 checks passed
@chenjiahan chenjiahan deleted the chenjiahan/fix-default-extension-filter branch April 8, 2026 03:41
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.

2 participants