perf: extend importPhasesPlugin only when enable deferImport (#19689)

This commit is contained in:
hai-x 2025-07-14 19:00:25 +08:00 committed by GitHub
parent 0a984462ab
commit 9eb964224c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 37 additions and 64 deletions

View File

@ -438,6 +438,13 @@ class WebpackOptionsApply extends OptionsApply {
new HttpUriPlugin(httpOptions).apply(compiler);
}
if (options.experiments.deferImport) {
const JavascriptParser = require("./javascript/JavascriptParser");
const importPhases = require("acorn-import-phases");
JavascriptParser.extend(importPhases({ source: false }));
}
new EntryOptionPlugin().apply(compiler);
compiler.hooks.entryOption.call(
/** @type {string} */

View File

@ -77,12 +77,7 @@ module.exports = class HarmonyExportDependencyParserPlugin {
clearDep.loc = /** @type {DependencyLocation} */ (statement.loc);
clearDep.loc.index = -1;
parser.state.module.addPresentationalDependency(clearDep);
const { defer } = getImportMode(
parser,
statement,
this.deferImport,
true
);
const { defer } = getImportMode(parser, statement);
if (defer) {
const error = new WebpackError(
"Deferred re-export (`export defer * as namespace from '...'`) is not a part of the Import Defer proposal.\nUse the following code instead:\n import defer * as namespace from '...';\n export { namespace };"
@ -207,12 +202,7 @@ module.exports = class HarmonyExportDependencyParserPlugin {
parser.state.harmonyStarExports || new HarmonyStarExportsList();
}
const attributes = getImportAttributes(statement);
const { defer } = getImportMode(
parser,
statement,
this.deferImport,
false
);
const { defer } = getImportMode(parser, statement);
const dep = new HarmonyExportImportedSpecifierDependency(
/** @type {string} */
(source),

View File

@ -7,7 +7,6 @@
const CommentCompilationWarning = require("../CommentCompilationWarning");
const HotModuleReplacementPlugin = require("../HotModuleReplacementPlugin");
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
const WebpackError = require("../WebpackError");
const { getImportAttributes } = require("../javascript/JavascriptParser");
const InnerGraph = require("../optimize/InnerGraph");
@ -125,12 +124,7 @@ module.exports = class HarmonyImportDependencyParserPlugin {
parser.state.module.addPresentationalDependency(clearDep);
parser.unsetAsiPosition(/** @type {Range} */ (statement.range)[1]);
const attributes = getImportAttributes(statement);
const { defer } = getImportMode(
parser,
statement,
this.deferImport,
true
);
const { defer } = getImportMode(parser, statement);
if (
defer &&
(statement.specifiers.length !== 1 ||
@ -156,12 +150,7 @@ module.exports = class HarmonyImportDependencyParserPlugin {
PLUGIN_NAME,
(statement, source, id, name) => {
const ids = id === null ? [] : [id];
const { defer } = getImportMode(
parser,
statement,
this.deferImport,
false
);
const { defer } = getImportMode(parser, statement);
parser.tagVariable(name, harmonySpecifierTag, {
name,
source,
@ -398,23 +387,10 @@ module.exports = class HarmonyImportDependencyParserPlugin {
/**
* @param {JavascriptParser} parser parser
* @param {ExportNamedDeclaration | ExportAllDeclaration | ImportDeclaration} node node
* @param {boolean | undefined} deferImportEnabled defer import enabled
* @param {boolean} reportSyntaxError report syntax error
* @returns {{defer: boolean}} import attributes
*/
function getImportMode(parser, node, deferImportEnabled, reportSyntaxError) {
const result = { defer: false };
if ("phase" in node && node.phase === "defer") {
if (deferImportEnabled) {
result.defer = true;
} else if (reportSyntaxError) {
const error = new WebpackError(
"Deferred import syntax (`import defer * as namespace from '...'`) cannot be used unless experimental.deferImport is true."
);
error.loc = node.loc || undefined;
parser.state.current.addError(error);
}
}
function getImportMode(parser, node) {
const result = { defer: "phase" in node && node.phase === "defer" };
if (!node.range) {
return result;
}
@ -437,7 +413,7 @@ function getImportMode(parser, node, deferImportEnabled, reportSyntaxError) {
result.defer = options.webpackDefer;
} else if (node.loc) {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
new CommentCompilationWarning(
"webpackDefer magic comment expected a boolean value.",
node.loc
)

View File

@ -7,7 +7,6 @@
const vm = require("vm");
const { Parser: AcornParser, tokTypes } = require("acorn");
const acornImportPhases = require("acorn-import-phases");
const { HookMap, SyncBailHook } = require("tapable");
const Parser = require("../Parser");
const StackedMap = require("../util/StackedMap");
@ -182,10 +181,7 @@ const importAssertions = Parser =>
};
// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
const parser = AcornParser.extend(
importAssertions,
acornImportPhases({ source: false })
);
let parser = AcornParser.extend(importAssertions);
/** @typedef {Record<string, string> & { _isLegacyAssert?: boolean }} ImportAttributes */
@ -5135,6 +5131,15 @@ class JavascriptParser extends Parser {
return /** @type {Program} */ (ast);
}
/**
* @param {((BaseParser: typeof AcornParser) => typeof AcornParser)[]} plugins parser plugin
* @returns {typeof JavascriptParser} parser
*/
static extend(...plugins) {
parser = parser.extend(...plugins);
return JavascriptParser;
}
}
module.exports = JavascriptParser;

View File

@ -1 +0,0 @@
module.exports = [[/cannot be used unless experimental\.deferImport is true/]];

View File

@ -1,2 +0,0 @@
import defer * as f3 from "./mod.js";
export default f3.default;

View File

@ -1,2 +0,0 @@
export function f() {}
export default function f2() {}

View File

@ -1,4 +0,0 @@
/** @type {import("../../../../").Configuration} */
module.exports = {
target: [`async-node${process.versions.node.split(".").map(Number)[0]}`]
};

22
types.d.ts vendored
View File

@ -4,6 +4,7 @@
* Run `yarn fix:special` to update
*/
import { Parser as ParserImport } from "acorn";
import { Buffer } from "buffer";
import { Scope } from "eslint-scope";
import {
@ -6431,7 +6432,7 @@ declare class JavascriptModulesPlugin {
): TemplatePath;
static chunkHasJs: (chunk: Chunk, chunkGraph: ChunkGraph) => boolean;
}
declare class JavascriptParser extends Parser {
declare class JavascriptParser extends ParserClass {
constructor(sourceType?: "module" | "auto" | "script");
hooks: Readonly<{
evaluateTypeof: HookMap<
@ -7759,6 +7760,9 @@ declare class JavascriptParser extends Parser {
rootInfo: ExportedVariableInfo;
getMembers: () => string[];
};
static extend(
...plugins: ((BaseParser: typeof ParserImport) => typeof ParserImport)[]
): typeof JavascriptParser;
static ALLOWED_MEMBER_TYPES_ALL: 3;
static ALLOWED_MEMBER_TYPES_CALL_EXPRESSION: 1;
static ALLOWED_MEMBER_TYPES_EXPRESSION: 2;
@ -10555,7 +10559,7 @@ declare class NormalModule extends Module {
userRequest: string;
rawRequest: string;
binary: boolean;
parser?: Parser;
parser?: ParserClass;
parserOptions?: ParserOptions;
generator?: Generator;
generatorOptions?: GeneratorOptions;
@ -10686,7 +10690,7 @@ declare interface NormalModuleCreateData {
/**
* the parser used
*/
parser: Parser;
parser: ParserClass;
/**
* the options of the parser used
@ -10734,7 +10738,7 @@ declare abstract class NormalModuleFactory extends ModuleFactory {
ResolveData
]
>;
createParser: HookMap<SyncBailHook<[ParserOptions], void | Parser>>;
createParser: HookMap<SyncBailHook<[ParserOptions], void | ParserClass>>;
parser: HookMap<SyncBailHook<[any, ParserOptions], void>>;
createGenerator: HookMap<
SyncBailHook<[GeneratorOptions], void | Generator>
@ -10754,7 +10758,7 @@ declare abstract class NormalModuleFactory extends ModuleFactory {
ruleSet: RuleSet;
context: string;
fs: InputFileSystem;
parserCache: Map<string, WeakMap<ParserOptions, Parser>>;
parserCache: Map<string, WeakMap<ParserOptions, ParserClass>>;
generatorCache: Map<string, WeakMap<GeneratorOptions, Generator>>;
cleanupForCache(): void;
resolveResource(
@ -10777,8 +10781,8 @@ declare abstract class NormalModuleFactory extends ModuleFactory {
resolveContext: ResolveContext,
callback: CallbackNormalModuleFactory<LoaderItem[]>
): void;
getParser(type: string, parserOptions?: ParserOptions): Parser;
createParser(type: string, parserOptions?: ParserOptions): Parser;
getParser(type: string, parserOptions?: ParserOptions): ParserClass;
createParser(type: string, parserOptions?: ParserOptions): ParserClass;
getGenerator(type: string, generatorOptions?: GeneratorOptions): Generator;
createGenerator(type: string, generatorOptions?: GeneratorOptions): Generator;
getResolver(
@ -12244,7 +12248,7 @@ declare interface ParsedIdentifier {
*/
internal: boolean;
}
declare class Parser {
declare class ParserClass {
constructor();
parse(
source: string | Buffer | PreparsedAst,
@ -18031,7 +18035,7 @@ declare namespace exports {
NormalModule,
NormalModuleReplacementPlugin,
MultiCompiler,
Parser,
ParserClass as Parser,
PlatformPlugin,
PrefetchPlugin,
ProgressPlugin,