Skip to content

Commit

Permalink
add parseResourceWithoutFragment to identifier.js
Browse files Browse the repository at this point in the history
  • Loading branch information
vankop committed Feb 3, 2022
1 parent 312239a commit 93ad324
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 71 deletions.
52 changes: 25 additions & 27 deletions lib/NormalModuleFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ const LazySet = require("./util/LazySet");
const { getScheme } = require("./util/URLAbsoluteSpecifier");
const { cachedCleverMerge, cachedSetProperty } = require("./util/cleverMerge");
const { join } = require("./util/fs");
const { parseResource } = require("./util/identifier");
const {
parseResource,
parseResourceWithoutFragment
} = require("./util/identifier");

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

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

/** @typedef {Object} ParsedLoaderRequest
* @property {string} loader loader
* @property {string|undefined} options options
*/

const EMPTY_RESOLVE_OPTIONS = {};
const EMPTY_PARSER_OPTIONS = {};
const EMPTY_GENERATOR_OPTIONS = {};
Expand Down Expand Up @@ -97,27 +105,6 @@ const stringifyLoadersAndResource = (loaders, resource) => {
return str + resource;
};

/**
* @param {string} resultString resultString
* @returns {{loader: string, options: string|undefined}} parsed loader request
*/
const identToLoaderRequest = resultString => {
const idx = resultString.indexOf("?");
if (idx >= 0) {
const loader = resultString.substr(0, idx).replace(/\0(.)/g, "$1");
const options = resultString.substr(idx + 1);
return {
loader,
options
};
} else {
return {
loader: resultString.replace(/\0(.)/g, "$1"),
options: undefined
};
}
};

