Skip to content

Commit f52b8c5

Browse files
authored
Merge pull request #14757 from webpack/fix-14755
use real loader path
2 parents 896efde + 5e12663 commit f52b8c5

8 files changed

Lines changed: 149 additions & 71 deletions

File tree

lib/NormalModuleFactory.js

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ const LazySet = require("./util/LazySet");
2828
const { getScheme } = require("./util/URLAbsoluteSpecifier");
2929
const { cachedCleverMerge, cachedSetProperty } = require("./util/cleverMerge");
3030
const { join } = require("./util/fs");
31-
const { parseResource } = require("./util/identifier");
31+
const {
32+
parseResource,
33+
parseResourceWithoutFragment
34+
} = require("./util/identifier");
3235

3336
/** @typedef {import("../declarations/WebpackOptions").ModuleOptionsNormalized} ModuleOptions */
3437
/** @typedef {import("./Generator")} Generator */
@@ -66,6 +69,11 @@ const { parseResource } = require("./util/identifier");
6669

6770
/** @typedef {ResourceData & { data: Record<string, any> }} ResourceDataWithData */
6871

72+
/** @typedef {Object} ParsedLoaderRequest
73+
* @property {string} loader loader
74+
* @property {string|undefined} options options
75+
*/
76+
6977
const EMPTY_RESOLVE_OPTIONS = {};
7078
const EMPTY_PARSER_OPTIONS = {};
7179
const EMPTY_GENERATOR_OPTIONS = {};
@@ -97,27 +105,6 @@ const stringifyLoadersAndResource = (loaders, resource) => {
97105
return str + resource;
98106
};
99107

