CommonJsImportsParserPlugin.js 24 KB

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