diff --git a/packages/plugin-react-oxc/CHANGELOG.md b/packages/plugin-react-oxc/CHANGELOG.md index f10da0efa..2e08df63d 100644 --- a/packages/plugin-react-oxc/CHANGELOG.md +++ b/packages/plugin-react-oxc/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Allow processing files in `node_modules` + +The default value of `exclude` options is now `[/\/node_modules\//]` to allow processing files in `node_modules` directory. It was previously `[]` and files in `node_modules` was always excluded regardless of the value of `exclude` option. + ### Require Node 20.19+, 22.12+ This plugin now requires Node 20.19+ or 22.12+. diff --git a/packages/plugin-react-oxc/README.md b/packages/plugin-react-oxc/README.md index f717cbf8e..23bdaa58a 100644 --- a/packages/plugin-react-oxc/README.md +++ b/packages/plugin-react-oxc/README.md @@ -25,7 +25,7 @@ export default defineConfig({ ### include/exclude -Includes `.js`, `.jsx`, `.ts` & `.tsx` by default. This option can be used to add fast refresh to `.mdx` files: +Includes `.js`, `.jsx`, `.ts` & `.tsx` and excludes `/node_modules/` by default. This option can be used to add fast refresh to `.mdx` files: ```js import { defineConfig } from 'vite' @@ -40,8 +40,6 @@ export default defineConfig({ }) ``` -> `node_modules` are never processed by this plugin (but Oxc will) - ### jsxImportSource Control where the JSX factory is imported from. Default to `'react'` diff --git a/packages/plugin-react-oxc/src/index.ts b/packages/plugin-react-oxc/src/index.ts index d54da0ded..3b6318b02 100644 --- a/packages/plugin-react-oxc/src/index.ts +++ b/packages/plugin-react-oxc/src/index.ts @@ -25,17 +25,11 @@ export interface Options { } const defaultIncludeRE = /\.[tj]sx?(?:$|\?)/ +const defaultExcludeRE = /\/node_modules\// export default function viteReact(opts: Options = {}): Plugin[] { const include = opts.include ?? defaultIncludeRE - const exclude = [ - ...(Array.isArray(opts.exclude) - ? opts.exclude - : opts.exclude - ? [opts.exclude] - : []), - /\/node_modules\//, - ] + const exclude = opts.exclude ?? defaultExcludeRE const jsxImportSource = opts.jsxImportSource ?? 'react' const jsxImportRuntime = `${jsxImportSource}/jsx-runtime` diff --git a/packages/plugin-react/CHANGELOG.md b/packages/plugin-react/CHANGELOG.md index 7ee10b3de..0aabccc3e 100644 --- a/packages/plugin-react/CHANGELOG.md +++ b/packages/plugin-react/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Allow processing files in `node_modules` + +The default value of `exclude` options is now `[/\/node_modules\//]` to allow processing files in `node_modules` directory. It was previously `[]` and files in `node_modules` was always excluded regardless of the value of `exclude` option. + ### `react` and `react-dom` is no longer added to [`resolve.dedupe`](https://vite.dev/config/#resolve-dedupe) automatically Adding values to `resolve.dedupe` forces Vite to resolve them differently from how Node.js does, which can be confusing and may not be expected. This plugin no longer adds `react` and `react-dom` to `resolve.dedupe` automatically. diff --git a/packages/plugin-react/README.md b/packages/plugin-react/README.md index af7a2d545..3b66e4658 100644 --- a/packages/plugin-react/README.md +++ b/packages/plugin-react/README.md @@ -21,7 +21,7 @@ export default defineConfig({ ### include/exclude -Includes `.js`, `.jsx`, `.ts` & `.tsx` by default. This option can be used to add fast refresh to `.mdx` files: +Includes `.js`, `.jsx`, `.ts` & `.tsx` and excludes `/node_modules/` by default. This option can be used to add fast refresh to `.mdx` files: ```js import { defineConfig } from 'vite' @@ -36,8 +36,6 @@ export default defineConfig({ }) ``` -> `node_modules` are never processed by this plugin (but esbuild will) - ### jsxImportSource Control where the JSX factory is imported from. Default to `'react'` diff --git a/packages/plugin-react/src/index.ts b/packages/plugin-react/src/index.ts index eecd32ea2..436091402 100644 --- a/packages/plugin-react/src/index.ts +++ b/packages/plugin-react/src/index.ts @@ -104,11 +104,12 @@ export type ViteReactPluginApi = { } const defaultIncludeRE = /\.[tj]sx?$/ +const defaultExcludeRE = /\/node_modules\// const tsRE = /\.tsx?$/ export default function viteReact(opts: Options = {}): Plugin[] { const include = opts.include ?? defaultIncludeRE - const exclude = opts.exclude + const exclude = opts.exclude ?? defaultExcludeRE const filter = createFilter(include, exclude) const jsxImportSource = opts.jsxImportSource ?? 'react' @@ -222,17 +223,10 @@ export default function viteReact(opts: Options = {}): Plugin[] { filter: { id: { include: makeIdFiltersToMatchWithQuery(include), - exclude: [ - ...(exclude - ? makeIdFiltersToMatchWithQuery(ensureArray(exclude)) - : []), - /\/node_modules\//, - ], + exclude: makeIdFiltersToMatchWithQuery(exclude), }, }, async handler(code, id, options) { - if (id.includes('/node_modules/')) return - const [filepath] = id.split('?') if (!filter(filepath)) return @@ -472,7 +466,3 @@ function getReactCompilerRuntimeModule( } return moduleName } - -function ensureArray(value: T | T[]): T[] { - return Array.isArray(value) ? value : [value] -} diff --git a/playground/include-node-modules/App.jsx b/playground/include-node-modules/App.jsx new file mode 100644 index 000000000..fb04124a6 --- /dev/null +++ b/playground/include-node-modules/App.jsx @@ -0,0 +1,17 @@ +import test from '@vitejs/test-package' + +function App() { + return ( +
+

