CommonJsImportsParserPlugin.js 25 KB

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