ImportParserPlugin.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
  7. const CommentCompilationWarning = require("../CommentCompilationWarning");
  8. const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
  9. const ContextDependencyHelpers = require("./ContextDependencyHelpers");
  10. const { getAttributes } = require("./HarmonyImportDependencyParserPlugin");
  11. const ImportContextDependency = require("./ImportContextDependency");
  12. const ImportDependency = require("./ImportDependency");
  13. const ImportEagerDependency = require("./ImportEagerDependency");
  14. const ImportWeakDependency = require("./ImportWeakDependency");
  15. /** @typedef {import("estree").ImportExpression} ImportExpression */
  16. /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
  17. /** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
  18. /** @typedef {import("../ContextModule").ContextMode} ContextMode */
  19. /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
  20. /** @typedef {import("../Module").BuildMeta} BuildMeta */
  21. /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
  22. /** @typedef {import("../javascript/JavascriptParser").Range} Range */
  23. class ImportParserPlugin {
  24. /**
  25. * @param {JavascriptParserOptions} options options
  26. */
  27. constructor(options) {
  28. this.options = options;
  29. }
  30. /**
  31. * @param {JavascriptParser} parser the parser
  32. * @returns {void}
  33. */
  34. apply(parser) {
  35. /**
  36. * @template T
  37. * @param {Iterable<T>} enumerable enumerable
  38. * @returns {T[][]} array of array
  39. */
  40. const exportsFromEnumerable = enumerable =>
  41. Array.from(enumerable, e => [e]);
  42. parser.hooks.importCall.tap("ImportParserPlugin", expr => {
  43. const param = parser.evaluateExpression(expr.source);
  44. let chunkName = null;
  45. let mode = /** @type {ContextMode} */ (this.options.dynamicImportMode);
  46. let include = null;
  47. let exclude = null;
  48. /** @type {string[][] | null} */
  49. let exports = null;
  50. /** @type {RawChunkGroupOptions} */
  51. const groupOptions = {};
  52. const {
  53. dynamicImportPreload,
  54. dynamicImportPrefetch,
  55. dynamicImportFetchPriority
  56. } = this.options;
  57. if (dynamicImportPreload !== undefined && dynamicImportPreload !== false)
  58. groupOptions.preloadOrder =
  59. dynamicImportPreload === true ? 0 : dynamicImportPreload;
  60. if (
  61. dynamicImportPrefetch !== undefined &&
  62. dynamicImportPrefetch !== false
  63. )
  64. groupOptions.prefetchOrder =
  65. dynamicImportPrefetch === true ? 0 : dynamicImportPrefetch;
  66. if (
  67. dynamicImportFetchPriority !== undefined &&
  68. dynamicImportFetchPriority !== false
  69. )
  70. groupOptions.fetchPriority = dynamicImportFetchPriority;
  71. const { options: importOptions, errors: commentErrors } =
  72. parser.parseCommentOptions(/** @type {Range} */ (expr.range));
  73. if (commentErrors) {
  74. for (const e of commentErrors) {
  75. const { comment } = e;
  76. parser.state.module.addWarning(
  77. new CommentCompilationWarning(
  78. `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
  79. comment.loc
  80. )
  81. );
  82. }
  83. }
  84. if (importOptions) {
  85. if (importOptions.webpackIgnore !== undefined) {
  86. if (typeof importOptions.webpackIgnore !== "boolean") {
  87. parser.state.module.addWarning(
  88. new UnsupportedFeatureWarning(
  89. `\`webpackIgnore\` expected a boolean, but received: ${importOptions.webpackIgnore}.`,
  90. /** @type {DependencyLocation} */ (expr.loc)
  91. )
  92. );
  93. } else {
  94. // Do not instrument `import()` if `webpackIgnore` is `true`
  95. if (importOptions.webpackIgnore) {
  96. return false;
  97. }
  98. }
  99. }
  100. if (importOptions.webpackChunkName !== undefined) {
  101. if (typeof importOptions.webpackChunkName !== "string") {
  102. parser.state.module.addWarning(
  103. new UnsupportedFeatureWarning(
  104. `\`webpackChunkName\` expected a string, but received: ${importOptions.webpackChunkName}.`,
  105. /** @type {DependencyLocation} */ (expr.loc)
  106. )
  107. );
  108. } else {
  109. chunkName = importOptions.webpackChunkName;
  110. }
  111. }
  112. if (importOptions.webpackMode !== undefined) {
  113. if (typeof importOptions.webpackMode !== "string") {
  114. parser.state.module.addWarning(
  115. new UnsupportedFeatureWarning(
  116. `\`webpackMode\` expected a string, but received: ${importOptions.webpackMode}.`,
  117. /** @type {DependencyLocation} */ (expr.loc)
  118. )
  119. );
  120. } else {
  121. mode = importOptions.webpackMode;
  122. }
  123. }
  124. if (importOptions.webpackPrefetch !== undefined) {
  125. if (importOptions.webpackPrefetch === true) {
  126. groupOptions.prefetchOrder = 0;
  127. } else if (typeof importOptions.webpackPrefetch === "number") {
  128. groupOptions.prefetchOrder = importOptions.webpackPrefetch;
  129. } else {
  130. parser.state.module.addWarning(
  131. new UnsupportedFeatureWarning(
  132. `\`webpackPrefetch\` expected true or a number, but received: ${importOptions.webpackPrefetch}.`,
  133. /** @type {DependencyLocation} */ (expr.loc)
  134. )
  135. );
  136. }
  137. }
  138. if (importOptions.webpackPreload !== undefined) {
  139. if (importOptions.webpackPreload === true) {
  140. groupOptions.preloadOrder = 0;
  141. } else if (typeof importOptions.webpackPreload === "number") {
  142. groupOptions.preloadOrder = importOptions.webpackPreload;
  143. } else {
  144. parser.state.module.addWarning(
  145. new UnsupportedFeatureWarning(
  146. `\`webpackPreload\` expected true or a number, but received: ${importOptions.webpackPreload}.`,
  147. /** @type {DependencyLocation} */ (expr.loc)
  148. )
  149. );
  150. }
  151. }
  152. if (importOptions.webpackFetchPriority !== undefined) {
  153. if (
  154. typeof importOptions.webpackFetchPriority === "string" &&
  155. ["high", "low", "auto"].includes(importOptions.webpackFetchPriority)
  156. ) {
  157. groupOptions.fetchPriority = importOptions.webpackFetchPriority;
  158. } else {
  159. parser.state.module.addWarning(
  160. new UnsupportedFeatureWarning(
  161. `\`webpackFetchPriority\` expected true or "low", "high" or "auto", but received: ${importOptions.webpackFetchPriority}.`,
  162. /** @type {DependencyLocation} */ (expr.loc)
  163. )
  164. );
  165. }
  166. }
  167. if (importOptions.webpackInclude !== undefined) {
  168. if (
  169. !importOptions.webpackInclude ||
  170. !(importOptions.webpackInclude instanceof RegExp)
  171. ) {
  172. parser.state.module.addWarning(
  173. new UnsupportedFeatureWarning(
  174. `\`webpackInclude\` expected a regular expression, but received: ${importOptions.webpackInclude}.`,
  175. /** @type {DependencyLocation} */ (expr.loc)
  176. )
  177. );
  178. } else {
  179. include = importOptions.webpackInclude;
  180. }
  181. }
  182. if (importOptions.webpackExclude !== undefined) {
  183. if (
  184. !importOptions.webpackExclude ||
  185. !(importOptions.webpackExclude instanceof RegExp)
  186. ) {
  187. parser.state.module.addWarning(
  188. new UnsupportedFeatureWarning(
  189. `\`webpackExclude\` expected a regular expression, but received: ${importOptions.webpackExclude}.`,
  190. /** @type {DependencyLocation} */ (expr.loc)
  191. )
  192. );
  193. } else {
  194. exclude = importOptions.webpackExclude;
  195. }
  196. }
  197. if (importOptions.webpackExports !== undefined) {
  198. if (
  199. !(
  200. typeof importOptions.webpackExports === "string" ||
  201. (Array.isArray(importOptions.webpackExports) &&
  202. /** @type {string[]} */ (importOptions.webpackExports).every(
  203. item => typeof item === "string"
  204. ))
  205. )
  206. ) {
  207. parser.state.module.addWarning(
  208. new UnsupportedFeatureWarning(
  209. `\`webpackExports\` expected a string or an array of strings, but received: ${importOptions.webpackExports}.`,
  210. /** @type {DependencyLocation} */ (expr.loc)
  211. )
  212. );
  213. } else {
  214. if (typeof importOptions.webpackExports === "string") {
  215. exports = [[importOptions.webpackExports]];
  216. } else {
  217. exports = exportsFromEnumerable(importOptions.webpackExports);
  218. }
  219. }
  220. }
  221. }
  222. if (
  223. mode !== "lazy" &&
  224. mode !== "lazy-once" &&
  225. mode !== "eager" &&
  226. mode !== "weak"
  227. ) {
  228. parser.state.module.addWarning(
  229. new UnsupportedFeatureWarning(
  230. `\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${mode}.`,
  231. /** @type {DependencyLocation} */ (expr.loc)
  232. )
  233. );
  234. mode = "lazy";
  235. }
  236. const referencedPropertiesInDestructuring =
  237. parser.destructuringAssignmentPropertiesFor(expr);
  238. if (referencedPropertiesInDestructuring) {
  239. if (exports) {
  240. parser.state.module.addWarning(
  241. new UnsupportedFeatureWarning(
  242. `\`webpackExports\` could not be used with destructuring assignment.`,
  243. /** @type {DependencyLocation} */ (expr.loc)
  244. )
  245. );
  246. }
  247. exports = exportsFromEnumerable(
  248. [...referencedPropertiesInDestructuring].map(({ id }) => id)
  249. );
  250. }
  251. if (param.isString()) {
  252. const attributes = getAttributes(expr);
  253. if (mode === "eager") {
  254. const dep = new ImportEagerDependency(
  255. /** @type {string} */ (param.string),
  256. /** @type {Range} */ (expr.range),
  257. exports,
  258. attributes
  259. );
  260. parser.state.current.addDependency(dep);
  261. } else if (mode === "weak") {
  262. const dep = new ImportWeakDependency(
  263. /** @type {string} */ (param.string),
  264. /** @type {Range} */ (expr.range),
  265. exports,
  266. attributes
  267. );
  268. parser.state.current.addDependency(dep);
  269. } else {
  270. const depBlock = new AsyncDependenciesBlock(
  271. {
  272. ...groupOptions,
  273. name: chunkName
  274. },
  275. /** @type {DependencyLocation} */ (expr.loc),
  276. param.string
  277. );
  278. const dep = new ImportDependency(
  279. /** @type {string} */ (param.string),
  280. /** @type {Range} */ (expr.range),
  281. exports,
  282. attributes
  283. );
  284. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  285. dep.optional = !!parser.scope.inTry;
  286. depBlock.addDependency(dep);
  287. parser.state.current.addBlock(depBlock);
  288. }
  289. return true;
  290. } else {
  291. if (mode === "weak") {
  292. mode = "async-weak";
  293. }
  294. const dep = ContextDependencyHelpers.create(
  295. ImportContextDependency,
  296. /** @type {Range} */ (expr.range),
  297. param,
  298. expr,
  299. this.options,
  300. {
  301. chunkName,
  302. groupOptions,
  303. include,
  304. exclude,
  305. mode,
  306. namespaceObject: /** @type {BuildMeta} */ (
  307. parser.state.module.buildMeta
  308. ).strictHarmonyModule
  309. ? "strict"
  310. : true,
  311. typePrefix: "import()",
  312. category: "esm",
  313. referencedExports: exports,
  314. attributes: getAttributes(expr)
  315. },
  316. parser
  317. );
  318. if (!dep) return;
  319. dep.loc = /** @type {DependencyLocation} */ (expr.loc);
  320. dep.optional = !!parser.scope.inTry;
  321. parser.state.current.addDependency(dep);
  322. return true;
  323. }
  324. });
  325. }
  326. }
  327. module.exports = ImportParserPlugin;