100-
/**
101-
* @param {string} resultString resultString
102-
* @returns {{loader: string, options: string|undefined}} parsed loader request
103-
*/
104-
const identToLoaderRequest = resultString => {
105-
const idx = resultString.indexOf("?");
106-
if (idx >= 0) {
107-
const loader = resultString.substr(0, idx);
108-
const options = resultString.substr(idx + 1);
109-
return {
110-
loader,
111-
options
112-
};
113-
} else {
114-
return {
115-
loader: resultString,
116-
options: undefined
117-
};
118-
}
119-
};
120-
121108
const needCalls = (times, callback) => {
122109
return err => {
123110
if (--times === 0) {
@@ -264,6 +251,9 @@ class NormalModuleFactory extends ModuleFactory {
264251
const cacheParseResource = parseResource.bindCache(
265252
associatedObjectForCache
266253
);
254+
const cachedParseResourceWithoutFragment =
255+
parseResourceWithoutFragment.bindCache(associatedObjectForCache);
256+
this._parseResourceWithoutFragment = cachedParseResourceWithoutFragment;
267257

268258
this.hooks.factorize.tapAsync(
269259
{
@@ -351,7 +341,7 @@ class NormalModuleFactory extends ModuleFactory {
351341
let matchResourceData = undefined;
352342
/** @type {string} */
353343
let unresolvedResource;
354-
/** @type {{loader: string, options: string|undefined}[]} */
344+
/** @type {ParsedLoaderRequest[]} */
355345
let elements;
356346
let noPreAutoLoaders = false;
357347
let noAutoLoaders = false;
@@ -405,7 +395,13 @@ class NormalModuleFactory extends ModuleFactory {
405395
)
406396
.split(/!+/);
407397
unresolvedResource = rawElements.pop();
408-
elements = rawElements.map(identToLoaderRequest);
398+
elements = rawElements.map(el => {
399+
const { path, query } = cachedParseResourceWithoutFragment(el);
400+
return {
401+
loader: path,
402+
options: query ? query.slice(1) : undefined
403+
};
404+
});
409405
scheme = getScheme(unresolvedResource);
410406
} else {
411407
unresolvedResource = requestWithoutMatchResource;
@@ -1017,12 +1013,14 @@ If changing the source code is not an option there is also a resolve options cal
10171013
}
10181014
if (err) return callback(err);
10191015

1020-
const parsedResult = identToLoaderRequest(result);
1016+
const parsedResult = this._parseResourceWithoutFragment(result);
10211017
const resolved = {
1022-
loader: parsedResult.loader,
1018+
loader: parsedResult.path,
10231019
options:
10241020
item.options === undefined
1025-
? parsedResult.options
1021+
? parsedResult.query
1022+
? parsedResult.query.slice(1)
1023+
: undefined
10261024
: item.options,
10271025
ident: item.options === undefined ? undefined : item.ident
10281026
};

lib/util/identifier.js

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,49 @@ const requestToAbsolute = (context, relativePath) => {
8181
return relativePath;
8282
};
8383

84-
const makeCacheable = fn => {
84+
const makeCacheable = realFn => {
85+
/** @type {WeakMap<object, Map<string, ParsedResource>>} */
86+
const cache = new WeakMap();
87+
88+
const getCache = associatedObjectForCache => {
89+
const entry = cache.get(associatedObjectForCache);
90+
if (entry !== undefined) return entry;
91+
/** @type {Map<string, ParsedResource>} */
92+
const map = new Map();
93+
cache.set(associatedObjectForCache, map);
94+
return map;
95+
};
96+
97+
/**
98+
* @param {string} str the path with query and fragment
99+
* @param {Object=} associatedObjectForCache an object to which the cache will be attached
100+
* @returns {ParsedResource} parsed parts
101+
*/
102+
const fn = (str, associatedObjectForCache) => {
103+
if (!associatedObjectForCache) return realFn(str);
104+
const cache = getCache(associatedObjectForCache);
105+
const entry = cache.get(str);
106+
if (entry !== undefined) return entry;
107+
const result = realFn(str);
108+
cache.set(str, result);
109+
return result;
110+
};
111+
112+
fn.bindCache = associatedObjectForCache => {
113+
const cache = getCache(associatedObjectForCache);
114+
return str => {
115+
const entry = cache.get(str);
116+
if (entry !== undefined) return entry;
117+
const result = realFn(str);
118+
cache.set(str, result);
119+
return result;
120+
};
121+
};
122+
123+
return fn;
124+
};
125+
126+
const makeCacheableWithContext = fn => {
85127
/** @type {WeakMap<object, Map<string, Map<string, string>>>} */
86128
const cache = new WeakMap();
87129

@@ -215,7 +257,7 @@ const _makePathsRelative = (context, identifier) => {
215257
.join("");
216258
};
217259

218-
exports.makePathsRelative = makeCacheable(_makePathsRelative);
260+
exports.makePathsRelative = makeCacheableWithContext(_makePathsRelative);
219261

220262
/**
221263
*
@@ -230,7 +272,7 @@ const _makePathsAbsolute = (context, identifier) => {
230272
.join("");
231273
};
232274

233-
exports.makePathsAbsolute = makeCacheable(_makePathsAbsolute);
275+
exports.makePathsAbsolute = makeCacheableWithContext(_makePathsAbsolute);
234276

235277
/**
236278
* @param {string} context absolute context path
@@ -244,7 +286,7 @@ const _contextify = (context, request) => {
244286
.join("!");
245287
};
246288

247-
const contextify = makeCacheable(_contextify);
289+
const contextify = makeCacheableWithContext(_contextify);
248290
exports.contextify = contextify;
249291

250292
/**
@@ -259,13 +301,15 @@ const _absolutify = (context, request) => {
259301
.join("!");
260302
};
261303

262-
const absolutify = makeCacheable(_absolutify);
304+
const absolutify = makeCacheableWithContext(_absolutify);
263305
exports.absolutify = absolutify;
264306

265307
const PATH_QUERY_FRAGMENT_REGEXP =
266308
/^((?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
309+
const PATH_QUERY_REGEXP = /^((?:\0.|[^?\0])*)(\?.*)?$/;
267310

268311
/** @typedef {{ resource: string, path: string, query: string, fragment: string }} ParsedResource */
312+
/** @typedef {{ resource: string, path: string, query: string }} ParsedResourceWithoutFragment */
269313

270314
/**
271315
* @param {string} str the path with query and fragment
@@ -280,47 +324,24 @@ const _parseResource = str => {
280324
fragment: match[3] || ""
281325
};
282326
};
283-
exports.parseResource = (realFn => {
284-
/** @type {WeakMap<object, Map<string, ParsedResource>>} */
285-
const cache = new WeakMap();
327+
exports.parseResource = makeCacheable(_parseResource);
286328

287-
const getCache = associatedObjectForCache => {
288-
const entry = cache.get(associatedObjectForCache);
289-
if (entry !== undefined) return entry;
290-
/** @type {Map<string, ParsedResource>} */
291-
const map = new Map();
292-
cache.set(associatedObjectForCache, map);
293-
return map;
294-
};
295-
296-
/**
297-
* @param {string} str the path with query and fragment
298-
* @param {Object=} associatedObjectForCache an object to which the cache will be attached
299-
* @returns {ParsedResource} parsed parts
300-
*/
301-
const fn = (str, associatedObjectForCache) => {
302-
if (!associatedObjectForCache) return realFn(str);
303-
const cache = getCache(associatedObjectForCache);
304-
const entry = cache.get(str);
305-
if (entry !== undefined) return entry;
306-
const result = realFn(str);
307-
cache.set(str, result);
308-
return result;
309-
};
310-
311-
fn.bindCache = associatedObjectForCache => {
312-
const cache = getCache(associatedObjectForCache);
313-
return str => {
314-
const entry = cache.get(str);
315-
if (entry !== undefined) return entry;
316-
const result = realFn(str);
317-
cache.set(str, result);
318-
return result;
319-
};
329+
/**
330+
* Parse resource, skips fragment part
331+
* @param {string} str the path with query and fragment
332+
* @returns {ParsedResourceWithoutFragment} parsed parts
333+
*/
334+
const _parseResourceWithoutFragment = str => {
335+
const match = PATH_QUERY_REGEXP.exec(str);
336+
return {
337+
resource: str,
338+
path: match[1].replace(/\0(.)/g, "$1"),
339+
query: match[2] ? match[2].replace(/\0(.)/g, "$1") : ""
320340
};
321-
322-
return fn;
323-
})(_parseResource);
341+
};
342+
exports.parseResourceWithoutFragment = makeCacheable(
343+
_parseResourceWithoutFragment
344+
);
324345

325346
/**
326347
* @param {string} filename the filename which should be undone
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[a-z]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import regexp from './#.my';
2+
3+
it("should load regexp correctly", () => {
4+
expect(regexp.test("1")).toBe(false);
5+
expect(regexp.test("a")).toBe(true);
6+
});

test/configCases/loaders/#-issue-14755-#/node_modules/regexp-#-loader/index.js

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/configCases/loaders/#-issue-14755-#/node_modules/regexp-#-loader/package.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/** @type {import("../../../../").Configuration} */
2+
module.exports = {
3+
module: {
4+
rules: [
5+
{
6+
test: /\.my$/,
7+
loader: "regexp-#-loader"
8+
}
9+
]
10+
}
11+
};

test/identifier.unittest.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,34 @@ describe("util/identifier", () => {
8989
});
9090
}
9191
});
92+
93+
describe("parseResourceWithoutFragment", () => {
94+
// [input, expectedPath, expectedQuery]
95+
/** @type {[string, string, string][]} */
96+
const cases = [
97+
["path#hash?query", "path#hash", "?query"],
98+
["path?query#hash", "path", "?query#hash"],
99+
["\0#path\0??\0#query#hash", "#path?", "?#query#hash"],
100+
[
101+
'./loader.js?{"items":["a\0^","b\0!","c#","d"]}',
102+
"./loader.js",
103+
'?{"items":["a^","b!","c#","d"]}'
104+
],
105+
[
106+
"C:\\Users\\\0#\\repo\\loader.js?",
107+
"C:\\Users\\#\\repo\\loader.js",
108+
"?"
109+
],
110+
["/Users/\0#/repo/loader-\0#.js", "/Users/#/repo/loader-#.js", ""]
111+
];
112+
cases.forEach(case_ => {
113+
it(case_[0], () => {
114+
const { resource, path, query } =
115+
identifierUtil.parseResourceWithoutFragment(case_[0]);
116+
expect(case_[0]).toBe(resource);
117+
expect(case_[1]).toBe(path);
118+
expect(case_[2]).toBe(query);
119+
});
120+
});
121+
});
92122
});

0 commit comments

Comments
 (0)