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;
 |