Node Modules Include Test

+

+ This playground tests that files in node_modules are processed + correctly. +

+ +

Result: {'' + test}

+
+ ) +} + +export default App diff --git a/playground/include-node-modules/__tests__/node-modules-include.spec.ts b/playground/include-node-modules/__tests__/node-modules-include.spec.ts new file mode 100644 index 000000000..0debee0a4 --- /dev/null +++ b/playground/include-node-modules/__tests__/node-modules-include.spec.ts @@ -0,0 +1,10 @@ +import { expect, test } from 'vitest' +import { page } from '~utils' + +test('should render', async () => { + expect(await page.textContent('h1')).toMatch('Node Modules Include Test') +}) + +test('babel should run on files in node_modules', async () => { + expect(await page.textContent('.result')).toMatch('Result: true') +}) diff --git a/playground/include-node-modules/index.html b/playground/include-node-modules/index.html new file mode 100644 index 000000000..7417c442d --- /dev/null +++ b/playground/include-node-modules/index.html @@ -0,0 +1,10 @@ +
+ diff --git a/playground/include-node-modules/package.json b/playground/include-node-modules/package.json new file mode 100644 index 000000000..7eb9673bb --- /dev/null +++ b/playground/include-node-modules/package.json @@ -0,0 +1,21 @@ +{ + "name": "@vitejs/test-node-modules-include", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@types/babel__core": "^7.20.5", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "workspace:*", + "@vitejs/test-package": "file:./test-package" + } +} diff --git a/playground/include-node-modules/test-package/index.js b/playground/include-node-modules/test-package/index.js new file mode 100644 index 000000000..9e12dfa54 --- /dev/null +++ b/playground/include-node-modules/test-package/index.js @@ -0,0 +1 @@ +export default TEST_BABEL diff --git a/playground/include-node-modules/test-package/package.json b/playground/include-node-modules/test-package/package.json new file mode 100644 index 000000000..99651ea20 --- /dev/null +++ b/playground/include-node-modules/test-package/package.json @@ -0,0 +1,6 @@ +{ + "name": "test-package", + "version": "1.0.0", + "main": "index.js", + "type": "module" +} diff --git a/playground/include-node-modules/vite.config.ts b/playground/include-node-modules/vite.config.ts new file mode 100644 index 000000000..28c3dd518 --- /dev/null +++ b/playground/include-node-modules/vite.config.ts @@ -0,0 +1,28 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import type { PluginItem as BabelPlugin } from '@babel/core' + +export default defineConfig({ + plugins: [ + react({ + exclude: [/\/node_modules\/(?!(\.pnpm\/)?test-package)/], + babel: { + plugins: [ + ({ types: t }): BabelPlugin => ({ + name: 'test-replace-test-babel', + visitor: { + Identifier(path) { + if (path.node.name === 'TEST_BABEL') { + path.replaceWith(t.booleanLiteral(true)) + } + }, + }, + }), + ], + }, + }), + ], + optimizeDeps: { + exclude: ['@vitejs/test-package'], + }, +}) diff --git a/playground/react/package.json b/playground/react/package.json index abb756b71..b3d6b13cb 100644 --- a/playground/react/package.json +++ b/playground/react/package.json @@ -9,7 +9,7 @@ "preview": "vite preview" }, "dependencies": { - "jsx-entry": "file:./jsx-entry", + "jsx-entry": "link:./jsx-entry", "react": "^19.1.0", "react-dom": "^19.1.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8bd988406..21e392157 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -853,6 +853,33 @@ importers: specifier: workspace:* version: link:../../packages/plugin-react + playground/include-node-modules: + dependencies: + react: + specifier: ^19.1.0 + version: 19.1.0 + react-dom: + specifier: ^19.1.0 + version: 19.1.0(react@19.1.0) + devDependencies: + '@types/babel__core': + specifier: ^7.20.5 + version: 7.20.5 + '@types/react': + specifier: ^19.1.8 + version: 19.1.8 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.1.6(@types/react@19.1.8) + '@vitejs/plugin-react': + specifier: workspace:* + version: link:../../packages/plugin-react + '@vitejs/test-package': + specifier: file:./test-package + version: test-package@file:playground/include-node-modules/test-package + + playground/include-node-modules/test-package: {} + playground/mdx: dependencies: react: @@ -878,7 +905,7 @@ importers: playground/react: dependencies: jsx-entry: - specifier: file:./jsx-entry + specifier: link:./jsx-entry version: link:jsx-entry react: specifier: ^19.1.0 @@ -1057,18 +1084,10 @@ packages: resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} @@ -1081,16 +1100,6 @@ packages: resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.2': - resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/parser@7.27.0': - resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.27.7': resolution: {integrity: sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==} engines: {node: '>=6.0.0'} @@ -1189,14 +1198,6 @@ packages: resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.0': - resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.27.0': - resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} - engines: {node: '>=6.9.0'} - '@babel/types@7.27.7': resolution: {integrity: sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==} engines: {node: '>=6.9.0'} @@ -5349,6 +5350,9 @@ packages: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} + test-package@file:playground/include-node-modules/test-package: + resolution: {directory: playground/include-node-modules/test-package, type: directory} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -5894,7 +5898,7 @@ snapshots: '@babel/helper-module-imports@7.25.9': dependencies: '@babel/traverse': 7.27.7 - '@babel/types': 7.27.7 + '@babel/types': 7.28.0 transitivePeerDependencies: - supports-color @@ -5936,12 +5940,8 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.25.9': {} - '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-identifier@7.27.1': {} '@babel/helper-validator-option@7.27.1': {} @@ -5951,14 +5951,6 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.28.0 - '@babel/parser@7.26.2': - dependencies: - '@babel/types': 7.27.0 - - '@babel/parser@7.27.0': - dependencies: - '@babel/types': 7.28.0 - '@babel/parser@7.27.7': dependencies: '@babel/types': 7.27.7 @@ -6025,7 +6017,7 @@ snapshots: '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) - '@babel/types': 7.27.7 + '@babel/types': 7.28.0 transitivePeerDependencies: - supports-color @@ -6085,16 +6077,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/types@7.26.0': - dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - - '@babel/types@7.27.0': - dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/types@7.27.7': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -7363,24 +7345,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.26.2 - '@babel/types': 7.26.0 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.28.0 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.28.0 '@types/chai@5.2.2': dependencies: @@ -10194,6 +10176,8 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 + test-package@file:playground/include-node-modules/test-package: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c26c75e23..69069a427 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,3 +10,5 @@ catalogs: overrides: '@types/estree': ^1.0.8 + +dedupeInjectedDeps: false