@vitejs/plugin-react 3.0.1 → 3.1.0-beta.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
@@ -105,3 +105,11 @@ Otherwise, you'll probably get this error:
105
105
  ```
106
106
  Uncaught Error: @vitejs/plugin-react can't detect preamble. Something is wrong.
107
107
  ```
108
+
109
+ ## Consistent components exports
110
+
111
+ For React refresh to work correctly, your file should only export React components. You can find a good explanation in the [Gatsby docs](https://www.gatsbyjs.com/docs/reference/local-development/fast-refresh/#how-it-works).
112
+
113
+ If an incompatible change in exports is found, the module will be invalidated and HMR will propagate. To make it easier to export simple constants alongside your component, the module is only invalidated when their value changes.
114
+
115
+ You can catch mistakes and get more detailed warning with this [eslint rule](https://github.com/ArnaudBarre/eslint-plugin-react-refresh).
package/dist/index.cjs CHANGED
@@ -32,14 +32,7 @@ const runtimeFilePath = path.join(
32
32
  const runtimeCode = `
33
33
  const exports = {}
34
34
  ${fs.readFileSync(runtimeFilePath, "utf-8")}
35
- function debounce(fn, delay) {
36
- let handle
37
- return () => {
38
- clearTimeout(handle)
39
- handle = setTimeout(fn, delay)
40
- }
41
- }
42
- exports.performReactRefresh = debounce(exports.performReactRefresh, 16)
35
+ ${fs.readFileSync(_require.resolve("./refreshUtils.js"), "utf-8")}
43
36
  export default exports
44
37
  `;
45
38
  const preambleCode = `
@@ -70,60 +63,26 @@ if (import.meta.hot) {
70
63
  };
71
64
  window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
72
65
  }`.replace(/\n+/g, "");
73
- const timeout = `
74
- if (!window.__vite_plugin_react_timeout) {
75
- window.__vite_plugin_react_timeout = setTimeout(() => {
76
- window.__vite_plugin_react_timeout = 0;
77
- RefreshRuntime.performReactRefresh();
78
- }, 30);
79
- }
80
- `;
81
- const checkAndAccept = `
82
- function isReactRefreshBoundary(mod) {
83
- if (mod == null || typeof mod !== 'object') {
84
- return false;
85
- }
86
- let hasExports = false;
87
- let areAllExportsComponents = true;
88
- for (const exportName in mod) {
89
- hasExports = true;
90
- if (exportName === '__esModule') {
91
- continue;
92
- }
93
- const desc = Object.getOwnPropertyDescriptor(mod, exportName);
94
- if (desc && desc.get) {
95
- // Don't invoke getters as they may have side effects.
96
- return false;
97
- }
98
- const exportValue = mod[exportName];
99
- if (!RefreshRuntime.isLikelyComponentType(exportValue)) {
100
- areAllExportsComponents = false;
101
- }
102
- }
103
- return hasExports && areAllExportsComponents;
104
- }
105
-
106
- import.meta.hot.accept(mod => {
107
- if (!mod) return;
108
- if (isReactRefreshBoundary(mod)) {
109
- ${timeout}
110
- } else {
111
- import.meta.hot.invalidate();
112
- }
113
- });
114
- `;
115
66
  const footer = `
116
67
  if (import.meta.hot) {
117
68
  window.$RefreshReg$ = prevRefreshReg;
118
69
  window.$RefreshSig$ = prevRefreshSig;
119
70
 
120
- ${checkAndAccept}
71
+ import(/* @vite-ignore */ import.meta.url).then((currentExports) => {
72
+ RefreshRuntime.registerExportsForReactRefresh(__SOURCE__, currentExports);
73
+ import.meta.hot.accept((nextExports) => {
74
+ if (!nextExports) return;
75
+ const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports);
76
+ if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
77
+ });
78
+ });
121
79
  }`;
122
80
  function addRefreshWrapper(code, id) {
123
- return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer;
81
+ return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer.replace("__SOURCE__", JSON.stringify(id));
124
82
  }
125
83
 
126
84
  const prependReactImportCode = "import React from 'react'; ";
85
+ const refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/;
127
86
  function viteReact(opts = {}) {
128
87
  let devBase = "/";
129
88
  let filter = vite.createFilter(opts.include, opts.exclude);
@@ -270,6 +229,9 @@ function viteReact(opts = {}) {
270
229
  const parserPlugins = [
271
230
  ...babelOptions.parserOpts.plugins,
272
231
  "importMeta",
232
+ // This plugin is applied before esbuild transforms the code,
233
+ // so we need to enable some stage 3 syntax that is supported in
234
+ // TypeScript and some environments already.
273
235
  "topLevelAwait",
274
236
  "classProperties",
275
237
  "classPrivateProperties",
@@ -298,11 +260,12 @@ function viteReact(opts = {}) {
298
260
  },
299
261
  plugins,
300
262
  sourceMaps: true,
263
+ // Vite handles sourcemap flattening
301
264
  inputSourceMap: inputMap ?? false
302
265
  });
303
266
  if (result) {
304
267
  let code2 = result.code;
305
- if (useFastRefresh && /\$RefreshReg\$\(/.test(code2)) {
268
+ if (useFastRefresh && refreshContentRE.test(code2)) {
306
269
  code2 = addRefreshWrapper(code2, id);
307
270
  }
308
271
  return {
@@ -352,6 +315,9 @@ function viteReact(opts = {}) {
352
315
  config() {
353
316
  return {
354
317
  optimizeDeps: {
318
+ // We can't add `react-dom` because the dependency is `react-dom/client`
319
+ // for React 18 while it's `react-dom` for React 17. We'd need to detect
320
+ // what React version the user has installed.
355
321
  include: [reactJsxRuntimeId, reactJsxDevRuntimeId, "react"]
356
322
  }
357
323
  };
package/dist/index.mjs CHANGED
@@ -17,14 +17,7 @@ const runtimeFilePath = path.join(
17
17
  const runtimeCode = `
18
18
  const exports = {}
19
19
  ${fs.readFileSync(runtimeFilePath, "utf-8")}
20
- function debounce(fn, delay) {
21
- let handle
22
- return () => {
23
- clearTimeout(handle)
24
- handle = setTimeout(fn, delay)
25
- }
26
- }
27
- exports.performReactRefresh = debounce(exports.performReactRefresh, 16)
20
+ ${fs.readFileSync(_require.resolve("./refreshUtils.js"), "utf-8")}
28
21
  export default exports
29
22
  `;
30
23
  const preambleCode = `
@@ -55,60 +48,26 @@ if (import.meta.hot) {
55
48
  };
56
49
  window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
57
50
  }`.replace(/\n+/g, "");
58
- const timeout = `
59
- if (!window.__vite_plugin_react_timeout) {
60
- window.__vite_plugin_react_timeout = setTimeout(() => {
61
- window.__vite_plugin_react_timeout = 0;
62
- RefreshRuntime.performReactRefresh();
63
- }, 30);
64
- }
65
- `;
66
- const checkAndAccept = `
67
- function isReactRefreshBoundary(mod) {
68
- if (mod == null || typeof mod !== 'object') {
69
- return false;
70
- }
71
- let hasExports = false;
72
- let areAllExportsComponents = true;
73
- for (const exportName in mod) {
74
- hasExports = true;
75
- if (exportName === '__esModule') {
76
- continue;
77
- }
78
- const desc = Object.getOwnPropertyDescriptor(mod, exportName);
79
- if (desc && desc.get) {
80
- // Don't invoke getters as they may have side effects.
81
- return false;
82
- }
83
- const exportValue = mod[exportName];
84
- if (!RefreshRuntime.isLikelyComponentType(exportValue)) {
85
- areAllExportsComponents = false;
86
- }
87
- }
88
- return hasExports && areAllExportsComponents;
89
- }
90
-
91
- import.meta.hot.accept(mod => {
92
- if (!mod) return;
93
- if (isReactRefreshBoundary(mod)) {
94
- ${timeout}
95
- } else {
96
- import.meta.hot.invalidate();
97
- }
98
- });
99
- `;
100
51
  const footer = `
101
52
  if (import.meta.hot) {
102
53
  window.$RefreshReg$ = prevRefreshReg;
103
54
  window.$RefreshSig$ = prevRefreshSig;
104
55
 
105
- ${checkAndAccept}
56
+ import(/* @vite-ignore */ import.meta.url).then((currentExports) => {
57
+ RefreshRuntime.registerExportsForReactRefresh(__SOURCE__, currentExports);
58
+ import.meta.hot.accept((nextExports) => {
59
+ if (!nextExports) return;
60
+ const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports);
61
+ if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
62
+ });
63
+ });
106
64
  }`;
107
65
  function addRefreshWrapper(code, id) {
108
- return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer;
66
+ return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer.replace("__SOURCE__", JSON.stringify(id));
109
67
  }
110
68
 
111
69
  const prependReactImportCode = "import React from 'react'; ";
70
+ const refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/;
112
71
  function viteReact(opts = {}) {
113
72
  let devBase = "/";
114
73
  let filter = createFilter(opts.include, opts.exclude);
@@ -255,6 +214,9 @@ function viteReact(opts = {}) {
255
214
  const parserPlugins = [
256
215
  ...babelOptions.parserOpts.plugins,
257
216
  "importMeta",
217
+ // This plugin is applied before esbuild transforms the code,
218
+ // so we need to enable some stage 3 syntax that is supported in
219
+ // TypeScript and some environments already.
258
220
  "topLevelAwait",
259
221
  "classProperties",
260
222
  "classPrivateProperties",
@@ -283,11 +245,12 @@ function viteReact(opts = {}) {
283
245
  },
284
246
  plugins,
285
247
  sourceMaps: true,
248
+ // Vite handles sourcemap flattening
286
249
  inputSourceMap: inputMap ?? false
287
250
  });
288
251
  if (result) {
289
252
  let code2 = result.code;
290
- if (useFastRefresh && /\$RefreshReg\$\(/.test(code2)) {
253
+ if (useFastRefresh && refreshContentRE.test(code2)) {
291
254
  code2 = addRefreshWrapper(code2, id);
292
255
  }
293
256
  return {
@@ -337,6 +300,9 @@ function viteReact(opts = {}) {
337
300
  config() {
338
301
  return {
339
302
  optimizeDeps: {
303
+ // We can't add `react-dom` because the dependency is `react-dom/client`
304
+ // for React 18 while it's `react-dom` for React 17. We'd need to detect
305
+ // what React version the user has installed.
340
306
  include: [reactJsxRuntimeId, reactJsxDevRuntimeId, "react"]
341
307
  }
342
308
  };
@@ -0,0 +1,57 @@
1
+ function debounce(fn, delay) {
2
+ let handle
3
+ return () => {
4
+ clearTimeout(handle)
5
+ handle = setTimeout(fn, delay)
6
+ }
7
+ }
8
+
9
+ const enqueueUpdate = debounce(exports.performReactRefresh, 16)
10
+
11
+ // Taken from https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/lib/runtime/RefreshUtils.js#L141
12
+ // This allows to resister components not detected by SWC like styled component
13
+ function registerExportsForReactRefresh(filename, moduleExports) {
14
+ for (const key in moduleExports) {
15
+ if (key === '__esModule') continue
16
+ const exportValue = moduleExports[key]
17
+ if (exports.isLikelyComponentType(exportValue)) {
18
+ exports.register(exportValue, filename + ' ' + key)
19
+ }
20
+ }
21
+ }
22
+
23
+ function validateRefreshBoundaryAndEnqueueUpdate(prevExports, nextExports) {
24
+ if (!predicateOnExport(prevExports, (key) => !!nextExports[key])) {
25
+ return 'Could not Fast Refresh (export removed)'
26
+ }
27
+
28
+ let hasExports = false
29
+ const allExportsAreComponentsOrUnchanged = predicateOnExport(
30
+ nextExports,
31
+ (key, value) => {
32
+ hasExports = true
33
+ if (exports.isLikelyComponentType(value)) return true
34
+ if (!prevExports[key]) return false
35
+ return prevExports[key] === nextExports[key]
36
+ },
37
+ )
38
+ if (hasExports && allExportsAreComponentsOrUnchanged) {
39
+ enqueueUpdate()
40
+ } else {
41
+ return 'Could not Fast Refresh. Learn more at https://github.com/vitejs/vite-plugin-react#consistent-components-exports'
42
+ }
43
+ }
44
+
45
+ function predicateOnExport(moduleExports, predicate) {
46
+ for (const key in moduleExports) {
47
+ if (key === '__esModule') continue
48
+ const desc = Object.getOwnPropertyDescriptor(moduleExports, key)
49
+ if (desc && desc.get) return false
50
+ if (!predicate(key, moduleExports[key])) return false
51
+ }
52
+ return true
53
+ }
54
+
55
+ exports.registerExportsForReactRefresh = registerExportsForReactRefresh
56
+ exports.validateRefreshBoundaryAndEnqueueUpdate =
57
+ validateRefreshBoundaryAndEnqueueUpdate
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@vitejs/plugin-react",
3
- "version": "3.0.1",
3
+ "version": "3.1.0-beta.0",
4
4
  "license": "MIT",
5
5
  "author": "Evan You",
6
6
  "contributors": [
7
- "Alec Larson"
7
+ "Alec Larson",
8
+ "Arnaud Barré"
8
9
  ],
9
10
  "files": [
10
11
  "dist"
@@ -21,7 +22,7 @@
21
22
  },
22
23
  "scripts": {
23
24
  "dev": "unbuild --stub",
24
- "build": "unbuild && pnpm run patch-cjs",
25
+ "build": "unbuild && pnpm run patch-cjs && tsx scripts/copyRefreshUtils.ts",
25
26
  "patch-cjs": "tsx ../../scripts/patchCJS.ts",
26
27
  "prepublishOnly": "npm run build"
27
28
  },
@@ -38,13 +39,13 @@
38
39
  },
39
40
  "homepage": "https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#readme",
40
41
  "dependencies": {
41
- "@babel/core": "^7.20.7",
42
+ "@babel/core": "^7.20.12",
42
43
  "@babel/plugin-transform-react-jsx-self": "^7.18.6",
43
44
  "@babel/plugin-transform-react-jsx-source": "^7.19.6",
44
45
  "magic-string": "^0.27.0",
45
46
  "react-refresh": "^0.14.0"
46
47
  },
47
48
  "peerDependencies": {
48
- "vite": "^4.0.0"
49
+ "vite": "^4.1.0-beta.0"
49
50
  }
50
51
  }