@vitejs/plugin-react 3.0.0 → 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,59 +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 (isReactRefreshBoundary(mod)) {
108
- ${timeout}
109
- } else {
110
- import.meta.hot.invalidate();
111
- }
112
- });
113
- `;
114
66
  const footer = `
115
67
  if (import.meta.hot) {
116
68
  window.$RefreshReg$ = prevRefreshReg;
117
69
  window.$RefreshSig$ = prevRefreshSig;
118
70
 
119
- ${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
+ });
120
79
  }`;
121
80
  function addRefreshWrapper(code, id) {
122
- return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer;
81
+ return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer.replace("__SOURCE__", JSON.stringify(id));
123
82
  }
124
83
 
125
84
  const prependReactImportCode = "import React from 'react'; ";
85
+ const refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/;
126
86
  function viteReact(opts = {}) {
127
87
  let devBase = "/";
128
88
  let filter = vite.createFilter(opts.include, opts.exclude);
@@ -269,6 +229,9 @@ function viteReact(opts = {}) {
269
229
  const parserPlugins = [
270
230
  ...babelOptions.parserOpts.plugins,
271
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.
272
235
  "topLevelAwait",
273
236
  "classProperties",
274
237
  "classPrivateProperties",
@@ -297,11 +260,12 @@ function viteReact(opts = {}) {
297
260
  },
298
261
  plugins,
299
262
  sourceMaps: true,
263
+ // Vite handles sourcemap flattening
300
264
  inputSourceMap: inputMap ?? false
301
265
  });
302
266
  if (result) {
303
267
  let code2 = result.code;
304
- if (useFastRefresh && /\$RefreshReg\$\(/.test(code2)) {
268
+ if (useFastRefresh && refreshContentRE.test(code2)) {
305
269
  code2 = addRefreshWrapper(code2, id);
306
270
  }
307
271
  return {
@@ -351,6 +315,9 @@ function viteReact(opts = {}) {
351
315
  config() {
352
316
  return {
353
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.
354
321
  include: [reactJsxRuntimeId, reactJsxDevRuntimeId, "react"]
355
322
  }
356
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,59 +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 (isReactRefreshBoundary(mod)) {
93
- ${timeout}
94
- } else {
95
- import.meta.hot.invalidate();
96
- }
97
- });
98
- `;
99
51
  const footer = `
100
52
  if (import.meta.hot) {
101
53
  window.$RefreshReg$ = prevRefreshReg;
102
54
  window.$RefreshSig$ = prevRefreshSig;
103
55
 
104
- ${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
+ });
105
64
  }`;
106
65
  function addRefreshWrapper(code, id) {
107
- return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer;
66
+ return header.replace("__SOURCE__", JSON.stringify(id)) + code + footer.replace("__SOURCE__", JSON.stringify(id));
108
67
  }
109
68
 
110
69
  const prependReactImportCode = "import React from 'react'; ";
70
+ const refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/;
111
71
  function viteReact(opts = {}) {
112
72
  let devBase = "/";
113
73
  let filter = createFilter(opts.include, opts.exclude);
@@ -254,6 +214,9 @@ function viteReact(opts = {}) {
254
214
  const parserPlugins = [
255
215
  ...babelOptions.parserOpts.plugins,
256
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.
257
220
  "topLevelAwait",
258
221
  "classProperties",
259
222
  "classPrivateProperties",
@@ -282,11 +245,12 @@ function viteReact(opts = {}) {
282
245
  },
283
246
  plugins,
284
247
  sourceMaps: true,
248
+ // Vite handles sourcemap flattening
285
249
  inputSourceMap: inputMap ?? false
286
250
  });
287
251
  if (result) {
288
252
  let code2 = result.code;
289
- if (useFastRefresh && /\$RefreshReg\$\(/.test(code2)) {
253
+ if (useFastRefresh && refreshContentRE.test(code2)) {
290
254
  code2 = addRefreshWrapper(code2, id);
291
255
  }
292
256
  return {
@@ -336,6 +300,9 @@ function viteReact(opts = {}) {
336
300
  config() {
337
301
  return {
338
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.
339
306
  include: [reactJsxRuntimeId, reactJsxDevRuntimeId, "react"]
340
307
  }
341
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.0",
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.5",
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
  }