ImportParserPlugin.js 8.5 KB

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