377 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			377 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
 | |
| const CommentCompilationWarning = require("../CommentCompilationWarning");
 | |
| const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
 | |
| const { getImportAttributes } = require("../javascript/JavascriptParser");
 | |
| const ContextDependencyHelpers = require("./ContextDependencyHelpers");
 | |
| const ImportContextDependency = require("./ImportContextDependency");
 | |
| const ImportDependency = require("./ImportDependency");
 | |
| const ImportEagerDependency = require("./ImportEagerDependency");
 | |
| const ImportWeakDependency = require("./ImportWeakDependency");
 | |
| 
 | |
| /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
 | |
| /** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
 | |
| /** @typedef {import("../ContextModule").ContextMode} ContextMode */
 | |
| /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
 | |
| /** @typedef {import("../Module").BuildMeta} BuildMeta */
 | |
| /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
 | |
| /** @typedef {import("../javascript/JavascriptParser").ImportExpression} ImportExpression */
 | |
| /** @typedef {import("../javascript/JavascriptParser").Range} Range */
 | |
| 
 | |
| const PLUGIN_NAME = "ImportParserPlugin";
 | |
| 
 | |
| class ImportParserPlugin {
 | |
| 	/**
 | |
| 	 * @param {JavascriptParserOptions} options options
 | |
| 	 */
 | |
| 	constructor(options) {
 | |
| 		this.options = options;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param {JavascriptParser} parser the parser
 | |
| 	 * @returns {void}
 | |
| 	 */
 | |
| 	apply(parser) {
 | |
| 		/**
 | |
| 		 * @template T
 | |
| 		 * @param {Iterable<T>} enumerable enumerable
 | |
| 		 * @returns {T[][]} array of array
 | |
| 		 */
 | |
| 		const exportsFromEnumerable = (enumerable) =>
 | |
| 			Array.from(enumerable, (e) => [e]);
 | |
| 		parser.hooks.collectDestructuringAssignmentProperties.tap(
 | |
| 			PLUGIN_NAME,
 | |
| 			(expr) => {
 | |
| 				if (expr.type === "ImportExpression") return true;
 | |
| 			}
 | |
| 		);
 | |
| 		parser.hooks.importCall.tap(PLUGIN_NAME, (expr) => {
 | |
| 			const param = parser.evaluateExpression(expr.source);
 | |
| 
 | |
| 			let chunkName = null;
 | |
| 			let mode = /** @type {ContextMode} */ (this.options.dynamicImportMode);
 | |
| 			let include = null;
 | |
| 			let exclude = null;
 | |
| 			/** @type {string[][] | null} */
 | |
| 			let exports = null;
 | |
| 			/** @type {RawChunkGroupOptions} */
 | |
| 			const groupOptions = {};
 | |
| 
 | |
| 			const {
 | |
| 				dynamicImportPreload,
 | |
| 				dynamicImportPrefetch,
 | |
| 				dynamicImportFetchPriority
 | |
| 			} = this.options;
 | |
| 			if (
 | |
| 				dynamicImportPreload !== undefined &&
 | |
| 				dynamicImportPreload !== false
 | |
| 			) {
 | |
| 				groupOptions.preloadOrder =
 | |
| 					dynamicImportPreload === true ? 0 : dynamicImportPreload;
 | |
| 			}
 | |
| 			if (
 | |
| 				dynamicImportPrefetch !== undefined &&
 | |
| 				dynamicImportPrefetch !== false
 | |
| 			) {
 | |
| 				groupOptions.prefetchOrder =
 | |
| 					dynamicImportPrefetch === true ? 0 : dynamicImportPrefetch;
 | |
| 			}
 | |
| 			if (
 | |
| 				dynamicImportFetchPriority !== undefined &&
 | |
| 				dynamicImportFetchPriority !== false
 | |
| 			) {
 | |
| 				groupOptions.fetchPriority = dynamicImportFetchPriority;
 | |
| 			}
 | |
| 
 | |
| 			const { options: importOptions, errors: commentErrors } =
 | |
| 				parser.parseCommentOptions(/** @type {Range} */ (expr.range));
 | |
| 
 | |
| 			if (commentErrors) {
 | |
| 				for (const e of commentErrors) {
 | |
| 					const { comment } = e;
 | |
| 					parser.state.module.addWarning(
 | |
| 						new CommentCompilationWarning(
 | |
| 							`Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
 | |
| 							/** @type {DependencyLocation} */ (comment.loc)
 | |
| 						)
 | |
| 					);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			let phase = expr.phase;
 | |
| 			if (!phase && importOptions && importOptions.webpackDefer !== undefined) {
 | |
| 				if (typeof importOptions.webpackDefer !== "boolean") {
 | |
| 					parser.state.module.addWarning(
 | |
| 						new UnsupportedFeatureWarning(
 | |
| 							`\`webpackDefer\` expected a boolean, but received: ${importOptions.webpackDefer}.`,
 | |
| 							/** @type {DependencyLocation} */ (expr.loc)
 | |
| 						)
 | |
| 					);
 | |
| 				} else if (importOptions.webpackDefer) {
 | |
| 					phase = "defer";
 | |
| 				}
 | |
| 			}
 | |
| 			if (phase === "defer") {
 | |
| 				parser.state.module.addWarning(
 | |
| 					new UnsupportedFeatureWarning(
 | |
| 						"import.defer() is not implemented yet.",
 | |
| 						/** @type {DependencyLocation} */ (expr.loc)
 | |
| 					)
 | |
| 				);
 | |
| 			}
 | |
| 
 | |
| 			if (importOptions) {
 | |
| 				if (importOptions.webpackIgnore !== undefined) {
 | |
| 					if (typeof importOptions.webpackIgnore !== "boolean") {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackIgnore\` expected a boolean, but received: ${importOptions.webpackIgnore}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					} else if (importOptions.webpackIgnore) {
 | |
| 						// Do not instrument `import()` if `webpackIgnore` is `true`
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 				if (importOptions.webpackChunkName !== undefined) {
 | |
| 					if (typeof importOptions.webpackChunkName !== "string") {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackChunkName\` expected a string, but received: ${importOptions.webpackChunkName}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					} else {
 | |
| 						chunkName = importOptions.webpackChunkName;
 | |
| 					}
 | |
| 				}
 | |
| 				if (importOptions.webpackMode !== undefined) {
 | |
| 					if (typeof importOptions.webpackMode !== "string") {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackMode\` expected a string, but received: ${importOptions.webpackMode}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					} else {
 | |
| 						mode = /** @type {ContextMode} */ (importOptions.webpackMode);
 | |
| 					}
 | |
| 				}
 | |
| 				if (importOptions.webpackPrefetch !== undefined) {
 | |
| 					if (importOptions.webpackPrefetch === true) {
 | |
| 						groupOptions.prefetchOrder = 0;
 | |
| 					} else if (typeof importOptions.webpackPrefetch === "number") {
 | |
| 						groupOptions.prefetchOrder = importOptions.webpackPrefetch;
 | |
| 					} else {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackPrefetch\` expected true or a number, but received: ${importOptions.webpackPrefetch}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					}
 | |
| 				}
 | |
| 				if (importOptions.webpackPreload !== undefined) {
 | |
| 					if (importOptions.webpackPreload === true) {
 | |
| 						groupOptions.preloadOrder = 0;
 | |
| 					} else if (typeof importOptions.webpackPreload === "number") {
 | |
| 						groupOptions.preloadOrder = importOptions.webpackPreload;
 | |
| 					} else {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackPreload\` expected true or a number, but received: ${importOptions.webpackPreload}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					}
 | |
| 				}
 | |
| 				if (importOptions.webpackFetchPriority !== undefined) {
 | |
| 					if (
 | |
| 						typeof importOptions.webpackFetchPriority === "string" &&
 | |
| 						["high", "low", "auto"].includes(importOptions.webpackFetchPriority)
 | |
| 					) {
 | |
| 						groupOptions.fetchPriority =
 | |
| 							/** @type {"low" | "high" | "auto"} */
 | |
| 							(importOptions.webpackFetchPriority);
 | |
| 					} else {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackFetchPriority\` expected true or "low", "high" or "auto", but received: ${importOptions.webpackFetchPriority}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					}
 | |
| 				}
 | |
| 				if (importOptions.webpackInclude !== undefined) {
 | |
| 					if (
 | |
| 						!importOptions.webpackInclude ||
 | |
| 						!(importOptions.webpackInclude instanceof RegExp)
 | |
| 					) {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackInclude\` expected a regular expression, but received: ${importOptions.webpackInclude}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					} else {
 | |
| 						include = importOptions.webpackInclude;
 | |
| 					}
 | |
| 				}
 | |
| 				if (importOptions.webpackExclude !== undefined) {
 | |
| 					if (
 | |
| 						!importOptions.webpackExclude ||
 | |
| 						!(importOptions.webpackExclude instanceof RegExp)
 | |
| 					) {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackExclude\` expected a regular expression, but received: ${importOptions.webpackExclude}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					} else {
 | |
| 						exclude = importOptions.webpackExclude;
 | |
| 					}
 | |
| 				}
 | |
| 				if (importOptions.webpackExports !== undefined) {
 | |
| 					if (
 | |
| 						!(
 | |
| 							typeof importOptions.webpackExports === "string" ||
 | |
| 							(Array.isArray(importOptions.webpackExports) &&
 | |
| 								/** @type {string[]} */ (importOptions.webpackExports).every(
 | |
| 									(item) => typeof item === "string"
 | |
| 								))
 | |
| 						)
 | |
| 					) {
 | |
| 						parser.state.module.addWarning(
 | |
| 							new UnsupportedFeatureWarning(
 | |
| 								`\`webpackExports\` expected a string or an array of strings, but received: ${importOptions.webpackExports}.`,
 | |
| 								/** @type {DependencyLocation} */ (expr.loc)
 | |
| 							)
 | |
| 						);
 | |
| 					} else if (typeof importOptions.webpackExports === "string") {
 | |
| 						exports = [[importOptions.webpackExports]];
 | |
| 					} else {
 | |
| 						exports = exportsFromEnumerable(importOptions.webpackExports);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (
 | |
| 				mode !== "lazy" &&
 | |
| 				mode !== "lazy-once" &&
 | |
| 				mode !== "eager" &&
 | |
| 				mode !== "weak"
 | |
| 			) {
 | |
| 				parser.state.module.addWarning(
 | |
| 					new UnsupportedFeatureWarning(
 | |
| 						`\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${mode}.`,
 | |
| 						/** @type {DependencyLocation} */ (expr.loc)
 | |
| 					)
 | |
| 				);
 | |
| 				mode = "lazy";
 | |
| 			}
 | |
| 
 | |
| 			const referencedPropertiesInDestructuring =
 | |
| 				parser.destructuringAssignmentPropertiesFor(expr);
 | |
| 			if (referencedPropertiesInDestructuring) {
 | |
| 				if (exports) {
 | |
| 					parser.state.module.addWarning(
 | |
| 						new UnsupportedFeatureWarning(
 | |
| 							"`webpackExports` could not be used with destructuring assignment.",
 | |
| 							/** @type {DependencyLocation} */ (expr.loc)
 | |
| 						)
 | |
| 					);
 | |
| 				}
 | |
| 
 | |
| 				exports = exportsFromEnumerable(
 | |
| 					[...referencedPropertiesInDestructuring].map(({ id }) => id)
 | |
| 				);
 | |
| 			}
 | |
| 
 | |
| 			if (param.isString()) {
 | |
| 				const attributes = getImportAttributes(expr);
 | |
| 
 | |
| 				if (mode === "eager") {
 | |
| 					const dep = new ImportEagerDependency(
 | |
| 						/** @type {string} */ (param.string),
 | |
| 						/** @type {Range} */ (expr.range),
 | |
| 						exports,
 | |
| 						attributes
 | |
| 					);
 | |
| 					parser.state.current.addDependency(dep);
 | |
| 				} else if (mode === "weak") {
 | |
| 					const dep = new ImportWeakDependency(
 | |
| 						/** @type {string} */ (param.string),
 | |
| 						/** @type {Range} */ (expr.range),
 | |
| 						exports,
 | |
| 						attributes
 | |
| 					);
 | |
| 					parser.state.current.addDependency(dep);
 | |
| 				} else {
 | |
| 					const depBlock = new AsyncDependenciesBlock(
 | |
| 						{
 | |
| 							...groupOptions,
 | |
| 							name: chunkName
 | |
| 						},
 | |
| 						/** @type {DependencyLocation} */ (expr.loc),
 | |
| 						param.string
 | |
| 					);
 | |
| 					const dep = new ImportDependency(
 | |
| 						/** @type {string} */ (param.string),
 | |
| 						/** @type {Range} */ (expr.range),
 | |
| 						exports,
 | |
| 						attributes
 | |
| 					);
 | |
| 					dep.loc = /** @type {DependencyLocation} */ (expr.loc);
 | |
| 					dep.optional = Boolean(parser.scope.inTry);
 | |
| 					depBlock.addDependency(dep);
 | |
| 					parser.state.current.addBlock(depBlock);
 | |
| 				}
 | |
| 				return true;
 | |
| 			}
 | |
| 			if (mode === "weak") {
 | |
| 				mode = "async-weak";
 | |
| 			}
 | |
| 			const dep = ContextDependencyHelpers.create(
 | |
| 				ImportContextDependency,
 | |
| 				/** @type {Range} */ (expr.range),
 | |
| 				param,
 | |
| 				expr,
 | |
| 				this.options,
 | |
| 				{
 | |
| 					chunkName,
 | |
| 					groupOptions,
 | |
| 					include,
 | |
| 					exclude,
 | |
| 					mode,
 | |
| 					namespaceObject:
 | |
| 						/** @type {BuildMeta} */
 | |
| 						(parser.state.module.buildMeta).strictHarmonyModule
 | |
| 							? "strict"
 | |
| 							: true,
 | |
| 					typePrefix: "import()",
 | |
| 					category: "esm",
 | |
| 					referencedExports: exports,
 | |
| 					attributes: getImportAttributes(expr)
 | |
| 				},
 | |
| 				parser
 | |
| 			);
 | |
| 			if (!dep) return;
 | |
| 			dep.loc = /** @type {DependencyLocation} */ (expr.loc);
 | |
| 			dep.optional = Boolean(parser.scope.inTry);
 | |
| 			parser.state.current.addDependency(dep);
 | |
| 			return true;
 | |
| 		});
 | |
| 	}
 | |
| }
 | |
| 
 | |
| module.exports = ImportParserPlugin;
 |