Skip to content

Commit 4bdd001

Browse files
authored
Merge pull request #326 from webpack/add-yield-in-resolve-context
add yield in resolve context
2 parents d1a3cb6 + 3e519b6 commit 4bdd001

File tree

7 files changed

+228
-2
lines changed

7 files changed

+228
-2
lines changed

lib/Resolver.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ const {
104104
* @property {WriteOnlySet<string>=} missingDependencies dependencies that was not found on file system
105105
* @property {Set<StackEntry>=} stack set of hooks' calls. For instance, `resolve → parsedResolve → describedResolve`,
106106
* @property {(function(string): void)=} log log function
107+
* @property {(function (ResolveRequest): void)=} yield yield result, if provided plugins can return several results
107108
*/
108109

109110
/** @typedef {AsyncSeriesBailHook<[ResolveRequest, ResolveContext], ResolveRequest | null>} ResolveStepHook */
@@ -272,6 +273,16 @@ class Resolver {
272273
request: request
273274
};
274275

276+
let yield_;
277+
let yieldCalled = false;
278+
if (typeof resolveContext.yield === "function") {
279+
const old = resolveContext.yield;
280+
yield_ = obj => {
281+
yieldCalled = true;
282+
old(obj);
283+
};
284+
}
285+
275286
const message = `resolve '${request}' in '${path}'`;
276287

277288
const finishResolved = result => {
@@ -309,6 +320,7 @@ class Resolver {
309320
parentLog(msg);
310321
log.push(msg);
311322
},
323+
yield: yield_,
312324
fileDependencies: resolveContext.fileDependencies,
313325
contextDependencies: resolveContext.contextDependencies,
314326
missingDependencies: resolveContext.missingDependencies,
@@ -318,6 +330,7 @@ class Resolver {
318330
if (err) return callback(err);
319331

320332
if (result) return finishResolved(result);
333+
if (yieldCalled) return callback(null);
321334

322335
return finishWithoutResolve(log);
323336
}
@@ -331,6 +344,7 @@ class Resolver {
331344
message,
332345
{
333346
log: undefined,
347+
yield: yield_,
334348
fileDependencies: resolveContext.fileDependencies,
335349
contextDependencies: resolveContext.contextDependencies,
336350
missingDependencies: resolveContext.missingDependencies,
@@ -340,6 +354,7 @@ class Resolver {
340354
if (err) return callback(err);
341355

342356
if (result) return finishResolved(result);
357+
if (yieldCalled) return callback(null);
343358

344359
// log is missing for the error details
345360
// so we redo the resolving for the log info
@@ -354,11 +369,15 @@ class Resolver {
354369
message,
355370
{
356371
log: msg => log.push(msg),
372+
yield: yield_,
357373
stack: resolveContext.stack
358374
},
359-
(err, result) => {
375+
err => {
360376
if (err) return callback(err);
361377

378+
// In a case that there is a race condition and yield will be called
379+
if (yieldCalled) return callback(null);
380+
362381
return finishWithoutResolve(log);
363382
}
364383
);
@@ -397,6 +416,7 @@ class Resolver {
397416
const innerContext = createInnerContext(
398417
{
399418
log: resolveContext.log,
419+
yield: resolveContext.yield,
400420
fileDependencies: resolveContext.fileDependencies,
401421
contextDependencies: resolveContext.contextDependencies,
402422
missingDependencies: resolveContext.missingDependencies,

lib/ResultPlugin.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ module.exports = class ResultPlugin {
2929
resolverContext.log("reporting result " + obj.path);
3030
resolver.hooks.result.callAsync(obj, resolverContext, err => {
3131
if (err) return callback(err);
32-
callback(null, obj);
32+
if (typeof resolverContext.yield === "function") {
33+
resolverContext.yield(obj);
34+
callback(null, null);
35+
} else {
36+
callback(null, obj);
37+
}
3338
});
3439
}
3540
);

lib/createInnerContext.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ module.exports = function createInnerContext(
2727
}
2828
const childContext = {
2929
log: innerLog,
30+
yield: options.yield,
3031
fileDependencies: options.fileDependencies,
3132
contextDependencies: options.contextDependencies,
3233
missingDependencies: options.missingDependencies,

lib/getPaths.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"use strict";
77

88
module.exports = function getPaths(path) {
9+
if (path === "/") return { paths: ["/"], seqments: [""] };
910
const parts = path.split(/(.*?[\\/]+)/);
1011
const paths = [path];
1112
const seqments = [parts[parts.length - 1]];

test/getPaths.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require("should");
2+
3+
const getPaths = require("../lib/getPaths");
4+
5+
/**
6+
* @type {[string,{paths: string[], seqments: string[]}][]}
7+
*/
8+
const cases = [
9+
["/a", { paths: ["/a", "/"], seqments: ["a", "/"] }],
10+
["/a/", { paths: ["/a/", "/a", "/"], seqments: ["", "a", "/"] }],
11+
["/a/b", { paths: ["/a/b", "/a", "/"], seqments: ["b", "a", "/"] }],
12+
[
13+
"/a/b/",
14+
{ paths: ["/a/b/", "/a/b", "/a", "/"], seqments: ["", "b", "a", "/"] }
15+
],
16+
["/", { paths: ["/"], seqments: [""] }]
17+
];
18+
19+
cases.forEach(case_ => {
20+
it(case_[0], () => {
21+
const { paths, seqments } = getPaths(case_[0]);
22+
paths.should.be.eql(case_[1].paths);
23+
seqments.should.be.eql(case_[1].seqments);
24+
});
25+
});

test/yield.js

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
const should = require("should");
2+
3+
const { ResolverFactory } = require("../");
4+
const { Volume } = require("memfs");
5+
6+
/** @typedef {import("../lib/Resolver").ResolveContext} ResolveContext */
7+
/** @typedef {ResolveContext & Required<Pick<ResolveContext, 'yield' | 'fileDependencies' | 'contextDependencies' | 'missingDependencies'>>} StrictResolveContext */
8+
9+
const fileSystem = Volume.fromJSON(
10+
{
11+
"/a/foo/a": "",
12+
"/a/foo/b": "",
13+
"/a/foo-2/b": "",
14+
"/a/foo-2/c": "",
15+
"/b/foo/a": ""
16+
},
17+
"/"
18+
);
19+
20+
describe("should resolve all aliases", () => {
21+
const resolver = ResolverFactory.createResolver({
22+
extensions: [".js"],
23+
alias: {
24+
index: ["/a/foo", "/a/foo-2"]
25+
},
26+
modules: "/",
27+
fileSystem
28+
});
29+
const modulesResolver = ResolverFactory.createResolver({
30+
extensions: [".js"],
31+
modules: ["a", "b"],
32+
fileSystem
33+
});
34+
35+
it("should yield all b files", done => {
36+
const paths = [];
37+
const yield_ = ({ path }) => paths.push(path);
38+
const fileDependencies = new Set();
39+
const contextDependencies = new Set();
40+
const missingDependencies = new Set();
41+
/** @type {ResolveContext} */
42+
const context = {
43+
yield: yield_,
44+
fileDependencies,
45+
contextDependencies,
46+
missingDependencies
47+
};
48+
49+
resolver.resolve({}, "/", "index/b", context, (err, result) => {
50+
should(err).be.eql(null);
51+
should(result).be.eql(undefined);
52+
should(paths).be.eql(["/a/foo/b", "/a/foo-2/b"]);
53+
should(Array.from(fileDependencies.values()).sort()).be.eql([
54+
"/",
55+
"/a",
56+
"/a/foo",
57+
"/a/foo-2",
58+
"/a/foo-2/b",
59+
"/a/foo/b"
60+
]);
61+
should(Array.from(missingDependencies.values()).sort()).be.eql([
62+
"/a/foo-2/b",
63+
"/a/foo-2/b.js",
64+
"/a/foo-2/package.json",
65+
"/a/foo/b",
66+
"/a/foo/b.js",
67+
"/a/foo/package.json",
68+
"/a/package.json",
69+
"/package.json"
70+
]);
71+
should(Array.from(contextDependencies.values()).sort()).be.eql([]);
72+
done();
73+
});
74+
});
75+
76+
it("should yield all foo files", done => {
77+
const paths = [];
78+
const yield_ = ({ path }) => paths.push(path);
79+
const fileDependencies = new Set();
80+
const contextDependencies = new Set();
81+
const missingDependencies = new Set();
82+
/** @type {ResolveContext} */
83+
const context = {
84+
yield: yield_,
85+
fileDependencies,
86+
contextDependencies,
87+
missingDependencies
88+
};
89+
90+
modulesResolver.resolve({}, "/", "foo/a", context, (err, result) => {
91+
should(err).be.eql(null);
92+
should(result).be.eql(undefined);
93+
should(paths).be.eql(["/a/foo/a", "/b/foo/a"]);
94+
should(Array.from(fileDependencies.values()).sort()).be.eql([
95+
"/",
96+
"/a",
97+
"/a/foo",
98+
"/a/foo/a",
99+
"/b",
100+
"/b/foo",
101+
"/b/foo/a"
102+
]);
103+
should(Array.from(missingDependencies.values()).sort()).be.eql([
104+
"/a/foo/a",
105+
"/a/foo/a.js",
106+
"/a/foo/package.json",
107+
"/a/package.json",
108+
"/b/foo/a",
109+
"/b/foo/a.js",
110+
"/b/foo/package.json",
111+
"/b/package.json",
112+
"/package.json"
113+
]);
114+
should(Array.from(contextDependencies.values()).sort()).be.eql([]);
115+
done();
116+
});
117+
});
118+
119+
it("should yield c file", done => {
120+
const paths = [];
121+
const yield_ = ({ path }) => paths.push(path);
122+
/** @type {ResolveContext} */
123+
const context = {
124+
yield: yield_
125+
};
126+
127+
resolver.resolve({}, "/", "index/c", context, (err, result) => {
128+
should(err).be.eql(null);
129+
should(result).be.eql(undefined);
130+
should(paths).be.eql(["/a/foo-2/c"]);
131+
done();
132+
});
133+
});
134+
135+
it("should return error if no resolve", done => {
136+
const paths = [];
137+
const yield_ = ({ path }) => paths.push(path);
138+
const fileDependencies = new Set();
139+
const contextDependencies = new Set();
140+
const missingDependencies = new Set();
141+
/** @type {ResolveContext} */
142+
const context = {
143+
yield: yield_,
144+
fileDependencies,
145+
contextDependencies,
146+
missingDependencies
147+
};
148+
149+
resolver.resolve({}, "/", "index/unknown", context, (err, result) => {
150+
should(err).not.be.eql(null);
151+
should(err).not.be.eql(undefined);
152+
should(result).be.eql(undefined);
153+
should(paths).be.eql([]);
154+
should(Array.from(fileDependencies.values()).sort()).be.eql([]);
155+
should(Array.from(missingDependencies.values()).sort()).be.eql([
156+
"/a/foo-2/package.json",
157+
"/a/foo-2/unknown",
158+
"/a/foo-2/unknown.js",
159+
"/a/foo/package.json",
160+
"/a/foo/unknown",
161+
"/a/foo/unknown.js",
162+
"/a/package.json",
163+
"/package.json"
164+
]);
165+
should(Array.from(contextDependencies.values()).sort()).be.eql([]);
166+
done();
167+
});
168+
});
169+
});

types.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ declare interface ResolveContext {
204204
* log function
205205
*/
206206
log?: (arg0: string) => void;
207+
208+
/**
209+
* yield result, if provided plugins can return several results
210+
*/
211+
yield?: (arg0: ResolveRequest) => void;
207212
}
208213
declare interface ResolveOptions {
209214
alias: AliasOption[];

0 commit comments

Comments
 (0)