Skip to content

Commit 8bca2dc

Browse files
faceyspaceysokra
authored andcommitted
fix/feat($weak): add context support for resolveWeak + create eager-weak asyncMode
1 parent 1102e60 commit 8bca2dc

33 files changed

Lines changed: 304 additions & 5164 deletions

lib/ContextModule.js

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,14 @@ class ContextModule extends Module {
140140
this.addBlock(block);
141141
}
142142

143-
} else if(this.async === "weak-context") {
144-
145-
// store the dependences in a different key than `this.dependences`
146-
// to prevent them from being bundled in the parent
147-
this.weakDependencies = dependencies;
143+
} else if(this.async === "weak") {
144+
this.dependencies = dependencies;
148145

149-
// if we are lazy create a new async dependency block per dependency
150-
// and add all blocks to this context
146+
// if we are weak create a new async dependency block per dependency
147+
// and add all blocks to this context, but mark each dep as weak, to
148+
// prevent them from being bundled in the parent
151149
dependencies.forEach((dep, idx) => {
150+
dep.weak = true;
152151
let chunkName = this.chunkName;
153152

154153
if(chunkName) {
@@ -161,10 +160,8 @@ class ContextModule extends Module {
161160
block.addDependency(dep);
162161
this.addBlock(block);
163162
});
164-
165163
} else {
166-
167-
// if we are lazy create a new async dependency block per dependency
164+
// if we are lazy or eager-weak create a new async dependency block per dependency
168165
// and add all blocks to this context
169166
dependencies.forEach((dep, idx) => {
170167
let chunkName = this.chunkName;
@@ -220,6 +217,68 @@ module.exports = webpackContext;
220217
webpackContext.id = ${JSON.stringify(id)};`;
221218
}
222219

220+
getWeakSyncSource(dependencies, id) {
221+
const map = this.getUserRequestMap(dependencies);
222+
return `var map = ${JSON.stringify(map, null, "\t")};
223+
function webpackContext(req) {
224+
var id = webpackContextResolve(req);
225+
if(!__webpack_require__.m[id])
226+
throw new Error("Module '" + id + "' is not available (weak dependency)");
227+
return __webpack_require__(id);
228+
};
229+
function webpackContextResolve(req) {
230+
var id = map[req];
231+
if(!(id + 1)) // check for number or string
232+
throw new Error("Cannot find module '" + req + "'.");
233+
return id;
234+
};
235+
webpackContext.keys = function webpackContextKeys() {
236+
return Object.keys(map);
237+
};
238+
webpackContext.resolve = webpackContextResolve;
239+
module.exports = webpackContext;
240+
webpackContext.id = ${JSON.stringify(id)};`;
241+
}
242+
243+
getWeakEagerSource(blocks, id) {
244+
const map = blocks
245+
.filter(block => block.dependencies[0].module)
246+
.map((block) => ({
247+
dependency: block.dependencies[0],
248+
block: block,
249+
userRequest: block.dependencies[0].userRequest
250+
})).sort((a, b) => {
251+
if(a.userRequest === b.userRequest) return 0;
252+
return a.userRequest < b.userRequest ? -1 : 1;
253+
}).reduce((map, item) => {
254+
const chunks = item.block.chunks || [];
255+
256+
map[item.userRequest] = [item.dependency.module.id]
257+
.concat(chunks.map(chunk => chunk.id));
258+
259+
return map;
260+
}, Object.create(null));
261+
262+
return `var map = ${JSON.stringify(map, null, "\t")};
263+
function webpackAsyncContext(req) {
264+
var ids = map[req];
265+
if(!ids)
266+
return Promise.reject(new Error("Cannot find module '" + req + "'."));
267+
return new Promise(function(resolve, reject) {
268+
var id = ids[0];
269+
if(!__webpack_require__.m[id])
270+
reject(new Error("Module '" + id + "' is not available (weak dependency)"));
271+
else
272+
resolve(__webpack_require__(id));
273+
});
274+
};
275+
webpackAsyncContext.keys = function webpackAsyncContextKeys() {
276+
return Object.keys(map);
277+
};
278+
module.exports = webpackAsyncContext;
279+
webpackAsyncContext.id = ${JSON.stringify(id)};`;
280+
}
281+
223282
getEagerSource(dependencies, id) {
224283
const map = this.getUserRequestMap(dependencies);
225284
return `var map = ${JSON.stringify(map, null, "\t")};
@@ -329,9 +388,6 @@ webpackEmptyAsyncContext.id = ${JSON.stringify(id)};`;
329388
}
330389

331390
getSourceString(asyncMode, outputOptions, requestShortener) {
332-
if(asyncMode === "weak-context") {
333-
return this.getSyncSource(this.weakDependencies, this.id);
334-
}
335391
if(asyncMode === "lazy") {
336392
if(this.blocks && this.blocks.length > 0) {
337393
return this.getLazySource(this.blocks, this.id);
@@ -343,13 +399,25 @@ webpackEmptyAsyncContext.id = ${JSON.stringify(id)};`;
343399
return this.getEagerSource(this.dependencies, this.id);
344400
}
345401
return this.getSourceForEmptyAsyncContext(this.id);
346-
} else if(asyncMode === "lazy-once") {
402+
}
403+
if(asyncMode === "lazy-once") {
347404
const block = this.blocks[0];
348405
if(block) {
349406
return this.getLazyOnceSource(block, block.dependencies, this.id, outputOptions, requestShortener);
350407
}
351408
return this.getSourceForEmptyAsyncContext(this.id);
352409
}
410+
if(asyncMode === "eager-weak") {
411+
if(this.blocks && this.blocks.length > 0) {
412+
return this.getWeakEagerSource(this.blocks, this.id);
413+
}
414+
return this.getSourceForEmptyAsyncContext(this.id);
415+
}
416+
if(asyncMode === "weak") {
417+
if(this.dependencies && this.dependencies.length > 0) {
418+
return this.getWeakSyncSource(this.dependencies, this.id);
419+
}
420+
}
353421
if(this.dependencies && this.dependencies.length > 0) {
354422
return this.getSyncSource(this.dependencies, this.id);
355423
}

lib/dependencies/ImportDependenciesBlock.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
"use strict";
66
const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
77
const ImportDependency = require("./ImportDependency");
8+
const ImportEagerWeakDependency = require("./ImportEagerWeakDependency");
89

910
module.exports = class ImportDependenciesBlock extends AsyncDependenciesBlock {
10-
constructor(request, range, chunkName, module, loc) {
11+
constructor(request, range, chunkName, module, loc, mode) {
1112
super(chunkName, module, loc);
1213
this.range = range;
13-
const dep = new ImportDependency(request, this);
14+
const dep = mode === "eager-weak" ? new ImportEagerWeakDependency(request, this) : new ImportDependency(request, this, mode);
1415
dep.loc = loc;
1516
this.addDependency(dep);
1617
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
"use strict";
6+
const ImportContextDependency = require("./ImportContextDependency");
7+
const ContextDependencyTemplateAsRequireCall = require("./ContextDependencyTemplateAsRequireCall");
8+
9+
class ImportEagerWeakContextDependency extends ImportContextDependency {
10+
constructor(request, recursive, regExp, range, valueRange, chunkName) {
11+
super(request, recursive, regExp, range, valueRange, chunkName);
12+
this.async = "eager-weak";
13+
}
14+
15+
get type() {
16+
return "import() context eager-weak";
17+
}
18+
}
19+
20+
ImportEagerWeakContextDependency.Template = ContextDependencyTemplateAsRequireCall;
21+
22+
module.exports = ImportEagerWeakContextDependency;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
"use strict";
6+
const ModuleDependency = require("./ModuleDependency");
7+
const DepBlockHelpers = require("./DepBlockHelpers");
8+
const webpackMissingPromiseModule = require("./WebpackMissingModule").promise;
9+
10+
class ImportEagerWeakDependency extends ModuleDependency {
11+
constructor(request, block) {
12+
super(request);
13+
this.block = block;
14+
}
15+
16+
get type() {
17+
return "import() eager-weak";
18+
}
19+
}
20+
21+
ImportEagerWeakDependency.Template = class ImportDependencyTemplate {
22+
apply(dep, source, outputOptions, requestShortener) {
23+
const depBlock = dep.block;
24+
const promise = DepBlockHelpers.getDepBlockPromise(depBlock, outputOptions, requestShortener, "import()");
25+
const comment = this.getOptionalComment(outputOptions.pathinfo, requestShortener.shorten(dep.request));
26+
27+
const content = this.getContent(promise, dep, comment);
28+
source.replace(depBlock.range[0], depBlock.range[1] - 1, content);
29+
}
30+
31+
getOptionalComment(pathinfo, shortenedRequest) {
32+
if(!pathinfo) {
33+
return "";
34+
}
35+
36+
return `/*! ${shortenedRequest} */ `;
37+
}
38+
39+
getContent(promise, dep, comment) {
40+
if(dep.module) {
41+
const stringifiedId = JSON.stringify(dep.module.id);
42+
return `new Promise(function(resolve) { resolve(__webpack_require__(${comment}${stringifiedId})); })`;
43+
}
44+
45+
return webpackMissingPromiseModule(dep.request);
46+
}
47+
};
48+
49+
module.exports = ImportEagerWeakDependency;

lib/dependencies/ImportParserPlugin.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"use strict";
66

77
const ImportEagerContextDependency = require("./ImportEagerContextDependency");
8+
const ImportEagerWeakContextDependency = require("./ImportEagerWeakContextDependency");
89
const ImportLazyOnceContextDependency = require("./ImportLazyOnceContextDependency");
910
const ImportLazyContextDependency = require("./ImportLazyContextDependency");
1011
const ImportDependenciesBlock = require("./ImportDependenciesBlock");
@@ -46,26 +47,29 @@ class ImportParserPlugin {
4647
}
4748

4849
if(param.isString()) {
49-
if(mode !== "lazy" && mode !== "eager") {
50+
if(mode !== "lazy" && mode !== "eager" && mode !== "eager-weak") {
5051
parser.state.module.warnings.push(new UnsupportedFeatureWarning(parser.state.module, `\`webpackMode\` expected 'lazy' or 'eager', but received: ${mode}.`));
5152
}
5253

5354
if(mode === "eager") {
5455
const dep = new ImportEagerDependency(param.string, expr.range);
5556
parser.state.current.addDependency(dep);
5657
} else {
57-
const depBlock = new ImportDependenciesBlock(param.string, expr.range, chunkName, parser.state.module, expr.loc);
58+
// both lazy and eager-weak modes use this; eager-weak needs the chunks created, but will expect them pre-served
59+
const depBlock = new ImportDependenciesBlock(param.string, expr.range, chunkName, parser.state.module, expr.loc, mode);
5860
parser.state.current.addBlock(depBlock);
5961
}
6062
return true;
6163
} else {
62-
if(mode !== "lazy" && mode !== "lazy-once" && mode !== "eager") {
64+
if(mode !== "lazy" && mode !== "lazy-once" && mode !== "eager" && mode !== "eager-weak") {
6365
parser.state.module.warnings.push(new UnsupportedFeatureWarning(parser.state.module, `\`webpackMode\` expected 'lazy', 'lazy-once' or 'eager', but received: ${mode}.`));
6466
}
6567

6668
let Dep = ImportLazyContextDependency;
6769
if(mode === "eager") {
6870
Dep = ImportEagerContextDependency;
71+
} else if(mode === "eager-weak") {
72+
Dep = ImportEagerWeakContextDependency;
6973
} else if(mode === "lazy-once") {
7074
Dep = ImportLazyOnceContextDependency;
7175
}

lib/dependencies/ImportPlugin.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
const ImportDependency = require("./ImportDependency");
88
const ImportEagerDependency = require("./ImportEagerDependency");
9+
const ImportEagerWeakDependency = require("./ImportEagerWeakDependency");
910
const ImportEagerContextDependency = require("./ImportEagerContextDependency");
11+
const ImportEagerWeakContextDependency = require("./ImportEagerWeakContextDependency");
1012
const ImportLazyOnceContextDependency = require("./ImportLazyOnceContextDependency");
1113
const ImportLazyContextDependency = require("./ImportLazyContextDependency");
1214
const ImportParserPlugin = require("./ImportParserPlugin");
@@ -28,9 +30,15 @@ class ImportPlugin {
2830
compilation.dependencyFactories.set(ImportEagerDependency, normalModuleFactory);
2931
compilation.dependencyTemplates.set(ImportEagerDependency, new ImportEagerDependency.Template());
3032

33+
compilation.dependencyFactories.set(ImportEagerWeakDependency, normalModuleFactory);
34+
compilation.dependencyTemplates.set(ImportEagerWeakDependency, new ImportEagerWeakDependency.Template());
35+
3136
compilation.dependencyFactories.set(ImportEagerContextDependency, contextModuleFactory);
3237
compilation.dependencyTemplates.set(ImportEagerContextDependency, new ImportEagerContextDependency.Template());
3338

39+
compilation.dependencyFactories.set(ImportEagerWeakContextDependency, contextModuleFactory);
40+
compilation.dependencyTemplates.set(ImportEagerWeakContextDependency, new ImportEagerWeakContextDependency.Template());
41+
3442
compilation.dependencyFactories.set(ImportLazyOnceContextDependency, contextModuleFactory);
3543
compilation.dependencyTemplates.set(ImportLazyOnceContextDependency, new ImportLazyOnceContextDependency.Template());
3644

lib/dependencies/RequireContextDependency.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ const ContextDependency = require("./ContextDependency");
77
const ModuleDependencyTemplateAsRequireId = require("./ModuleDependencyTemplateAsRequireId");
88

99
class RequireContextDependency extends ContextDependency {
10-
constructor(request, recursive, regExp, weak, chunkName, range) {
10+
constructor(request, recursive, regExp, weak, range) {
1111
super(request, recursive, regExp);
1212
this.range = range;
1313

1414
if(weak) {
15-
this.async = "weak-context";
16-
this.chunkName = chunkName;
15+
this.async = weak;
1716
}
1817
}
1918

lib/dependencies/RequireContextDependencyParserPlugin.js

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,13 @@ module.exports = class RequireContextDependencyParserPlugin {
1111
parser.plugin("call require.context", expr => {
1212
let regExp = /^\.\/.*$/;
1313
let recursive = true;
14-
let weak = false;
15-
let chunkName;
14+
let asyncMode;
1615
switch(expr.arguments.length) {
17-
case 5:
18-
{
19-
const chunkNameExpr = parser.evaluateExpression(expr.arguments[4]);
20-
if(!chunkNameExpr.isString()) return;
21-
chunkName = chunkNameExpr.string;
22-
}
23-
// falls through
2416
case 4:
2517
{
26-
const weakExpr = parser.evaluateExpression(expr.arguments[3]);
27-
if(!weakExpr.isBoolean()) return;
28-
weak = weakExpr.bool;
18+
const asyncModeExpr = parser.evaluateExpression(expr.arguments[3]);
19+
if(!asyncModeExpr.isString()) return;
20+
asyncMode = asyncModeExpr.string;
2921
}
3022
// falls through
3123
case 3:
@@ -46,7 +38,7 @@ module.exports = class RequireContextDependencyParserPlugin {
4638
{
4739
const requestExpr = parser.evaluateExpression(expr.arguments[0]);
4840
if(!requestExpr.isString()) return;
49-
const dep = new RequireContextDependency(requestExpr.string, recursive, regExp, weak, chunkName, expr.range);
41+
const dep = new RequireContextDependency(requestExpr.string, recursive, regExp, asyncMode, expr.range);
5042
dep.loc = expr.loc;
5143
dep.optional = parser.scope.inTry;
5244
parser.state.current.addDependency(dep);

lib/dependencies/RequireResolveDependencyParserPlugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class RequireResolveDependencyParserPlugin {
6262
if(!dep) return;
6363
dep.loc = expr.loc;
6464
dep.optional = !!parser.scope.inTry;
65-
dep.weak = weak;
65+
dep.async = weak ? "weak" : false;
6666
parser.state.current.addDependency(dep);
6767
return true;
6868
});

0 commit comments

Comments
 (0)