Skip to content

Commit f57c7c4

Browse files
fix: exports and imports array target resolving
2 parents ce50aa8 + cea5ad6 commit f57c7c4

File tree

13 files changed

+295
-48
lines changed

13 files changed

+295
-48
lines changed

lib/ExportsFieldPlugin.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,18 @@ module.exports = class ExportsFieldPlugin {
125125
/**
126126
* @param {string} p path
127127
* @param {(err?: null|Error, result?: null|ResolveRequest) => void} callback callback
128+
* @param {number} i index
128129
* @returns {void}
129130
*/
130-
(p, callback) => {
131+
(p, callback, i) => {
131132
const parsedIdentifier = parseIdentifier(p);
132133

133134
if (!parsedIdentifier) return callback();
134135

135136
const [relativePath, query, fragment] = parsedIdentifier;
136137

137138
if (relativePath.length === 0 || !relativePath.startsWith("./")) {
138-
if (paths.length === 1) {
139+
if (paths.length === i) {
139140
return callback(
140141
new Error(
141142
`Invalid "exports" target "${p}" defined for "${usedField}" in the package config ${request.descriptionFilePath}, targets must start with "./"`
@@ -150,10 +151,10 @@ module.exports = class ExportsFieldPlugin {
150151
invalidSegmentRegEx.exec(relativePath.slice(2)) !== null &&
151152
deprecatedInvalidSegmentRegEx.test(relativePath.slice(2)) !== null
152153
) {
153-
if (paths.length === 1) {
154+
if (paths.length === i) {
154155
return callback(
155156
new Error(
156-
`Trying to access out of package scope. Requesting ${relativePath}`
157+
`Invalid "exports" target "${p}" defined for "${usedField}" in the package config ${request.descriptionFilePath}, targets must start with "./"`
157158
)
158159
);
159160
}
@@ -179,7 +180,12 @@ module.exports = class ExportsFieldPlugin {
179180
obj,
180181
"using exports field: " + p,
181182
resolveContext,
182-
callback
183+
(err, result) => {
184+
if (err) return callback(err);
185+
// Don't allow to continue - https://github.com/webpack/enhanced-resolve/issues/400
186+
if (result === undefined) return callback(null, null);
187+
callback(null, result);
188+
}
183189
);
184190
},
185191
/**

lib/ImportsFieldPlugin.js

+22-6
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ module.exports = class ImportsFieldPlugin {
8585

8686
/** @type {string[]} */
8787
let paths;
88+
/** @type {string | null} */
89+
let usedField;
8890

8991
try {
9092
// We attach the cache to the description file instead of the importsField value
@@ -100,7 +102,10 @@ module.exports = class ImportsFieldPlugin {
100102
fieldProcessor
101103
);
102104
}
103-
[paths] = fieldProcessor(remainingRequest, this.conditionNames);
105+
[paths, usedField] = fieldProcessor(
106+
remainingRequest,
107+
this.conditionNames
108+
);
104109
} catch (/** @type {unknown} */ err) {
105110
if (resolveContext.log) {
106111
resolveContext.log(
@@ -123,9 +128,10 @@ module.exports = class ImportsFieldPlugin {
123128
/**
124129
* @param {string} p path
125130
* @param {(err?: null|Error, result?: null|ResolveRequest) => void} callback callback
131+
* @param {number} i index
126132
* @returns {void}
127133
*/
128-
(p, callback) => {
134+
(p, callback, i) => {
129135
const parsedIdentifier = parseIdentifier(p);
130136

131137
if (!parsedIdentifier) return callback();
@@ -139,10 +145,10 @@ module.exports = class ImportsFieldPlugin {
139145
invalidSegmentRegEx.exec(path_.slice(2)) !== null &&
140146
deprecatedInvalidSegmentRegEx.test(path_.slice(2)) !== null
141147
) {
142-
if (paths.length === 1) {
148+
if (paths.length === i) {
143149
return callback(
144150
new Error(
145-
`Trying to access out of package scope. Requesting ${path_}`
151+
`Invalid "imports" target "${p}" defined for "${usedField}" in the package config ${request.descriptionFilePath}, targets must start with "./"`
146152
)
147153
);
148154
}
@@ -168,7 +174,12 @@ module.exports = class ImportsFieldPlugin {
168174
obj,
169175
"using imports field: " + p,
170176
resolveContext,
171-
callback
177+
(err, result) => {
178+
if (err) return callback(err);
179+
// Don't allow to continue - https://github.com/webpack/enhanced-resolve/issues/400
180+
if (result === undefined) return callback(null, null);
181+
callback(null, result);
182+
}
172183
);
173184
break;
174185
}
@@ -190,7 +201,12 @@ module.exports = class ImportsFieldPlugin {
190201
obj,
191202
"using imports field: " + p,
192203
resolveContext,
193-
callback
204+
(err, result) => {
205+
if (err) return callback(err);
206+
// Don't allow to continue - https://github.com/webpack/enhanced-resolve/issues/400
207+
if (result === undefined) return callback(null, null);
208+
callback(null, result);
209+
}
194210
);
195211
}
196212
}

lib/forEachBail.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* @template Z
2323
* @param {T[]} array array
2424
* @param {Iterator<T, Z>} iterator iterator
25-
* @param {(err?: null|Error, result?: null|Z) => void} callback callback after all items are iterated
25+
* @param {(err?: null|Error, result?: null|Z, i?: number) => void} callback callback after all items are iterated
2626
* @returns {void}
2727
*/
2828
module.exports = function forEachBail(array, iterator, callback) {
@@ -36,7 +36,7 @@ module.exports = function forEachBail(array, iterator, callback) {
3636
array[i++],
3737
(err, result) => {
3838
if (err || result !== undefined || i >= array.length) {
39-
return callback(err, result);
39+
return callback(err, result, i);
4040
}
4141
if (loop === false) while (next());
4242
loop = true;

test/__snapshots__/exportsField.test.js.snap

-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ Array [
99
" looking for modules in .../node_modules",
1010
" existing directory .../node_modules/exports-field",
1111
" using description file: .../node_modules/exports-field/package.json (relative path: .)",
12-
" using exports field: ./lib/lib2/browser.js",
13-
" using description file: .../node_modules/exports-field/package.json (relative path: ./lib/lib2/browser.js)",
14-
" .../node_modules/exports-field/lib/lib2/browser.js doesn't exist",
1512
" using exports field: ./lib/browser.js",
1613
" using description file: .../node_modules/exports-field/package.json (relative path: ./lib/browser.js)",
1714
" existing file: .../node_modules/exports-field/lib/browser.js",

test/__snapshots__/importsField.test.js.snap

-8
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,6 @@ Array [
1212
" looking for modules in .../node_modules",
1313
" existing directory .../node_modules/a",
1414
" using description file: .../node_modules/a/package.json (relative path: .)",
15-
" using exports field: ./lib/lib2/index.js",
16-
" using description file: .../node_modules/a/package.json (relative path: ./lib/lib2/index.js)",
17-
" no extension",
18-
" .../node_modules/a/lib/lib2/index.js doesn't exist",
19-
" .js",
20-
" .../node_modules/a/lib/lib2/index.js.js doesn't exist",
21-
" as directory",
22-
" .../node_modules/a/lib/lib2/index.js doesn't exist",
2315
" using exports field: ./lib/index.js",
2416
" using description file: .../node_modules/a/package.json (relative path: ./lib/index.js)",
2517
" no extension",

test/exportsField.test.js

+162-9
Original file line numberDiff line numberDiff line change
@@ -2181,7 +2181,7 @@ describe("ExportsFieldPlugin", () => {
21812181
it("resolve using exports field, not a browser field #1", done => {
21822182
const resolver = ResolverFactory.createResolver({
21832183
aliasFields: ["browser"],
2184-
conditionNames: ["webpack"],
2184+
conditionNames: ["custom"],
21852185
extensions: [".js"],
21862186
fileSystem: nodeFileSystem
21872187
});
@@ -2205,9 +2205,9 @@ describe("ExportsFieldPlugin", () => {
22052205
it("resolve using exports field and a browser alias field #2", done => {
22062206
const resolver = ResolverFactory.createResolver({
22072207
aliasFields: ["browser"],
2208+
conditionNames: ["node"],
22082209
extensions: [".js"],
2209-
fileSystem: nodeFileSystem,
2210-
conditionNames: ["node"]
2210+
fileSystem: nodeFileSystem
22112211
});
22122212

22132213
resolver.resolve(
@@ -2266,7 +2266,7 @@ describe("ExportsFieldPlugin", () => {
22662266
if (err) return done(err);
22672267
if (!result) return done(new Error("No result"));
22682268
expect(result).toEqual(
2269-
path.resolve(fixture2, "node_modules/exports-field/lib/lib2/main.js")
2269+
path.resolve(fixture2, "node_modules/exports-field/lib/main.js")
22702270
);
22712271
done();
22722272
}
@@ -2283,7 +2283,7 @@ describe("ExportsFieldPlugin", () => {
22832283
if (err) return done(err);
22842284
if (!result) return done(new Error("No result"));
22852285
expect(result).toEqual(
2286-
path.resolve(fixture, "node_modules/exports-field/lib/lib2/main.js")
2286+
path.resolve(fixture, "node_modules/exports-field/lib/main.js")
22872287
);
22882288
done();
22892289
}
@@ -2406,7 +2406,9 @@ describe("ExportsFieldPlugin", () => {
24062406
(err, result) => {
24072407
if (!err) return done(new Error(`expect error, got ${result}`));
24082408
expect(err).toBeInstanceOf(Error);
2409-
expect(err.message).toMatch(/Can't resolve/);
2409+
expect(err.message).toMatch(
2410+
/Invalid "exports" target "\.\/lib\/lib2\/\.\.\/\.\.\/\.\.\/a\.js" defined for "\.\/dist\/"/
2411+
);
24102412
done();
24112413
}
24122414
);
@@ -2421,7 +2423,9 @@ describe("ExportsFieldPlugin", () => {
24212423
(err, result) => {
24222424
if (!err) return done(new Error(`expect error, got ${result}`));
24232425
expect(err).toBeInstanceOf(Error);
2424-
expect(err.message).toMatch(/out of package scope/);
2426+
expect(err.message).toMatch(
2427+
/Invalid "exports" target "\.\/\.\.\/\.\.\/a\.js" defined for "\.\/dist\/a\.js"/
2428+
);
24252429
done();
24262430
}
24272431
);
@@ -2572,7 +2576,9 @@ describe("ExportsFieldPlugin", () => {
25722576
resolver.resolve({}, fixture4, "exports-field", {}, (err, result) => {
25732577
if (!err) return done(new Error(`expect error, got ${result}`));
25742578
expect(err).toBeInstanceOf(Error);
2575-
expect(err.message).toMatch(/Trying to access out of package scope/);
2579+
expect(err.message).toMatch(
2580+
/Invalid "exports" target "\.\/a\/\.\.\/b\/\.\.\/\.\.\/pack1\/index\.js" defined for "\."/
2581+
);
25762582
done();
25772583
});
25782584
});
@@ -3000,7 +3006,9 @@ describe("ExportsFieldPlugin", () => {
30003006
(err, result) => {
30013007
if (!err) return done(new Error(`expect error, got ${result}`));
30023008
expect(err).toBeInstanceOf(Error);
3003-
expect(err.message).toMatch(/Can't resolve/);
3009+
expect(err.message).toMatch(
3010+
/Invalid "exports" target "foo" defined for "\.\/baz-multi"/
3011+
);
30043012
done();
30053013
}
30063014
);
@@ -3169,4 +3177,149 @@ describe("ExportsFieldPlugin", () => {
31693177
}
31703178
);
31713179
});
3180+
3181+
it("invalid package target #15", done => {
3182+
resolver.resolve(
3183+
{},
3184+
fixture5,
3185+
"@exports-field/bad-specifier/non-existent.js",
3186+
{},
3187+
(err, result) => {
3188+
if (!err) return done(new Error(`expect error, got ${result}`));
3189+
expect(err).toBeInstanceOf(Error);
3190+
expect(err.message).toMatch(
3191+
/Can't resolve '@exports-field\/bad-specifier\/non-existent\.js'/
3192+
);
3193+
done();
3194+
}
3195+
);
3196+
});
3197+
3198+
it("invalid package target #16", done => {
3199+
resolver.resolve(
3200+
{},
3201+
fixture5,
3202+
"@exports-field/bad-specifier/dep/multi1",
3203+
{},
3204+
(err, result) => {
3205+
if (!err) return done(new Error(`expect error, got ${result}`));
3206+
expect(err).toBeInstanceOf(Error);
3207+
expect(err.message).toMatch(
3208+
/Invalid "exports" target "\.\.\/\.\.\/test\/foo" defined for "\.\/dep\/multi1"/
3209+
);
3210+
done();
3211+
}
3212+
);
3213+
});
3214+
3215+
it("invalid package target #17", done => {
3216+
resolver.resolve(
3217+
{},
3218+
fixture5,
3219+
"@exports-field/bad-specifier/dep/multi2",
3220+
{},
3221+
(err, result) => {
3222+
if (!err) return done(new Error(`expect error, got ${result}`));
3223+
expect(err).toBeInstanceOf(Error);
3224+
expect(err.message).toMatch(
3225+
/Invalid "exports" target "\.\.\/\.\.\/test" defined for "\.\/dep\/multi2"/
3226+
);
3227+
done();
3228+
}
3229+
);
3230+
});
3231+
3232+
it("invalid package target #18", done => {
3233+
resolver.resolve(
3234+
{},
3235+
fixture5,
3236+
"@exports-field/bad-specifier/dep/multi4",
3237+
{},
3238+
(err, result) => {
3239+
if (!err) return done(new Error(`expect error, got ${result}`));
3240+
expect(err).toBeInstanceOf(Error);
3241+
expect(err.message).toMatch(
3242+
/Invalid "exports" target "\.\/c\/\.\.\/b\/\.\.\/\.\.\/pack1\/index\.js" defined for "\.\/dep\/multi4"/
3243+
);
3244+
done();
3245+
}
3246+
);
3247+
});
3248+
3249+
it("invalid package target #19", done => {
3250+
resolver.resolve(
3251+
{},
3252+
fixture5,
3253+
"@exports-field/bad-specifier/dep/multi5",
3254+
{},
3255+
(err, result) => {
3256+
if (!err) return done(new Error(`expect error, got ${result}`));
3257+
expect(err).toBeInstanceOf(Error);
3258+
expect(err.message).toMatch(
3259+
/Invalid "exports" target "\.\/a\/\.\.\/b\/\.\.\/\.\.\/pack1\/index\.js" defined for "\.\/dep\/multi5"/
3260+
);
3261+
done();
3262+
}
3263+
);
3264+
});
3265+
3266+
it("should resolve the valid thing in array of export #1", done => {
3267+
resolver.resolve(
3268+
{},
3269+
fixture5,
3270+
"@exports-field/bad-specifier/bad-specifier.js",
3271+
{},
3272+
(err, result) => {
3273+
if (err) return done(err);
3274+
if (!result) return done(new Error("No result"));
3275+
expect(result).toEqual(path.resolve(fixture5, "./a.js"));
3276+
done();
3277+
}
3278+
);
3279+
});
3280+
3281+
it("should resolve the valid thing in array of export #2", done => {
3282+
resolver.resolve(
3283+
{},
3284+
fixture5,
3285+
"@exports-field/bad-specifier/bad-specifier1.js",
3286+
{},
3287+
(err, result) => {
3288+
if (err) return done(err);
3289+
if (!result) return done(new Error("No result"));
3290+
expect(result).toEqual(path.resolve(fixture5, "./a.js"));
3291+
done();
3292+
}
3293+
);
3294+
});
3295+
3296+
it("should resolve the valid thing in array of export #3", done => {
3297+
resolver.resolve(
3298+
{},
3299+
fixture5,
3300+
"@exports-field/bad-specifier/dep/multi",
3301+
{},
3302+
(err, result) => {
3303+
if (err) return done(err);
3304+
if (!result) return done(new Error("No result"));
3305+
expect(result).toEqual(path.resolve(fixture5, "./a.js"));
3306+
done();
3307+
}
3308+
);
3309+
});
3310+
3311+
it("should resolve the valid thing in array of export #4", done => {
3312+
resolver.resolve(
3313+
{},
3314+
fixture5,
3315+
"@exports-field/bad-specifier/dep/multi3",
3316+
{},
3317+
(err, result) => {
3318+
if (err) return done(err);
3319+
if (!result) return done(new Error("No result"));
3320+
expect(result).toEqual(path.resolve(fixture5, "./a.js"));
3321+
done();
3322+
}
3323+
);
3324+
});
31723325
});

0 commit comments

Comments
 (0)