source: frontend/node_modules/webpack/lib/dependencies/CommonJsImportsParserPlugin.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 25.3 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const RuntimeGlobals = require("../RuntimeGlobals");
9const CommentCompilationWarning = require("../errors/CommentCompilationWarning");
10const UnsupportedFeatureWarning = require("../errors/UnsupportedFeatureWarning");
11const {
12 evaluateToIdentifier,
13 evaluateToString,
14 expressionIsUnsupported,
15 toConstantDependency
16} = require("../javascript/JavascriptParserHelpers");
17const traverseDestructuringAssignmentProperties = require("../util/traverseDestructuringAssignmentProperties");
18const CommonJsFullRequireDependency = require("./CommonJsFullRequireDependency");
19const CommonJsRequireContextDependency = require("./CommonJsRequireContextDependency");
20const CommonJsRequireDependency = require("./CommonJsRequireDependency");
21const ConstDependency = require("./ConstDependency");
22const ContextDependencyHelpers = require("./ContextDependencyHelpers");
23const LocalModuleDependency = require("./LocalModuleDependency");
24const { getLocalModule } = require("./LocalModulesHelpers");
25const RequireHeaderDependency = require("./RequireHeaderDependency");
26const RequireResolveContextDependency = require("./RequireResolveContextDependency");
27const RequireResolveDependency = require("./RequireResolveDependency");
28const RequireResolveHeaderDependency = require("./RequireResolveHeaderDependency");
29
30/** @typedef {import("estree").CallExpression} CallExpression */
31/** @typedef {import("estree").Expression} Expression */
32/** @typedef {import("estree").NewExpression} NewExpression */
33/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
34/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
35/** @typedef {import("../Dependency").RawReferencedExports} RawReferencedExports */
36/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
37/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
38/** @typedef {import("../javascript/JavascriptParser").ImportSource} ImportSource */
39/** @typedef {import("../javascript/JavascriptParser").Range} Range */
40/** @typedef {import("../javascript/JavascriptParser").Members} Members */
41/** @typedef {import("../javascript/JavascriptParser").CalleeMembers} CalleeMembers */
42/** @typedef {import("./LocalModule")} LocalModule */
43
44/**
45 * Defines the common js import settings type used by this module.
46 * @typedef {object} CommonJsImportSettings
47 * @property {string=} name
48 * @property {string} context
49 */
50
51/**
52 * Per-`const NAME = require(LITERAL)` binding state used to forward
53 * member-access references on `NAME` to the `CommonJsRequireDependency`
54 * created for the `require()` call.
55 * @typedef {object} RequireBindingData
56 * @property {RawReferencedExports} referencedExports mutable list shared with the dependency; pushed to as `NAME.x.y` accesses are walked
57 * @property {InstanceType<typeof import("./CommonJsRequireDependency")> | null} dep dependency for the `require()` call (assigned during walk)
58 */
59
60/** @type {WeakMap<CallExpression, RequireBindingData>} */
61const requireBindingData = new WeakMap();
62
63const REQUIRE_BINDING_TAG = Symbol(
64 "CommonJsImportsParserPlugin require binding"
65);
66
67const PLUGIN_NAME = "CommonJsImportsParserPlugin";
68
69/**
70 * Checks whether this object is require call expression.
71 * @param {Expression} expression expression
72 * @returns {boolean} true, when expression is `require(...)` or `module.require(...)`
73 */
74const isRequireCallExpression = (expression) => {
75 if (expression.type !== "CallExpression") return false;
76 const { callee } = expression;
77 if (callee.type === "Identifier") {
78 return callee.name === "require";
79 }
80 if (callee.type === "MemberExpression" && !callee.computed) {
81 const object = callee.object;
82 const property = callee.property;
83 return (
84 object.type === "Identifier" &&
85 object.name === "module" &&
86 property.type === "Identifier" &&
87 property.name === "require"
88 );
89 }
90 return false;
91};
92
93/**
94 * Gets require referenced exports from destructuring.
95 * @param {JavascriptParser} parser parser
96 * @param {CallExpression | NewExpression} expr expression
97 * @returns {RawReferencedExports | null} referenced exports from destructuring
98 */
99const getRequireReferencedExportsFromDestructuring = (parser, expr) => {
100 const referencedPropertiesInDestructuring =
101 parser.destructuringAssignmentPropertiesFor(expr);
102 if (!referencedPropertiesInDestructuring) return null;
103
104 /** @type {RawReferencedExports} */
105 const referencedExports = [];
106 traverseDestructuringAssignmentProperties(
107 referencedPropertiesInDestructuring,
108 (stack) => referencedExports.push(stack.map((p) => p.id))
109 );
110 return referencedExports;
111};
112
113/**
114 * Creates a require cache dependency.
115 * @param {JavascriptParser} parser parser
116 * @returns {(expr: Expression) => boolean} handler
117 */
118const createRequireCacheDependency = (parser) =>
119 toConstantDependency(parser, RuntimeGlobals.moduleCache, [
120 RuntimeGlobals.moduleCache,
121 RuntimeGlobals.moduleId,
122 RuntimeGlobals.moduleLoaded
123 ]);
124
125/**
126 * Creates a require as expression handler.
127 * @param {JavascriptParser} parser parser
128 * @param {JavascriptParserOptions} options options
129 * @param {() => undefined | string} getContext context accessor
130 * @returns {(expr: Expression) => boolean} handler
131 */
132const createRequireAsExpressionHandler =
133 (parser, options, getContext) => (expr) => {
134 const dep = new CommonJsRequireContextDependency(
135 {
136 request: /** @type {string} */ (options.unknownContextRequest),
137 recursive: /** @type {boolean} */ (options.unknownContextRecursive),
138 regExp: /** @type {RegExp} */ (options.unknownContextRegExp),
139 mode: "sync"
140 },
141 /** @type {Range} */ (expr.range),
142 undefined,
143 parser.scope.inShorthand,
144 getContext()
145 );
146 dep.critical =
147 options.unknownContextCritical &&
148 "require function is used in a way in which dependencies cannot be statically extracted";
149 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
150 dep.optional = Boolean(parser.scope.inTry);
151 parser.state.current.addDependency(dep);
152 return true;
153 };
154
155/**
156 * Creates a require call handler.
157 * @param {JavascriptParser} parser parser
158 * @param {JavascriptParserOptions} options options
159 * @param {() => undefined | string} getContext context accessor
160 * @returns {(callNew: boolean) => (expr: CallExpression | NewExpression) => (boolean | void)} handler factory
161 */
162const createRequireCallHandler = (parser, options, getContext) => {
163 /**
164 * Process require item.
165 * @param {CallExpression | NewExpression} expr expression
166 * @param {BasicEvaluatedExpression} param param
167 * @returns {boolean | void} true when handled
168 */
169 const processRequireItem = (expr, param) => {
170 if (param.isString()) {
171 let referencedExports = getRequireReferencedExportsFromDestructuring(
172 parser,
173 expr
174 );
175 const binding = requireBindingData.get(
176 /** @type {CallExpression} */ (expr)
177 );
178 if (binding && !referencedExports) {
179 // `const NAME = require(LITERAL)` — let later member-access walks
180 // on `NAME` populate the dependency's referenced exports.
181 referencedExports = binding.referencedExports;
182 }
183 const dep = new CommonJsRequireDependency(
184 /** @type {string} */ (param.string),
185 /** @type {Range} */ (param.range),
186 getContext(),
187 referencedExports,
188 /** @type {Range} */ (expr.range)
189 );
190 if (binding) binding.dep = dep;
191 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
192 dep.optional = Boolean(parser.scope.inTry);
193 parser.state.current.addDependency(dep);
194 return true;
195 }
196 };
197 /**
198 * Process require context.
199 * @param {CallExpression | NewExpression} expr expression
200 * @param {BasicEvaluatedExpression} param param
201 * @returns {boolean | void} true when handled
202 */
203 const processRequireContext = (expr, param) => {
204 const referencedExports = getRequireReferencedExportsFromDestructuring(
205 parser,
206 expr
207 );
208 const dep = ContextDependencyHelpers.create(
209 CommonJsRequireContextDependency,
210 /** @type {Range} */ (expr.range),
211 param,
212 expr,
213 options,
214 {
215 category: "commonjs",
216 referencedExports
217 },
218 parser,
219 undefined,
220 getContext()
221 );
222 if (!dep) return;
223 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
224 dep.optional = Boolean(parser.scope.inTry);
225 parser.state.current.addDependency(dep);
226 return true;
227 };
228
229 return (callNew) => (expr) => {
230 if (options.commonjsMagicComments) {
231 const { options: requireOptions, errors: commentErrors } =
232 parser.parseCommentOptions(/** @type {Range} */ (expr.range));
233
234 if (commentErrors) {
235 for (const e of commentErrors) {
236 const { comment } = e;
237 parser.state.module.addWarning(
238 new CommentCompilationWarning(
239 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
240 /** @type {DependencyLocation} */ (comment.loc)
241 )
242 );
243 }
244 }
245 if (requireOptions && requireOptions.webpackIgnore !== undefined) {
246 if (typeof requireOptions.webpackIgnore !== "boolean") {
247 parser.state.module.addWarning(
248 new UnsupportedFeatureWarning(
249 `\`webpackIgnore\` expected a boolean, but received: ${requireOptions.webpackIgnore}.`,
250 /** @type {DependencyLocation} */ (expr.loc)
251 )
252 );
253 } else if (requireOptions.webpackIgnore) {
254 // Do not instrument `require()` if `webpackIgnore` is `true`
255 return true;
256 }
257 }
258 }
259
260 if (expr.arguments.length !== 1) return;
261 /** @type {null | LocalModule} */
262 let localModule;
263 const param = parser.evaluateExpression(expr.arguments[0]);
264 if (param.isConditional()) {
265 let isExpression = false;
266 for (const p of /** @type {BasicEvaluatedExpression[]} */ (
267 param.options
268 )) {
269 const result = processRequireItem(expr, p);
270 if (result === undefined) {
271 isExpression = true;
272 }
273 }
274 if (!isExpression) {
275 const dep = new RequireHeaderDependency(
276 /** @type {Range} */ (expr.callee.range)
277 );
278 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
279 parser.state.module.addPresentationalDependency(dep);
280 return true;
281 }
282 }
283 if (
284 param.isString() &&
285 (localModule = getLocalModule(
286 parser.state,
287 /** @type {string} */ (param.string)
288 ))
289 ) {
290 localModule.flagUsed();
291 const dep = new LocalModuleDependency(
292 localModule,
293 /** @type {Range} */ (expr.range),
294 callNew
295 );
296 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
297 parser.state.module.addPresentationalDependency(dep);
298 } else {
299 const result = processRequireItem(expr, param);
300 if (result === undefined) {
301 processRequireContext(expr, param);
302 } else {
303 const dep = new RequireHeaderDependency(
304 /** @type {Range} */ (expr.callee.range)
305 );
306 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
307 parser.state.module.addPresentationalDependency(dep);
308 }
309 }
310 return true;
311 };
312};
313
314/**
315 * Creates a process resolve handler.
316 * @param {JavascriptParser} parser parser
317 * @param {JavascriptParserOptions} options options
318 * @param {() => undefined | string} getContext context accessor
319 * @returns {(expr: CallExpression, weak: boolean) => (boolean | void)} resolver
320 */
321const createProcessResolveHandler = (parser, options, getContext) => {
322 /**
323 * Process resolve item.
324 * @param {CallExpression} expr call expression
325 * @param {BasicEvaluatedExpression} param param
326 * @param {boolean} weak weak
327 * @returns {boolean | void} true when handled
328 */
329 const processResolveItem = (expr, param, weak) => {
330 if (param.isString()) {
331 const dep = new RequireResolveDependency(
332 /** @type {string} */ (param.string),
333 /** @type {Range} */ (param.range),
334 getContext()
335 );
336 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
337 dep.optional = Boolean(parser.scope.inTry);
338 dep.weak = weak;
339 parser.state.current.addDependency(dep);
340 return true;
341 }
342 };
343 /**
344 * Process resolve context.
345 * @param {CallExpression} expr call expression
346 * @param {BasicEvaluatedExpression} param param
347 * @param {boolean} weak weak
348 * @returns {boolean | void} true when handled
349 */
350 const processResolveContext = (expr, param, weak) => {
351 const dep = ContextDependencyHelpers.create(
352 RequireResolveContextDependency,
353 /** @type {Range} */ (param.range),
354 param,
355 expr,
356 options,
357 {
358 category: "commonjs",
359 mode: weak ? "weak" : "sync"
360 },
361 parser,
362 getContext()
363 );
364 if (!dep) return;
365 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
366 dep.optional = Boolean(parser.scope.inTry);
367 parser.state.current.addDependency(dep);
368 return true;
369 };
370
371 return (expr, weak) => {
372 if (!weak && options.commonjsMagicComments) {
373 const { options: requireOptions, errors: commentErrors } =
374 parser.parseCommentOptions(/** @type {Range} */ (expr.range));
375
376 if (commentErrors) {
377 for (const e of commentErrors) {
378 const { comment } = e;
379 parser.state.module.addWarning(
380 new CommentCompilationWarning(
381 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
382 /** @type {DependencyLocation} */ (comment.loc)
383 )
384 );
385 }
386 }
387 if (requireOptions && requireOptions.webpackIgnore !== undefined) {
388 if (typeof requireOptions.webpackIgnore !== "boolean") {
389 parser.state.module.addWarning(
390 new UnsupportedFeatureWarning(
391 `\`webpackIgnore\` expected a boolean, but received: ${requireOptions.webpackIgnore}.`,
392 /** @type {DependencyLocation} */ (expr.loc)
393 )
394 );
395 } else if (requireOptions.webpackIgnore) {
396 // Do not instrument `require()` if `webpackIgnore` is `true`
397 return true;
398 }
399 }
400 }
401
402 if (expr.arguments.length !== 1) return;
403 const param = parser.evaluateExpression(expr.arguments[0]);
404 if (param.isConditional()) {
405 for (const option of /** @type {BasicEvaluatedExpression[]} */ (
406 param.options
407 )) {
408 const result = processResolveItem(expr, option, weak);
409 if (result === undefined) {
410 processResolveContext(expr, option, weak);
411 }
412 }
413 const dep = new RequireResolveHeaderDependency(
414 /** @type {Range} */ (expr.callee.range)
415 );
416 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
417 parser.state.module.addPresentationalDependency(dep);
418 return true;
419 }
420 const result = processResolveItem(expr, param, weak);
421 if (result === undefined) {
422 processResolveContext(expr, param, weak);
423 }
424 const dep = new RequireResolveHeaderDependency(
425 /** @type {Range} */ (expr.callee.range)
426 );
427 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
428 parser.state.module.addPresentationalDependency(dep);
429 return true;
430 };
431};
432
433class CommonJsImportsParserPlugin {
434 /**
435 * Creates an instance of CommonJsImportsParserPlugin.
436 * @param {JavascriptParserOptions} options parser options
437 */
438 constructor(options) {
439 this.options = options;
440 }
441
442 /**
443 * Applies the plugin by registering its hooks on the compiler.
444 * @param {JavascriptParser} parser the parser
445 * @returns {void}
446 */
447 apply(parser) {
448 const options = this.options;
449 parser.hooks.collectDestructuringAssignmentProperties.tap(
450 PLUGIN_NAME,
451 (expr) => {
452 if (isRequireCallExpression(expr)) return true;
453 }
454 );
455
456 const getContext = () => {
457 if (parser.currentTagData) {
458 const { context } =
459 /** @type {CommonJsImportSettings} */
460 (parser.currentTagData);
461 return context;
462 }
463 };
464
465 // #region metadata
466 /**
467 * Tap require expression.
468 * @param {string} expression expression
469 * @param {() => Members} getMembers get members
470 */
471 const tapRequireExpression = (expression, getMembers) => {
472 parser.hooks.typeof
473 .for(expression)
474 .tap(
475 PLUGIN_NAME,
476 toConstantDependency(parser, JSON.stringify("function"))
477 );
478 parser.hooks.evaluateTypeof
479 .for(expression)
480 .tap(PLUGIN_NAME, evaluateToString("function"));
481 parser.hooks.evaluateIdentifier
482 .for(expression)
483 .tap(
484 PLUGIN_NAME,
485 evaluateToIdentifier(expression, "require", getMembers, true)
486 );
487 };
488 tapRequireExpression("require", () => []);
489 tapRequireExpression("require.resolve", () => ["resolve"]);
490 tapRequireExpression("require.resolveWeak", () => ["resolveWeak"]);
491 // #endregion
492
493 // Weird stuff //
494 parser.hooks.assign.for("require").tap(PLUGIN_NAME, (expr) => {
495 // to not leak to global "require", we need to define a local require here.
496 const dep = new ConstDependency("var require;", 0);
497 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
498 parser.state.module.addPresentationalDependency(dep);
499 return true;
500 });
501
502 // #region Unsupported
503 parser.hooks.call
504 .for("require.main.require")
505 .tap(
506 PLUGIN_NAME,
507 expressionIsUnsupported(
508 parser,
509 "require.main.require is not supported by webpack."
510 )
511 );
512 parser.hooks.expression
513 .for("module.parent.require")
514 .tap(
515 PLUGIN_NAME,
516 expressionIsUnsupported(
517 parser,
518 "module.parent.require is not supported by webpack."
519 )
520 );
521 parser.hooks.call
522 .for("module.parent.require")
523 .tap(
524 PLUGIN_NAME,
525 expressionIsUnsupported(
526 parser,
527 "module.parent.require is not supported by webpack."
528 )
529 );
530 // #endregion
531
532 // #region Renaming
533 /**
534 * Returns true when set undefined.
535 * @param {Expression} expr expression
536 * @returns {boolean} true when set undefined
537 */
538 const defineUndefined = (expr) => {
539 // To avoid "not defined" error, replace the value with undefined
540 const dep = new ConstDependency(
541 "undefined",
542 /** @type {Range} */ (expr.range)
543 );
544 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
545 parser.state.module.addPresentationalDependency(dep);
546 return false;
547 };
548 parser.hooks.canRename.for("require").tap(PLUGIN_NAME, () => true);
549 parser.hooks.rename.for("require").tap(PLUGIN_NAME, defineUndefined);
550 // #endregion
551
552 // #region Inspection
553 const requireCache = createRequireCacheDependency(parser);
554
555 parser.hooks.expression.for("require.cache").tap(PLUGIN_NAME, requireCache);
556 // #endregion
557
558 // #region Require as expression
559 /**
560 * Require as expression handler.
561 * @param {Expression} expr expression
562 * @returns {boolean} true when handled
563 */
564 const requireAsExpressionHandler = createRequireAsExpressionHandler(
565 parser,
566 options,
567 getContext
568 );
569 parser.hooks.expression
570 .for("require")
571 .tap(PLUGIN_NAME, requireAsExpressionHandler);
572 // #endregion
573
574 // #region Require
575 /**
576 * Creates a require handler.
577 * @param {boolean} callNew true, when require is called with new
578 * @returns {(expr: CallExpression | NewExpression) => (boolean | void)} handler
579 */
580 const createRequireHandler = createRequireCallHandler(
581 parser,
582 options,
583 getContext
584 );
585 parser.hooks.call
586 .for("require")
587 .tap(PLUGIN_NAME, createRequireHandler(false));
588 parser.hooks.new
589 .for("require")
590 .tap(PLUGIN_NAME, createRequireHandler(true));
591 parser.hooks.call
592 .for("module.require")
593 .tap(PLUGIN_NAME, createRequireHandler(false));
594 parser.hooks.new
595 .for("module.require")
596 .tap(PLUGIN_NAME, createRequireHandler(true));
597 // #endregion
598
599 // #region Require with property access
600 /**
601 * Returns true when handled.
602 * @param {Expression} expr expression
603 * @param {CalleeMembers} calleeMembers callee members
604 * @param {CallExpression} callExpr call expression
605 * @param {Members} members members
606 * @param {Range[]} memberRanges member ranges
607 * @returns {boolean | void} true when handled
608 */
609 const chainHandler = (
610 expr,
611 calleeMembers,
612 callExpr,
613 members,
614 memberRanges
615 ) => {
616 if (callExpr.arguments.length !== 1) return;
617 const param = parser.evaluateExpression(callExpr.arguments[0]);
618 if (
619 param.isString() &&
620 !getLocalModule(parser.state, /** @type {string} */ (param.string))
621 ) {
622 const dep = new CommonJsFullRequireDependency(
623 /** @type {string} */ (param.string),
624 /** @type {Range} */ (expr.range),
625 members,
626 /** @type {Range[]} */ memberRanges
627 );
628 dep.asiSafe = !parser.isAsiPosition(
629 /** @type {Range} */ (expr.range)[0]
630 );
631 dep.optional = Boolean(parser.scope.inTry);
632 dep.loc = /** @type {DependencyLocation} */ (expr.loc);
633 parser.state.current.addDependency(dep);
634 return true;
635 }
636 };
637 /**
638 * Call chain handler.
639 * @param {CallExpression} expr expression
640 * @param {CalleeMembers} calleeMembers callee members
641 * @param {CallExpression} callExpr call expression
642 * @param {Members} members members
643 * @param {Range[]} memberRanges member ranges
644 * @returns {boolean | void} true when handled
645 */
646 const callChainHandler = (
647 expr,
648 calleeMembers,
649 callExpr,
650 members,
651 memberRanges
652 ) => {
653 if (callExpr.arguments.length !== 1) return;
654 const param = parser.evaluateExpression(callExpr.arguments[0]);
655 if (
656 param.isString() &&
657 !getLocalModule(parser.state, /** @type {string} */ (param.string))
658 ) {
659 const dep = new CommonJsFullRequireDependency(
660 /** @type {string} */ (param.string),
661 /** @type {Range} */ (expr.callee.range),
662 members,
663 /** @type {Range[]} */ memberRanges
664 );
665 dep.call = true;
666 dep.asiSafe = !parser.isAsiPosition(
667 /** @type {Range} */ (expr.range)[0]
668 );
669 dep.optional = Boolean(parser.scope.inTry);
670 dep.loc = /** @type {DependencyLocation} */ (expr.callee.loc);
671 parser.state.current.addDependency(dep);
672 parser.walkExpressions(expr.arguments);
673 return true;
674 }
675 };
676 parser.hooks.memberChainOfCallMemberChain
677 .for("require")
678 .tap(PLUGIN_NAME, chainHandler);
679 parser.hooks.memberChainOfCallMemberChain
680 .for("module.require")
681 .tap(PLUGIN_NAME, chainHandler);
682 parser.hooks.callMemberChainOfCallMemberChain
683 .for("require")
684 .tap(PLUGIN_NAME, callChainHandler);
685 parser.hooks.callMemberChainOfCallMemberChain
686 .for("module.require")
687 .tap(PLUGIN_NAME, callChainHandler);
688 // #endregion
689
690 // #region Require bound to a const variable
691 // Track `const NAME = require(LITERAL)` so that static member accesses on
692 // `NAME` (e.g. `NAME.foo`, `NAME.foo()`) are forwarded to the same
693 // `CommonJsRequireDependency` as referenced exports — enabling tree
694 // shaking of CommonJS modules that are imported into a named binding
695 // rather than destructured.
696 parser.hooks.preDeclarator.tap(PLUGIN_NAME, (declarator, statement) => {
697 if (statement.kind !== "const") return;
698 if (declarator.id.type !== "Identifier") return;
699 if (!declarator.init || declarator.init.type !== "CallExpression") {
700 return;
701 }
702 const init = declarator.init;
703 if (
704 init.callee.type !== "Identifier" ||
705 init.callee.name !== "require" ||
706 init.arguments.length !== 1
707 ) {
708 return;
709 }
710 const arg = init.arguments[0];
711 if (arg.type !== "Literal" || typeof arg.value !== "string") return;
712 // Only attach binding state when `require` resolves to the free
713 // `require` (i.e. it isn't shadowed in the current scope).
714 const requireInfo = parser.getFreeInfoFromVariable("require");
715 if (!requireInfo || requireInfo.name !== "require") return;
716 /** @type {RequireBindingData} */
717 const binding = {
718 referencedExports: [],
719 dep: null
720 };
721 requireBindingData.set(init, binding);
722 parser.tagVariable(declarator.id.name, REQUIRE_BINDING_TAG, binding);
723 return true;
724 });
725
726 parser.hooks.expression.for(REQUIRE_BINDING_TAG).tap(PLUGIN_NAME, () => {
727 const binding =
728 /** @type {RequireBindingData} */
729 (parser.currentTagData);
730 if (binding && binding.dep) {
731 // `NAME` is read as a value (not as the object of a static member
732 // chain), so we have to assume the whole exports object is used.
733 binding.dep.referencedExports = null;
734 }
735 });
736
737 parser.hooks.expressionMemberChain
738 .for(REQUIRE_BINDING_TAG)
739 .tap(PLUGIN_NAME, (_expr, members) => {
740 const binding =
741 /** @type {RequireBindingData} */
742 (parser.currentTagData);
743 if (binding && binding.dep && binding.dep.referencedExports) {
744 binding.dep.referencedExports.push(members);
745 }
746 // Returning truthy suppresses the parser's fallback chain (which
747 // would otherwise walk `NAME` as a bare expression and trigger our
748 // `expression` hook above, marking the whole namespace as used).
749 return true;
750 });
751
752 parser.hooks.callMemberChain
753 .for(REQUIRE_BINDING_TAG)
754 .tap(PLUGIN_NAME, (expr, members) => {
755 const binding =
756 /** @type {RequireBindingData} */
757 (parser.currentTagData);
758 if (binding && binding.dep && binding.dep.referencedExports) {
759 if (members.length === 0) {
760 // `NAME(...)` — calling the require result directly; the
761 // whole exports object is observable.
762 binding.dep.referencedExports = null;
763 } else {
764 binding.dep.referencedExports.push(members);
765 }
766 }
767 if (expr.arguments) parser.walkExpressions(expr.arguments);
768 return true;
769 });
770 // #endregion
771
772 // #region Require.resolve
773 /**
774 * Processes the provided expr.
775 * @param {CallExpression} expr call expression
776 * @param {boolean} weak weak
777 * @returns {boolean | void} true when handled
778 */
779 const processResolve = createProcessResolveHandler(
780 parser,
781 options,
782 getContext
783 );
784
785 parser.hooks.call
786 .for("require.resolve")
787 .tap(PLUGIN_NAME, (expr) => processResolve(expr, false));
788 parser.hooks.call
789 .for("require.resolveWeak")
790 .tap(PLUGIN_NAME, (expr) => processResolve(expr, true));
791 // #endregion
792 }
793}
794
795module.exports = CommonJsImportsParserPlugin;
796module.exports.createProcessResolveHandler = createProcessResolveHandler;
797module.exports.createRequireAsExpressionHandler =
798 createRequireAsExpressionHandler;
799module.exports.createRequireCacheDependency = createRequireCacheDependency;
800module.exports.createRequireHandler = createRequireCallHandler;
Note: See TracBrowser for help on using the repository browser.