injectRefreshEntry.js
3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const querystring = require('querystring');
const createError = require('./createError');
/** @typedef {string | string[] | import('webpack').Entry} StaticEntry */
/** @typedef {StaticEntry | import('webpack').EntryFunc} WebpackEntry */
/**
* Checks if a Webpack entry string is related to socket integrations.
* @param {string} entry A Webpack entry string.
* @returns {boolean} Whether the entry is related to socket integrations.
*/
function isSocketEntry(entry) {
/**
* Webpack entries related to socket integrations.
* They have to run before any code that sets up the error overlay.
* @type {string[]}
*/
const socketEntries = [
'webpack-dev-server/client',
'webpack-hot-middleware/client',
'webpack-plugin-serve/client',
'react-dev-utils/webpackHotDevClient',
];
return socketEntries.some((socketEntry) => entry.includes(socketEntry));
}
/**
* Injects an entry to the bundle for react-refresh.
* @param {WebpackEntry} [originalEntry] A Webpack entry object.
* @param {import('../types').NormalizedPluginOptions} options Configuration options for this plugin.
* @returns {WebpackEntry} An injected entry object.
*/
function injectRefreshEntry(originalEntry, options) {
/** @type {Record<string, *>} */
let resourceQuery = {};
if (options.overlay) {
options.overlay.sockHost &&
(resourceQuery.sockHost = options.overlay.sockHost);
options.overlay.sockPath &&
(resourceQuery.sockPath = options.overlay.sockPath);
options.overlay.sockPort &&
(resourceQuery.sockPort = options.overlay.sockPort);
}
// We don't need to URI encode the resourceQuery as it will be parsed by Webpack
const queryString = querystring.stringify(
resourceQuery,
undefined,
undefined,
{
/**
* @param {string} string
* @returns {string}
*/
encodeURIComponent(string) {
return string;
},
},
);
const prependEntries = [
// React-refresh runtime
require.resolve('../../client/ReactRefreshEntry'),
];
const overlayEntries = [
// Legacy WDS SockJS integration
options.overlay &&
options.overlay.useLegacyWDSSockets &&
require.resolve('../../client/LegacyWDSSocketEntry'),
// Error overlay runtime
options.overlay &&
options.overlay.entry &&
options.overlay.entry + (queryString && `?${queryString}`),
].filter(Boolean);
// Single string entry point
if (typeof originalEntry === 'string') {
if (isSocketEntry(originalEntry)) {
return [...prependEntries, originalEntry, ...overlayEntries];
}
return [...prependEntries, ...overlayEntries, originalEntry];
}
// Single array entry point
if (Array.isArray(originalEntry)) {
const socketEntryIndex = originalEntry.findIndex(isSocketEntry);
let socketAndPrecedingEntries = [];
if (socketEntryIndex !== -1) {
socketAndPrecedingEntries = originalEntry.splice(0, socketEntryIndex + 1);
}
return [
...prependEntries,
...socketAndPrecedingEntries,
...overlayEntries,
...originalEntry,
];
}
// Multiple entry points
if (typeof originalEntry === 'object') {
return Object.entries(originalEntry).reduce(
(acc, [curKey, curEntry]) => ({
...acc,
[curKey]:
typeof curEntry === 'object' && curEntry.import
? {
...curEntry,
import: injectRefreshEntry(curEntry.import, options),
}
: injectRefreshEntry(curEntry, options),
}),
{},
);
}
// Dynamic entry points
if (typeof originalEntry === 'function') {
return (...args) =>
Promise.resolve(originalEntry(...args)).then((resolvedEntry) =>
injectRefreshEntry(resolvedEntry, options),
);
}
throw createError('Failed to parse the Webpack `entry` object!');
}
module.exports = injectRefreshEntry;