const needCalls = (times, callback) => {
return err => {
if (--times === 0) {
Expand Down Expand Up @@ -264,6 +251,9 @@ class NormalModuleFactory extends ModuleFactory {
const cacheParseResource = parseResource.bindCache(
associatedObjectForCache
);
const cachedParseResourceWithoutFragment =
parseResourceWithoutFragment.bindCache(associatedObjectForCache);
this._parseResourceWithoutFragment = cachedParseResourceWithoutFragment;

this.hooks.factorize.tapAsync(
{
Expand Down Expand Up @@ -351,7 +341,7 @@ class NormalModuleFactory extends ModuleFactory {
let matchResourceData = undefined;
/** @type {string} */
let unresolvedResource;
/** @type {{loader: string, options: string|undefined}[]} */
/** @type {ParsedLoaderRequest[]} */
let elements;
let noPreAutoLoaders = false;
let noAutoLoaders = false;
Expand Down Expand Up @@ -405,7 +395,13 @@ class NormalModuleFactory extends ModuleFactory {
)
.split(/!+/);
unresolvedResource = rawElements.pop();
elements = rawElements.map(identToLoaderRequest);
elements = rawElements.map(el => {
const { path, query } = cachedParseResourceWithoutFragment(el);
return {
loader: path,
options: query ? query.slice(1) : undefined
};
});
scheme = getScheme(unresolvedResource);
} else {
unresolvedResource = requestWithoutMatchResource;
Expand Down Expand Up @@ -1017,12 +1013,14 @@ If changing the source code is not an option there is also a resolve options cal
}
if (err) return callback(err);

const parsedResult = identToLoaderRequest(result);
const parsedResult = this._parseResourceWithoutFragment(result);
const resolved = {
loader: parsedResult.loader,
loader: parsedResult.path,
options:
item.options === undefined
? parsedResult.options
? parsedResult.query
? parsedResult.query.slice(1)
: undefined
: item.options,
ident: item.options === undefined ? undefined : item.ident
};
Expand Down
109 changes: 65 additions & 44 deletions lib/util/identifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,49 @@ const requestToAbsolute = (context, relativePath) => {
return relativePath;
};

const makeCacheable = fn => {
const makeCacheable = realFn => {
/** @type {WeakMap<object, Map<string, ParsedResource>>} */
const cache = new WeakMap();

const getCache = associatedObjectForCache => {
const entry = cache.get(associatedObjectForCache);
if (entry !== undefined) return entry;
/** @type {Map<string, ParsedResource>} */
const map = new Map();
cache.set(associatedObjectForCache, map);
return map;
};

/**
* @param {string} str the path with query and fragment
* @param {Object=} associatedObjectForCache an object to which the cache will be attached
* @returns {ParsedResource} parsed parts
*/
const fn = (str, associatedObjectForCache) => {
if (!associatedObjectForCache) return realFn(str);
const cache = getCache(associatedObjectForCache);
const entry = cache.get(str);
if (entry !== undefined) return entry;
const result = realFn(str);
cache.set(str, result);
return result;
};

fn.bindCache = associatedObjectForCache => {
const cache = getCache(associatedObjectForCache);
return str => {
const entry = cache.get(str);
if (entry !== undefined) return entry;
const result = realFn(str);
cache.set(str, result);
return result;
};
};

return fn;
};

const makeCacheableWithContext = fn => {
/** @type {WeakMap<object, Map<string, Map<string, string>>>} */
const cache = new WeakMap();

Expand Down Expand Up @@ -215,7 +257,7 @@ const _makePathsRelative = (context, identifier) => {
.join("");
};

exports.makePathsRelative = makeCacheable(_makePathsRelative);
exports.makePathsRelative = makeCacheableWithContext(_makePathsRelative);

/**
*
Expand All @@ -230,7 +272,7 @@ const _makePathsAbsolute = (context, identifier) => {
.join("");
};

exports.makePathsAbsolute = makeCacheable(_makePathsAbsolute);
exports.makePathsAbsolute = makeCacheableWithContext(_makePathsAbsolute);

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

const contextify = makeCacheable(_contextify);
const contextify = makeCacheableWithContext(_contextify);
exports.contextify = contextify;

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

const absolutify = makeCacheable(_absolutify);
const absolutify = makeCacheableWithContext(_absolutify);
exports.absolutify = absolutify;

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

/** @typedef {{ resource: string, path: string, query: string, fragment: string }} ParsedResource */
/** @typedef {{ resource: string, path: string, query: string }} ParsedResourceWithoutFragment */

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

const getCache = associatedObjectForCache => {
const entry = cache.get(associatedObjectForCache);
if (entry !== undefined) return entry;
/** @type {Map<string, ParsedResource>} */
const map = new Map();
cache.set(associatedObjectForCache, map);
return map;
};

/**
* @param {string} str the path with query and fragment
* @param {Object=} associatedObjectForCache an object to which the cache will be attached
* @returns {ParsedResource} parsed parts
*/
const fn = (str, associatedObjectForCache) => {
if (!associatedObjectForCache) return realFn(str);
const cache = getCache(associatedObjectForCache);
const entry = cache.get(str);
if (entry !== undefined) return entry;
const result = realFn(str);
cache.set(str, result);
return result;
};

fn.bindCache = associatedObjectForCache => {
const cache = getCache(associatedObjectForCache);
return str => {
const entry = cache.get(str);
if (entry !== undefined) return entry;
const result = realFn(str);
cache.set(str, result);
return result;
};
/**
* Parse resource, skips fragment part
* @param {string} str the path with query and fragment
* @returns {ParsedResourceWithoutFragment} parsed parts
*/
const _parseResourceWithoutFragment = str => {
const match = PATH_QUERY_REGEXP.exec(str);
return {
resource: str,
path: match[1].replace(/\0(.)/g, "$1"),
query: match[2] ? match[2].replace(/\0(.)/g, "$1") : ""
};

return fn;
})(_parseResource);
};
exports.parseResourceWithoutFragment = makeCacheable(
_parseResourceWithoutFragment
);

/**
* @param {string} filename the filename which should be undone
Expand Down
29 changes: 29 additions & 0 deletions test/identifier.unittest.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,33 @@ describe("util/identifier", () => {
});
}
});

describe("parseResourceWithoutFragment", () => {
// [input, expectedPath, expectedQuery]
/** @type {[string, string, string][]} */
const cases = [
["path?query#hash", "path", "?query#hash"],
["\0#path\0??\0#query#hash", "#path?", "?#query#hash"],
[
'./loader.js?{"items":["a\0^","b\0!","c#","d"]}',
"./loader.js",
'?{"items":["a^","b!","c#","d"]}'
],
[
"C:\\Users\\\0#\\repo\\loader.js?",
"C:\\Users\\#\\repo\\loader.js",
"?"
],
["/Users/\0#/repo/loader-\0#.js", "/Users/#/repo/loader-#.js", ""]
];
cases.forEach(case_ => {
it(case_[0], () => {
const { resource, path, query } =
identifierUtil.parseResourceWithoutFragment(case_[0]);
expect(case_[0]).toBe(resource);
expect(case_[1]).toBe(path);
expect(case_[2]).toBe(query);
});
});
});
});

0 comments on commit 93ad324

Please sign in to comment.