CommonJsImportsParserPlugin.js 25 KB

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