webpack
Version:
Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
118 lines (107 loc) • 3.45 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
;
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);
}
}
};