Skip to content

Commit 2764e91

Browse files
committed
import.meta.webpackContext
1 parent 4abf353 commit 2764e91

7 files changed

Lines changed: 181 additions & 19 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Ivan Kopeykin @vankop
4+
*/
5+
6+
"use strict";
7+
8+
const makeSerializable = require("../util/makeSerializable");
9+
const ContextDependency = require("./ContextDependency");
10+
const ModuleDependencyTemplateAsRequireId = require("./ModuleDependencyTemplateAsRequireId");
11+
12+
class ImportMetaContextDependency extends ContextDependency {
13+
constructor(options, range) {
14+
super(options);
15+
16+
this.range = range;
17+
}
18+
19+
get category() {
20+
return "esm";
21+
}
22+
23+
get type() {
24+
return "import.meta.webpackContext";
25+
}
26+
}
27+
28+
makeSerializable(
29+
ImportMetaContextDependency,
30+
"webpack/lib/dependencies/ImportMetaContextDependency"
31+
);
32+
33+
ImportMetaContextDependency.Template = ModuleDependencyTemplateAsRequireId;
34+
35+
module.exports = ImportMetaContextDependency;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Ivan Kopeykin @vankop
4+
*/
5+
6+
"use strict";
7+
8+
const {
9+
evaluateToIdentifier
10+
} = require("../javascript/JavascriptParserHelpers");
11+
const ImportMetaContextDependency = require("./ImportMetaContextDependency");
12+
13+
/** @typedef {import("estree").Expression} ExpressionNode */
14+
/** @typedef {import("estree").ObjectExpression} ObjectExpressionNode */
15+
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
16+
17+
/**
18+
* @param {JavascriptParser} parser parser
19+
* @param {ObjectExpressionNode} optionsNode node
20+
* @returns {{mode: string, recursive: boolean, regExp: RegExp}} options
21+
*/
22+
function getOptions(parser, optionsNode) {
23+
let regExp = /^\.\/.*$/;
24+
let recursive = true;
25+
let mode = "sync";
26+
if (optionsNode) {
27+
for (const prop of optionsNode.properties) {
28+
if (prop.type !== "Property" || prop.key.type !== "Identifier") return;
29+
switch (prop.key.name) {
30+
case "regExp": {
31+
const regExpExpr = parser.evaluateExpression(
32+
/** @type {ExpressionNode} */ (prop.value)
33+
);
34+
if (!regExpExpr.isRegExp()) return;
35+
regExp = regExpExpr.regExp;
36+
break;
37+
}
38+
case "mode": {
39+
const modeExpr = parser.evaluateExpression(
40+
/** @type {ExpressionNode} */ (prop.value)
41+
);
42+
if (!modeExpr.isString()) return;
43+
mode = modeExpr.string;
44+
break;
45+
}
46+
case "recursive": {
47+
const recursiveExpr = parser.evaluateExpression(
48+
/** @type {ExpressionNode} */ (prop.value)
49+
);
50+
if (!recursiveExpr.isBoolean()) return;
51+
recursive = recursiveExpr.bool;
52+
}
53+
}
54+
}
55+
}
56+
57+
return { recursive, regExp, mode };
58+
}
59+
60+
module.exports = class ImportMetaContextDependencyParserPlugin {
61+
apply(parser) {
62+
parser.hooks.evaluateIdentifier
63+
.for("import.meta.webpackContext")
64+
.tap("HotModuleReplacementPlugin", expr => {
65+
return evaluateToIdentifier(
66+
"import.meta.webpackContext",
67+
"import.meta",
68+
() => ["webpackContext"],
69+
true
70+
)(expr);
71+
});
72+
parser.hooks.call
73+
.for("import.meta.webpackContext")
74+
.tap("ImportMetaContextDependencyParserPlugin", expr => {
75+
if (expr.arguments.length < 1 || expr.arguments.length > 2) return;
76+
const [directoryNode, optionsNode] = expr.arguments;
77+
if (optionsNode && optionsNode.type !== "ObjectExpression") return;
78+
const requestExpr = parser.evaluateExpression(directoryNode);
79+
if (!requestExpr.isString()) return;
80+
const request = requestExpr.string;
81+
const options = getOptions(parser, optionsNode);
82+
if (!options) return;
83+
84+
const dep = new ImportMetaContextDependency(
85+
{
86+
request,
87+
...options,
88+
category: "esm"
89+
},
90+
expr.range
91+
);
92+
dep.loc = expr.loc;
93+
dep.optional = !!parser.scope.inTry;
94+
parser.state.current.addDependency(dep);
95+
return true;
96+
});
97+
}
98+
};

