@@ -23,9 +23,9 @@ class ModuleConcatenationPlugin {
23
23
} ) ;
24
24
const bailoutReasonMap = new Map ( ) ;
25
25
26
- function setBailoutReason ( module , reason ) {
26
+ function setBailoutReason ( module , prefix , reason ) {
27
27
bailoutReasonMap . set ( module , reason ) ;
28
- module . optimizationBailout . push ( reason ) ;
28
+ module . optimizationBailout . push ( typeof reason === "function" ? ( rs ) => ` ${ prefix } : ${ reason ( rs ) } ` : ` ${ prefix } : ${ reason } ` ) ;
29
29
}
30
30
31
31
function getBailoutReason ( module , requestShortener ) {
@@ -35,141 +35,135 @@ class ModuleConcatenationPlugin {
35
35
}
36
36
37
37
compilation . plugin ( "optimize-chunk-modules" , ( chunks , modules ) => {
38
- chunks . forEach ( chunk => {
39
- const relevantModules = [ ] ;
40
- const possibleInners = new Set ( ) ;
41
- for ( const module of chunk . modulesIterable ) {
42
- // Only harmony modules are valid for optimization
43
- if ( ! module . meta || ! module . meta . harmonyModule ) {
44
- continue ;
45
- }
38
+ const relevantModules = [ ] ;
39
+ const possibleInners = new Set ( ) ;
40
+ for ( const module of modules ) {
41
+ // Only harmony modules are valid for optimization
42
+ if ( ! module . meta || ! module . meta . harmonyModule ) {
43
+ continue ;
44
+ }
46
45
47
- // Module must not be in other chunks
48
- // TODO add an option to allow module to be in other entry points
49
- if ( module . getNumberOfChunks ( ) !== 1 ) {
50
- setBailoutReason ( module , "ModuleConcatenation: module is in multiple chunks" ) ;
51
- continue ;
52
- }
46
+ // Because of variable renaming we can't use modules with eval
47
+ if ( module . meta && module . meta . hasEval ) {
48
+ setBailoutReason ( module , "ModuleConcatenation" , "eval is used in the module" ) ;
49
+ continue ;
50
+ }
53
51
54
- // Because of variable renaming we can't use modules with eval
55
- if ( module . meta && module . meta . hasEval ) {
56
- setBailoutReason ( module , "ModuleConcatenation: eval is used in the module" ) ;
57
- continue ;
58
- }
52
+ relevantModules . push ( module ) ;
59
53
60
- relevantModules . push ( module ) ;
54
+ // Module must not be the entry points
55
+ if ( module . getChunks ( ) . some ( chunk => chunk . entryModule === module ) ) {
56
+ setBailoutReason ( module , "ModuleConcatenation (inner)" , "module is an entrypoint" ) ;
57
+ continue ;
58
+ }
61
59
62
- // Module must not be the entry points
63
- if ( chunk . entryModule === module ) {
64
- setBailoutReason ( module , "ModuleConcatenation (inner): module is an entrypoint " ) ;
65
- continue ;
66
- }
60
+ // Exports must be known (and not dynamic)
61
+ if ( ! Array . isArray ( module . providedExports ) ) {
62
+ setBailoutReason ( module , "ModuleConcatenation (inner)" , "exports are not known ") ;
63
+ continue ;
64
+ }
67
65
68
- // Exports must be known (and not dynamic)
69
- if ( ! Array . isArray ( module . providedExports ) ) {
70
- setBailoutReason ( module , "ModuleConcatenation (inner): exports are not known " ) ;
71
- continue ;
72
- }
66
+ // Using dependency variables is not possible as this wraps the code in a function
67
+ if ( module . variables . length > 0 ) {
68
+ setBailoutReason ( module , "ModuleConcatenation (inner)" , "dependency variables are used (i. e. ProvidePlugin) ") ;
69
+ continue ;
70
+ }
73
71
74
- // Using dependency variables is not possible as this wraps the code in a function
75
- if ( module . variables . length > 0 ) {
76
- setBailoutReason ( module , "ModuleConcatenation (inner): dependency variables are used (i. e. ProvidePlugin)" ) ;
77
- continue ;
78
- }
72
+ // Module must only be used by Harmony Imports
73
+ const nonHarmonyReasons = module . reasons . filter ( reason => ! ( reason . dependency instanceof HarmonyImportDependency ) ) ;
74
+ if ( nonHarmonyReasons . length > 0 ) {
75
+ const importingModules = new Set ( nonHarmonyReasons . map ( r => r . module ) ) ;
76
+ setBailoutReason ( module , "ModuleConcatenation (inner)" , ( requestShortener ) => {
77
+ const names = Array . from ( importingModules ) . map ( m => m . readableIdentifier ( requestShortener ) ) ;
78
+ return `module is used with non-harmony imports from ${ names . join ( ", " ) } ` ;
79
+ } ) ;
80
+ continue ;
81
+ }
79
82
80
- // Module must only be used by Harmony Imports
81
- const nonHarmonyReasons = module . reasons . filter ( reason => ! ( reason . dependency instanceof HarmonyImportDependency ) ) ;
82
- if ( nonHarmonyReasons . length > 0 ) {
83
- const importingModules = new Set ( nonHarmonyReasons . map ( r => r . module ) ) ;
84
- setBailoutReason ( module , ( requestShortener ) => {
85
- const names = Array . from ( importingModules ) . map ( m => m . readableIdentifier ( requestShortener ) ) ;
86
- return `ModuleConcatenation (inner): module is used with non-harmony imports from ${ names . join ( ", " ) } ` ;
87
- } ) ;
88
- continue ;
83
+ possibleInners . add ( module ) ;
84
+ }
85
+ // sort by depth
86
+ // modules with lower depth are more likly suited as roots
87
+ // this improves performance, because modules already selected as inner are skipped
88
+ relevantModules . sort ( ( a , b ) => {
89
+ return a . depth - b . depth ;
90
+ } ) ;
91
+ const concatConfigurations = [ ] ;
92
+ const usedAsInner = new Set ( ) ;
93
+ for ( const currentRoot of relevantModules ) {
94
+ // when used by another configuration as inner:
95
+ // the other configuration is better and we can skip this one
96
+ if ( usedAsInner . has ( currentRoot ) )
97
+ continue ;
98
+
99
+ // create a configuration with the root
100
+ const currentConfiguration = new ConcatConfiguration ( currentRoot ) ;
101
+
102
+ // cache failures to add modules
103
+ const failureCache = new Map ( ) ;
104
+
105
+ // try to add all imports
106
+ for ( const imp of this . getImports ( currentRoot ) ) {
107
+ const problem = this . tryToAdd ( currentConfiguration , imp , possibleInners , failureCache ) ;
108
+ if ( problem ) {
109
+ failureCache . set ( imp , problem ) ;
110
+ currentConfiguration . addWarning ( imp , problem ) ;
89
111
}
90
-
91
- possibleInners . add ( module ) ;
92
112
}
93
- // sort by depth
94
- // modules with lower depth are more likly suited as roots
95
- // this improves performance, because modules already selected as inner are skipped
96
- relevantModules . sort ( ( a , b ) => {
97
- return a . depth - b . depth ;
98
- } ) ;
99
- const concatConfigurations = [ ] ;
100
- const usedAsInner = new Set ( ) ;
101
- for ( const currentRoot of relevantModules ) {
102
- // when used by another configuration as inner:
103
- // the other configuration is better and we can skip this one
104
- if ( usedAsInner . has ( currentRoot ) )
105
- continue ;
106
-
107
- // create a configuration with the root
108
- const currentConfiguration = new ConcatConfiguration ( currentRoot ) ;
109
-
110
- // cache failures to add modules
111
- const failureCache = new Map ( ) ;
112
-
113
- // try to add all imports
114
- for ( const imp of this . getImports ( currentRoot ) ) {
115
- const problem = this . tryToAdd ( currentConfiguration , imp , possibleInners , failureCache ) ;
116
- if ( problem ) {
117
- failureCache . set ( imp , problem ) ;
118
- currentConfiguration . addWarning ( imp , problem ) ;
119
- }
120
- }
121
- if ( ! currentConfiguration . isEmpty ( ) ) {
122
- concatConfigurations . push ( currentConfiguration ) ;
123
- for ( const module of currentConfiguration . modules ) {
124
- if ( module !== currentConfiguration . rootModule )
125
- usedAsInner . add ( module ) ;
126
- }
113
+ if ( ! currentConfiguration . isEmpty ( ) ) {
114
+ concatConfigurations . push ( currentConfiguration ) ;
115
+ for ( const module of currentConfiguration . modules ) {
116
+ if ( module !== currentConfiguration . rootModule )
117
+ usedAsInner . add ( module ) ;
127
118
}
128
119
}
129
- // HACK: Sort configurations by length and start with the longest one
130
- // to get the biggers groups possible. Used modules are marked with usedModules
131
- // TODO: Allow to reuse existing configuration while trying to add dependencies.
132
- // This would improve performance. O(n^2) -> O(n)
133
- concatConfigurations . sort ( ( a , b ) => {
134
- return b . modules . size - a . modules . size ;
135
- } ) ;
136
- const usedModules = new Set ( ) ;
137
- for ( const concatConfiguration of concatConfigurations ) {
138
- if ( usedModules . has ( concatConfiguration . rootModule ) )
139
- continue ;
140
- const orderedModules = new Set ( ) ;
141
- this . addInOrder ( concatConfiguration . rootModule , concatConfiguration . modules , orderedModules ) ;
142
- const newModule = new ConcatenatedModule ( concatConfiguration . rootModule , Array . from ( orderedModules ) ) ;
143
- for ( const warning of concatConfiguration . warnings ) {
144
- newModule . optimizationBailout . push ( ( requestShortener ) => {
145
- const reason = getBailoutReason ( warning [ 0 ] , requestShortener ) ;
146
- const reasonPrefix = reason ? `: ${ reason } ` : "" ;
147
- if ( warning [ 0 ] === warning [ 1 ] )
148
- return `ModuleConcatenation: Cannot concat with ${ warning [ 0 ] . readableIdentifier ( requestShortener ) } ${ reasonPrefix } ` ;
149
- else
150
- return `ModuleConcatenation: Cannot concat with ${ warning [ 0 ] . readableIdentifier ( requestShortener ) } because of ${ warning [ 1 ] . readableIdentifier ( requestShortener ) } ${ reasonPrefix } ` ;
151
- } ) ;
152
- }
153
- for ( const m of orderedModules ) {
154
- usedModules . add ( m ) ;
155
- chunk . removeModule ( m ) ;
156
- }
120
+ }
121
+ // HACK: Sort configurations by length and start with the longest one
122
+ // to get the biggers groups possible. Used modules are marked with usedModules
123
+ // TODO: Allow to reuse existing configuration while trying to add dependencies.
124
+ // This would improve performance. O(n^2) -> O(n)
125
+ concatConfigurations . sort ( ( a , b ) => {
126
+ return b . modules . size - a . modules . size ;
127
+ } ) ;
128
+ const usedModules = new Set ( ) ;
129
+ for ( const concatConfiguration of concatConfigurations ) {
130
+ if ( usedModules . has ( concatConfiguration . rootModule ) )
131
+ continue ;
132
+ const orderedModules = new Set ( ) ;
133
+ this . addInOrder ( concatConfiguration . rootModule , concatConfiguration . modules , orderedModules ) ;
134
+ const newModule = new ConcatenatedModule ( concatConfiguration . rootModule , Array . from ( orderedModules ) ) ;
135
+ for ( const warning of concatConfiguration . warnings ) {
136
+ newModule . optimizationBailout . push ( ( requestShortener ) => {
137
+ const reason = getBailoutReason ( warning [ 0 ] , requestShortener ) ;
138
+ const reasonPrefix = reason ? `: ${ reason } ` : "" ;
139
+ if ( warning [ 0 ] === warning [ 1 ] )
140
+ return `ModuleConcatenation: Cannot concat with ${ warning [ 0 ] . readableIdentifier ( requestShortener ) } ${ reasonPrefix } ` ;
141
+ else
142
+ return `ModuleConcatenation: Cannot concat with ${ warning [ 0 ] . readableIdentifier ( requestShortener ) } because of ${ warning [ 1 ] . readableIdentifier ( requestShortener ) } ${ reasonPrefix } ` ;
143
+ } ) ;
144
+ }
145
+ const chunks = concatConfiguration . rootModule . getChunks ( ) ;
146
+ for ( const m of orderedModules ) {
147
+ usedModules . add ( m ) ;
148
+ chunks . forEach ( chunk => chunk . removeModule ( m ) ) ;
149
+ }
150
+ chunks . forEach ( chunk => {
157
151
chunk . addModule ( newModule ) ;
158
- compilation . modules . push ( newModule ) ;
159
152
if ( chunk . entryModule === concatConfiguration . rootModule )
160
153
chunk . entryModule = newModule ;
161
- newModule . reasons . forEach ( reason => reason . dependency . module = newModule ) ;
162
- newModule . dependencies . forEach ( dep => {
163
- if ( dep . module ) {
164
- dep . module . reasons . forEach ( reason => {
165
- if ( reason . dependency === dep )
166
- reason . module = newModule ;
167
- } ) ;
168
- }
169
- } ) ;
170
- }
171
- compilation . modules = compilation . modules . filter ( m => ! usedModules . has ( m ) ) ;
172
- } ) ;
154
+ } ) ;
155
+ compilation . modules . push ( newModule ) ;
156
+ newModule . reasons . forEach ( reason => reason . dependency . module = newModule ) ;
157
+ newModule . dependencies . forEach ( dep => {
158
+ if ( dep . module ) {
159
+ dep . module . reasons . forEach ( reason => {
160
+ if ( reason . dependency === dep )
161
+ reason . module = newModule ;
162
+ } ) ;
163
+ }
164
+ } ) ;
165
+ }
166
+ compilation . modules = compilation . modules . filter ( m => ! usedModules . has ( m ) ) ;
173
167
} ) ;
174
168
} ) ;
175
169
}
@@ -208,6 +202,13 @@ class ModuleConcatenationPlugin {
208
202
209
203
// Not possible to add?
210
204
if ( ! possibleModules . has ( module ) ) {
205
+ failureCache . set ( module , module ) ; // cache failures for performance
206
+ return module ;
207
+ }
208
+
209
+ // module must be in the same chunks
210
+ if ( ! config . rootModule . hasEqualsChunks ( module ) ) {
211
+ failureCache . set ( module , module ) ; // cache failures for performance
211
212
return module ;
212
213
}
213
214
0 commit comments