118 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			118 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| const EnvironmentNotSupportAsyncWarning = require("../EnvironmentNotSupportAsyncWarning");
 | |
| const { JAVASCRIPT_MODULE_TYPE_ESM } = require("../ModuleTypeConstants");
 | |
| const DynamicExports = require("./DynamicExports");
 | |
| const HarmonyCompatibilityDependency = require("./HarmonyCompatibilityDependency");
 | |
| const HarmonyExports = require("./HarmonyExports");
 | |
| 
 | |
| /** @typedef {import("../Module").BuildMeta} BuildMeta */
 | |
| /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
 | |
| /** @typedef {import("./HarmonyModulesPlugin").HarmonyModulesPluginOptions} HarmonyModulesPluginOptions */
 | |
| 
 | |
| const PLUGIN_NAME = "HarmonyDetectionParserPlugin";
 | |
| 
 | |
| module.exports = class HarmonyDetectionParserPlugin {
 | |
| 	/**
 | |
| 	 * @param {HarmonyModulesPluginOptions} options options
 | |
| 	 */
 | |
| 	constructor(options) {
 | |
| 		const { topLevelAwait = false } = options || {};
 | |
| 		this.topLevelAwait = topLevelAwait;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param {JavascriptParser} parser the parser
 | |
| 	 * @returns {void}
 | |
| 	 */
 | |
| 	apply(parser) {
 | |
| 		parser.hooks.program.tap(PLUGIN_NAME, (ast) => {
 | |
| 			const isStrictHarmony =
 | |
| 				parser.state.module.type === JAVASCRIPT_MODULE_TYPE_ESM;
 | |
| 			const isHarmony =
 | |
| 				isStrictHarmony ||
 | |
| 				ast.body.some(
 | |
| 					(statement) =>
 | |
| 						statement.type === "ImportDeclaration" ||
 | |
| 						statement.type === "ExportDefaultDeclaration" ||
 | |
| 						statement.type === "ExportNamedDeclaration" ||
 | |
| 						statement.type === "ExportAllDeclaration"
 | |
| 				);
 | |
| 			if (isHarmony) {
 | |
| 				const module = parser.state.module;
 | |
| 				const compatDep = new HarmonyCompatibilityDependency();
 | |
| 				compatDep.loc = {
 | |
| 					start: {
 | |
| 						line: -1,
 | |
| 						column: 0
 | |
| 					},
 | |
| 					end: {
 | |
| 						line: -1,
 | |
| 						column: 0
 | |
| 					},
 | |
| 					index: -3
 | |
| 				};
 | |
| 				module.addPresentationalDependency(compatDep);
 | |
| 				DynamicExports.bailout(parser.state);
 | |
| 				HarmonyExports.enable(parser.state, isStrictHarmony);
 | |
| 				parser.scope.isStrict = true;
 | |
| 			}
 | |
| 		});
 | |
| 
 | |
| 		parser.hooks.topLevelAwait.tap(PLUGIN_NAME, () => {
 | |
| 			const module = parser.state.module;
 | |
| 			if (!this.topLevelAwait) {
 | |
| 				throw new Error(
 | |
| 					"The top-level-await experiment is not enabled (set experiments.topLevelAwait: true to enable it)"
 | |
| 				);
 | |
| 			}
 | |
| 			if (!HarmonyExports.isEnabled(parser.state)) {
 | |
| 				throw new Error(
 | |
| 					"Top-level-await is only supported in EcmaScript Modules"
 | |
| 				);
 | |
| 			}
 | |
| 			/** @type {BuildMeta} */
 | |
| 			(module.buildMeta).async = true;
 | |
| 			EnvironmentNotSupportAsyncWarning.check(
 | |
| 				module,
 | |
| 				parser.state.compilation.runtimeTemplate,
 | |
| 				"topLevelAwait"
 | |
| 			);
 | |
| 		});
 | |
| 
 | |
| 		/**
 | |
| 		 * @returns {boolean | undefined} true if in harmony
 | |
| 		 */
 | |
| 		const skipInHarmony = () => {
 | |
| 			if (HarmonyExports.isEnabled(parser.state)) {
 | |
| 				return true;
 | |
| 			}
 | |
| 		};
 | |
| 
 | |
| 		/**
 | |
| 		 * @returns {null | undefined} null if in harmony
 | |
| 		 */
 | |
| 		const nullInHarmony = () => {
 | |
| 			if (HarmonyExports.isEnabled(parser.state)) {
 | |
| 				return null;
 | |
| 			}
 | |
| 		};
 | |
| 
 | |
| 		const nonHarmonyIdentifiers = ["define", "exports"];
 | |
| 		for (const identifier of nonHarmonyIdentifiers) {
 | |
| 			parser.hooks.evaluateTypeof
 | |
| 				.for(identifier)
 | |
| 				.tap(PLUGIN_NAME, nullInHarmony);
 | |
| 			parser.hooks.typeof.for(identifier).tap(PLUGIN_NAME, skipInHarmony);
 | |
| 			parser.hooks.evaluate.for(identifier).tap(PLUGIN_NAME, nullInHarmony);
 | |
| 			parser.hooks.expression.for(identifier).tap(PLUGIN_NAME, skipInHarmony);
 | |
| 			parser.hooks.call.for(identifier).tap(PLUGIN_NAME, skipInHarmony);
 | |
| 		}
 | |
| 	}
 | |
| };
 |