Compare commits

...

10 Commits

Author SHA1 Message Date
Xiao ec78a303e6
Merge 4c30c3ee83 into e1afcd4cc2 2025-10-03 12:18:59 +00:00
xiaoxiaojx 4c30c3ee83 update 2025-10-03 20:18:49 +08:00
xiaoxiaojx cf1065c50c update 2025-10-03 20:18:47 +08:00
xiaoxiaojx c7fc38d76f update 2025-10-03 20:18:18 +08:00
xiaoxiaojx 4cccfafaeb update 2025-10-03 20:18:18 +08:00
xiaoxiaojx 29feac18a4 feat: add DotenvPlugin 2025-10-03 20:18:16 +08:00
Alexander Akait e1afcd4cc2
test: stability and avoid extra output (#19974)
Github Actions / lint (push) Has been cancelled Details
Github Actions / validate-legacy-node (push) Has been cancelled Details
Github Actions / benchmark (1/4) (push) Has been cancelled Details
Github Actions / benchmark (2/4) (push) Has been cancelled Details
Github Actions / benchmark (3/4) (push) Has been cancelled Details
Github Actions / benchmark (4/4) (push) Has been cancelled Details
Github Actions / basic (push) Has been cancelled Details
Github Actions / unit (push) Has been cancelled Details
Github Actions / integration (10.x, macos-latest, a) (push) Has been cancelled Details
Github Actions / integration (10.x, macos-latest, b) (push) Has been cancelled Details
Github Actions / integration (10.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (10.x, ubuntu-latest, b) (push) Has been cancelled Details
Github Actions / integration (10.x, windows-latest, a) (push) Has been cancelled Details
Github Actions / integration (10.x, windows-latest, b) (push) Has been cancelled Details
Github Actions / integration (12.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (14.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (16.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (18.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (20.x, macos-latest, a) (push) Has been cancelled Details
Github Actions / integration (20.x, macos-latest, b) (push) Has been cancelled Details
Github Actions / integration (20.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (20.x, ubuntu-latest, b) (push) Has been cancelled Details
Github Actions / integration (20.x, windows-latest, a) (push) Has been cancelled Details
Github Actions / integration (20.x, windows-latest, b) (push) Has been cancelled Details
Github Actions / integration (22.x, macos-latest, a) (push) Has been cancelled Details
Github Actions / integration (22.x, macos-latest, b) (push) Has been cancelled Details
Github Actions / integration (22.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (22.x, ubuntu-latest, b) (push) Has been cancelled Details
Github Actions / integration (22.x, windows-latest, a) (push) Has been cancelled Details
Github Actions / integration (22.x, windows-latest, b) (push) Has been cancelled Details
Github Actions / integration (24.x, macos-latest, a) (push) Has been cancelled Details
Github Actions / integration (24.x, macos-latest, b) (push) Has been cancelled Details
Github Actions / integration (24.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (24.x, ubuntu-latest, b) (push) Has been cancelled Details
Github Actions / integration (24.x, windows-latest, a) (push) Has been cancelled Details
Github Actions / integration (24.x, windows-latest, b) (push) Has been cancelled Details
Github Actions / integration (lts/*, ubuntu-latest, a, 1) (push) Has been cancelled Details
Github Actions / integration (lts/*, ubuntu-latest, b, 1) (push) Has been cancelled Details
2025-10-02 21:55:56 +03:00
Alexander Akait d45073a2b3
fix: dynamic import support in workers using browserslist (#19972) 2025-10-02 19:19:35 +03:00
Alexander Akait 568bb1d789
fix: types (#19971) 2025-10-02 18:26:16 +03:00
fregante 11144e4eec
fix: support web workers loading for jsonp format 2025-10-02 17:00:39 +03:00
43 changed files with 1214 additions and 213 deletions

View File

@ -314,7 +314,9 @@
"Kumar",
"spacek",
"thelarkinn",
"behaviour"
"behaviour",
"WHATWG",
"systemvars"
],
"ignoreRegExpList": [
"/Author.+/",

View File

@ -47,6 +47,21 @@ export type DevServer =
* A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map).
*/
export type DevTool = (false | "eval") | string;
/**
* Enable and configure the Dotenv plugin to load environment variables from .env files.
*/
export type Dotenv =
| boolean
| {
/**
* The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.
*/
envDir?: string;
/**
* Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.
*/
prefixes?: string[] | string;
};
/**
* The entry point(s) of the compilation.
*/
@ -884,6 +899,10 @@ export interface WebpackOptions {
* A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map).
*/
devtool?: DevTool;
/**
* Enable and configure the Dotenv plugin to load environment variables from .env files.
*/
dotenv?: Dotenv;
/**
* The entry point(s) of the compilation.
*/
@ -3061,6 +3080,19 @@ export interface CssParserOptions {
*/
url?: CssParserUrl;
}
/**
* Options for Dotenv plugin.
*/
export interface DotenvPluginOptions {
/**
* The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.
*/
envDir?: string;
/**
* Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.
*/
prefixes?: string[] | string;
}
/**
* No generator options are supported for this module type.
*/
@ -3798,6 +3830,10 @@ export interface WebpackOptionsNormalized {
* A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map).
*/
devtool?: DevTool;
/**
* Enable and configure the Dotenv plugin to load environment variables from .env files.
*/
dotenv?: Dotenv;
/**
* The entry point(s) of the compilation.
*/

36
declarations/plugins/DotenvPlugin.d.ts vendored Normal file
View File

@ -0,0 +1,36 @@
/*
* This file was automatically generated.
* DO NOT MODIFY BY HAND.
* Run `yarn fix:special` to update
*/
export interface DotenvPluginOptions {
/**
* Whether to allow empty strings in safe mode. If false, will throw an error if any env variables are empty (but only if safe mode is enabled).
*/
allowEmptyValues?: boolean;
/**
* Adds support for dotenv-defaults. If set to true, uses ./.env.defaults. If a string, uses that location for a defaults file.
*/
defaults?: boolean | string;
/**
* Allows your variables to be "expanded" for reusability within your .env file.
*/
expand?: boolean;
/**
* The path to your environment variables. This same path applies to the .env.example and .env.defaults files.
*/
path?: string;
/**
* The prefix to use before the name of your env variables.
*/
prefix?: string;
/**
* If true, load '.env.example' to verify the '.env' variables are all set. Can also be a string to a different file.
*/
safe?: boolean | string;
/**
* Set to true if you would rather load all system variables as well (useful for CI purposes).
*/
systemvars?: boolean;
}

423
lib/DotenvPlugin.js Normal file
View File

@ -0,0 +1,423 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Natsu @xiaoxiaojx
*/
"use strict";
const createSchemaValidation = require("./util/create-schema-validation");
const { join } = require("./util/fs");
/** @typedef {import("../declarations/WebpackOptions").DotenvPluginOptions} DotenvPluginOptions */
/** @typedef {import("./Compiler")} Compiler */
/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
const DEFAULT_OPTIONS = {
prefixes: "WEBPACK_",
envDir: true
};
// Regex for parsing .env files
// ported from https://github.com/motdotla/dotenv/blob/master/lib/main.js#L32
const LINE =
/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
const PLUGIN_NAME = "DotenvPlugin";
const validate = createSchemaValidation(
undefined,
() => {
const { definitions } = require("../schemas/WebpackOptions.json");
return {
definitions,
oneOf: [{ $ref: "#/definitions/DotenvPluginOptions" }]
};
},
{
name: "Dotenv Plugin",
baseDataPath: "options"
}
);
/**
* Parse .env file content
* ported from https://github.com/motdotla/dotenv/blob/master/lib/main.js#L49
* @param {string|Buffer} src the source content to parse
* @returns {Record<string, string>} parsed environment variables object
*/
function parse(src) {
const obj = /** @type {Record<string, string>} */ ({});
// Convert buffer to string
let lines = src.toString();
// Convert line breaks to same format
lines = lines.replace(/\r\n?/gm, "\n");
let match;
while ((match = LINE.exec(lines)) !== null) {
const key = match[1];
// Default undefined or null to empty string
let value = match[2] || "";
// Remove whitespace
value = value.trim();
// Check if double quoted
const maybeQuote = value[0];
// Remove surrounding quotes
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
// Expand newlines if double quoted
if (maybeQuote === '"') {
value = value.replace(/\\n/g, "\n");
value = value.replace(/\\r/g, "\r");
}
// Add to object
obj[key] = value;
}
return obj;
}
/**
* Resolve escape sequences
* ported from https://github.com/motdotla/dotenv-expand
* @param {string} value value to resolve
* @returns {string} resolved value
*/
function _resolveEscapeSequences(value) {
return value.replace(/\\\$/g, "$");
}
/**
* Expand environment variable value
* ported from https://github.com/motdotla/dotenv-expand
* @param {string} value value to expand
* @param {Record<string, string | undefined>} processEnv process.env object
* @param {Record<string, string>} runningParsed running parsed object
* @returns {string} expanded value
*/
function expandValue(value, processEnv, runningParsed) {
const env = { ...runningParsed, ...processEnv }; // process.env wins
const regex = /(?<!\\)\$\{([^{}]+)\}|(?<!\\)\$([A-Za-z_][A-Za-z0-9_]*)/g;
let result = value;
let match;
const seen = new Set(); // self-referential checker
while ((match = regex.exec(result)) !== null) {
seen.add(result);
const [template, bracedExpression, unbracedExpression] = match;
const expression = bracedExpression || unbracedExpression;
// match the operators `:+`, `+`, `:-`, and `-`
const opRegex = /(:\+|\+|:-|-)/;
// find first match
const opMatch = expression.match(opRegex);
const splitter = opMatch ? opMatch[0] : null;
const r = expression.split(/** @type {string} */ (splitter));
// const r = splitter ? expression.split(splitter) : [expression];
let defaultValue;
let value;
const key = r.shift();
if ([":+", "+"].includes(splitter || "")) {
defaultValue = env[key || ""] ? r.join(splitter || "") : "";
value = null;
} else {
defaultValue = r.join(splitter || "");
value = env[key || ""];
}
if (value) {
// self-referential check
result = seen.has(value)
? result.replace(template, defaultValue)
: result.replace(template, value);
} else {
result = result.replace(template, defaultValue);
}
// if the result equaled what was in process.env and runningParsed then stop expanding
if (result === runningParsed[key || ""]) {
break;
}
regex.lastIndex = 0; // reset regex search position to re-evaluate after each replacement
}
return result;
}
/**
* Expand environment variables in parsed object
* ported from https://github.com/motdotla/dotenv-expand
* @param {{ parsed: Record<string, string>, processEnv?: Record<string, string | undefined> }} options expand options
* @returns {{ parsed: Record<string, string> }} expanded options
*/
function expand(options) {
// for use with progressive expansion
const runningParsed = /** @type {Record<string, string>} */ ({});
let processEnv = process.env;
if (
options &&
options.processEnv !== null &&
options.processEnv !== undefined
) {
processEnv = options.processEnv;
}
// dotenv.config() ran before this so the assumption is process.env has already been set
for (const key in options.parsed) {
let value = options.parsed[key];
// short-circuit scenario: process.env was already set prior to the file value
value =
processEnv[key] && processEnv[key] !== value
? /** @type {string} */ (processEnv[key])
: expandValue(value, processEnv, runningParsed);
options.parsed[key] = _resolveEscapeSequences(value);
// for use with progressive expansion
runningParsed[key] = _resolveEscapeSequences(value);
}
for (const processKey in options.parsed) {
if (processEnv) {
processEnv[processKey] = options.parsed[processKey];
}
}
return options;
}
/**
* Resolve and validate env prefixes
* Similar to Vite's resolveEnvPrefix
* @param {string | string[] | undefined} rawPrefixes raw prefixes option
* @returns {string[]} normalized prefixes array
*/
const resolveEnvPrefix = (rawPrefixes) => {
const prefixes = Array.isArray(rawPrefixes)
? rawPrefixes
: [rawPrefixes || "WEBPACK_"];
// Check for empty prefix (security issue like Vite does)
if (prefixes.includes("")) {
throw new Error(
"prefixes option contains value '', which could lead to unexpected exposure of sensitive information."
);
}
return prefixes;
};
/**
* Get list of env files to load based on mode
* Similar to Vite's getEnvFilesForMode
* @param {InputFileSystem} inputFileSystem the input file system
* @param {string} envDir the directory containing .env files
* @param {string | undefined} mode the mode (e.g., 'production', 'development')
* @returns {string[]} array of file paths to load
*/
const getEnvFilesForMode = (inputFileSystem, envDir, mode) => {
if (envDir) {
return [
/** default file */ ".env",
/** local file */ ".env.local",
/** mode file */ `.env.${mode}`,
/** mode local file */ `.env.${mode}.local`
].map((file) => join(inputFileSystem, envDir, file));
}
return [];
};
/**
* Format environment variables as DefinePlugin definitions
* @param {Record<string, string>} env environment variables
* @returns {Record<string, string>} formatted definitions
*/
const formatDefinitions = (env) => {
const definitions = /** @type {Record<string, string>} */ ({});
for (const [key, value] of Object.entries(env)) {
// Always use process.env. prefix for DefinePlugin
definitions[`process.env.${key}`] = JSON.stringify(value);
}
return definitions;
};
class DotenvPlugin {
/**
* @param {DotenvPluginOptions=} options options object
*/
constructor(options = {}) {
validate(options);
this.config = { ...DEFAULT_OPTIONS, ...options };
}
/**
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
/** @type {string[] | undefined} */
let fileDependenciesCache;
compiler.hooks.beforeCompile.tapAsync(PLUGIN_NAME, (_params, callback) => {
const inputFileSystem = /** @type {InputFileSystem} */ (
compiler.inputFileSystem
);
const context = compiler.context;
// Use webpack mode or fallback to NODE_ENV
const mode = /** @type {string | undefined} */ (
compiler.options.mode || process.env.NODE_ENV
);
this.loadEnv(
inputFileSystem,
mode,
context,
(err, env, fileDependencies) => {
if (err) return callback(err);
const definitions = formatDefinitions(env || {});
const DefinePlugin = compiler.webpack.DefinePlugin;
new DefinePlugin(definitions).apply(compiler);
fileDependenciesCache = fileDependencies;
callback();
}
);
});
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
compilation.fileDependencies.addAll(fileDependenciesCache || []);
});
}
/**
* Load environment variables from .env files
* Similar to Vite's loadEnv implementation
* @param {InputFileSystem} fs the input file system
* @param {string | undefined} mode the mode
* @param {string} context the compiler context
* @param {(err: Error | null, env?: Record<string, string>, fileDependencies?: string[]) => void} callback callback function
* @returns {void}
*/
loadEnv(fs, mode, context, callback) {
const { envDir: rawEnvDir, prefixes: rawPrefixes } =
/** @type {DotenvPluginOptions} */ (this.config);
let prefixes;
try {
prefixes = resolveEnvPrefix(rawPrefixes);
} catch (err) {
return callback(/** @type {Error} */ (err));
}
const getEnvDir = () => {
if (typeof rawEnvDir === "string") {
return join(fs, context, rawEnvDir);
}
if (rawEnvDir === true) {
return context;
}
return "";
};
const envDir = getEnvDir();
// Get env files to load
const envFiles = getEnvFilesForMode(fs, envDir, mode);
/** @type {string[]} */
const fileDependencies = [];
// Read all files
const readPromises = envFiles.map((filePath) =>
this.loadFile(fs, filePath).then(
(content) => {
fileDependencies.push(filePath);
return { content, filePath };
},
() =>
// File doesn't exist, skip it (this is normal)
({ content: "", filePath })
)
);
Promise.all(readPromises)
.then((results) => {
// Parse all files and merge (later files override earlier ones)
// Similar to Vite's implementation
const parsed = /** @type {Record<string, string>} */ ({});
for (const { content } of results) {
if (!content) continue;
const entries = parse(content);
for (const key in entries) {
parsed[key] = entries[key];
}
}
// Always expand environment variables (like Vite does)
// Make a copy of process.env so that dotenv-expand doesn't modify global process.env
const processEnv = { ...process.env };
expand({ parsed, processEnv });
// Filter by prefixes and prioritize process.env (like Vite)
const env = /** @type {Record<string, string>} */ ({});
// First, add filtered vars from parsed .env files
for (const [key, value] of Object.entries(parsed)) {
if (prefixes.some((prefix) => key.startsWith(prefix))) {
env[key] = value;
}
}
// Then, prioritize actual env variables starting with prefixes
// These are typically provided inline and should be prioritized (like Vite)
for (const key in process.env) {
if (prefixes.some((prefix) => key.startsWith(prefix))) {
env[key] = /** @type {string} */ (process.env[key]);
}
}
callback(null, env, fileDependencies);
})
.catch((err) => {
callback(err);
});
}
/**
* Load a file with proper path resolution
* @param {InputFileSystem} fs the input file system
* @param {string} file the file to load
* @returns {Promise<string>} the content of the file
*/
loadFile(fs, file) {
return new Promise((resolve, reject) => {
fs.readFile(file, "utf8", (err, content) => {
if (err) reject(err);
else resolve(content || "");
});
});
}
}
module.exports = DotenvPlugin;

View File

@ -114,6 +114,7 @@ const {
/** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE_INLINE} ASSET_MODULE_TYPE_INLINE */
/** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE_RESOURCE} ASSET_MODULE_TYPE_RESOURCE */
/** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE_SOURCE} ASSET_MODULE_TYPE_SOURCE */
/** @typedef {import("./ModuleTypeConstants").ASSET_MODULE_TYPE_BYTES} ASSET_MODULE_TYPE_BYTES */
/** @typedef {import("./ModuleTypeConstants").WEBASSEMBLY_MODULE_TYPE_ASYNC} WEBASSEMBLY_MODULE_TYPE_ASYNC */
/** @typedef {import("./ModuleTypeConstants").WEBASSEMBLY_MODULE_TYPE_SYNC} WEBASSEMBLY_MODULE_TYPE_SYNC */
/** @typedef {import("./ModuleTypeConstants").CSS_MODULE_TYPE} CSS_MODULE_TYPE */
@ -268,6 +269,76 @@ const ruleSetCompiler = new RuleSetCompiler([
new UseEffectRulePlugin()
]);
/** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
/** @typedef {import("./javascript/JavascriptGenerator")} JavascriptGenerator */
/** @typedef {import("../declarations/WebpackOptions").EmptyGeneratorOptions} EmptyGeneratorOptions */
/** @typedef {import("./json/JsonParser")} JsonParser */
/** @typedef {import("../declarations/WebpackOptions").JsonParserOptions} JsonParserOptions */
/** @typedef {import("./json/JsonGenerator")} JsonGenerator */
/** @typedef {import("../declarations/WebpackOptions").JsonGeneratorOptions} JsonGeneratorOptions */
/** @typedef {import("./asset/AssetParser")} AssetParser */
/** @typedef {import("./asset/AssetSourceParser")} AssetSourceParser */
/** @typedef {import("./asset/AssetBytesParser")} AssetBytesParser */
/** @typedef {import("../declarations/WebpackOptions").AssetParserOptions} AssetParserOptions */
/** @typedef {import("../declarations/WebpackOptions").EmptyParserOptions} EmptyParserOptions */
/** @typedef {import("./asset/AssetGenerator")} AssetGenerator */
/** @typedef {import("../declarations/WebpackOptions").AssetGeneratorOptions} AssetGeneratorOptions */
/** @typedef {import("../declarations/WebpackOptions").AssetInlineGeneratorOptions} AssetInlineGeneratorOptions */
/** @typedef {import("../declarations/WebpackOptions").AssetResourceGeneratorOptions} AssetResourceGeneratorOptions */
/** @typedef {import("./asset/AssetSourceGenerator")} AssetSourceGenerator */
/** @typedef {import("./asset/AssetBytesGenerator")} AssetBytesGenerator */
/** @typedef {import("./wasm-async/AsyncWebAssemblyParser")} AsyncWebAssemblyParser */
/** @typedef {import("./wasm-sync/WebAssemblyParser")} WebAssemblyParser */
/** @typedef {import("./css/CssParser")} CssParser */
/** @typedef {import("../declarations/WebpackOptions").CssParserOptions} CssParserOptions */
/** @typedef {import("../declarations/WebpackOptions").CssAutoParserOptions} CssAutoParserOptions */
/** @typedef {import("../declarations/WebpackOptions").CssGlobalParserOptions} CssGlobalParserOptions */
/** @typedef {import("../declarations/WebpackOptions").CssModuleParserOptions} CssModuleParserOptions */
/** @typedef {import("./css/CssGenerator")} CssGenerator */
/** @typedef {import("../declarations/WebpackOptions").CssGeneratorOptions} CssGeneratorOptions */
/** @typedef {import("../declarations/WebpackOptions").CssGlobalGeneratorOptions} CssGlobalGeneratorOptions */
/** @typedef {import("../declarations/WebpackOptions").CssModuleGeneratorOptions} CssModuleGeneratorOptions */
/** @typedef {import("../declarations/WebpackOptions").CssAutoGeneratorOptions} CssAutoGeneratorOptions */
/**
* @typedef {[
* [JAVASCRIPT_MODULE_TYPE_AUTO, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
* [JAVASCRIPT_MODULE_TYPE_DYNAMIC, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
* [JAVASCRIPT_MODULE_TYPE_ESM, JavascriptParser, JavascriptParserOptions, JavascriptGenerator, EmptyGeneratorOptions],
* [JSON_MODULE_TYPE, JsonParser, JsonParserOptions, JsonGenerator, JsonGeneratorOptions],
* [ASSET_MODULE_TYPE, AssetParser, AssetParserOptions, AssetGenerator, AssetGeneratorOptions],
* [ASSET_MODULE_TYPE_INLINE, AssetParser, EmptyParserOptions, AssetGenerator, AssetGeneratorOptions],
* [ASSET_MODULE_TYPE_RESOURCE, AssetParser, EmptyParserOptions, AssetGenerator, AssetGeneratorOptions],
* [ASSET_MODULE_TYPE_SOURCE, AssetSourceParser, EmptyParserOptions, AssetSourceGenerator, EmptyGeneratorOptions],
* [ASSET_MODULE_TYPE_BYTES, AssetBytesParser, EmptyParserOptions, AssetBytesGenerator, EmptyGeneratorOptions],
* [WEBASSEMBLY_MODULE_TYPE_ASYNC, AsyncWebAssemblyParser, EmptyParserOptions, Generator, EmptyParserOptions],
* [WEBASSEMBLY_MODULE_TYPE_SYNC, WebAssemblyParser, EmptyParserOptions, Generator, EmptyParserOptions],
* [CSS_MODULE_TYPE, CssParser, CssParserOptions, CssGenerator, CssGeneratorOptions],
* [CSS_MODULE_TYPE_AUTO, CssParser, CssAutoParserOptions, CssGenerator, CssAutoGeneratorOptions],
* [CSS_MODULE_TYPE_MODULE, CssParser, CssModuleParserOptions, CssGenerator, CssModuleGeneratorOptions],
* [CSS_MODULE_TYPE_GLOBAL, CssParser, CssGlobalParserOptions, CssGenerator, CssGlobalGeneratorOptions],
* [string, Parser, ParserOptions, Generator, GeneratorOptions],
* ]} ParsersAndGeneratorsByTypes
*/
/**
* @template {unknown[]} T
* @template {number[]} I
* @typedef {{ [K in keyof I]: K extends keyof I ? I[K] extends keyof T ? T[I[K]] : never : never }} ExtractTupleElements
*/
/**
* @template {unknown[]} T
* @template {number[]} A
* @template [R=void]
* @typedef {T extends [infer Head extends [string, ...unknown[]], ...infer Tail extends [string, ...unknown[]][]] ? Record<Head[0], SyncBailHook<ExtractTupleElements<Head, A>, R extends number ? Head[R] : R>> & RecordFactoryFromTuple<Tail, A, R> : unknown } RecordFactoryFromTuple
*/
class NormalModuleFactory extends ModuleFactory {
/**
* @param {object} param params
@ -306,15 +377,15 @@ class NormalModuleFactory extends ModuleFactory {
createModule: new AsyncSeriesBailHook(["createData", "resolveData"]),
/** @type {SyncWaterfallHook<[Module, CreateData, ResolveData]>} */
module: new SyncWaterfallHook(["module", "createData", "resolveData"]),
/** @type {HookMap<SyncBailHook<[ParserOptions], Parser | void>>} */
/** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [2], 1>>} */
createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
/** @type {HookMap<SyncBailHook<[EXPECTED_ANY, ParserOptions], void>>} */
/** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [1, 2]>>} */
parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
/** @type {HookMap<SyncBailHook<[GeneratorOptions], Generator | void>>} */
/** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [4], 3>>} */
createGenerator: new HookMap(
() => new SyncBailHook(["generatorOptions"])
),
/** @type {HookMap<SyncBailHook<[EXPECTED_ANY, GeneratorOptions], void>>} */
/** @type {import("tapable").TypedHookMap<RecordFactoryFromTuple<ParsersAndGeneratorsByTypes, [3, 4]>>} */
generator: new HookMap(
() => new SyncHook(["generator", "generatorOptions"])
),

View File

@ -294,6 +294,14 @@ class WebpackOptionsApply extends OptionsApply {
).apply(compiler);
}
if (options.dotenv) {
const DotenvPlugin = require("./DotenvPlugin");
new DotenvPlugin(
typeof options.dotenv === "boolean" ? {} : options.dotenv
).apply(compiler);
}
if (options.devtool) {
if (options.devtool.includes("source-map")) {
const hidden = options.devtool.includes("hidden");

View File

@ -91,28 +91,6 @@ const resolve = (browsers) => {
const anyBrowser = browsers.some((b) => /^(?!node)/.test(b));
const browserProperty = !anyBrowser ? false : anyNode ? null : true;
const nodeProperty = !anyNode ? false : anyBrowser ? null : true;
// Internet Explorer Mobile, Blackberry browser and Opera Mini are very old browsers, they do not support new features
const es6DynamicImport = rawChecker({
/* eslint-disable camelcase */
chrome: 63,
and_chr: 63,
edge: 79,
firefox: 67,
and_ff: 67,
// ie: Not supported
opera: 50,
op_mob: 46,
safari: [11, 1],
ios_saf: [11, 3],
samsung: [8, 2],
android: 63,
and_qq: [10, 4],
baidu: [13, 18],
and_uc: [15, 5],
kaios: [3, 0],
node: [12, 17]
/* eslint-enable camelcase */
});
return {
/* eslint-disable camelcase */
@ -175,9 +153,9 @@ const resolve = (browsers) => {
ios_saf: 7,
samsung: [3, 0],
android: 38,
// and_qq: Unknown support
and_qq: [10, 4],
// baidu: Unknown support
// and_uc: Unknown support
and_uc: [12, 12],
kaios: [3, 0],
node: [0, 12]
}),
@ -194,9 +172,9 @@ const resolve = (browsers) => {
ios_saf: 8,
samsung: [5, 0],
android: 49,
// and_qq: Unknown support
and_qq: [10, 4],
// baidu: Unknown support
// and_uc: Unknown support
and_uc: [12, 12],
kaios: [2, 5],
node: [6, 0]
}),
@ -240,8 +218,44 @@ const resolve = (browsers) => {
kaios: [3, 0],
node: [12, 17]
}),
dynamicImport: es6DynamicImport,
dynamicImportInWorker: es6DynamicImport && !anyNode,
dynamicImport: rawChecker({
chrome: 63,
and_chr: 63,
edge: 79,
firefox: 67,
and_ff: 67,
// ie: Not supported
opera: 50,
op_mob: 46,
safari: [11, 1],
ios_saf: [11, 3],
samsung: [8, 2],
android: 63,
and_qq: [10, 4],
baidu: [13, 18],
and_uc: [15, 5],
kaios: [3, 0],
node: [12, 17]
}),
dynamicImportInWorker: rawChecker({
chrome: 80,
and_chr: 80,
edge: 80,
firefox: 114,
and_ff: 114,
// ie: Not supported
opera: 67,
op_mob: 57,
safari: [15, 0],
ios_saf: [15, 0],
samsung: [13, 0],
android: 80,
and_qq: [10, 4],
baidu: [13, 18],
and_uc: [15, 5],
kaios: [3, 0],
node: [12, 17]
}),
// browserslist does not have info about globalThis
// so this is based on mdn-browser-compat-data
globalThis: rawChecker({
@ -257,9 +271,9 @@ const resolve = (browsers) => {
ios_saf: [12, 2],
samsung: [10, 1],
android: 71,
// and_qq: Unknown support
and_qq: [13, 1],
// baidu: Unknown support
// and_uc: Unknown support
and_uc: [15, 5],
kaios: [3, 0],
node: 12
}),
@ -276,9 +290,9 @@ const resolve = (browsers) => {
ios_saf: [13, 4],
samsung: 13,
android: 80,
// and_qq: Not supported
and_qq: [13, 1],
// baidu: Not supported
// and_uc: Not supported
and_uc: [15, 5],
kaios: [3, 0],
node: 14
}),
@ -314,9 +328,9 @@ const resolve = (browsers) => {
ios_saf: 11,
samsung: [6, 2],
android: 55,
and_qq: [13, 1],
and_qq: [10, 4],
baidu: [13, 18],
and_uc: [15, 5],
and_uc: [12, 12],
kaios: 3,
node: [7, 6]
}),
@ -332,7 +346,7 @@ const resolve = (browsers) => {
fetchWasm: browserProperty,
global: nodeProperty,
importScripts: false,
importScriptsInWorker: true,
importScriptsInWorker: Boolean(browserProperty),
nodeBuiltins: nodeProperty,
nodePrefixForCoreModules:
nodeProperty &&

View File

@ -202,6 +202,7 @@ const {
* & { performance: NonNullable<WebpackOptionsNormalized["performance"]> }
* & { recordsInputPath: NonNullable<WebpackOptionsNormalized["recordsInputPath"]> }
* & { recordsOutputPath: NonNullable<WebpackOptionsNormalized["recordsOutputPath"]>
* & { dotenv: NonNullable<WebpackOptionsNormalized["dotenv"]> }
* }} WebpackOptionsNormalizedWithDefaults
*/

View File

@ -178,6 +178,9 @@ const getNormalizedWebpackOptions = (config) => ({
return { ...devServer };
}),
devtool: config.devtool,
dotenv: optionalNestedConfig(config.dotenv, (dotenv) =>
dotenv === true ? {} : dotenv
),
entry:
config.entry === undefined
? { main: {} }

View File

@ -232,6 +232,9 @@ module.exports = mergeExports(fn, {
get DynamicEntryPlugin() {
return require("./DynamicEntryPlugin");
},
get DotenvPlugin() {
return require("./DotenvPlugin");
},
get EntryOptionPlugin() {
return require("./EntryOptionPlugin");
},

View File

@ -106,7 +106,7 @@ const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
/** @typedef {Set<DestructuringAssignmentProperty>} DestructuringAssignmentProperties */
// TODO remove cast when @types/estree has been updated to import assertions
/** @typedef {import("estree").ImportExpression & { options?: Expression | null, phase?: "defer" }} ImportExpression */
/** @typedef {import("estree").ImportExpression & { phase?: "defer" }} ImportExpression */
/** @type {string[]} */
const EMPTY_ARRAY = [];

View File

@ -8,7 +8,7 @@
const memoize = require("./memoize");
/** @typedef {import("schema-utils").Schema} Schema */
/** @typedef {import("schema-utils/declarations/validate").ValidationErrorConfiguration} ValidationErrorConfiguration */
/** @typedef {import("schema-utils").ValidationErrorConfiguration} ValidationErrorConfiguration */
const getValidate = memoize(() => require("schema-utils").validate);

View File

@ -26,15 +26,6 @@ const decoderOpts = {
};
class WebAssemblyParser extends Parser {
/**
* @param {{}=} options parser options
*/
constructor(options) {
super();
this.hooks = Object.freeze({});
this.options = options;
}
/**
* @param {string | Buffer | PreparsedAst} source the source to parse
* @param {ParserState} state the parser state

View File

@ -63,15 +63,6 @@ const decoderOpts = {
};
class WebAssemblyParser extends Parser {
/**
* @param {{}=} options parser options
*/
constructor(options) {
super();
this.hooks = Object.freeze({});
this.options = options;
}
/**
* @param {string | Buffer | PreparsedAst} source the source to parse
* @param {ParserState} state the parser state

View File

@ -69,7 +69,7 @@ class JsonpChunkLoadingRuntimeModule extends RuntimeModule {
if (options && options.baseUri) {
return `${RuntimeGlobals.baseURI} = ${JSON.stringify(options.baseUri)};`;
}
return `${RuntimeGlobals.baseURI} = (document && document.baseURI) || self.location.href;`;
return `${RuntimeGlobals.baseURI} = (typeof document !== 'undefined' && document.baseURI) || self.location.href;`;
}
/**

View File

@ -101,8 +101,8 @@
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^4.3.2",
"tapable": "^2.2.3",
"schema-utils": "^4.3.3",
"tapable": "^2.3.0",
"terser-webpack-plugin": "^5.3.11",
"watchpack": "^2.4.4",
"webpack-sources": "^3.3.3"
@ -184,7 +184,7 @@
"toml": "^3.0.0",
"tooling": "webpack/tooling#v1.24.3",
"ts-loader": "^9.5.1",
"typescript": "^5.9.2",
"typescript": "^5.9.3",
"url-loader": "^4.1.0",
"wast-loader": "^1.12.1",
"webassembly-feature": "1.3.0",

File diff suppressed because one or more lines are too long

View File

@ -629,6 +629,50 @@
"description": "Module namespace to use when interpolating filename template string for the sources array in a generated SourceMap. Defaults to `output.library` if not set. It's useful for avoiding runtime collisions in sourcemaps from multiple webpack projects built as libraries.",
"type": "string"
},
"Dotenv": {
"description": "Enable and configure the Dotenv plugin to load environment variables from .env files.",
"cli": {
"exclude": false
},
"anyOf": [
{
"description": "Enable Dotenv plugin with default options.",
"type": "boolean"
},
{
"$ref": "#/definitions/DotenvPluginOptions",
"description": "Options for Dotenv plugin."
}
]
},
"DotenvPluginOptions": {
"description": "Options for Dotenv plugin.",
"type": "object",
"additionalProperties": false,
"properties": {
"envDir": {
"description": "The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.",
"type": "string",
"minLength": 1
},
"prefixes": {
"description": "Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.",
"anyOf": [
{
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
{
"type": "string",
"minLength": 1
}
]
}
}
},
"EmptyGeneratorOptions": {
"description": "No generator options are supported for this module type.",
"type": "object",
@ -5800,6 +5844,9 @@
"devtool": {
"$ref": "#/definitions/DevTool"
},
"dotenv": {
"$ref": "#/definitions/Dotenv"
},
"entry": {
"$ref": "#/definitions/EntryNormalized"
},
@ -5950,6 +5997,9 @@
"devtool": {
"$ref": "#/definitions/DevTool"
},
"dotenv": {
"$ref": "#/definitions/Dotenv"
},
"entry": {
"$ref": "#/definitions/Entry"
},

View File

@ -2909,12 +2909,6 @@ describe("Targets", () => {
- Expected
+ Received
@@ ... @@
- "dynamicImportInWorker": true,
+ "dynamicImportInWorker": false,
@@ ... @@
- "dynamicImportInWorker": true,
+ "dynamicImportInWorker": false,
@@ ... @@
- "target": "node12.17",
+ "target": "browserslist: node 12.17",

View File

@ -465,6 +465,58 @@ Object {
"multiple": false,
"simpleType": "string",
},
"dotenv": Object {
"configs": Array [
Object {
"description": "Enable Dotenv plugin with default options.",
"multiple": false,
"path": "dotenv",
"type": "boolean",
},
],
"description": "Enable Dotenv plugin with default options.",
"multiple": false,
"simpleType": "boolean",
},
"dotenv-env-dir": Object {
"configs": Array [
Object {
"description": "The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.",
"multiple": false,
"path": "dotenv.envDir",
"type": "string",
},
],
"description": "The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.",
"multiple": false,
"simpleType": "string",
},
"dotenv-prefixes": Object {
"configs": Array [
Object {
"description": "Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.",
"multiple": true,
"path": "dotenv.prefixes[]",
"type": "string",
},
],
"description": "Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.",
"multiple": true,
"simpleType": "string",
},
"dotenv-prefixes-reset": Object {
"configs": Array [
Object {
"description": "Clear all items provided in 'dotenv.prefixes' configuration. Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.",
"multiple": false,
"path": "dotenv.prefixes",
"type": "reset",
},
],
"description": "Clear all items provided in 'dotenv.prefixes' configuration. Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.",
"multiple": false,
"simpleType": "boolean",
},
"entry": Object {
"configs": Array [
Object {

View File

@ -31,6 +31,37 @@ Object {
}
`;
exports[`browserslist target ["and_chr 140","and_ff 142","and_qq 14.9","and_uc 15.5","android 140","chrome 140","chrome 139","chrome 138","chrome 137","chrome 112","chrome 109","chrome 105","edge 140","edge 139","edge 138","firefox 143","firefox 142","firefox 141","firefox 140","ios_saf 26.0","ios_saf 18.5-18.6","kaios 3.0-3.1","node 24.8.0","node 22.19.0","node 20.19.0","op_mob 80","opera 122","opera 121","opera 120","safari 26.0","safari 18.5-18.6","samsung 28","samsung 27"] 1`] = `
Object {
"arrowFunction": true,
"asyncFunction": true,
"bigIntLiteral": true,
"browser": null,
"const": true,
"destructuring": true,
"document": null,
"dynamicImport": true,
"dynamicImportInWorker": true,
"electron": false,
"fetchWasm": null,
"forOf": true,
"global": null,
"globalThis": true,
"importScripts": false,
"importScriptsInWorker": false,
"module": true,
"node": null,
"nodeBuiltins": null,
"nodePrefixForCoreModules": null,
"nwjs": false,
"optionalChaining": true,
"require": null,
"templateLiteral": true,
"web": null,
"webworker": false,
}
`;
exports[`browserslist target ["and_ff 68"] 1`] = `
Object {
"arrowFunction": true,
@ -41,7 +72,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -65,17 +96,17 @@ Object {
exports[`browserslist target ["and_qq 10.4"] 1`] = `
Object {
"arrowFunction": true,
"asyncFunction": false,
"asyncFunction": true,
"bigIntLiteral": false,
"browser": true,
"const": true,
"destructuring": false,
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"electron": false,
"fetchWasm": true,
"forOf": false,
"forOf": true,
"global": false,
"globalThis": false,
"importScripts": false,
@ -93,37 +124,6 @@ Object {
}
`;
exports[`browserslist target ["and_uc 12.12"] 1`] = `
Object {
"arrowFunction": true,
"asyncFunction": false,
"bigIntLiteral": false,
"browser": true,
"const": true,
"destructuring": false,
"document": true,
"dynamicImport": false,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": false,
"global": false,
"globalThis": false,
"importScripts": false,
"importScriptsInWorker": true,
"module": false,
"node": false,
"nodeBuiltins": false,
"nodePrefixForCoreModules": false,
"nwjs": false,
"optionalChaining": false,
"require": false,
"templateLiteral": true,
"web": true,
"webworker": false,
}
`;
exports[`browserslist target ["android 4"] 1`] = `
Object {
"arrowFunction": false,
@ -320,14 +320,45 @@ Object {
"destructuring": true,
"document": null,
"dynamicImport": true,
"dynamicImportInWorker": false,
"dynamicImportInWorker": true,
"electron": false,
"fetchWasm": null,
"forOf": true,
"global": null,
"globalThis": true,
"importScripts": false,
"importScriptsInWorker": true,
"importScriptsInWorker": false,
"module": true,
"node": null,
"nodeBuiltins": null,
"nodePrefixForCoreModules": null,
"nwjs": false,
"optionalChaining": false,
"require": null,
"templateLiteral": true,
"web": null,
"webworker": false,
}
`;
exports[`browserslist target ["chrome 80","node 13.12.0"] 1`] = `
Object {
"arrowFunction": true,
"asyncFunction": true,
"bigIntLiteral": true,
"browser": null,
"const": true,
"destructuring": true,
"document": null,
"dynamicImport": true,
"dynamicImportInWorker": true,
"electron": false,
"fetchWasm": null,
"forOf": true,
"global": null,
"globalThis": true,
"importScripts": false,
"importScriptsInWorker": false,
"module": true,
"node": null,
"nodeBuiltins": null,
@ -382,7 +413,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -413,7 +444,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -444,7 +475,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -537,7 +568,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -606,7 +637,7 @@ Object {
"global": true,
"globalThis": false,
"importScripts": false,
"importScriptsInWorker": true,
"importScriptsInWorker": false,
"module": false,
"node": true,
"nodeBuiltins": true,
@ -637,7 +668,7 @@ Object {
"global": true,
"globalThis": false,
"importScripts": false,
"importScriptsInWorker": true,
"importScriptsInWorker": false,
"module": false,
"node": true,
"nodeBuiltins": true,
@ -668,7 +699,7 @@ Object {
"global": true,
"globalThis": false,
"importScripts": false,
"importScriptsInWorker": true,
"importScriptsInWorker": false,
"module": false,
"node": true,
"nodeBuiltins": true,
@ -699,7 +730,7 @@ Object {
"global": true,
"globalThis": false,
"importScripts": false,
"importScriptsInWorker": true,
"importScriptsInWorker": false,
"module": false,
"node": true,
"nodeBuiltins": true,
@ -723,14 +754,45 @@ Object {
"destructuring": true,
"document": false,
"dynamicImport": true,
"dynamicImportInWorker": false,
"dynamicImportInWorker": true,
"electron": false,
"fetchWasm": false,
"forOf": true,
"global": true,
"globalThis": true,
"importScripts": false,
"importScriptsInWorker": true,
"importScriptsInWorker": false,
"module": true,
"node": true,
"nodeBuiltins": true,
"nodePrefixForCoreModules": false,
"nwjs": false,
"optionalChaining": false,
"require": true,
"templateLiteral": true,
"web": false,
"webworker": false,
}
`;
exports[`browserslist target ["node 13.12.0"] 1`] = `
Object {
"arrowFunction": true,
"asyncFunction": true,
"bigIntLiteral": true,
"browser": false,
"const": true,
"destructuring": true,
"document": false,
"dynamicImport": true,
"dynamicImportInWorker": true,
"electron": false,
"fetchWasm": false,
"forOf": true,
"global": true,
"globalThis": true,
"importScripts": false,
"importScriptsInWorker": false,
"module": true,
"node": true,
"nodeBuiltins": true,
@ -785,7 +847,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -816,7 +878,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -909,7 +971,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -940,7 +1002,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -971,7 +1033,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -1064,7 +1126,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,
@ -1095,7 +1157,7 @@ Object {
"destructuring": true,
"document": true,
"dynamicImport": true,
"dynamicImportInWorker": true,
"dynamicImportInWorker": false,
"electron": false,
"fetchWasm": true,
"forOf": true,

View File

@ -15,7 +15,7 @@ module.exports = {
"destructuring": true,
"document": false,
"dynamicImport": true,
"dynamicImportInWorker": false,
"dynamicImportInWorker": true,
"forOf": true,
"globalThis": true,
"module": true,

View File

@ -15,7 +15,7 @@ module.exports = {
"destructuring": true,
"document": false,
"dynamicImport": true,
"dynamicImportInWorker": false,
"dynamicImportInWorker": true,
"forOf": true,
"globalThis": true,
"module": true,

View File

@ -3,7 +3,7 @@ import * as style from "./style.css";
import * as text1 from "./text-with-bom.txt";
import * as text2 from "./test-without-bom.text";
it("should remove BOM", function() {
it("should remove BOM", async function() {
const url = new URL("./resource-with-bom.ext", import.meta.url);
expect(mod).toBeDefined();
@ -13,7 +13,7 @@ it("should remove BOM", function() {
expect(url).toBeDefined();
const module = "module.js"
const modules = import("./dir/" + module);
const modules = await import("./dir/" + module);
expect(modules).toBeDefined();
});

View File

@ -4,6 +4,9 @@ const fs = require("fs");
const path = require("path");
module.exports = {
findBundle() {
return ["dir_module_js.bundle0.js", "bundle0.js"];
},
afterExecute(options) {
const outputPath = options.output.path;
const files = fs.readdirSync(outputPath);

View File

@ -6,6 +6,9 @@ module.exports = {
output: {
assetModuleFilename: "[name][ext]"
},
optimization: {
chunkIds: "named"
},
module: {
rules: [
{

View File

@ -0,0 +1,13 @@
# Basic test
WEBPACK_API_URL=https://api.example.com
WEBPACK_MODE=test
SECRET_KEY=should-not-be-exposed
PRIVATE_VAR=also-hidden
# Expand test
WEBPACK_BASE=example.com
WEBPACK_FULL_URL=${WEBPACK_API_URL}/v1
WEBPACK_PORT=${PORT:-3000}
# Mode-specific base value
WEBPACK_ENV=development

View File

@ -0,0 +1,3 @@
# Production overrides
WEBPACK_API_URL=https://prod-api.example.com
WEBPACK_ENV=production

View File

@ -0,0 +1,10 @@
"use strict";
it("should expose only WEBPACK_ prefixed env vars", () => {
expect(process.env.WEBPACK_API_URL).toBe("https://api.example.com");
expect(process.env.WEBPACK_MODE).toBe("test");
// Non-prefixed vars should not be exposed
expect(typeof process.env.SECRET_KEY).toBe("undefined");
expect(typeof process.env.PRIVATE_VAR).toBe("undefined");
});

View File

@ -0,0 +1,7 @@
"use strict";
it("should load from custom envDir", () => {
expect(process.env.WEBPACK_FROM_ENVS).toBe("loaded-from-envs-dir");
expect(process.env.WEBPACK_API_URL).toBe("https://custom.example.com");
});

View File

@ -0,0 +1,13 @@
"use strict";
it("should expose only APP_ and CONFIG_ prefixed vars", () => {
expect(process.env.APP_NAME).toBe("MyApp");
expect(process.env.CONFIG_TIMEOUT).toBe("5000");
// WEBPACK_ prefixed should not be exposed
expect(typeof process.env.WEBPACK_API_URL).toBe("undefined");
// Non-prefixed should not be exposed
expect(typeof process.env.SECRET_KEY).toBe("undefined");
});

View File

@ -0,0 +1,2 @@
WEBPACK_FROM_ENVS=loaded-from-envs-dir
WEBPACK_API_URL=https://custom.example.com

View File

@ -0,0 +1,10 @@
"use strict";
it("should expand variables by default", () => {
expect(process.env.WEBPACK_BASE).toBe("example.com");
expect(process.env.WEBPACK_API_URL).toBe("https://api.example.com");
expect(process.env.WEBPACK_FULL_URL).toBe("https://api.example.com/v1");
// Test default value operator
expect(process.env.WEBPACK_PORT).toBe("3000");
});

View File

@ -0,0 +1,11 @@
"use strict";
it("should load .env.production and override .env values", () => {
// Value from .env.production should override .env
expect(process.env.WEBPACK_API_URL).toBe("https://prod-api.example.com");
expect(process.env.WEBPACK_ENV).toBe("production");
// Value only in .env
expect(process.env.WEBPACK_MODE).toBe("test");
});

View File

@ -0,0 +1,4 @@
APP_NAME=MyApp
CONFIG_TIMEOUT=5000
WEBPACK_API_URL=should-not-be-exposed
SECRET_KEY=also-hidden

View File

@ -0,0 +1,45 @@
"use strict";
/** @type {import("../../../../").Configuration[]} */
module.exports = [
// Test 1: Basic - default behavior with WEBPACK_ prefix
{
name: "basic",
mode: "development",
entry: "./basic.js",
dotenv: true
},
// Test 2: Expand - variables are always expanded
{
name: "expand",
mode: "development",
entry: "./expand.js",
dotenv: true
},
// Test 3: Custom envDir - load from different directory
{
name: "custom-envdir",
mode: "development",
entry: "./custom-envdir.js",
dotenv: {
envDir: "./envs"
}
},
// Test 4: Custom prefixes - multiple prefixes
{
name: "custom-prefixes",
mode: "development",
entry: "./custom-prefixes.js",
dotenv: {
envDir: "./prefixes-env",
prefixes: ["APP_", "CONFIG_"]
}
},
// Test 5: Mode-specific - .env.[mode] overrides
{
name: "mode-specific",
mode: "production",
entry: "./mode-specific.js",
dotenv: true
}
];

View File

@ -1,27 +1,39 @@
it("should set fetchPriority", () => {
import(/* webpackFetchPriority: "high" */ "./a");
function abortable(fn) {
return new Promise((resolve) => {
const timeoutId = setTimeout(() => {
fn = undefined;
resolve('Promise resolved after delay');
clearTimeout(timeoutId);
}, 1000);
return fn();
});
}
it("should set fetchPriority", async () => {
abortable(() => import(/* webpackFetchPriority: "high" */ "./a"));
expect(document.head._children).toHaveLength(4);
const script1 = document.head._children[2];
expect(script1._attributes.fetchpriority).toBe("high");
import(/* webpackFetchPriority: "low" */ "./b");
abortable(() => import(/* webpackFetchPriority: "low" */ "./b"));
expect(document.head._children).toHaveLength(5);
const script2 = document.head._children[4];
expect(script2._attributes.fetchpriority).toBe("low");
import(/* webpackFetchPriority: "low" */ "./c");
abortable(() => import(/* webpackFetchPriority: "low" */ "./c"));
expect(document.head._children).toHaveLength(6);
const script3 = document.head._children[5];
expect(script3._attributes.fetchpriority).toBe("low");
import(/* webpackPrefetch: 20, webpackFetchPriority: "auto" */ "./c");
abortable(() => import(/* webpackPrefetch: 20, webpackFetchPriority: "auto" */ "./c"));
import("./d")
abortable(() => import("./d"))
expect(document.head._children).toHaveLength(7);
const script4 = document.head._children[6];
expect(script4._attributes.fetchpriority).toBeUndefined();
import(/* webpackPrefetch: -20 */ "./d3");
abortable(() => import(/* webpackPrefetch: -20 */ "./d3"));
expect(document.head._children).toHaveLength(8);
const script5 = document.head._children[7];
expect(script5._attributes.fetchpriority).toBeUndefined();
@ -29,12 +41,12 @@ it("should set fetchPriority", () => {
const condition = true;
if (!condition) {
import(/* webpackFetchPriority: "high", webpackChunkName: "one" */ "./e");
abortable( () => import(/* webpackFetchPriority: "high", webpackChunkName: "one" */ "./e"));
expect(document.head._children).toHaveLength(9);
const script6 = document.head._children[8];
expect(script6._attributes.fetchpriority).toBe("high");
} else {
import(/* webpackFetchPriority: "low", webpackChunkName: "two" */ "./e");
abortable(() => import(/* webpackFetchPriority: "low", webpackChunkName: "two" */ "./e"));
expect(document.head._children).toHaveLength(9);
const script6 = document.head._children[8];
expect(script6._attributes.fetchpriority).toBe("low");

View File

@ -1,13 +1,25 @@
function abortable(fn) {
return new Promise((resolve) => {
const timeoutId = setTimeout(() => {
fn = undefined;
resolve('Promise resolved after delay');
clearTimeout(timeoutId);
}, 1000);
return fn();
});
}
it("should set fetchPriority", () => {
// Single Chunk
import(/* webpackFetchPriority: "high" */ "./a");
abortable(() => import(/* webpackFetchPriority: "high" */ "./a"));
expect(document.head._children).toHaveLength(1);
const script1 = document.head._children[0];
expect(script1._attributes.fetchpriority).toBe("high");
// Multiple Chunks
import(/* webpackFetchPriority: "high" */ "./b");
import(/* webpackFetchPriority: "high" */ "./b2");
abortable(() => import(/* webpackFetchPriority: "high" */ "./b"));
abortable(() => import(/* webpackFetchPriority: "high" */ "./b2"));
expect(document.head._children).toHaveLength(4);
const script2 = document.head._children[1];
const script3 = document.head._children[2];
@ -17,19 +29,19 @@ it("should set fetchPriority", () => {
expect(script4._attributes.fetchpriority).toBe("high");
// Single Chunk, low
import(/* webpackFetchPriority: "low" */ "./c");
abortable(() => import(/* webpackFetchPriority: "low" */ "./c"));
expect(document.head._children).toHaveLength(5);
const script5 = document.head._children[4];
expect(script5._attributes.fetchpriority).toBe("low");
// Single Chunk, auto
import(/* webpackFetchPriority: "auto" */ "./d");
abortable(() => import(/* webpackFetchPriority: "auto" */ "./d"));
expect(document.head._children).toHaveLength(6);
const script6 = document.head._children[5];
expect(script6._attributes.fetchpriority).toBe("auto");
// No fetch priority
import("./e");
abortable(() => import("./e"));
expect(document.head._children).toHaveLength(7);
const script7 = document.head._children[6];
expect(script7._attributes.fetchpriority).toBeUndefined();
@ -44,49 +56,49 @@ it("should set fetchPriority", () => {
const script8 = document.head._children[7];
expect(script8._attributes.fetchpriority).toBeUndefined();
import(/* webpackFetchPriority: "auto" */ "./g");
abortable(() => import(/* webpackFetchPriority: "auto" */ "./g"));
expect(document.head._children).toHaveLength(9);
const script9 = document.head._children[8];
expect(script9._attributes.fetchpriority).toBe("auto");
import(/* webpackFetchPriority: "unknown" */ "./h.js");
abortable(() => import(/* webpackFetchPriority: "unknown" */ "./h.js"));
expect(document.head._children).toHaveLength(10);
const script10 = document.head._children[9];
expect(script10._attributes.fetchpriority).toBeUndefined();
import(/* webpackFetchPriority: "high" */ "./i");
import(/* webpackFetchPriority: "low" */ "./i");
abortable(() => import(/* webpackFetchPriority: "high" */ "./i"));
abortable(() => import(/* webpackFetchPriority: "low" */ "./i"));
expect(document.head._children).toHaveLength(11);
const script11 = document.head._children[10];
expect(script11._attributes.fetchpriority).toBe("high");
import(/* webpackFetchPriority: "low" */ "./j");
import(/* webpackFetchPriority: "high" */ "./j");
abortable(() => import(/* webpackFetchPriority: "low" */ "./j"));
abortable(() => import(/* webpackFetchPriority: "high" */ "./j"));
expect(document.head._children).toHaveLength(12);
const script12 = document.head._children[11];
expect(script12._attributes.fetchpriority).toBe("low");
import(/* webpackFetchPriority: "low" */ "./k");
import("./e");
import(/* webpackFetchPriority: "high" */ "./k");
expect(document.head._children).toHaveLength(13);
abortable(() => import(/* webpackFetchPriority: "low" */ "./k"));
abortable(() => import("./e"));
abortable(() => import(/* webpackFetchPriority: "high" */ "./k"));
abortable(() => expect(document.head._children).toHaveLength(13));
const script13 = document.head._children[12];
expect(script13._attributes.fetchpriority).toBe("low");
__non_webpack_require__("./125.js");
import(/* webpackFetchPriority: "high" */ "./style.css");
abortable(() => import(/* webpackFetchPriority: "high" */ "./style.css"));
expect(document.head._children).toHaveLength(14);
const link1 = document.head._children[13];
expect(link1._attributes.fetchpriority).toBe("high");
__non_webpack_require__("./499.js");
import("./style-1.css");
abortable(() => import("./style-1.css"));
expect(document.head._children).toHaveLength(15);
const link2 = document.head._children[14];
expect(link2._attributes.fetchpriority).toBeUndefined();
__non_webpack_require__("./616.js");
import(/* webpackFetchPriority: "low" */ "./style-2.css");
abortable(() => import(/* webpackFetchPriority: "low" */ "./style-2.css"));
expect(document.head._children).toHaveLength(16);
const link3 = document.head._children[15];
expect(link3._attributes.fetchpriority).toBe("low");

View File

@ -57,9 +57,7 @@ describe("browserslist target", () => {
["node 10.0.0"],
["node 10.17.0"],
["node 12.19.0"],
// UC browsers for Android
["and_uc 12.12"],
["node 13.12.0"],
// QQ browser
["and_qq 10.4"],
@ -73,6 +71,45 @@ describe("browserslist target", () => {
// Multiple
["firefox 80", "chrome 80"],
["chrome 80", "node 12.19.0"],
["chrome 80", "node 13.12.0"],
// defaults and fully supports es6-module
// maintained node versions
[
"and_chr 140",
"and_ff 142",
"and_qq 14.9",
"and_uc 15.5",
"android 140",
"chrome 140",
"chrome 139",
"chrome 138",
"chrome 137",
"chrome 112",
"chrome 109",
"chrome 105",
"edge 140",
"edge 139",
"edge 138",
"firefox 143",
"firefox 142",
"firefox 141",
"firefox 140",
"ios_saf 26.0",
"ios_saf 18.5-18.6",
"kaios 3.0-3.1",
"node 24.8.0",
"node 22.19.0",
"node 20.19.0",
"op_mob 80",
"opera 122",
"opera 121",
"opera 120",
"safari 26.0",
"safari 18.5-18.6",
"samsung 28",
"samsung 27"
],
// Unknown
["unknown 50"]

View File

@ -17,7 +17,7 @@ module.exports = {
]
},
plugins: [
new webpack.ProgressPlugin(),
new webpack.ProgressPlugin(() => {}),
{
apply(compiler) {
compiler.hooks.done.tapPromise("CacheTest", async () => {

View File

@ -16,7 +16,7 @@ module.exports = {
]
},
plugins: [
new webpack.ProgressPlugin(),
new webpack.ProgressPlugin(() => {}),
{
apply(compiler) {
compiler.hooks.done.tapPromise("CacheTest", async () => {

151
types.d.ts vendored
View File

@ -2947,6 +2947,22 @@ declare interface Configuration {
*/
devtool?: string | false;
/**
* Enable and configure the Dotenv plugin to load environment variables from .env files.
*/
dotenv?:
| boolean
| {
/**
* The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.
*/
envDir?: string;
/**
* Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.
*/
prefixes?: string | string[];
};
/**
* The entry point(s) of the compilation.
*/
@ -4336,6 +4352,55 @@ declare interface DllReferencePluginOptionsManifest {
| "jsonp"
| "system";
}
declare class DotenvPlugin {
constructor(options?: DotenvPluginOptions);
config: {
/**
* The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.
*/
envDir: string | boolean;
/**
* Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.
*/
prefixes: string | string[];
};
apply(compiler: Compiler): void;
/**
* Load environment variables from .env files
* Similar to Vite's loadEnv implementation
*/
loadEnv(
fs: InputFileSystem,
mode: undefined | string,
context: string,
callback: (
err: null | Error,
env?: Record<string, string>,
fileDependencies?: string[]
) => void
): void;
/**
* Load a file with proper path resolution
*/
loadFile(fs: InputFileSystem, file: string): Promise<string>;
}
/**
* Options for Dotenv plugin.
*/
declare interface DotenvPluginOptions {
/**
* The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.
*/
envDir?: string;
/**
* Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.
*/
prefixes?: string | string[];
}
declare class DynamicEntryPlugin {
constructor(context: string, entry: () => Promise<EntryStaticNormalized>);
context: string;
@ -6380,35 +6445,6 @@ declare interface ImportDependencyMeta {
externalType?: "import" | "module";
}
type ImportExpressionJavascriptParser = ImportExpressionImport & {
options?:
| null
| ImportExpressionImport
| UnaryExpression
| ArrayExpression
| ArrowFunctionExpression
| AssignmentExpression
| AwaitExpression
| BinaryExpression
| SimpleCallExpression
| NewExpression
| ChainExpression
| ClassExpression
| ConditionalExpression
| FunctionExpression
| Identifier
| SimpleLiteral
| RegExpLiteral
| BigIntLiteral
| LogicalExpression
| MemberExpression
| MetaProperty
| ObjectExpression
| SequenceExpression
| TaggedTemplateExpression
| TemplateLiteral
| ThisExpression
| UpdateExpression
| YieldExpression;
phase?: "defer";
};
declare interface ImportModuleOptions {
@ -11288,12 +11324,6 @@ declare abstract class NormalModuleFactory extends ModuleFactory {
],
Module
>;
createParser: HookMap<SyncBailHook<[ParserOptions], void | ParserClass>>;
parser: HookMap<SyncBailHook<[any, ParserOptions], void>>;
createGenerator: HookMap<
SyncBailHook<[GeneratorOptions], void | Generator>
>;
generator: HookMap<SyncBailHook<[any, GeneratorOptions], void>>;
createModuleClass: HookMap<
SyncBailHook<
[
@ -17992,6 +18022,22 @@ declare interface WebpackOptionsNormalized {
*/
devtool?: string | false;
/**
* Enable and configure the Dotenv plugin to load environment variables from .env files.
*/
dotenv?:
| boolean
| {
/**
* The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.
*/
envDir?: string;
/**
* Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.
*/
prefixes?: string | string[];
};
/**
* The entry point(s) of the compilation.
*/
@ -18193,7 +18239,39 @@ type WebpackOptionsNormalizedWithDefaults = WebpackOptionsNormalized & {
} & { watch: NonNullable<undefined | boolean> } & {
performance: NonNullable<undefined | false | PerformanceOptions>;
} & { recordsInputPath: NonNullable<undefined | string | false> } & {
recordsOutputPath: NonNullable<undefined | string | false>;
recordsOutputPath:
| (string & {
dotenv: NonNullable<
| undefined
| boolean
| {
/**
* The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.
*/
envDir?: string;
/**
* Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.
*/
prefixes?: string | string[];
}
>;
})
| (false & {
dotenv: NonNullable<
| undefined
| boolean
| {
/**
* The directory from which .env files are loaded. Defaults to the webpack context. Loads .env, .env.local, .env.[mode], .env.[mode].local in order.
*/
envDir?: string;
/**
* Only expose environment variables that start with these prefixes. Defaults to 'WEBPACK_'.
*/
prefixes?: string | string[];
}
>;
});
};
/**
@ -18916,6 +18994,7 @@ declare namespace exports {
DllPlugin,
DllReferencePlugin,
DynamicEntryPlugin,
DotenvPlugin,
EntryOptionPlugin,
EntryPlugin,
EnvironmentPlugin,

View File

@ -2393,7 +2393,7 @@ camelcase@^6.3.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001737, caniuse-lite@^1.0.30001746:
caniuse-lite@^1.0.30001746:
version "1.0.30001746"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz#199d20f04f5369825e00ff7067d45d5dfa03aee7"
integrity sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==
@ -3034,7 +3034,7 @@ eastasianwidth@^0.2.0:
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
electron-to-chromium@^1.5.211, electron-to-chromium@^1.5.227:
electron-to-chromium@^1.5.227:
version "1.5.228"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz#38b849bc8714bd21fb64f5ad56bf8cfd8638e1e9"
integrity sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==
@ -6247,7 +6247,7 @@ node-preload@^0.2.1:
dependencies:
process-on-spawn "^1.0.0"
node-releases@^2.0.19, node-releases@^2.0.21:
node-releases@^2.0.21:
version "2.0.21"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.21.tgz#f59b018bc0048044be2d4c4c04e4c8b18160894c"
integrity sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==
@ -7179,10 +7179,10 @@ schema-utils@^3.0.0, schema-utils@^3.1.1:
ajv "^6.12.5"
ajv-keywords "^3.5.2"
schema-utils@^4.0.0, schema-utils@^4.3.0, schema-utils@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.2.tgz#0c10878bf4a73fd2b1dfd14b9462b26788c806ae"
integrity sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==
schema-utils@^4.0.0, schema-utils@^4.3.0, schema-utils@^4.3.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46"
integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==
dependencies:
"@types/json-schema" "^7.0.9"
ajv "^8.9.0"
@ -7685,10 +7685,10 @@ synckit@^0.11.7, synckit@^0.11.8:
dependencies:
"@pkgr/core" "^0.2.4"
tapable@^2.2.0, tapable@^2.2.1, tapable@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.3.tgz#4b67b635b2d97578a06a2713d2f04800c237e99b"
integrity sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==
tapable@^2.2.0, tapable@^2.2.1, tapable@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6"
integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==
tar@^7.4.3:
version "7.4.3"
@ -7973,10 +7973,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typescript@^5.9.2:
version "5.9.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6"
integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==
typescript@^5.9.3:
version "5.9.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
uglify-js@^3.1.4:
version "3.19.3"