UNPKG

7.66 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Ivan Kopeykin @vankop
4*/
5
6"use strict";
7
8const WebpackError = require("../WebpackError");
9const {
10 evaluateToIdentifier
11} = require("../javascript/JavascriptParserHelpers");
12const ImportMetaContextDependency = require("./ImportMetaContextDependency");
13
14/** @typedef {import("estree").Expression} ExpressionNode */
15/** @typedef {import("estree").ObjectExpression} ObjectExpressionNode */
16/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
17/** @typedef {import("../ContextModule").ContextModuleOptions} ContextModuleOptions */
18/** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
19/** @typedef {Pick<ContextModuleOptions, 'mode'|'recursive'|'regExp'|'include'|'exclude'|'chunkName'>&{groupOptions: RawChunkGroupOptions, exports?: ContextModuleOptions["referencedExports"]}} ImportMetaContextOptions */
20
21function createPropertyParseError(prop, expect) {
22 return createError(
23 `Parsing import.meta.webpackContext options failed. Unknown value for property ${JSON.stringify(
24 prop.key.name
25 )}, expected type ${expect}.`,
26 prop.value.loc
27 );
28}
29
30function createError(msg, loc) {
31 const error = new WebpackError(msg);
32 error.name = "ImportMetaContextError";
33 error.loc = loc;
34 return error;
35}
36
37module.exports = class ImportMetaContextDependencyParserPlugin {
38 apply(parser) {
39 parser.hooks.evaluateIdentifier
40 .for("import.meta.webpackContext")
41 .tap("HotModuleReplacementPlugin", expr => {
42 return evaluateToIdentifier(
43 "import.meta.webpackContext",
44 "import.meta",
45 () => ["webpackContext"],
46 true
47 )(expr);
48 });
49 parser.hooks.call
50 .for("import.meta.webpackContext")
51 .tap("ImportMetaContextDependencyParserPlugin", expr => {
52 if (expr.arguments.length < 1 || expr.arguments.length > 2) return;
53 const [directoryNode, optionsNode] = expr.arguments;
54 if (optionsNode && optionsNode.type !== "ObjectExpression") return;
55 const requestExpr = parser.evaluateExpression(directoryNode);
56 if (!requestExpr.isString()) return;
57 const request = requestExpr.string;
58 const errors = [];
59 let regExp = /^\.\/.*$/;
60 let recursive = true;
61 /** @type {ContextModuleOptions["mode"]} */
62 let mode = "sync";
63 /** @type {ContextModuleOptions["include"]} */
64 let include;
65 /** @type {ContextModuleOptions["exclude"]} */
66 let exclude;
67 /** @type {RawChunkGroupOptions} */
68 const groupOptions = {};
69 /** @type {ContextModuleOptions["chunkName"]} */
70 let chunkName;
71 /** @type {ContextModuleOptions["referencedExports"]} */
72 let exports;
73 if (optionsNode) {
74 for (const prop of optionsNode.properties) {
75 if (prop.type !== "Property" || prop.key.type !== "Identifier") {
76 errors.push(
77 createError(
78 "Parsing import.meta.webpackContext options failed.",
79 optionsNode.loc
80 )
81 );
82 break;
83 }
84 switch (prop.key.name) {
85 case "regExp": {
86 const regExpExpr = parser.evaluateExpression(
87 /** @type {ExpressionNode} */ (prop.value)
88 );
89 if (!regExpExpr.isRegExp()) {
90 errors.push(createPropertyParseError(prop, "RegExp"));
91 } else {
92 regExp = regExpExpr.regExp;
93 }
94 break;
95 }
96 case "include": {
97 const regExpExpr = parser.evaluateExpression(
98 /** @type {ExpressionNode} */ (prop.value)
99 );
100 if (!regExpExpr.isRegExp()) {
101 errors.push(createPropertyParseError(prop, "RegExp"));
102 } else {
103 include = regExpExpr.regExp;
104 }
105 break;
106 }
107 case "exclude": {
108 const regExpExpr = parser.evaluateExpression(
109 /** @type {ExpressionNode} */ (prop.value)
110 );
111 if (!regExpExpr.isRegExp()) {
112 errors.push(createPropertyParseError(prop, "RegExp"));
113 } else {
114 exclude = regExpExpr.regExp;
115 }
116 break;
117 }
118 case "mode": {
119 const modeExpr = parser.evaluateExpression(
120 /** @type {ExpressionNode} */ (prop.value)
121 );
122 if (!modeExpr.isString()) {
123 errors.push(createPropertyParseError(prop, "string"));
124 } else {
125 mode = /** @type {ContextModuleOptions["mode"]} */ (
126 modeExpr.string
127 );
128 }
129 break;
130 }
131 case "chunkName": {
132 const expr = parser.evaluateExpression(
133 /** @type {ExpressionNode} */ (prop.value)
134 );
135 if (!expr.isString()) {
136 errors.push(createPropertyParseError(prop, "string"));
137 } else {
138 chunkName = expr.string;
139 }
140 break;
141 }
142 case "exports": {
143 const expr = parser.evaluateExpression(
144 /** @type {ExpressionNode} */ (prop.value)
145 );
146 if (expr.isString()) {
147 exports = [[expr.string]];
148 } else if (expr.isArray()) {
149 const items = expr.items;
150 if (
151 items.every(i => {
152 if (!i.isArray()) return false;
153 const innerItems = i.items;
154 return innerItems.every(i => i.isString());
155 })
156 ) {
157 exports = [];
158 for (const i1 of items) {
159 const export_ = [];
160 for (const i2 of i1.items) {
161 export_.push(i2.string);
162 }
163 exports.push(export_);
164 }
165 } else {
166 errors.push(
167 createPropertyParseError(prop, "string|string[][]")
168 );
169 }
170 } else {
171 errors.push(
172 createPropertyParseError(prop, "string|string[][]")
173 );
174 }
175 break;
176 }
177 case "prefetch": {
178 const expr = parser.evaluateExpression(
179 /** @type {ExpressionNode} */ (prop.value)
180 );
181 if (expr.isBoolean()) {
182 groupOptions.prefetchOrder = 0;
183 } else if (expr.isNumber()) {
184 groupOptions.prefetchOrder = expr.number;
185 } else {
186 errors.push(createPropertyParseError(prop, "boolean|number"));
187 }
188 break;
189 }
190 case "preload": {
191 const expr = parser.evaluateExpression(
192 /** @type {ExpressionNode} */ (prop.value)
193 );
194 if (expr.isBoolean()) {
195 groupOptions.preloadOrder = 0;
196 } else if (expr.isNumber()) {
197 groupOptions.preloadOrder = expr.number;
198 } else {
199 errors.push(createPropertyParseError(prop, "boolean|number"));
200 }
201 break;
202 }
203 case "recursive": {
204 const recursiveExpr = parser.evaluateExpression(
205 /** @type {ExpressionNode} */ (prop.value)
206 );
207 if (!recursiveExpr.isBoolean()) {
208 errors.push(createPropertyParseError(prop, "boolean"));
209 } else {
210 recursive = recursiveExpr.bool;
211 }
212 break;
213 }
214 default:
215 errors.push(
216 createError(
217 `Parsing import.meta.webpackContext options failed. Unknown property ${JSON.stringify(
218 prop.key.name
219 )}.`,
220 optionsNode.loc
221 )
222 );
223 }
224 }
225 }
226 if (errors.length) {
227 for (const error of errors) parser.state.current.addError(error);
228 return;
229 }
230
231 const dep = new ImportMetaContextDependency(
232 {
233 request,
234 include,
235 exclude,
236 recursive,
237 regExp,
238 groupOptions,
239 chunkName,
240 referencedExports: exports,
241 mode,
242 category: "esm"
243 },
244 expr.range
245 );
246 dep.loc = expr.loc;
247 dep.optional = !!parser.scope.inTry;
248 parser.state.current.addDependency(dep);
249 return true;
250 });
251 }
252};