lib/dependencies/RequireContextDependency.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,6 @@ class RequireContextDependency extends ContextDependency {
1919
get type() {
2020
return "require.context";
2121
}
22-
23-
serialize(context) {
24-
const { write } = context;
25-
26-
write(this.range);
27-
28-
super.serialize(context);
29-
}
30-
31-
deserialize(context) {
32-
const { read } = context;
33-
34-
this.range = read();
35-
36-
super.deserialize(context);
37-
}
3822
}
3923

4024
makeSerializable(

lib/dependencies/RequireContextPlugin.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
const { cachedSetProperty } = require("../util/cleverMerge");
99
const ContextElementDependency = require("./ContextElementDependency");
10+
const ImportMetaContextDependency = require("./ImportMetaContextDependency");
11+
const ImportMetaContextDependencyParserPlugin = require("./ImportMetaContextDependencyParserPlugin");
1012
const RequireContextDependency = require("./RequireContextDependency");
1113
const RequireContextDependencyParserPlugin = require("./RequireContextDependencyParserPlugin");
1214

@@ -34,6 +36,14 @@ class RequireContextPlugin {
3436
RequireContextDependency,
3537
new RequireContextDependency.Template()
3638
);
39+
compilation.dependencyFactories.set(
40+
ImportMetaContextDependency,
41+
contextModuleFactory
42+
);
43+
compilation.dependencyTemplates.set(
44+
ImportMetaContextDependency,
45+
new ImportMetaContextDependency.Template()
46+
);
3747

3848
compilation.dependencyFactories.set(
3949
ContextElementDependency,
@@ -50,6 +60,22 @@ class RequireContextPlugin {
5060
new RequireContextDependencyParserPlugin().apply(parser);
5161
};
5262

63+
const handlerImportMeta = (parser, parserOptions) => {
64+
if (
65+
parserOptions.requireContext !== undefined &&
66+
!parserOptions.requireContext
67+
)
68+
return;
69+
70+
new ImportMetaContextDependencyParserPlugin().apply(parser);
71+
};
72+
73+
normalModuleFactory.hooks.parser
74+
.for("javascript/auto")
75+
.tap("RequireContextPlugin", handlerImportMeta);
76+
normalModuleFactory.hooks.parser
77+
.for("javascript/esm")
78+
.tap("RequireContextPlugin", handlerImportMeta);
5379
normalModuleFactory.hooks.parser
5480
.for("javascript/auto")
5581
.tap("RequireContextPlugin", handler);

lib/util/internalSerializables.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ module.exports = {
126126
require("../dependencies/ImportMetaHotAcceptDependency"),
127127
"dependencies/ImportMetaHotDeclineDependency": () =>
128128
require("../dependencies/ImportMetaHotDeclineDependency"),
129+
"dependencies/ImportMetaContextDependency": () =>
130+
require("../dependencies/ImportMetaContextDependency"),
129131
"dependencies/ProvidedDependency": () =>
130132
require("../dependencies/ProvidedDependency"),
131133
"dependencies/PureExpressionDependency": () =>

module.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ interface ImportMeta {
147147
url: string;
148148
webpack: number;
149149
webpackHot: webpack.Hot;
150+
webpackContext: (
151+
request: string,
152+
options?: {
153+
recursive?: boolean;
154+
regExp?: RegExp;
155+
mode?: "sync" | "eager" | "weak" | "lazy" | "lazy-once";
156+
}
157+
) => webpack.Context;
150158
}
151159

152160
declare const __resourceQuery: string;
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
1+
it("import.meta.webpackContext without arguments should work", function() {
2+
const contextRequire = import.meta.webpackContext("./dir");
3+
expect(contextRequire("./four")).toBe(4);
4+
});
5+
16
it("should not bundle context requires with asyncMode === 'weak'", function() {
2-
var contextRequire = require.context(".", false, /two/, "weak");
7+
const contextRequire = import.meta.webpackContext(".", {
8+
recursive: false,
9+
regExp: /two/,
10+
mode: "weak"
11+
});
312
expect(function() {
413
contextRequire("./two")
514
}).toThrowError(/not available/);
615
});
716

817
it("should find module with asyncMode === 'weak' when required elsewhere", function() {
9-
var contextRequire = require.context(".", false, /.+/, "weak");
18+
const contextRequire = require.context(".", false, /.+/, "weak");
1019
expect(contextRequire("./three")).toBe(3);
1120
require("./three"); // in a real app would be served as a separate chunk
1221
});
1322

1423
it("should find module with asyncMode === 'weak' when required elsewhere (recursive)", function() {
15-
var contextRequire = require.context(".", true, /.+/, "weak");
24+
const contextRequire = require.context(".", true, /.+/, "weak");
1625
expect(contextRequire("./dir/four")).toBe(4);
1726
require("./dir/four"); // in a real app would be served as a separate chunk
1827
});

0 commit comments

Comments
 (0)