Skip to content

Commit 3afdccb

Browse files
authored
Merge pull request webpack#8459 from webpack/feature/source-type-size
Add support for multiple different sizes per module
2 parents 6b75ff0 + 387803b commit 3afdccb

36 files changed

+857
-308
lines changed

declarations/WebpackOptions.d.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,18 @@ export type WebpackPluginFunction = (
233233
* via the `definition` "RuleSetRules".
234234
*/
235235
export type RuleSetRules = RuleSetRule[];
236+
/**
237+
* This interface was referenced by `WebpackOptions`'s JSON-Schema
238+
* via the `definition` "OptimizationSplitChunksSizes".
239+
*/
240+
export type OptimizationSplitChunksSizes =
241+
| number
242+
| {
243+
/**
244+
* Size of the part of the chunk with the type of the key
245+
*/
246+
[k: string]: number;
247+
};
236248
/**
237249
* This interface was referenced by `WebpackOptions`'s JSON-Schema
238250
* via the `definition` "FilterTypes".
@@ -926,15 +938,15 @@ export interface OptimizationSplitChunksOptions {
926938
/**
927939
* Maximal size hint for the created chunks
928940
*/
929-
maxSize?: number;
941+
maxSize?: OptimizationSplitChunksSizes;
930942
/**
931943
* Minimum number of times a module has to be duplicated until it's considered for splitting
932944
*/
933945
minChunks?: number;
934946
/**
935947
* Minimal size for the created chunk
936948
*/
937-
minSize?: number;
949+
minSize?: OptimizationSplitChunksSizes;
938950
/**
939951
* Give chunks for this cache group a name (chunks with equal name are merged)
940952
*/
@@ -972,11 +984,11 @@ export interface OptimizationSplitChunksOptions {
972984
/**
973985
* Maximal size hint for the created chunks
974986
*/
975-
maxSize?: number;
987+
maxSize?: OptimizationSplitChunksSizes;
976988
/**
977989
* Minimal size for the created chunk
978990
*/
979-
minSize?: number;
991+
minSize?: OptimizationSplitChunksSizes;
980992
};
981993
/**
982994
* Sets the template for the filename for created chunks (Only works for initial chunks)
@@ -997,15 +1009,15 @@ export interface OptimizationSplitChunksOptions {
9971009
/**
9981010
* Maximal size hint for the created chunks
9991011
*/
1000-
maxSize?: number;
1012+
maxSize?: OptimizationSplitChunksSizes;
10011013
/**
10021014
* Minimum number of times a module has to be duplicated until it's considered for splitting
10031015
*/
10041016
minChunks?: number;
10051017
/**
10061018
* Minimal size for the created chunks
10071019
*/
1008-
minSize?: number;
1020+
minSize?: OptimizationSplitChunksSizes;
10091021
/**
10101022
* Give chunks created a name (chunks with equal name are merged)
10111023
*/

lib/ChunkGraph.js

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,33 @@ const createOrderedArrayFunction = comparator => {
8181
};
8282

8383
/**
84-
* @param {SortableSet<Module>} set the sortable Set to get the count/size of
84+
* @param {Iterable<Module>} modules the modules to get the count/size of
8585
* @returns {number} the size of the modules
8686
*/
87-
const getModulesSize = set => {
87+
const getModulesSize = modules => {
8888
let size = 0;
89-
for (const module of set) {
90-
size += module.size();
89+
for (const module of modules) {
90+
for (const type of module.getSourceTypes()) {
91+
size += module.size(type);
92+
}
9193
}
9294
return size;
9395
};
9496

97+
/**
98+
* @param {Iterable<Module>} modules the sortable Set to get the size of
99+
* @returns {Record<string, number>} the sizes of the modules
100+
*/
101+
const getModulesSizes = modules => {
102+
let sizes = Object.create(null);
103+
for (const module of modules) {
104+
for (const type of module.getSourceTypes()) {
105+
sizes[type] = (sizes[type] || 0) + module.size(type);
106+
}
107+
}
108+
return sizes;
109+
};
110+
95111
class ChunkGraphModule {
96112
constructor() {
97113
/** @type {SortableSet<Chunk>} */
@@ -509,6 +525,15 @@ class ChunkGraph {
509525
return cgc.modules.getFromUnorderedCache(getModulesSize);
510526
}
511527

528+
/**
529+
* @param {Chunk} chunk the chunk
530+
* @returns {Record<string, number>} total sizes of all modules in the chunk by source type
531+
*/
532+
getChunkModulesSizes(chunk) {
533+
const cgc = this._getChunkGraphChunk(chunk);
534+
return cgc.modules.getFromUnorderedCache(getModulesSizes);
535+
}
536+
512537
/**
513538
* @param {Chunk} chunk the chunk
514539
* @param {ChunkSizeOptions} options options object
@@ -540,10 +565,7 @@ class ChunkGraph {
540565
const cgcB = this._getChunkGraphChunk(chunkB);
541566
const allModules = new Set(cgcA.modules);
542567
for (const m of cgcB.modules) allModules.add(m);
543-
let modulesSize = 0;
544-
for (const module of allModules) {
545-
modulesSize += module.size();
546-
}
568+
let modulesSize = getModulesSize(allModules);
547569
const chunkOverhead =
548570
typeof options.chunkOverhead === "number" ? options.chunkOverhead : 10000;
549571
const entryChunkMultiplicator =

lib/ContextModule.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,9 +875,10 @@ webpackEmptyAsyncContext.id = ${JSON.stringify(id)};`;
875875
}
876876

877877
/**
878+
* @param {string=} type the source type for which the size should be estimated
878879
* @returns {number} the estimated size of the module
879880
*/
880-
size() {
881+
size(type) {
881882
// base penalty
882883
const initialSize = 160;
883884

lib/DelegatedModule.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,10 @@ class DelegatedModule extends Module {
148148
}
149149

150150
/**
151+
* @param {string=} type the source type for which the size should be estimated
151152
* @returns {number} the estimated size of the module
152153
*/
153-
size() {
154+
size(type) {
154155
return 42;
155156
}
156157

lib/DllModule.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ class DllModule extends Module {
8585
}
8686

8787
/**
88+
* @param {string=} type the source type for which the size should be estimated
8889
* @returns {number} the estimated size of the module
8990
*/
90-
size() {
91+
size(type) {
9192
return 12;
9293
}
9394

lib/ExternalModule.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,10 @@ class ExternalModule extends Module {
244244
}
245245

246246
/**
247+
* @param {string=} type the source type for which the size should be estimated
247248
* @returns {number} the estimated size of the module
248249
*/
249-
size() {
250+
size(type) {
250251
return 42;
251252
}
252253

lib/Generator.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,24 @@ class Generator {
3131
return new ByTypeGenerator(map);
3232
}
3333

34+
/**
35+
* @abstract
36+
* @returns {Set<string>} available types (do not mutate)
37+
*/
38+
getTypes() {
39+
throw new Error("Generator.getTypes: must be overridden");
40+
}
41+
42+
/**
43+
* @abstract
44+
* @param {NormalModule} module the module
45+
* @param {string=} type source type
46+
* @returns {number} estimate size of the module
47+
*/
48+
getSize(module, type) {
49+
throw new Error("Generator.getSize: must be overridden");
50+
}
51+
3452
/**
3553
* @abstract
3654
* @param {NormalModule} module module for which the code should be generated
@@ -49,6 +67,25 @@ class ByTypeGenerator extends Generator {
4967
constructor(map) {
5068
super();
5169
this.map = map;
70+
this._types = new Set(Object.keys(map));
71+
}
72+
73+
/**
74+
* @returns {Set<string>} available types (do not mutate)
75+
*/
76+
getTypes() {
77+
return this._types;
78+
}
79+
80+
/**
81+
* @param {NormalModule} module the module
82+
* @param {string=} type source type
83+
* @returns {number} estimate size of the module
84+
*/
85+
getSize(module, type) {
86+
const t = type || "javascript";
87+
const generator = this.map[t];
88+
return generator ? generator.getSize(module, t) : 0;
5289
}
5390

5491
/**

lib/JavascriptGenerator.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,29 @@ const sortFragmentWithIndex = ([a, i], [b, j]) => {
4848
return i - j;
4949
};
5050

51+
const TYPES = new Set(["javascript"]);
52+
5153
class JavascriptGenerator extends Generator {
54+
/**
55+
* @returns {Set<string>} available types (do not mutate)
56+
*/
57+
getTypes() {
58+
return TYPES;
59+
}
60+
61+
/**
62+
* @param {NormalModule} module the module
63+
* @param {string=} type source type
64+
* @returns {number} estimate size of the module
65+
*/
66+
getSize(module, type) {
67+
const originalSource = module.originalSource();
68+
if (!originalSource) {
69+
return 39;
70+
}
71+
return originalSource.size();
72+
}
73+
5274
/**
5375
* @param {NormalModule} module module for which the code should be generated
5476
* @param {GenerateContext} generateContext context for generate

lib/JavascriptModulesPlugin.js

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55

66
"use strict";
77

8-
const { SyncBailHook } = require("tapable");
98
const { ConcatSource } = require("webpack-sources");
10-
const Compilation = require("./Compilation");
119
const HotUpdateChunk = require("./HotUpdateChunk");
1210
const JavascriptGenerator = require("./JavascriptGenerator");
1311
const JavascriptParser = require("./JavascriptParser");
@@ -18,41 +16,14 @@ const createHash = require("./util/createHash");
1816
/** @typedef {import("webpack-sources").Source} Source */
1917
/** @typedef {import("./Chunk")} Chunk */
2018
/** @typedef {import("./ChunkTemplate")} ChunkTemplate */
19+
/** @typedef {import("./Compilation")} Compilation */
2120
/** @typedef {import("./Compiler")} Compiler */
2221
/** @typedef {import("./Module")} Module */
2322
/** @typedef {import("./ModuleTemplate")} ModuleTemplate */
2423
/** @typedef {import("./ModuleTemplate").RenderContext} RenderContext */
2524
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
2625

27-
/**
28-
* @typedef {Object} JavascriptModulesPluginHooks
29-
* @property {SyncBailHook<Module, Chunk>} shouldRender
30-
*/
31-
32-
/** @type {WeakMap<Compilation, JavascriptModulesPluginHooks>} */
33-
const compilationHooksMap = new WeakMap();
34-
3526
class JavascriptModulesPlugin {
36-
/**
37-
* @param {Compilation} compilation the compilation
38-
* @returns {JavascriptModulesPluginHooks} hooks
39-
*/
40-
static getHooks(compilation) {
41-
if (!(compilation instanceof Compilation)) {
42-
throw new TypeError(
43-
"The 'compilation' argument must be an instance of Compilation"
44-
);
45-
}
46-
let hooks = compilationHooksMap.get(compilation);
47-
if (hooks === undefined) {
48-
hooks = {
49-
shouldRender: new SyncBailHook(["module", "chunk"])
50-
};
51-
compilationHooksMap.set(compilation, hooks);
52-
}
53-
return hooks;
54-
}
55-
5627
/**
5728
* @param {Compiler} compiler webpack compiler
5829
* @returns {void}
@@ -62,12 +33,6 @@ class JavascriptModulesPlugin {
6233
"JavascriptModulesPlugin",
6334
(compilation, { normalModuleFactory }) => {
6435
const moduleGraph = compilation.moduleGraph;
65-
const hooks = JavascriptModulesPlugin.getHooks(compilation);
66-
hooks.shouldRender.tap("JavascriptModulesPlugin", module => {
67-
if (module.type === "javascript/auto") return true;
68-
if (module.type === "javascript/dynamic") return true;
69-
if (module.type === "javascript/esm") return true;
70-
});
7136
normalModuleFactory.hooks.createParser
7237
.for("javascript/auto")
7338
.tap("JavascriptModulesPlugin", options => {
@@ -134,10 +99,9 @@ class JavascriptModulesPlugin {
13499
compilation.mainTemplate.hooks.modules.tap(
135100
"JavascriptModulesPlugin",
136101
(source, moduleTemplate, renderContext) => {
137-
const chunk = renderContext.chunk;
138102
return Template.renderChunkModules(
139103
renderContext,
140-
m => hooks.shouldRender.call(m, chunk),
104+
m => m.getSourceTypes().has("javascript"),
141105
moduleTemplate,
142106
"/******/ "
143107
);
@@ -220,7 +184,7 @@ class JavascriptModulesPlugin {
220184
chunk,
221185
compareModulesById(chunkGraph)
222186
)) {
223-
if (hooks.shouldRender.call(m, chunk)) {
187+
if (m.getSourceTypes().has("javascript")) {
224188
hash.update(chunkGraph.getModuleHash(m));
225189
}
226190
}
@@ -243,12 +207,10 @@ class JavascriptModulesPlugin {
243207
* @returns {Source} the rendered source
244208
*/
245209
renderJavascript(compilation, chunkTemplate, moduleTemplate, renderContext) {
246-
const hooks = JavascriptModulesPlugin.getHooks(compilation);
247-
248210
const chunk = renderContext.chunk;
249211
const moduleSources = Template.renderChunkModules(
250212
renderContext,
251-
m => hooks.shouldRender.call(m, chunk),
213+
m => m.getSourceTypes().has("javascript"),
252214
moduleTemplate
253215
);
254216
const core = chunkTemplate.hooks.modules.call(

lib/JsonGenerator.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,27 @@ const stringifySafe = data => {
2525
); // invalid in JavaScript but valid JSON
2626
};
2727

28+
const TYPES = new Set(["javascript"]);
29+
2830
class JsonGenerator extends Generator {
31+
/**
32+
* @returns {Set<string>} available types (do not mutate)
33+
*/
34+
getTypes() {
35+
return TYPES;
36+
}
37+
38+
/**
39+
* @param {NormalModule} module the module
40+
* @param {string=} type source type
41+
* @returns {number} estimate size of the module
42+
*/
43+
getSize(module, type) {
44+
let data = module.buildInfo.jsonData;
45+
if (!data) return 0;
46+
return stringifySafe(data).length + 10;
47+
}
48+
2949
/**
3050
* @param {NormalModule} module module for which the code should be generated
3151
* @param {GenerateContext} generateContext context for generate

0 commit comments

Comments
 (0)