Skip to content

Commit 3cce460

Browse files
committed
fix: make build pass with entry hooks
1 parent 957bbe3 commit 3cce460

File tree

2 files changed

+89
-29
lines changed

2 files changed

+89
-29
lines changed

lib/index.js

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,27 @@ const REPLACEMENTS = {
3333
},
3434
};
3535

36+
/**
37+
* Gets the socket integration to use for Webpack messages.
38+
* @param {'wds' | 'whm' | 'wps' | string} integrationType A valid socket integration type or a path to a module.
39+
* @returns {string | undefined} Path to the resolved socket integration module.
40+
*/
41+
function getSocketIntegrationEntry(integrationType) {
42+
let resolvedEntry;
43+
switch (integrationType) {
44+
case 'whm': {
45+
resolvedEntry = 'webpack-hot-middleware/client';
46+
break;
47+
}
48+
case 'wps': {
49+
resolvedEntry = 'webpack-plugin-serve/client';
50+
break;
51+
}
52+
}
53+
54+
return resolvedEntry;
55+
}
56+
3657
class ReactRefreshPlugin {
3758
/**
3859
* @param {import('./types').ReactRefreshPluginOptions} [options] Options for react-refresh-plugin.
@@ -81,36 +102,73 @@ class ReactRefreshPlugin {
81102
// Inject react-refresh context to all Webpack entry points.
82103
// This should create `EntryDependency` objects when available,
83104
// and fallback to patching the `entry` object for legacy workflows.
84-
const additionalEntries = getAdditionalEntries({
105+
const additional = getAdditionalEntries({
85106
devServer: compiler.options.devServer,
86107
options: this.options,
87108
});
88-
if (!EntryPlugin) {
89-
compiler.options.entry = injectRefreshEntry(compiler.options.entry, additionalEntries);
90-
} else {
91-
compiler.hooks.make.tapAsync(
92-
// `Number.NEGATIVE_INFINITY` ensures this will run before any other entries
93-
{ name: this.constructor.name, stage: Number.NEGATIVE_INFINITY },
94-
(compilation, callback) => {
95-
additionalEntries.prependEntries.forEach((entry) => {
96-
/** @type {import('webpack').EntryOptions} */
97-
const entryOptions = { name: undefined };
98-
const dependency = EntryPlugin.createDependency(entry, entryOptions);
99-
compilation.addEntry(compilation.context, dependency, entryOptions, (err) => {
100-
callback(err);
101-
});
102-
});
109+
if (EntryPlugin) {
110+
additional.prependEntries.forEach((entry) => {
111+
new EntryPlugin(compiler.context, entry, { name: undefined }).apply(compiler);
112+
});
103113

104-
additionalEntries.overlayEntries.forEach((entry) => {
105-
/** @type {import('webpack').EntryOptions} */
106-
const entryOptions = { dependOn: injectRefreshEntry.socketEntries, name: undefined };
107-
const dependency = EntryPlugin.createDependency(entry, entryOptions);
108-
compilation.addEntry(compilation.context, dependency, entryOptions, (err) => {
109-
callback(err);
110-
});
111-
});
114+
const socketEntryData = [];
115+
compiler.hooks.make.tap(
116+
{ name: this.constructor.name, stage: Number.POSITIVE_INFINITY },
117+
(compilation) => {
118+
const integrationEntry = getSocketIntegrationEntry(this.options.overlay.sockIntegration);
119+
120+
// Exhaustively search all entries for `integrationEntry`.
121+
// If found, inject `overlayEntries` to those entries,
122+
// and ensure the order of dependencies either here or in `seal`.
123+
// Else, inject `overlayEntries` to global entries.
124+
for (const [name, entryData] of compilation.entries.entries()) {
125+
const index = entryData.dependencies.findIndex((dep) =>
126+
dep.request.includes(integrationEntry)
127+
);
128+
if (index !== -1) {
129+
socketEntryData.push({ name, index });
130+
}
131+
}
112132
}
113133
);
134+
135+
additional.overlayEntries.forEach((entry, idx, arr) => {
136+
compiler.hooks.finishMake.tapPromise(
137+
{ name: this.constructor.name, stage: Number.MIN_SAFE_INTEGER + (arr.length - idx - 1) },
138+
(compilation) => {
139+
// Only hook into the current compiler
140+
if (compilation.compiler !== compiler) {
141+
return Promise.resolve();
142+
}
143+
144+
const injectData = socketEntryData.length ? socketEntryData : [{ name: undefined }];
145+
return Promise.all(
146+
injectData.map(({ name, index }) => {
147+
return new Promise((resolve, reject) => {
148+
const options = { name };
149+
const dep = EntryPlugin.createDependency(entry, options);
150+
compilation.addEntry(compiler.context, dep, options, (err) => {
151+
if (err) return reject(err);
152+
153+
if (name && typeof index !== 'undefined') {
154+
const entryData = compilation.entries.get(name);
155+
entryData.dependencies.splice(
156+
index + 1,
157+
0,
158+
entryData.dependencies.splice(entryData.dependencies.length - 1, 1)[0]
159+
);
160+
}
161+
162+
resolve();
163+
});
164+
});
165+
})
166+
).then(() => {});
167+
}
168+
);
169+
});
170+
} else {
171+
compiler.options.entry = injectRefreshEntry(compiler.options.entry, additional);
114172
}
115173

116174
// Inject necessary modules to bundle's global scope
@@ -149,10 +207,8 @@ class ReactRefreshPlugin {
149207
}
150208
}
151209

152-
const definePlugin = new DefinePlugin(definedModules);
153-
definePlugin.apply(compiler);
154-
const providePlugin = new ProvidePlugin(providedModules);
155-
providePlugin.apply(compiler);
210+
new DefinePlugin(definedModules).apply(compiler);
211+
new ProvidePlugin(providedModules).apply(compiler);
156212

157213
const match = ModuleFilenameHelpers.matchObject.bind(undefined, this.options);
158214
const { evaluateToString, toConstantDependency } = getParserHelpers();
@@ -272,10 +328,14 @@ class ReactRefreshPlugin {
272328
break;
273329
}
274330
case 5: {
331+
const EntryDependency = require('webpack/lib/dependencies/EntryDependency');
275332
const NormalModule = require('webpack/lib/NormalModule');
276333
const RuntimeGlobals = require('webpack/lib/RuntimeGlobals');
277334
const ReactRefreshRuntimeModule = require('./RefreshRuntimeModule');
278335

336+
// Set factory for EntryDependency which is used to initialise the module
337+
compilation.dependencyFactories.set(EntryDependency, normalModuleFactory);
338+
279339
compilation.hooks.additionalTreeRuntimeRequirements.tap(
280340
this.constructor.name,
281341
// Setup react-refresh globals with a Webpack runtime module

lib/utils/getAdditionalEntries.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function getAdditionalEntries({ devServer, options }) {
5757
// Error overlay runtime
5858
options.overlay &&
5959
options.overlay.entry &&
60-
`${options.overlay.entry}${queryString ? `?${queryString}` : ''}`,
60+
`${require.resolve(options.overlay.entry)}${queryString ? `?${queryString}` : ''}`,
6161
].filter(Boolean);
6262

6363
return { prependEntries, overlayEntries };

0 commit comments

Comments
 (0)