Skip to content

Commit 7442a21

Browse files
committed
add support for resource fragments
1 parent 7dad1c1 commit 7442a21

14 files changed

+99
-52
lines changed

declarations/WebpackOptions.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,10 @@ export interface RuleSetRule {
10591059
* Match the resource path of the module.
10601060
*/
10611061
resource?: RuleSetConditionOrConditionsAbsolute;
1062+
/**
1063+
* Match the resource fragment of the module.
1064+
*/
1065+
resourceFragment?: RuleSetConditionOrConditions;
10621066
/**
10631067
* Match the resource query of the module.
10641068
*/

lib/ConstPlugin.js

+33-9
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,10 @@
88
const CachedConstDependency = require("./dependencies/CachedConstDependency");
99
const ConstDependency = require("./dependencies/ConstDependency");
1010
const { evaluateToString } = require("./javascript/JavascriptParserHelpers");
11+
const { parsePathQueryFragment } = require("./util/identifier");
1112

1213
/** @typedef {import("./Compiler")} Compiler */
1314

14-
const getQuery = request => {
15-
const i = request.indexOf("?");
16-
return i !== -1 ? request.substr(i) : "";
17-
};
18-
1915
const collectDeclaration = (declarations, pattern) => {
2016
const stack = [pattern];
2117
while (stack.length > 0) {
@@ -327,24 +323,52 @@ class ConstPlugin {
327323
.tap("ConstPlugin", expr => {
328324
if (parser.scope.isAsmJs) return;
329325
if (!parser.state.module) return;
330-
return evaluateToString(getQuery(parser.state.module.resource))(
331-
expr
332-
);
326+
return evaluateToString(
327+
parsePathQueryFragment(parser.state.module.resource).query
328+
)(expr);
333329
});
334330
parser.hooks.expression
335331
.for("__resourceQuery")
336332
.tap("ConstPlugin", expr => {
337333
if (parser.scope.isAsmJs) return;
338334
if (!parser.state.module) return;
339335
const dep = new CachedConstDependency(
340-
JSON.stringify(getQuery(parser.state.module.resource)),
336+
JSON.stringify(
337+
parsePathQueryFragment(parser.state.module.resource).query
338+
),
341339
expr.range,
342340
"__resourceQuery"
343341
);
344342
dep.loc = expr.loc;
345343
parser.state.module.addPresentationalDependency(dep);
346344
return true;
347345
});
346+
347+
parser.hooks.evaluateIdentifier
348+
.for("__resourceFragment")
349+
.tap("ConstPlugin", expr => {
350+
if (parser.scope.isAsmJs) return;
351+
if (!parser.state.module) return;
352+
return evaluateToString(
353+
parsePathQueryFragment(parser.state.module.resource).fragment
354+
)(expr);
355+
});
356+
parser.hooks.expression
357+
.for("__resourceFragment")
358+
.tap("ConstPlugin", expr => {
359+
if (parser.scope.isAsmJs) return;
360+
if (!parser.state.module) return;
361+
const dep = new CachedConstDependency(
362+
JSON.stringify(
363+
parsePathQueryFragment(parser.state.module.resource).fragment
364+
),
365+
expr.range,
366+
"__resourceFragment"
367+
);
368+
dep.loc = expr.loc;
369+
parser.state.module.addPresentationalDependency(dep);
370+
return true;
371+
});
348372
};
349373

350374
normalModuleFactory.hooks.parser

lib/ContextModule.js

+12-18
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const {
1919
keepOriginalOrder
2020
} = require("./util/comparators");
2121
const { compareModulesById } = require("./util/comparators");
22-
const { contextify } = require("./util/identifier");
22+
const { contextify, parsePathQueryFragment } = require("./util/identifier");
2323
const makeSerializable = require("./util/makeSerializable");
2424

2525
/** @typedef {import("webpack-sources").Source} Source */
@@ -60,6 +60,7 @@ const makeSerializable = require("./util/makeSerializable");
6060
* @typedef {Object} ContextModuleOptionsExtras
6161
* @property {string} resource
6262
* @property {string=} resourceQuery
63+
* @property {string=} resourceFragment
6364
* @property {TODO} resolveOptions
6465
*/
6566

@@ -86,30 +87,20 @@ class ContextModule extends Module {
8687
* @param {ContextModuleOptions} options options object
8788
*/
8889
constructor(resolveDependencies, options) {
89-
let resource;
90-
let resourceQuery;
91-
if (options.resourceQuery) {
92-
resource = options.resource;
93-
resourceQuery = options.resourceQuery;
94-
} else {
95-
const queryIdx = options.resource.indexOf("?");
96-
if (queryIdx >= 0) {
97-
resource = options.resource.substr(0, queryIdx);
98-
resourceQuery = options.resource.substr(queryIdx);
99-
} else {
100-
resource = options.resource;
101-
resourceQuery = "";
102-
}
103-
}
90+
const parsed = parsePathQueryFragment(options.resource);
91+
const resource = parsed.path;
92+
const resourceQuery = options.resourceQuery || parsed.query;
93+
const resourceFragment = options.resourceFragment || parsed.fragment;
10494

10595
super("javascript/dynamic", resource);
10696

10797
// Info from Factory
10898
this.resolveDependencies = resolveDependencies;
10999
/** @type {Omit<ContextModuleOptions, "resolveOptions">} */
110100
this.options = {
111-
resource: resource,
112-
resourceQuery: resourceQuery,
101+
resource,
102+
resourceQuery,
103+
resourceFragment,
113104
mode: options.mode,
114105
recursive: options.recursive,
115106
addon: options.addon,
@@ -169,6 +160,9 @@ class ContextModule extends Module {
169160
if (this.options.resourceQuery) {
170161
identifier += `|${this.options.resourceQuery}`;
171162
}
163+
if (this.options.resourceFragment) {
164+
identifier += `|${this.options.resourceFragment}`;
165+
}
172166
if (this.options.mode) {
173167
identifier += `|${this.options.mode}`;
174168
}

lib/ContextModuleFactory.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
232232
const {
233233
resource,
234234
resourceQuery,
235+
resourceFragment,
235236
recursive,
236237
regExp,
237238
include,
@@ -286,7 +287,7 @@ module.exports = class ContextModuleFactory extends ModuleFactory {
286287
.filter(obj => regExp.test(obj.request))
287288
.map(obj => {
288289
const dep = new ContextElementDependency(
289-
obj.request + resourceQuery,
290+
obj.request + resourceQuery + resourceFragment,
290291
obj.request,
291292
category,
292293
referencedExports

lib/ContextReplacementPlugin.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ const createResolveDependenciesFromContextMap = createContextMap => {
141141
if (err) return callback(err);
142142
const dependencies = Object.keys(map).map(key => {
143143
return new ContextElementDependency(
144-
map[key] + options.resourceQuery,
144+
map[key] + options.resourceQuery + options.resourceFragment,
145145
key,
146146
options.category,
147147
options.referencedExports

lib/NormalModuleFactory.js

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ const ruleSetCompiler = new RuleSetCompiler([
135135
new BasicMatcherRulePlugin("exclude", "resource", true),
136136
new BasicMatcherRulePlugin("resource"),
137137
new BasicMatcherRulePlugin("resourceQuery"),
138+
new BasicMatcherRulePlugin("resourceFragment"),
138139
new BasicMatcherRulePlugin("realResource"),
139140
new BasicMatcherRulePlugin("issuer"),
140141
new BasicMatcherRulePlugin("compiler"),

lib/dependencies/ContextDependencyHelpers.js

+12-18
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
"use strict";
77

8+
const { parsePathQueryFragment } = require("../util/identifier");
9+
810
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
911
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
1012
/** @typedef {import("./ContextDependency")} ContextDependency */
@@ -32,19 +34,6 @@ const splitContextFromPrefix = prefix => {
3234
};
3335
};
3436

35-
const splitQueryFromPostfix = postfix => {
36-
const idx = postfix.indexOf("?");
37-
let query = "";
38-
if (idx >= 0) {
39-
query = postfix.substr(idx);
40-
postfix = postfix.substr(0, idx);
41-
}
42-
return {
43-
postfix,
44-
query
45-
};
46-
};
47-
4837
// TODO Use Omit<> type for contextOptions when typescript >= 3.5
4938
/** @typedef {Partial<Pick<ContextDependencyOptions, Exclude<keyof ContextDependencyOptions, "resource"|"recursive"|"regExp">>>} PartialContextDependencyOptions */
5039

@@ -70,7 +59,9 @@ exports.create = (Dep, range, param, expr, options, contextOptions, parser) => {
7059

7160
const valueRange = param.range;
7261
const { context, prefix } = splitContextFromPrefix(prefixRaw);
73-
const { postfix, query } = splitQueryFromPostfix(postfixRaw);
62+
const { path: postfix, query, fragment } = parsePathQueryFragment(
63+
postfixRaw
64+
);
7465

7566
// When there are more than two quasis, the generated RegExp can be more precise
7667
// We join the quasis with the expression regexp
@@ -81,20 +72,21 @@ exports.create = (Dep, range, param, expr, options, contextOptions, parser) => {
8172
.map(q => quoteMeta(q.string) + options.wrappedContextRegExp.source)
8273
.join("");
8374

84-
// Example: `./context/pre${e}inner${e}inner2${e}post?query`
75+
// Example: `./context/pre${e}inner${e}inner2${e}post?query#frag`
8576
// context: "./context"
8677
// prefix: "./pre"
8778
// innerQuasis: [BEE("inner"), BEE("inner2")]
8879
// (BEE = BasicEvaluatedExpression)
8980
// postfix: "post"
9081
// query: "?query"
82+
// fragment: "#frag"
9183
// regExp: /^\.\/pre.*inner.*inner2.*post$/
9284
const regExp = new RegExp(
9385
`^${quoteMeta(prefix)}${innerRegExp}${quoteMeta(postfix)}$`
9486
);
9587
const dep = new Dep(
9688
{
97-
request: context + query,
89+
request: context + query + fragment,
9890
recursive: options.wrappedContextRecursive,
9991
regExp,
10092
mode: "sync",
@@ -165,15 +157,17 @@ exports.create = (Dep, range, param, expr, options, contextOptions, parser) => {
165157
param.postfix && param.postfix.isString() ? param.postfix.range : null;
166158
const valueRange = param.range;
167159
const { context, prefix } = splitContextFromPrefix(prefixRaw);
168-
const { postfix, query } = splitQueryFromPostfix(postfixRaw);
160+
const { path: postfix, query, fragment } = parsePathQueryFragment(
161+
postfixRaw
162+
);
169163
const regExp = new RegExp(
170164
`^${quoteMeta(prefix)}${options.wrappedContextRegExp.source}${quoteMeta(
171165
postfix
172166
)}$`
173167
);
174168
const dep = new Dep(
175169
{
176-
request: context + query,
170+
request: context + query + fragment,
177171
recursive: options.wrappedContextRecursive,
178172
regExp,
179173
mode: "sync",

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"glob-to-regexp": "^0.4.1",
1919
"graceful-fs": "^4.1.15",
2020
"json-parse-better-errors": "^1.0.2",
21-
"loader-runner": "^3.1.0",
21+
"loader-runner": "^4.0.0",
2222
"mime-types": "^2.1.26",
2323
"neo-async": "^2.6.1",
2424
"pkg-dir": "^4.2.0",

schemas/WebpackOptions.json

+8
Original file line numberDiff line numberDiff line change
@@ -2567,6 +2567,14 @@
25672567
}
25682568
]
25692569
},
2570+
"resourceFragment": {
2571+
"description": "Match the resource fragment of the module.",
2572+
"oneOf": [
2573+
{
2574+
"$ref": "#/definitions/RuleSetConditionOrConditions"
2575+
}
2576+
]
2577+
},
25702578
"resourceQuery": {
25712579
"description": "Match the resource query of the module.",
25722580
"oneOf": [

test/cases/parsing/evaluate/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,14 @@ it("should evaluate __dirname and __resourceQuery with replace and substr", func
3838
var result = require("./resourceQuery/index?" + __dirname);
3939
expect(result).toEqual("?resourceQuery");
4040
});
41+
42+
it("should evaluate __dirname and __resourceFragment with replace and substr", function() {
43+
var result = require("./resourceFragment/index#" + __dirname);
44+
expect(result).toEqual("#resourceFragment");
45+
});
46+
47+
it("should allow resourceFragment in context", function() {
48+
var fn = x => require(`./resourceFragment/${x}#..`);
49+
expect(fn("index")).toEqual("#resourceFragment");
50+
expect(fn("returnRF")).toBe("#..")
51+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = require((
2+
__resourceFragment.substr(1) + "/resourceFragment/returnRF#XXXFragment"
3+
).replace(/XXX/g, "resource"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = __resourceFragment;

types.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,7 @@ declare abstract class ContextModuleFactory extends ModuleFactory {
17251725
referencedExports?: string[][];
17261726
resource: string;
17271727
resourceQuery?: string;
1728+
resourceFragment?: string;
17281729
resolveOptions: any;
17291730
},
17301731
callback: (err?: Error, dependencies?: ContextElementDependency[]) => any
@@ -6569,6 +6570,11 @@ declare interface RuleSetRule {
65696570
*/
65706571
resource?: RuleSetConditionAbsolute;
65716572

6573+
/**
6574+
* Match the resource fragment of the module.
6575+
*/
6576+
resourceFragment?: RuleSetCondition;
6577+
65726578
/**
65736579
* Match the resource query of the module.
65746580
*/

yarn.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -4465,10 +4465,10 @@ load-json-file@^4.0.0:
44654465
pify "^3.0.0"
44664466
strip-bom "^3.0.0"
44674467

4468-
loader-runner@^3.1.0:
4469-
version "3.1.0"
4470-
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-3.1.0.tgz#e9440e5875f2ad2f968489cd2c7b59a4f2847fcb"
4471-
integrity sha512-wE/bOCdTKMR2rm7Xxh+eirDOmN7Vx7hntWgiTayuFPtF8MgsFDo49SP8kkYz8IVlEBTOtR7P+XI7bE1xjo/IkA==
4468+
loader-runner@^4.0.0:
4469+
version "4.0.0"
4470+
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.0.0.tgz#02abcfd9fe6ff7a5aeb3547464746c4dc6ba333d"
4471+
integrity sha512-Rqf48ufrr48gFjnaqss04QesoXB7VenbpFFIV/0yOKGnpbejrVlOPqTsoX42FG5goXM5Ixekcs4DqDzHOX2z7Q==
44724472

44734473
loader-utils@^1.0.0, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
44744474
version "1.4.0"

0 commit comments

Comments
 (0)