| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Ivan Kopeykin @vankop
- */
- "use strict";
- const { pathToFileURL } = require("url");
- const { SyncBailHook } = require("tapable");
- /** @import Compilation from "../Compilation" */
- const DefinePlugin = require("../DefinePlugin");
- const {
- JAVASCRIPT_MODULE_TYPE_AUTO,
- JAVASCRIPT_MODULE_TYPE_ESM
- } = require("../ModuleTypeConstants");
- const RuntimeGlobals = require("../RuntimeGlobals");
- const Template = require("../Template");
- const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression");
- const {
- evaluateToIdentifier,
- evaluateToNumber,
- evaluateToString,
- toConstantDependency
- } = require("../javascript/JavascriptParserHelpers");
- const { getInnerGraphUtils } = require("../optimize/InnerGraph");
- const createHooksRegistry = require("../util/createHooksRegistry");
- const { propertyAccess } = require("../util/property");
- const BuildTimeConstDependency = require("./BuildTimeConstDependency");
- const ConstDependency = require("./ConstDependency");
- const ImportMetaResolveDependency = require("./ImportMetaResolveDependency");
- const ModuleInitFragmentDependency = require("./ModuleInitFragmentDependency");
- // Loaded at module scope, not inside the parser handler: the async resolver
- // cache tail can run that handler after a test tears down the module registry.
- const WEBPACK_VERSION = Number.parseInt(
- require("../../package.json").version,
- 10
- );
- /**
- * @import {
- * MemberExpression,
- * Identifier,
- * CallExpression,
- * Expression,
- * Super
- * } from "estree"
- */
- /**
- * @import {
- * JavascriptParserOptions
- * } from "../../declarations/WebpackOptions"
- */
- /** @import Compiler from "../Compiler" */
- /** @import { MergedDefinitionNode } from "../DefinePlugin" */
- /** @import { ValueCacheVersion } from "../Module" */
- /** @import NormalModule, { NormalModuleBuildInfo } from "../NormalModule" */
- /**
- * @import {
- * JavascriptModuleBuildInfo
- * } from "../javascript/JavascriptModule"
- */
- /**
- * @import Parser, {
- * Range,
- * Members,
- * DestructuringAssignmentProperty
- * } from "../javascript/JavascriptParser"
- */
- /** @import { RawRuntimeRequirements } from "./NullDependency" */
- const PLUGIN_NAME = "ImportMetaPlugin";
- const IMPORT_META = "import.meta";
- const IMPORT_META_URL = "import.meta.url";
- const IMPORT_META_WEBPACK = "import.meta.webpack";
- const IMPORT_META_MAIN = "import.meta.main";
- const IMPORT_META_ENV = "import.meta.env";
- const IMPORT_META_DIRNAME = "import.meta.dirname";
- const IMPORT_META_FILENAME = "import.meta.filename";
- const IMPORT_META_RESOLVE = "import.meta.resolve";
- const IMPORT_META_FIELDS = new Set([
- "url",
- "webpack",
- "main",
- "env",
- "dirname",
- "filename",
- "resolve",
- "webpackContext",
- "webpackHot"
- ]);
- // Single source of truth for `import.meta` accesses that only exist in ES modules
- // and therefore mark a module as ESM (consumed by HarmonyDetectionParserPlugin).
- // `dirname`/`filename` are resolved by NodeStuffPlugin, the rest here. The webpack
- // pragmas `import.meta.webpackHot`/`webpackContext` are intentionally excluded:
- // they are usable in CommonJS modules and must not force ESM.
- const IMPORT_META_NAMES = [
- IMPORT_META,
- IMPORT_META_URL,
- IMPORT_META_WEBPACK,
- IMPORT_META_MAIN,
- IMPORT_META_ENV,
- IMPORT_META_DIRNAME,
- IMPORT_META_FILENAME,
- IMPORT_META_RESOLVE
- ];
- // Ordering contract for import.meta parser hooks across plugins: ESM detection
- // (HarmonyDetectionParserPlugin, non-bailing) must observe every access before
- // any bailing replacement; the merged replacements here must run before
- // DefinePlugin object defines (default stage 0) and its nested destructuring
- // handler (stage 100).
- const IMPORT_META_STAGE_ESM_DETECTION = -1000;
- const IMPORT_META_STAGE_REPLACEMENT = -100;
- /**
- * import.meta definitions collected from DefinePlugin's merged view.
- * @typedef {object} ImportMetaDefinitions
- * @property {MergedDefinitionNode | undefined} envNode `import.meta.env` node (a map unless custom-defined)
- * @property {Map<string, MergedDefinitionNode>} customProps custom (non-field) property nodes
- */
- /** @type {WeakMap<Compilation, ImportMetaDefinitions>} */
- const compilationMetaDefinitionsMap = new WeakMap();
- /**
- * Returns true if unknown import.meta properties should be preserved.
- * @param {JavascriptParserOptions["importMeta"]} importMeta parser option
- * @returns {boolean} true when unknown properties should be preserved
- */
- const preserveUnknown = (importMeta) =>
- importMeta === "preserve-unknown" ||
- (Boolean(importMeta) && typeof importMeta === "object");
- /**
- * Checks whether webpack should handle an import.meta field.
- * @param {JavascriptParserOptions["importMeta"]} importMeta parser option
- * @param {string} field field name
- * @returns {boolean} true when the field should be handled
- */
- const isImportMetaFieldEnabled = (importMeta, field) => {
- if (importMeta === false) {
- return false;
- }
- return !(
- Boolean(importMeta) &&
- typeof importMeta === "object" &&
- /** @type {Record<string, boolean | undefined>} */ (importMeta)[field] ===
- false
- );
- };
- /**
- * Renders the `import.meta.main` comparison and bails out of scope hoisting.
- * @param {Parser} parser the parser
- * @returns {string} the comparison expression
- */
- const renderImportMetaMain = (parser) => {
- const currentModule = parser.state.module;
- /** @type {JavascriptModuleBuildInfo} */
- (currentModule.buildInfo).moduleConcatenationBailout = IMPORT_META_MAIN;
- return `${RuntimeGlobals.moduleCache}[${RuntimeGlobals.entryModuleId}] === ${currentModule.moduleArgument}`;
- };
- /**
- * Checks whether the callee is the `import.meta.resolve` member.
- * @param {Expression | Super} callee call callee
- * @returns {boolean} true when the callee is `import.meta.resolve`
- */
- const isImportMetaResolveCallee = (callee) =>
- callee.type === "MemberExpression" &&
- callee.object.type === "MetaProperty" &&
- callee.object.meta.name === "import" &&
- callee.object.property.name === "meta" &&
- (callee.computed
- ? callee.property.type === "Literal" && callee.property.value === "resolve"
- : callee.property.type === "Identifier" &&
- callee.property.name === "resolve");
- /**
- * Only a single statically known specifier maps to an asset.
- * @param {CallExpression} call the call expression
- * @param {Parser} parser the parser
- * @returns {string | undefined} the resolved static specifier, if any
- */
- const getImportMetaResolveRequest = (call, parser) => {
- if (call.arguments.length !== 1) return undefined;
- const [arg] = call.arguments;
- if (arg.type === "SpreadElement") return undefined;
- return parser.evaluateExpression(arg).asString();
- };
- /**
- * Collects import.meta definitions from DefinePlugin's merged view
- * (object form and dotted keys of every instance).
- * @param {Compilation} compilation the compilation
- * @returns {ImportMetaDefinitions} collected definitions
- */
- const collectImportMetaDefinitions = (compilation) => {
- const cached = compilationMetaDefinitionsMap.get(compilation);
- if (cached) {
- return cached;
- }
- const metaNode = DefinePlugin.getMergedDefinitionNode(
- compilation,
- IMPORT_META
- );
- /** @type {MergedDefinitionNode | undefined} */
- let envNode;
- /** @type {Map<string, MergedDefinitionNode>} */
- const customProps = new Map();
- if (metaNode instanceof Map) {
- for (const [prop, node] of metaNode) {
- if (prop === "env") {
- envNode = node;
- } else if (!IMPORT_META_FIELDS.has(prop)) {
- // fields (url, webpack, …) are handled by their own logic
- customProps.set(prop, node);
- }
- }
- }
- const result = { envNode, customProps };
- compilationMetaDefinitionsMap.set(compilation, result);
- return result;
- };
- /**
- * Defines the import meta plugin hooks type used by this module.
- * @typedef {object} ImportMetaPluginHooks
- * @property {SyncBailHook<[DestructuringAssignmentProperty], string | void>} propertyInDestructuring
- */
- class ImportMetaPlugin {
- /**
- * Applies the plugin by registering its hooks on the compiler.
- * @param {Compiler} compiler compiler
- */
- apply(compiler) {
- compiler.hooks.compilation.tap(
- PLUGIN_NAME,
- (compilation, { normalModuleFactory }) => {
- const hooks = ImportMetaPlugin.getCompilationHooks(compilation);
- compilation.dependencyTemplates.set(
- ModuleInitFragmentDependency,
- new ModuleInitFragmentDependency.Template()
- );
- compilation.dependencyFactories.set(
- ImportMetaResolveDependency,
- normalModuleFactory
- );
- compilation.dependencyTemplates.set(
- ImportMetaResolveDependency,
- new ImportMetaResolveDependency.Template()
- );
- compilation.dependencyTemplates.set(
- BuildTimeConstDependency,
- new BuildTimeConstDependency.Template()
- );
- /**
- * Returns file url.
- * @param {NormalModule} module module
- * @returns {string} file url
- */
- const getUrl = (module) => pathToFileURL(module.resource).toString();
- /**
- * Processes the provided parser.
- * @param {Parser} parser parser parser
- * @param {JavascriptParserOptions} parserOptions parserOptions
- * @returns {void}
- */
- const parserHandler = (parser, { importMeta, url }) => {
- if (importMeta === false) {
- const { importMetaName } = compilation.outputOptions;
- if (importMetaName === "import.meta") return;
- parser.hooks.expression
- .for(IMPORT_META)
- .tap(PLUGIN_NAME, (metaProperty) => {
- const dep = new ConstDependency(
- /** @type {string} */ (importMetaName),
- /** @type {Range} */ (metaProperty.range)
- );
- dep.loc = parser.getLocation(metaProperty);
- parser.state.module.addPresentationalDependency(dep);
- return true;
- });
- return;
- }
- const { envNode, customProps: allCustomProps } =
- collectImportMetaDefinitions(compilation);
- // a non-object definition: DefinePlugin replaces the whole object
- const envIsCustom =
- envNode !== undefined && !(envNode instanceof Map);
- // per-field parser options may disable custom properties too
- /** @type {Map<string, MergedDefinitionNode>} */
- const customProps = new Map();
- for (const [prop, node] of allCustomProps) {
- if (isImportMetaFieldEnabled(importMeta, prop)) {
- customProps.set(prop, node);
- }
- }
- /** @type {Map<string, ValueCacheVersion>} */
- const definitionValueDependencies = new Map();
- /**
- * @param {string} name value cache key
- */
- const addDefinitionValueDependency = (name) => {
- const version = compilation.valueCacheVersions.get(name);
- // an undefined version would force a rebuild on every build
- if (version !== undefined) {
- definitionValueDependencies.set(name, version);
- }
- };
- addDefinitionValueDependency(DefinePlugin.VALUE_DEP_MAIN);
- if (isImportMetaFieldEnabled(importMeta, "env")) {
- addDefinitionValueDependency(
- DefinePlugin.VALUE_DEP_PREFIX + IMPORT_META_ENV
- );
- if (envNode instanceof Map) {
- for (const envKey of envNode.keys()) {
- addDefinitionValueDependency(
- `${DefinePlugin.VALUE_DEP_PREFIX + IMPORT_META_ENV}.${envKey}`
- );
- }
- }
- }
- for (const prop of customProps.keys()) {
- addDefinitionValueDependency(
- `${DefinePlugin.VALUE_DEP_PREFIX + IMPORT_META}.${prop}`
- );
- }
- /**
- * Records value dependencies for every inlined definition so
- * cached modules rebuild when a definition value changes.
- * @returns {void}
- */
- const addDefinitionValueDependencies = () => {
- if (definitionValueDependencies.size === 0) return;
- const buildInfo = /** @type {NormalModuleBuildInfo} */ (
- parser.state.module.buildInfo
- );
- const valueDependencies =
- buildInfo.valueDependencies ||
- (buildInfo.valueDependencies = new Map());
- for (const [name, version] of definitionValueDependencies) {
- valueDependencies.set(name, version);
- }
- };
- /**
- * Renders a merged definition node at a use site.
- * @param {MergedDefinitionNode | undefined} node node
- * @param {string} key definition key
- * @param {Set<string>=} objKeys used keys
- * @returns {string} code
- */
- const renderDefinition = (node, key, objKeys) =>
- node === undefined
- ? "{}"
- : DefinePlugin.stringifyMergedDefinition(
- compilation,
- parser,
- node,
- key,
- objKeys
- );
- // import.meta direct
- const importMetaUrl = () =>
- JSON.stringify(getUrl(parser.state.module));
- const importMetaWebpackVersion = () =>
- JSON.stringify(WEBPACK_VERSION);
- /**
- * Import meta unknown property.
- * @param {Members} members members
- * @returns {string} error message
- */
- const importMetaUnknownProperty = (members) => {
- if (
- preserveUnknown(importMeta) ||
- (IMPORT_META_FIELDS.has(members[0]) &&
- !isImportMetaFieldEnabled(importMeta, members[0]))
- ) {
- return `import.meta${propertyAccess(members, 0)}`;
- }
- return `${Template.toNormalComment(
- `unsupported import.meta.${members.join(".")}`
- )} undefined${propertyAccess(members, 1)}`;
- };
- /**
- * @param {string} field field name
- * @returns {string} generated property
- */
- const importMetaRuntimeProperty = (field) =>
- `[${JSON.stringify(field)}]: ${importMetaUnknownProperty([
- field
- ])},`;
- /**
- * Import meta runtime property for build-time execution.
- * @param {string} field field name
- * @returns {string} generated property
- */
- const importMetaBuildTimeProperty = (field) =>
- `[${JSON.stringify(field)}]: ${Template.toNormalComment(
- `unsupported import.meta.${field}`
- )} undefined,`;
- parser.hooks.typeof
- .for(IMPORT_META)
- .tap(
- PLUGIN_NAME,
- toConstantDependency(parser, JSON.stringify("object"))
- );
- parser.hooks.collectDestructuringAssignmentProperties.tap(
- PLUGIN_NAME,
- (expr) => {
- if (expr.type === "MetaProperty") return true;
- }
- );
- // `import.meta` is always replaced by the synthesized object, so it
- // must never become a variable alias: the alias declaration would keep
- // the raw MetaProperty. Staged before DefinePlugin's canRename taps.
- parser.hooks.canRename
- .for(IMPORT_META)
- .tap(
- { name: PLUGIN_NAME, stage: IMPORT_META_STAGE_REPLACEMENT },
- () => false
- );
- parser.hooks.expression
- .for(IMPORT_META)
- .tap(PLUGIN_NAME, (metaProperty) => {
- /** @type {RawRuntimeRequirements} */
- const runtimeRequirements = [];
- const referencedPropertiesInDestructuring =
- parser.destructuringAssignmentPropertiesFor(metaProperty);
- if (!referencedPropertiesInDestructuring) {
- const varName = "__webpack_import_meta__";
- const knownProps = [];
- if (isImportMetaFieldEnabled(importMeta, "url")) {
- knownProps.push(`url: ${importMetaUrl()}`);
- }
- if (isImportMetaFieldEnabled(importMeta, "webpack")) {
- knownProps.push(`webpack: ${importMetaWebpackVersion()}`);
- }
- if (isImportMetaFieldEnabled(importMeta, "main")) {
- knownProps.push(`main: ${renderImportMetaMain(parser)}`);
- }
- if (isImportMetaFieldEnabled(importMeta, "env")) {
- addDefinitionValueDependencies();
- knownProps.push(
- `env: ${renderDefinition(envNode, IMPORT_META_ENV)}`
- );
- }
- for (const [prop, node] of customProps) {
- addDefinitionValueDependencies();
- knownProps.push(
- `${DefinePlugin.toPropertyKey(prop)}: ${renderDefinition(
- node,
- `${IMPORT_META}.${prop}`
- )}`
- );
- }
- const plainInitCode = `var ${varName} = {${knownProps.join(
- ", "
- )}};\n`;
- const initCode = preserveUnknown(importMeta)
- ? `var ${varName} = Object.assign(import.meta, {${knownProps.join(
- ", "
- )}});\n`
- : plainInitCode;
- /** @type {RawRuntimeRequirements} */
- const initRuntimeRequirements = [
- RuntimeGlobals.moduleCache,
- RuntimeGlobals.entryModuleId,
- RuntimeGlobals.module
- ];
- const initCodeRequirements =
- DefinePlugin.getRuntimeRequirements(initCode);
- if (initCodeRequirements) {
- initRuntimeRequirements.push(...initCodeRequirements);
- }
- const initDep = new ModuleInitFragmentDependency(
- initCode,
- initRuntimeRequirements,
- varName,
- // native `import.meta` does not parse in the build-time vm
- initCode === plainInitCode ? undefined : plainInitCode
- );
- initDep.loc = parser.getLocation(metaProperty);
- parser.state.module.addPresentationalDependency(initDep);
- const dep = new ConstDependency(
- varName,
- /** @type {Range} */ (metaProperty.range),
- runtimeRequirements
- );
- dep.loc = parser.getLocation(metaProperty);
- parser.state.module.addPresentationalDependency(dep);
- return true;
- }
- let str = "";
- let strBuildTime = "";
- /**
- * Appends a property shared by both variants.
- * @param {string} code generated property
- */
- const append = (code) => {
- str += code;
- strBuildTime += code;
- };
- /**
- * Appends a property reading native `import.meta`, which
- * does not parse in the build-time vm.
- * @param {string} field field name
- */
- const appendRuntimeProperty = (field) => {
- str += importMetaRuntimeProperty(field);
- strBuildTime += importMetaBuildTimeProperty(field);
- };
- for (const prop of referencedPropertiesInDestructuring) {
- const fieldEnabled = isImportMetaFieldEnabled(
- importMeta,
- prop.id
- );
- const value = fieldEnabled
- ? hooks.propertyInDestructuring.call(prop)
- : undefined;
- if (value) {
- append(value);
- continue;
- }
- switch (prop.id) {
- case "url":
- if (fieldEnabled) {
- append(`url: ${importMetaUrl()},`);
- } else {
- appendRuntimeProperty(prop.id);
- }
- break;
- case "webpack":
- if (fieldEnabled) {
- append(`webpack: ${importMetaWebpackVersion()},`);
- } else {
- appendRuntimeProperty(prop.id);
- }
- break;
- case "main":
- if (fieldEnabled) {
- append(`main: ${renderImportMetaMain(parser)},`);
- runtimeRequirements.push(
- RuntimeGlobals.moduleCache,
- RuntimeGlobals.entryModuleId,
- RuntimeGlobals.module
- );
- } else {
- appendRuntimeProperty(prop.id);
- }
- break;
- case "env":
- if (fieldEnabled) {
- addDefinitionValueDependencies();
- append(
- `env: ${renderDefinition(envNode, IMPORT_META_ENV)},`
- );
- } else {
- appendRuntimeProperty(prop.id);
- }
- break;
- default: {
- const node = customProps.get(prop.id);
- if (node !== undefined) {
- addDefinitionValueDependencies();
- append(
- `${DefinePlugin.toPropertyKey(prop.id)}: ${renderDefinition(
- node,
- `${IMPORT_META}.${prop.id}`
- )},`
- );
- } else {
- appendRuntimeProperty(prop.id);
- }
- break;
- }
- }
- }
- const strCode = `({${str}})`;
- const codeRequirements =
- DefinePlugin.getRuntimeRequirements(strCode);
- if (codeRequirements) {
- runtimeRequirements.push(...codeRequirements);
- }
- const dep =
- strBuildTime === str
- ? new ConstDependency(
- strCode,
- /** @type {Range} */ (metaProperty.range),
- runtimeRequirements
- )
- : new BuildTimeConstDependency(
- strCode,
- `({${strBuildTime}})`,
- /** @type {Range} */ (metaProperty.range),
- runtimeRequirements
- );
- dep.loc = parser.getLocation(metaProperty);
- parser.state.module.addPresentationalDependency(dep);
- return true;
- });
- parser.hooks.evaluateTypeof
- .for(IMPORT_META)
- .tap(PLUGIN_NAME, evaluateToString("object"));
- parser.hooks.evaluateIdentifier.for(IMPORT_META).tap(
- PLUGIN_NAME,
- evaluateToIdentifier(IMPORT_META, IMPORT_META, () => [], true)
- );
- // custom import.meta properties defined only via nested dotted keys,
- // e.g. reading `import.meta.build` with `"import.meta.build.time"` defined
- if (customProps.size > 0) {
- parser.hooks.expressionMemberChain
- .for(IMPORT_META)
- .tap(PLUGIN_NAME, (expr, members) => {
- const node = customProps.get(members[0]);
- if (node === undefined) return;
- addDefinitionValueDependencies();
- const strCode = `(${renderDefinition(
- node,
- `${IMPORT_META}.${members[0]}`
- )})${propertyAccess(members, 1)}`;
- return toConstantDependency(
- parser,
- strCode,
- DefinePlugin.getRuntimeRequirements(strCode)
- )(expr);
- });
- }
- // import.meta.resolve — resolve a static specifier to the emitted
- // asset URL string (the string form of `new URL(x, import.meta.url)`).
- if (isImportMetaFieldEnabled(importMeta, "resolve")) {
- const relative = url === "relative";
- parser.hooks.call
- .for(IMPORT_META_RESOLVE)
- .tap(PLUGIN_NAME, (expr) => {
- const call = /** @type {CallExpression} */ (expr);
- // Non-static forms are left untouched for runtime evaluation.
- const request = getImportMetaResolveRequest(call, parser);
- if (request === undefined) return;
- const range = /** @type {Range} */ (call.range);
- const dep = new ImportMetaResolveDependency(
- request,
- range,
- range,
- relative
- );
- dep.loc = parser.getLocation(call);
- parser.state.current.addDependency(dep);
- getInnerGraphUtils(parser.state.compilation).onUsage(
- parser.state,
- (e) => (dep.usedByExports = e)
- );
- return true;
- });
- // Computing a URL string is side-effect free, so an unused result
- // can be tree-shaken (matching `new URL(..., import.meta.url)`).
- parser.hooks.isPure
- .for("CallExpression")
- .tap(PLUGIN_NAME, (expr) => {
- const call = /** @type {CallExpression} */ (expr);
- if (!isImportMetaResolveCallee(call.callee)) return;
- if (getImportMetaResolveRequest(call, parser) !== undefined) {
- return true;
- }
- });
- }
- // import.meta.url
- if (isImportMetaFieldEnabled(importMeta, "url")) {
- parser.hooks.typeof
- .for(IMPORT_META_URL)
- .tap(
- PLUGIN_NAME,
- toConstantDependency(parser, JSON.stringify("string"))
- );
- parser.hooks.expression
- .for(IMPORT_META_URL)
- .tap(PLUGIN_NAME, (expr) => {
- const dep = new ConstDependency(
- importMetaUrl(),
- /** @type {Range} */ (expr.range)
- );
- dep.loc = parser.getLocation(expr);
- parser.state.module.addPresentationalDependency(dep);
- return true;
- });
- parser.hooks.evaluateTypeof
- .for(IMPORT_META_URL)
- .tap(PLUGIN_NAME, evaluateToString("string"));
- parser.hooks.evaluateIdentifier
- .for(IMPORT_META_URL)
- .tap(PLUGIN_NAME, (expr) =>
- new BasicEvaluatedExpression()
- .setString(getUrl(parser.state.module))
- .setRange(/** @type {Range} */ (expr.range))
- );
- }
- // import.meta.webpack
- if (isImportMetaFieldEnabled(importMeta, "webpack")) {
- parser.hooks.expression
- .for(IMPORT_META_WEBPACK)
- .tap(
- PLUGIN_NAME,
- toConstantDependency(parser, importMetaWebpackVersion())
- );
- parser.hooks.typeof
- .for(IMPORT_META_WEBPACK)
- .tap(
- PLUGIN_NAME,
- toConstantDependency(parser, JSON.stringify("number"))
- );
- parser.hooks.evaluateTypeof
- .for(IMPORT_META_WEBPACK)
- .tap(PLUGIN_NAME, evaluateToString("number"));
- parser.hooks.evaluateIdentifier
- .for(IMPORT_META_WEBPACK)
- .tap(PLUGIN_NAME, evaluateToNumber(WEBPACK_VERSION));
- }
- if (isImportMetaFieldEnabled(importMeta, "main")) {
- parser.hooks.expression
- .for(IMPORT_META_MAIN)
- .tap(PLUGIN_NAME, (expr) =>
- toConstantDependency(parser, renderImportMetaMain(parser), [
- RuntimeGlobals.moduleCache,
- RuntimeGlobals.entryModuleId,
- RuntimeGlobals.module
- ])(expr)
- );
- parser.hooks.typeof
- .for(IMPORT_META_MAIN)
- .tap(
- PLUGIN_NAME,
- toConstantDependency(parser, JSON.stringify("boolean"))
- );
- parser.hooks.evaluateTypeof
- .for(IMPORT_META_MAIN)
- .tap(PLUGIN_NAME, evaluateToString("boolean"));
- }
- // import.meta.env
- if (isImportMetaFieldEnabled(importMeta, "env")) {
- parser.hooks.typeof
- .for(IMPORT_META_ENV)
- .tap(
- PLUGIN_NAME,
- toConstantDependency(parser, JSON.stringify("object"))
- );
- parser.hooks.expressionMemberChain
- .for(IMPORT_META)
- .tap(PLUGIN_NAME, (expr, members) => {
- if (members[0] === "env" && members[1]) {
- // members of a custom definition resolve on it at runtime
- if (envIsCustom) return;
- if (!(envNode instanceof Map) || !envNode.has(members[1])) {
- return toConstantDependency(parser, "undefined")(expr);
- }
- }
- });
- parser.hooks.expression.for(IMPORT_META_ENV).tap(
- // before DefinePlugin object defines: merge every definition source
- { name: PLUGIN_NAME, stage: IMPORT_META_STAGE_REPLACEMENT },
- (expr) => {
- // a custom definition replaces the whole object via DefinePlugin
- if (envIsCustom) return;
- addDefinitionValueDependencies();
- const destructured =
- parser.destructuringAssignmentPropertiesFor(expr);
- const objKeys =
- destructured &&
- new Set([...destructured].map((prop) => prop.id));
- const code = renderDefinition(
- envNode,
- IMPORT_META_ENV,
- objKeys
- );
- const strCode = parser.isAsiPosition(
- /** @type {Range} */ (expr.range)[0]
- )
- ? `;(${code})`
- : `(${code})`;
- return toConstantDependency(
- parser,
- strCode,
- DefinePlugin.getRuntimeRequirements(strCode)
- )(expr);
- }
- );
- parser.hooks.evaluateTypeof
- .for(IMPORT_META_ENV)
- .tap(PLUGIN_NAME, evaluateToString("object"));
- parser.hooks.evaluateIdentifier
- .for(IMPORT_META_ENV)
- .tap(PLUGIN_NAME, (expr) =>
- new BasicEvaluatedExpression()
- .setTruthy()
- .setSideEffects(false)
- .setRange(/** @type {Range} */ (expr.range))
- );
- }
- // Unknown properties
- parser.hooks.unhandledExpressionMemberChain
- .for(IMPORT_META)
- .tap(PLUGIN_NAME, (expr, members) => {
- // keep import.meta.env unknown property
- // don't evaluate import.meta.env.UNKNOWN_PROPERTY -> undefined.UNKNOWN_PROPERTY
- // `dirname` and `filename` logic in NodeStuffPlugin
- if (
- members[0] === "env" ||
- members[0] === "dirname" ||
- members[0] === "filename"
- ) {
- return true;
- }
- // unknown import.meta properties should be determined at runtime
- if (
- preserveUnknown(importMeta) ||
- (IMPORT_META_FIELDS.has(members[0]) &&
- !isImportMetaFieldEnabled(importMeta, members[0]))
- ) {
- // keep the native access verbatim for emitted output, but
- // native `import.meta` does not parse in the build-time vm
- const dep = new BuildTimeConstDependency(
- `${IMPORT_META}${propertyAccess(members, 0)}`,
- `${Template.toNormalComment(
- `unsupported import.meta.${members.join(".")}`
- )} undefined${propertyAccess(members, 1)}`,
- /** @type {Range} */ (expr.range)
- );
- dep.loc = parser.getLocation(expr);
- parser.state.module.addPresentationalDependency(dep);
- return true;
- }
- const dep = new ConstDependency(
- importMetaUnknownProperty(members),
- /** @type {Range} */ (expr.range)
- );
- dep.loc = parser.getLocation(expr);
- parser.state.module.addPresentationalDependency(dep);
- return true;
- });
- parser.hooks.evaluate
- .for("MemberExpression")
- .tap(PLUGIN_NAME, (expression) => {
- const expr = /** @type {MemberExpression} */ (expression);
- if (
- expr.object.type === "MetaProperty" &&
- expr.object.meta.name === "import" &&
- expr.object.property.name === "meta" &&
- expr.property.type ===
- (expr.computed ? "Literal" : "Identifier")
- ) {
- const prop = expr.computed
- ? /** @type {import("estree").Literal} */ (expr.property)
- .value
- : /** @type {Identifier} */ (expr.property).name;
- if (
- !isImportMetaFieldEnabled(importMeta, String(prop)) ||
- (preserveUnknown(importMeta) &&
- !IMPORT_META_FIELDS.has(String(prop)))
- ) {
- return;
- }
- if (IMPORT_META_FIELDS.has(String(prop))) {
- return;
- }
- // custom-defined properties evaluate via DefinePlugin/runtime
- if (customProps.has(String(prop))) {
- return;
- }
- return new BasicEvaluatedExpression()
- .setUndefined()
- .setRange(/** @type {Range} */ (expr.range));
- }
- });
- };
- normalModuleFactory.hooks.parser
- .for(JAVASCRIPT_MODULE_TYPE_AUTO)
- .tap(PLUGIN_NAME, parserHandler);
- normalModuleFactory.hooks.parser
- .for(JAVASCRIPT_MODULE_TYPE_ESM)
- .tap(PLUGIN_NAME, parserHandler);
- }
- );
- }
- }
- ImportMetaPlugin.getCompilationHooks = createHooksRegistry(
- () =>
- /** @type {ImportMetaPluginHooks} */ ({
- propertyInDestructuring: new SyncBailHook(["property"])
- })
- );
- ImportMetaPlugin.IMPORT_META_DIRNAME = IMPORT_META_DIRNAME;
- ImportMetaPlugin.IMPORT_META_FILENAME = IMPORT_META_FILENAME;
- ImportMetaPlugin.IMPORT_META_NAMES = IMPORT_META_NAMES;
- ImportMetaPlugin.IMPORT_META_STAGE_ESM_DETECTION =
- IMPORT_META_STAGE_ESM_DETECTION;
- ImportMetaPlugin.isImportMetaFieldEnabled = isImportMetaFieldEnabled;
- module.exports = ImportMetaPlugin;
|