Skip to content

Commit 870fcad

Browse files
authored
Merge pull request #15423 from webpack/fix/issue-15409
Fix resolving with yield
2 parents cbfea76 + 0fe4470 commit 870fcad

File tree

30 files changed

+490
-222
lines changed

30 files changed

+490
-222
lines changed

lib/ContextModule.js

+90-26
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const makeSerializable = require("./util/makeSerializable");
6161

6262
/**
6363
* @typedef {Object} ContextModuleOptionsExtras
64-
* @property {string} resource
64+
* @property {false|string|string[]} resource
6565
* @property {string=} resourceQuery
6666
* @property {string=} resourceFragment
6767
* @property {TODO} resolveOptions
@@ -92,23 +92,36 @@ class ContextModule extends Module {
9292
* @param {ContextModuleOptions} options options object
9393
*/
9494
constructor(resolveDependencies, options) {
95-
const parsed = parseResource(options ? options.resource : "");
96-
const resource = parsed.path;
97-
const resourceQuery = (options && options.resourceQuery) || parsed.query;
98-
const resourceFragment =
99-
(options && options.resourceFragment) || parsed.fragment;
100-
101-
super("javascript/dynamic", resource);
95+
if (!options || typeof options.resource === "string") {
96+
const parsed = parseResource(
97+
options ? /** @type {string} */ (options.resource) : ""
98+
);
99+
const resource = parsed.path;
100+
const resourceQuery = (options && options.resourceQuery) || parsed.query;
101+
const resourceFragment =
102+
(options && options.resourceFragment) || parsed.fragment;
103+
104+
super("javascript/dynamic", resource);
105+
/** @type {ContextModuleOptions} */
106+
this.options = {
107+
...options,
108+
resource,
109+
resourceQuery,
110+
resourceFragment
111+
};
112+
} else {
113+
super("javascript/dynamic");
114+
/** @type {ContextModuleOptions} */
115+
this.options = {
116+
...options,
117+
resource: options.resource,
118+
resourceQuery: options.resourceQuery || "",
119+
resourceFragment: options.resourceFragment || ""
120+
};
121+
}
102122

103123
// Info from Factory
104124
this.resolveDependencies = resolveDependencies;
105-
/** @type {ContextModuleOptions} */
106-
this.options = {
107-
...options,
108-
resource,
109-
resourceQuery,
110-
resourceFragment
111-
};
112125
if (options && options.resolveOptions !== undefined) {
113126
this.resolveOptions = options.resolveOptions;
114127
}
@@ -155,7 +168,12 @@ class ContextModule extends Module {
155168
}
156169

157170
_createIdentifier() {
158-
let identifier = this.context;
171+
let identifier =
172+
this.context ||
173+
(typeof this.options.resource === "string" ||
174+
this.options.resource === false
175+
? `${this.options.resource}`
176+
: this.options.resource.join("|"));
159177
if (this.options.resourceQuery) {
160178
identifier += `|${this.options.resourceQuery}`;
161179
}
@@ -220,7 +238,19 @@ class ContextModule extends Module {
220238
* @returns {string} a user readable identifier of the module
221239
*/
222240
readableIdentifier(requestShortener) {
223-
let identifier = requestShortener.shorten(this.context) + "/";
241+
let identifier;
242+
if (this.context) {
243+
identifier = requestShortener.shorten(this.context) + "/";
244+
} else if (
245+
typeof this.options.resource === "string" ||
246+
this.options.resource === false
247+
) {
248+
identifier = requestShortener.shorten(`${this.options.resource}`) + "/";
249+
} else {
250+
identifier = this.options.resource
251+
.map(r => requestShortener.shorten(r) + "/")
252+
.join(" ");
253+
}
224254
if (this.options.resourceQuery) {
225255
identifier += ` ${this.options.resourceQuery}`;
226256
}
@@ -270,11 +300,30 @@ class ContextModule extends Module {
270300
* @returns {string | null} an identifier for library inclusion
271301
*/
272302
libIdent(options) {
273-
let identifier = contextify(
274-
options.context,
275-
this.context,
276-
options.associatedObjectForCache
277-
);
303+
let identifier;
304+
305+
if (this.context) {
306+
identifier = contextify(
307+
options.context,
308+
this.context,
309+
options.associatedObjectForCache
310+
);
311+
} else if (typeof this.options.resource === "string") {
312+
identifier = contextify(
313+
options.context,
314+
this.options.resource,
315+
options.associatedObjectForCache
316+
);
317+
} else if (this.options.resource === false) {
318+
identifier = "false";
319+
} else {
320+
identifier = this.options.resource
321+
.map(res =>
322+
contextify(options.context, res, options.associatedObjectForCache)
323+
)
324+
.join(" ");
325+
}
326+
278327
if (this.layer) identifier = `(${this.layer})/${identifier}`;
279328
if (this.options.mode) {
280329
identifier += ` ${this.options.mode}`;
@@ -323,8 +372,9 @@ class ContextModule extends Module {
323372
// build if enforced
324373
if (this._forceBuild) return callback(null, true);
325374

326-
// always build when we have no snapshot
327-
if (!this.buildInfo.snapshot) return callback(null, true);
375+
// always build when we have no snapshot and context
376+
if (!this.buildInfo.snapshot)
377+
return callback(null, Boolean(this.context || this.options.resource));
328378

329379
fileSystemInfo.checkSnapshotValid(this.buildInfo.snapshot, (err, valid) => {
330380
callback(err, !valid);
@@ -439,10 +489,16 @@ class ContextModule extends Module {
439489
);
440490
return;
441491
}
492+
if (!this.context && !this.options.resource) return callback();
493+
442494
compilation.fileSystemInfo.createSnapshot(
443495
startTime,
444496
null,
445-
[this.context],
497+
this.context
498+
? [this.context]
499+
: typeof this.options.resource === "string"
500+
? [this.options.resource]
501+
: /** @type {string[]} */ (this.options.resource),
446502
null,
447503
SNAPSHOT_OPTIONS,
448504
(err, snapshot) => {
@@ -466,7 +522,15 @@ class ContextModule extends Module {
466522
missingDependencies,
467523
buildDependencies
468524
) {
469-
contextDependencies.add(this.context);
525+
if (this.context) {
526+
contextDependencies.add(this.context);
527+
} else if (typeof this.options.resource === "string") {
528+
contextDependencies.add(this.options.resource);
529+
} else if (this.options.resource === false) {
530+
return;
531+
} else {
532+
for (const res of this.options.resource) contextDependencies.add(res);
533+
}
470534
}
471535

472536
/**

lib/ContextModuleFactory.js

+65-21
Original file line numberDiff line numberDiff line change
@@ -167,18 +167,22 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
167167
asyncLib.parallel(
168168
[
169169
callback => {
170+
const results = [];
171+
const yield_ = obj => results.push(obj);
172+
170173
contextResolver.resolve(
171174
{},
172175
context,
173176
resource,
174177
{
175178
fileDependencies,
176179
missingDependencies,
177-
contextDependencies
180+
contextDependencies,
181+
yield: yield_
178182
},
179-
(err, result) => {
183+
err => {
180184
if (err) return callback(err);
181-
callback(null, result);
185+
callback(null, results);
182186
}
183187
);
184188
},
@@ -213,15 +217,25 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
213217
contextDependencies
214218
});
215219
}
216-
220+
let [contextResult, loaderResult] = result;
221+
if (contextResult.length > 1) {
222+
const first = contextResult[0];
223+
contextResult = contextResult.filter(r => r.path);
224+
if (contextResult.length === 0) contextResult.push(first);
225+
}
217226
this.hooks.afterResolve.callAsync(
218227
{
219228
addon:
220229
loadersPrefix +
221-
result[1].join("!") +
222-
(result[1].length > 0 ? "!" : ""),
223-
resource: result[0],
230+
loaderResult.join("!") +
231+
(loaderResult.length > 0 ? "!" : ""),
232+
resource:
233+
contextResult.length > 1
234+
? contextResult.map(r => r.path)
235+
: contextResult[0].path,
224236
resolveDependencies: this.resolveDependencies.bind(this),
237+
resourceQuery: contextResult[0].query,
238+
resourceFragment: contextResult[0].fragment,
225239
...beforeResolveResult
226240
},
227241
(err, result) => {
@@ -278,26 +292,28 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
278292
} = options;
279293
if (!regExp || !resource) return callback(null, []);
280294

281-
const addDirectoryChecked = (directory, visited, callback) => {
295+
let severalContexts = false;
296+
const addDirectoryChecked = (ctx, directory, visited, callback) => {
282297
fs.realpath(directory, (err, realPath) => {
283298
if (err) return callback(err);
284299
if (visited.has(realPath)) return callback(null, []);
285300
let recursionStack;
286301
addDirectory(
302+
ctx,
287303
directory,
288-
(dir, callback) => {
304+
(_, dir, callback) => {
289305
if (recursionStack === undefined) {
290306
recursionStack = new Set(visited);
291307
recursionStack.add(realPath);
292308
}
293-
addDirectoryChecked(dir, recursionStack, callback);
309+
addDirectoryChecked(ctx, dir, recursionStack, callback);
294310
},
295311
callback
296312
);
297313
});
298314
};
299315

300-
const addDirectory = (directory, addSubDirectory, callback) => {
316+
const addDirectory = (ctx, directory, addSubDirectory, callback) => {
301317
fs.readdir(directory, (err, files) => {
302318
if (err) return callback(err);
303319
const processedFiles = cmf.hooks.contextModuleFiles.call(
@@ -324,16 +340,15 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
324340

325341
if (stat.isDirectory()) {
326342
if (!recursive) return callback();
327-
addSubDirectory(subResource, callback);
343+
addSubDirectory(ctx, subResource, callback);
328344
} else if (
329345
stat.isFile() &&
330346
(!include || subResource.match(include))
331347
) {
332348
const obj = {
333-
context: resource,
349+
context: ctx,
334350
request:
335-
"." +
336-
subResource.substr(resource.length).replace(/\\/g, "/")
351+
"." + subResource.substr(ctx.length).replace(/\\/g, "/")
337352
};
338353

339354
this.hooks.alternativeRequests.callAsync(
@@ -344,8 +359,11 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
344359
alternatives = alternatives
345360
.filter(obj => regExp.test(obj.request))
346361
.map(obj => {
362+
const request = severalContexts
363+
? join(fs, obj.context, obj.request)
364+
: obj.request;
347365
const dep = new ContextElementDependency(
348-
obj.request + resourceQuery + resourceFragment,
366+
request + resourceQuery + resourceFragment,
349367
obj.request,
350368
typePrefix,
351369
category,
@@ -382,12 +400,38 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
382400
});
383401
};
384402

385-
if (typeof fs.realpath === "function") {
386-
addDirectoryChecked(resource, new Set(), callback);
403+
const addSubDirectory = (ctx, dir, callback) =>
404+
addDirectory(ctx, dir, addSubDirectory, callback);
405+
406+
const visitResource = (resource, callback) => {
407+
if (typeof fs.realpath === "function") {
408+
addDirectoryChecked(resource, resource, new Set(), callback);
409+
} else {
410+
addDirectory(resource, resource, addSubDirectory, callback);
411+
}
412+
};
413+
414+
if (typeof resource === "string") {
415+
visitResource(resource, callback);
387416
} else {
388-
const addSubDirectory = (dir, callback) =>
389-
addDirectory(dir, addSubDirectory, callback);
390-
addDirectory(resource, addSubDirectory, callback);
417+
severalContexts = true;
418+
asyncLib.map(resource, visitResource, (err, result) => {
419+
if (err) return callback(err);
420+
421+
// result dependencies should have unique userRequest
422+
// ordered by resolve result
423+
const temp = new Set();
424+
const res = [];
425+
for (let i = 0; i < result.length; i++) {
426+
const inner = result[i];
427+
for (const el of inner) {
428+
if (temp.has(el.userRequest)) continue;
429+
res.push(el);
430+
temp.add(el.userRequest);
431+
}
432+
}
433+
callback(null, res);
434+
});
391435
}
392436
}
393437
};

0 commit comments

Comments
 (0)