@vitejs/plugin-react-swc 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -125,6 +125,38 @@ If set, disables the recommendation to use `@vitejs/plugin-react-oxc` (which is
125
125
  react({ disableOxcRecommendation: true })
126
126
  ```
127
127
 
128
+ ## `@vitejs/plugin-react-swc/preamble`
129
+
130
+ The package provides `@vitejs/plugin-react-swc/preamble` to initialize HMR runtime from client entrypoint for SSR applications which don't use [`transformIndexHtml` API](https://vite.dev/guide/api-javascript.html#vitedevserver). For example:
131
+
132
+ ```js
133
+ // [entry.client.js]
134
+ import '@vitejs/plugin-react-swc/preamble'
135
+ ```
136
+
137
+ Alternatively, you can manually call `transformIndexHtml` during SSR, which sets up equivalent initialization code. Here's an example for an Express server:
138
+
139
+ ```js
140
+ app.get('/', async (req, res, next) => {
141
+ try {
142
+ let html = fs.readFileSync(path.resolve(root, 'index.html'), 'utf-8')
143
+
144
+ // Transform HTML using Vite plugins.
145
+ html = await viteServer.transformIndexHtml(req.url, html)
146
+
147
+ res.send(html)
148
+ } catch (e) {
149
+ return next(e)
150
+ }
151
+ })
152
+ ```
153
+
154
+ Otherwise, you'll get the following error:
155
+
156
+ ```
157
+ Uncaught Error: @vitejs/plugin-react-swc can't detect preamble. Something is wrong.
158
+ ```
159
+
128
160
  ## Consistent components exports
129
161
 
130
162
  For React refresh to work correctly, your file should only export React components. The best explanation I've read is the one from the [Gatsby docs](https://www.gatsbyjs.com/docs/reference/local-development/fast-refresh/#how-it-works).
package/index.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { readFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
3
+ import { join } from "node:path";
5
4
  import { transform } from "@swc/core";
6
- import * as vite from "vite";
7
5
  import { exactRegex } from "@rolldown/pluginutils";
6
+ import * as vite from "vite";
8
7
 
9
8
  //#region ../common/refresh-utils.ts
10
9
  const runtimePublicPath = "/@react-refresh";
@@ -15,22 +14,15 @@ injectIntoGlobalHook(window);
15
14
  window.$RefreshReg$ = () => {};
16
15
  window.$RefreshSig$ = () => (type) => type;`;
17
16
  const getPreambleCode = (base) => preambleCode.replace("__BASE__", base);
18
- const avoidSourceMapOption = Symbol();
19
- function addRefreshWrapper(code, map, pluginName, id, reactRefreshHost = "") {
17
+ function addRefreshWrapper(code, pluginName, id, reactRefreshHost = "") {
20
18
  const hasRefresh = refreshContentRE.test(code);
21
19
  const onlyReactComp = !hasRefresh && reactCompRE.test(code);
22
- const normalizedMap = map === avoidSourceMapOption ? null : map;
23
- if (!hasRefresh && !onlyReactComp) return {
24
- code,
25
- map: normalizedMap
26
- };
27
- const avoidSourceMap = map === avoidSourceMapOption;
28
- const newMap = typeof normalizedMap === "string" ? JSON.parse(normalizedMap) : normalizedMap;
20
+ if (!hasRefresh && !onlyReactComp) return void 0;
29
21
  let newCode = code;
30
- if (hasRefresh) {
31
- const refreshHead = removeLineBreaksIfNeeded(`let prevRefreshReg;
32
- let prevRefreshSig;
22
+ newCode += `
33
23
 
24
+ import * as RefreshRuntime from "${reactRefreshHost}${runtimePublicPath}";
25
+ const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
34
26
  if (import.meta.hot && !inWebWorker) {
35
27
  if (!window.$RefreshReg$) {
36
28
  throw new Error(
@@ -38,29 +30,6 @@ if (import.meta.hot && !inWebWorker) {
38
30
  );
39
31
  }
40
32
 
41
- prevRefreshReg = window.$RefreshReg$;
42
- prevRefreshSig = window.$RefreshSig$;
43
- window.$RefreshReg$ = RefreshRuntime.getRefreshReg(${JSON.stringify(id)});
44
- window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
45
- }
46
-
47
- `, avoidSourceMap);
48
- newCode = `${refreshHead}${newCode}
49
-
50
- if (import.meta.hot && !inWebWorker) {
51
- window.$RefreshReg$ = prevRefreshReg;
52
- window.$RefreshSig$ = prevRefreshSig;
53
- }
54
- `;
55
- if (newMap) newMap.mappings = ";".repeat(16) + newMap.mappings;
56
- }
57
- const sharedHead = removeLineBreaksIfNeeded(`import * as RefreshRuntime from "${reactRefreshHost}${runtimePublicPath}";
58
- const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
59
-
60
- `, avoidSourceMap);
61
- newCode = `${sharedHead}${newCode}
62
-
63
- if (import.meta.hot && !inWebWorker) {
64
33
  RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
65
34
  RefreshRuntime.registerExportsForReactRefresh(${JSON.stringify(id)}, currentExports);
66
35
  import.meta.hot.accept((nextExports) => {
@@ -71,15 +40,32 @@ if (import.meta.hot && !inWebWorker) {
71
40
  });
72
41
  }
73
42
  `;
74
- if (newMap) newMap.mappings = ";;;" + newMap.mappings;
43
+ if (hasRefresh) newCode += `function $RefreshReg$(type, id) { return RefreshRuntime.register(type, ${JSON.stringify(id)} + ' ' + id) }
44
+ function $RefreshSig$() { return RefreshRuntime.createSignatureFunctionForTransform(); }
45
+ `;
46
+ return newCode;
47
+ }
48
+ function virtualPreamblePlugin({ name, isEnabled }) {
75
49
  return {
76
- code: newCode,
77
- map: newMap
50
+ name: "vite:react-virtual-preamble",
51
+ resolveId: {
52
+ order: "pre",
53
+ filter: { id: exactRegex(name) },
54
+ handler(source) {
55
+ if (source === name) return "\0" + source;
56
+ }
57
+ },
58
+ load: {
59
+ filter: { id: exactRegex("\0" + name) },
60
+ handler(id) {
61
+ if (id === "\0" + name) {
62
+ if (isEnabled()) return preambleCode.replace("__BASE__", "/");
63
+ return "";
64
+ }
65
+ }
66
+ }
78
67
  };
79
68
  }
80
- function removeLineBreaksIfNeeded(code, enabled) {
81
- return enabled ? code.replace(/\n/g, "") : code;
82
- }
83
69
 
84
70
  //#endregion
85
71
  //#region ../common/warning.ts
@@ -92,10 +78,10 @@ const silenceUseClientWarning = (userConfig) => ({ rollupOptions: { onwarn(warni
92
78
 
93
79
  //#endregion
94
80
  //#region src/index.ts
95
- const _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
96
- const resolve = createRequire(typeof __filename !== "undefined" ? __filename : import.meta.url).resolve;
81
+ const resolve = createRequire(import.meta.url).resolve;
97
82
  const react = (_options) => {
98
83
  let hmrDisabled = false;
84
+ let viteCacheRoot;
99
85
  const options = {
100
86
  jsxImportSource: _options?.jsxImportSource ?? "react",
101
87
  tsDecorators: _options?.tsDecorators,
@@ -117,7 +103,7 @@ const react = (_options) => {
117
103
  },
118
104
  load: {
119
105
  filter: { id: exactRegex(runtimePublicPath) },
120
- handler: (id) => id === runtimePublicPath ? readFileSync(join(_dirname, "refresh-runtime.js"), "utf-8").replace(/__README_URL__/g, "https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react-swc") : void 0
106
+ handler: (id) => id === runtimePublicPath ? readFileSync(join(import.meta.dirname, "refresh-runtime.js"), "utf-8").replace(/__README_URL__/g, "https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react-swc") : void 0
121
107
  }
122
108
  },
123
109
  {
@@ -132,6 +118,7 @@ const react = (_options) => {
132
118
  }
133
119
  }),
134
120
  configResolved(config) {
121
+ viteCacheRoot = config.cacheDir;
135
122
  if (config.server.hmr === false) hmrDisabled = true;
136
123
  const mdxIndex = config.plugins.findIndex((p) => p.name === "@mdx-js/rollup");
137
124
  if (mdxIndex !== -1 && mdxIndex > config.plugins.findIndex((p) => p.name === "vite:react-swc")) throw new Error("[vite:react-swc] The MDX plugin should be placed before this plugin");
@@ -147,7 +134,7 @@ const react = (_options) => {
147
134
  async transform(code, _id, transformOptions) {
148
135
  const id = _id.split("?")[0];
149
136
  const refresh = !transformOptions?.ssr && !hmrDisabled;
150
- const result = await transformWithOptions(id, code, options.devTarget, options, {
137
+ const result = await transformWithOptions(id, code, options.devTarget, options, viteCacheRoot, {
151
138
  refresh,
152
139
  development: true,
153
140
  runtime: "automatic",
@@ -155,15 +142,21 @@ const react = (_options) => {
155
142
  });
156
143
  if (!result) return;
157
144
  if (!refresh) return result;
158
- return addRefreshWrapper(result.code, result.map, "@vitejs/plugin-react-swc", id, options.reactRefreshHost);
145
+ return {
146
+ code: addRefreshWrapper(result.code, "@vitejs/plugin-react-swc", id, options.reactRefreshHost) ?? result.code,
147
+ map: result.map
148
+ };
159
149
  }
160
150
  },
161
- options.plugins ? {
151
+ options.plugins || options.useAtYourOwnRisk_mutateSwcOptions ? {
162
152
  name: "vite:react-swc",
163
153
  apply: "build",
164
154
  enforce: "pre",
165
155
  config: (userConfig) => ({ build: silenceUseClientWarning(userConfig) }),
166
- transform: (code, _id) => transformWithOptions(_id.split("?")[0], code, "esnext", options, {
156
+ configResolved(config) {
157
+ viteCacheRoot = config.cacheDir;
158
+ },
159
+ transform: (code, _id) => transformWithOptions(_id.split("?")[0], code, "esnext", options, viteCacheRoot, {
167
160
  runtime: "automatic",
168
161
  importSource: options.jsxImportSource
169
162
  })
@@ -177,11 +170,18 @@ const react = (_options) => {
177
170
  jsxImportSource: options.jsxImportSource,
178
171
  tsconfigRaw: { compilerOptions: { useDefineForClassFields: true } }
179
172
  }
180
- })
181
- }
173
+ }),
174
+ configResolved(config) {
175
+ viteCacheRoot = config.cacheDir;
176
+ }
177
+ },
178
+ virtualPreamblePlugin({
179
+ name: "@vitejs/plugin-react-swc/preamble",
180
+ isEnabled: () => !hmrDisabled
181
+ })
182
182
  ];
183
183
  };
184
- const transformWithOptions = async (id, code, target, options, reactConfig) => {
184
+ const transformWithOptions = async (id, code, target, options, viteCacheRoot, reactConfig) => {
185
185
  const decorators = options?.tsDecorators ?? false;
186
186
  const parser = options.parserConfig ? options.parserConfig(id) : id.endsWith(".tsx") ? {
187
187
  syntax: "typescript",
@@ -209,7 +209,10 @@ const transformWithOptions = async (id, code, target, options, reactConfig) => {
209
209
  jsc: {
210
210
  target,
211
211
  parser,
212
- experimental: { plugins: options.plugins },
212
+ experimental: {
213
+ plugins: options.plugins,
214
+ cacheRoot: join(viteCacheRoot ?? "node_modules/.vite", ".swc")
215
+ },
213
216
  transform: {
214
217
  useDefineForClassFields: true,
215
218
  react: reactConfig
@@ -232,10 +235,11 @@ const transformWithOptions = async (id, code, target, options, reactConfig) => {
232
235
  }
233
236
  return result;
234
237
  };
238
+ var src_default = react;
235
239
  function pluginForCjs(options) {
236
240
  return react.call(this, options);
237
241
  }
238
242
  Object.assign(pluginForCjs, { default: pluginForCjs });
239
243
 
240
244
  //#endregion
241
- export { react as default, pluginForCjs as "module.exports" };
245
+ export { src_default as default, pluginForCjs as "module.exports" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitejs/plugin-react-swc",
3
- "version": "4.0.1",
3
+ "version": "4.2.0",
4
4
  "license": "MIT",
5
5
  "author": "Arnaud Barré (https://github.com/ArnaudBarre)",
6
6
  "description": "Speed up your Vite dev server with SWC",
@@ -26,11 +26,14 @@
26
26
  },
27
27
  "homepage": "https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react-swc#readme",
28
28
  "dependencies": {
29
- "@rolldown/pluginutils": "1.0.0-beta.32",
30
- "@swc/core": "^1.13.2"
29
+ "@rolldown/pluginutils": "1.0.0-beta.43",
30
+ "@swc/core": "^1.13.5"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "vite": "^4 || ^5 || ^6 || ^7"
34
34
  },
35
- "exports": "./index.js"
35
+ "exports": {
36
+ ".": "./index.js",
37
+ "./preamble": "./types/preamble.d.ts"
38
+ }
36
39
  }
@@ -243,7 +243,7 @@ function performReactRefresh() {
243
243
  }
244
244
  }
245
245
 
246
- function register(type, id) {
246
+ export function register(type, id) {
247
247
  if (type === null) {
248
248
  return
249
249
  }
@@ -564,10 +564,6 @@ function isPlainObject(obj) {
564
564
  * Plugin utils
565
565
  */
566
566
 
567
- export function getRefreshReg(filename) {
568
- return (type, id) => register(type, filename + ' ' + id)
569
- }
570
-
571
567
  // Taken from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/lib/runtime/RefreshUtils.js#L141
572
568
  // This allows to resister components not detected by SWC like styled component
573
569
  export function registerExportsForReactRefresh(filename, moduleExports) {
@@ -653,10 +649,7 @@ export function validateRefreshBoundaryAndEnqueueUpdate(
653
649
 
654
650
  function predicateOnExport(ignoredExports, moduleExports, predicate) {
655
651
  for (const key in moduleExports) {
656
- if (key === '__esModule') continue
657
652
  if (ignoredExports.includes(key)) continue
658
- const desc = Object.getOwnPropertyDescriptor(moduleExports, key)
659
- if (desc && desc.get) return key
660
653
  if (!predicate(key, moduleExports[key])) return key
661
654
  }
662
655
  return true
@@ -0,0 +1 @@
1
+ export {}