ImportParserPlugin.js 10 KB

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