1
1
const { validate : validateOptions } = require ( 'schema-utils' ) ;
2
- const { DefinePlugin, ModuleFilenameHelpers, ProvidePlugin, Template } = require ( 'webpack' ) ;
2
+ const {
3
+ DefinePlugin,
4
+ EntryPlugin,
5
+ ModuleFilenameHelpers,
6
+ ProvidePlugin,
7
+ Template,
8
+ } = require ( 'webpack' ) ;
3
9
const ConstDependency = require ( 'webpack/lib/dependencies/ConstDependency' ) ;
4
10
const { refreshGlobal, webpackRequire, webpackVersion } = require ( './globals' ) ;
5
11
const {
12
+ getAdditionalEntries,
13
+ getIntegrationEntry,
6
14
getParserHelpers,
7
15
getRefreshGlobal,
8
16
getSocketIntegration,
9
17
injectRefreshEntry,
10
18
injectRefreshLoader,
11
19
normalizeOptions,
12
- getAdditionalEntries,
13
20
} = require ( './utils' ) ;
14
21
const schema = require ( './options.json' ) ;
15
22
@@ -72,11 +79,83 @@ class ReactRefreshPlugin {
72
79
const logger = compiler . getInfrastructureLogger ( this . constructor . name ) ;
73
80
let loggedHotWarning = false ;
74
81
75
- // Inject react-refresh context to all Webpack entry points
76
- compiler . options . entry = injectRefreshEntry (
77
- compiler . options . entry ,
78
- getAdditionalEntries ( { options : this . options , devServer : compiler . options . devServer } )
79
- ) ;
82
+ // Inject react-refresh context to all Webpack entry points.
83
+ // This should create `EntryDependency` objects when available,
84
+ // and fallback to patching the `entry` object for legacy workflows.
85
+ const additional = getAdditionalEntries ( {
86
+ devServer : compiler . options . devServer ,
87
+ options : this . options ,
88
+ } ) ;
89
+ if ( EntryPlugin ) {
90
+ // Prepended entries does not care about injection order,
91
+ // so we can utilise EntryPlugin for simpler logic.
92
+ additional . prependEntries . forEach ( ( entry ) => {
93
+ new EntryPlugin ( compiler . context , entry , { name : undefined } ) . apply ( compiler ) ;
94
+ } ) ;
95
+
96
+ const integrationEntry = getIntegrationEntry ( this . options . overlay . sockIntegration ) ;
97
+ const socketEntryData = [ ] ;
98
+ compiler . hooks . make . tap (
99
+ { name : this . constructor . name , stage : Number . POSITIVE_INFINITY } ,
100
+ ( compilation ) => {
101
+ // Exhaustively search all entries for `integrationEntry`.
102
+ // If found, mark those entries and the index of `integrationEntry`.
103
+ for ( const [ name , entryData ] of compilation . entries . entries ( ) ) {
104
+ const index = entryData . dependencies . findIndex ( ( dep ) =>
105
+ dep . request . includes ( integrationEntry )
106
+ ) ;
107
+ if ( index !== - 1 ) {
108
+ socketEntryData . push ( { name, index } ) ;
109
+ }
110
+ }
111
+ }
112
+ ) ;
113
+
114
+ // Overlay entries need to be injected AFTER integration's entry,
115
+ // so we will loop through everything in `finishMake` instead of `make`.
116
+ // This ensures we can traverse all entry points and inject stuff with the correct order.
117
+ additional . overlayEntries . forEach ( ( entry , idx , arr ) => {
118
+ compiler . hooks . finishMake . tapPromise (
119
+ { name : this . constructor . name , stage : Number . MIN_SAFE_INTEGER + ( arr . length - idx - 1 ) } ,
120
+ ( compilation ) => {
121
+ // Only hook into the current compiler
122
+ if ( compilation . compiler !== compiler ) {
123
+ return Promise . resolve ( ) ;
124
+ }
125
+
126
+ const injectData = socketEntryData . length ? socketEntryData : [ { name : undefined } ] ;
127
+ return Promise . all (
128
+ injectData . map ( ( { name, index } ) => {
129
+ return new Promise ( ( resolve , reject ) => {
130
+ const options = { name } ;
131
+ const dep = EntryPlugin . createDependency ( entry , options ) ;
132
+ compilation . addEntry ( compiler . context , dep , options , ( err ) => {
133
+ if ( err ) return reject ( err ) ;
134
+
135
+ // If the entry is not a global one,
136
+ // and we have registered the index for integration entry,
137
+ // we will reorder all entry dependencies to our desired order.
138
+ // That is, to have additional entries DIRECTLY behind integration entry.
139
+ if ( name && typeof index !== 'undefined' ) {
140
+ const entryData = compilation . entries . get ( name ) ;
141
+ entryData . dependencies . splice (
142
+ index + 1 ,
143
+ 0 ,
144
+ entryData . dependencies . splice ( entryData . dependencies . length - 1 , 1 ) [ 0 ]
145
+ ) ;
146
+ }
147
+
148
+ resolve ( ) ;
149
+ } ) ;
150
+ } ) ;
151
+ } )
152
+ ) . then ( ( ) => { } ) ;
153
+ }
154
+ ) ;
155
+ } ) ;
156
+ } else {
157
+ compiler . options . entry = injectRefreshEntry ( compiler . options . entry , additional ) ;
158
+ }
80
159
81
160
// Inject necessary modules to bundle's global scope
82
161
/** @type {Record<string, string | boolean> }*/
@@ -114,10 +193,8 @@ class ReactRefreshPlugin {
114
193
}
115
194
}
116
195
117
- const definePlugin = new DefinePlugin ( definedModules ) ;
118
- definePlugin . apply ( compiler ) ;
119
- const providePlugin = new ProvidePlugin ( providedModules ) ;
120
- providePlugin . apply ( compiler ) ;
196
+ new DefinePlugin ( definedModules ) . apply ( compiler ) ;
197
+ new ProvidePlugin ( providedModules ) . apply ( compiler ) ;
121
198
122
199
const match = ModuleFilenameHelpers . matchObject . bind ( undefined , this . options ) ;
123
200
const { evaluateToString, toConstantDependency } = getParserHelpers ( ) ;
@@ -217,8 +294,8 @@ class ReactRefreshPlugin {
217
294
) ;
218
295
219
296
compilation . hooks . normalModuleLoader . tap (
220
- // `Infinity ` ensures this check will run only after all other taps
221
- { name : this . constructor . name , stage : Infinity } ,
297
+ // `Number.POSITIVE_INFINITY ` ensures this check will run only after all other taps
298
+ { name : this . constructor . name , stage : Number . POSITIVE_INFINITY } ,
222
299
// Check for existence of the HMR runtime -
223
300
// it is the foundation to this plugin working correctly
224
301
( context ) => {
@@ -237,10 +314,14 @@ class ReactRefreshPlugin {
237
314
break ;
238
315
}
239
316
case 5 : {
317
+ const EntryDependency = require ( 'webpack/lib/dependencies/EntryDependency' ) ;
240
318
const NormalModule = require ( 'webpack/lib/NormalModule' ) ;
241
319
const RuntimeGlobals = require ( 'webpack/lib/RuntimeGlobals' ) ;
242
320
const ReactRefreshRuntimeModule = require ( './RefreshRuntimeModule' ) ;
243
321
322
+ // Set factory for EntryDependency which is used to initialise the module
323
+ compilation . dependencyFactories . set ( EntryDependency , normalModuleFactory ) ;
324
+
244
325
compilation . hooks . additionalTreeRuntimeRequirements . tap (
245
326
this . constructor . name ,
246
327
// Setup react-refresh globals with a Webpack runtime module
0 commit comments