fix: types and tests
Github Actions / lint (push) Has been cancelled Details
Github Actions / basic (push) Has been cancelled Details
Github Actions / validate-legacy-node (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 (18.x, ubuntu-latest, b) (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 (23.x, ubuntu-latest, a) (push) Has been cancelled Details
Github Actions / integration (23.x, ubuntu-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

This commit is contained in:
Alexander Akait 2025-03-27 17:50:19 +03:00 committed by GitHub
commit 97d4961cd1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 70 additions and 17 deletions

View File

@ -3175,7 +3175,7 @@ export interface ExternalItemFunctionData {
| (( | ((
context: string, context: string,
request: string, request: string,
callback: (err?: Error, result?: string) => void callback: (err?: Error | null, result?: string | false) => void
) => void) ) => void)
| ((context: string, request: string) => Promise<string>); | ((context: string, request: string) => Promise<string>);
/** /**

View File

@ -15,10 +15,13 @@ const ImportDependency = require("./dependencies/ImportDependency");
const { resolveByProperty, cachedSetProperty } = require("./util/cleverMerge"); const { resolveByProperty, cachedSetProperty } = require("./util/cleverMerge");
/** @typedef {import("../declarations/WebpackOptions").ExternalItemFunctionData} ExternalItemFunctionData */ /** @typedef {import("../declarations/WebpackOptions").ExternalItemFunctionData} ExternalItemFunctionData */
/** @typedef {import("../declarations/WebpackOptions").ExternalItemObjectKnown} ExternalItemObjectKnown */
/** @typedef {import("../declarations/WebpackOptions").ExternalItemObjectUnknown} ExternalItemObjectUnknown */
/** @typedef {import("../declarations/WebpackOptions").Externals} Externals */ /** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
/** @typedef {import("./Compilation").DepConstructor} DepConstructor */ /** @typedef {import("./Compilation").DepConstructor} DepConstructor */
/** @typedef {import("./ExternalModule").DependencyMeta} DependencyMeta */ /** @typedef {import("./ExternalModule").DependencyMeta} DependencyMeta */
/** @typedef {import("./Module")} Module */ /** @typedef {import("./Module")} Module */
/** @typedef {import("./ModuleFactory").IssuerLayer} IssuerLayer */
/** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */ /** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9-]+ /; const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9-]+ /;
@ -27,7 +30,7 @@ const EMPTY_RESOLVE_OPTIONS = {};
// TODO webpack 6 remove this // TODO webpack 6 remove this
const callDeprecatedExternals = util.deprecate( const callDeprecatedExternals = util.deprecate(
/** /**
* @param {TODO} externalsFunction externals function * @param {EXPECTED_FUNCTION} externalsFunction externals function
* @param {string} context context * @param {string} context context
* @param {string} request request * @param {string} request request
* @param {(err: Error | null | undefined, value: ExternalValue | undefined, ty: ExternalType | undefined) => void} cb cb * @param {(err: Error | null | undefined, value: ExternalValue | undefined, ty: ExternalType | undefined) => void} cb cb
@ -40,19 +43,26 @@ const callDeprecatedExternals = util.deprecate(
"DEP_WEBPACK_EXTERNALS_FUNCTION_PARAMETERS" "DEP_WEBPACK_EXTERNALS_FUNCTION_PARAMETERS"
); );
/** @typedef {ExternalItemObjectKnown & ExternalItemObjectUnknown} ExternalItemObject */
/**
* @template {ExternalItemObject} T
* @typedef {WeakMap<T, Map<IssuerLayer, Omit<T, "byLayer">>>} ExternalWeakCache
*/
/** @type {ExternalWeakCache<ExternalItemObject>} */
const cache = new WeakMap(); const cache = new WeakMap();
/** /**
* @template {object} T * @param {ExternalItemObject} obj obj
* @param {T} obj obj * @param {IssuerLayer} layer layer
* @param {TODO} layer layer * @returns {Omit<ExternalItemObject, "byLayer">} result
* @returns {Omit<T, "byLayer">} result
*/ */
const resolveLayer = (obj, layer) => { const resolveLayer = (obj, layer) => {
let map = cache.get(/** @type {object} */ (obj)); let map = cache.get(obj);
if (map === undefined) { if (map === undefined) {
map = new Map(); map = new Map();
cache.set(/** @type {object} */ (obj), map); cache.set(obj, map);
} else { } else {
const cacheEntry = map.get(layer); const cacheEntry = map.get(layer);
if (cacheEntry !== undefined) return cacheEntry; if (cacheEntry !== undefined) return cacheEntry;
@ -275,8 +285,7 @@ class ExternalModuleFactoryPlugin {
context, context,
request, request,
resolveContext, resolveContext,
/** @type {TODO} */ callback
(callback)
); );
} else { } else {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -302,7 +311,8 @@ class ExternalModuleFactoryPlugin {
} else if (typeof externals === "object") { } else if (typeof externals === "object") {
const resolvedExternals = resolveLayer( const resolvedExternals = resolveLayer(
externals, externals,
contextInfo.issuerLayer /** @type {IssuerLayer} */
(contextInfo.issuerLayer)
); );
if ( if (
Object.prototype.hasOwnProperty.call( Object.prototype.hasOwnProperty.call(

View File

@ -18,11 +18,13 @@
* @property {boolean=} cacheable allow to use the unsafe cache * @property {boolean=} cacheable allow to use the unsafe cache
*/ */
/** @typedef {string | null} IssuerLayer */
/** /**
* @typedef {object} ModuleFactoryCreateDataContextInfo * @typedef {object} ModuleFactoryCreateDataContextInfo
* @property {string} issuer * @property {string} issuer
* @property {string | null=} issuerLayer * @property {IssuerLayer=} issuerLayer
* @property {string} compiler * @property {string=} compiler
*/ */
/** /**

View File

@ -1206,7 +1206,7 @@
"getResolve": { "getResolve": {
"description": "Get a resolve function with the current resolver options.", "description": "Get a resolve function with the current resolver options.",
"instanceof": "Function", "instanceof": "Function",
"tsType": "((options?: ResolveOptions) => ((context: string, request: string, callback: (err?: Error, result?: string) => void) => void) | ((context: string, request: string) => Promise<string>))" "tsType": "((options?: ResolveOptions) => ((context: string, request: string, callback: (err?: Error | null, result?: string | false) => void) => void) | ((context: string, request: string) => Promise<string>))"
}, },
"request": { "request": {
"description": "The request as written by the user in the require/import expression/statement.", "description": "The request as written by the user in the require/import expression/statement.",

View File

@ -0,0 +1,7 @@
it("should allow functions as externals with promise and resolver", function () {
const result = require("external");
expect(result).toMatch(/^[a-z]:\\|\//i);
expect(result).toMatch(/resolve-callback.node_modules.external\.js$/);
const result1 = require("external-false");
expect(JSON.stringify(result1)).toBe('{}');
});

View File

View File

@ -0,0 +1,28 @@
/** @type {import("../../../../").Configuration} */
module.exports = {
optimization: {
concatenateModules: true
},
externals: [
({ context, request, getResolve }, callback) => {
if (request !== "external" && request !== "external-false") {
return callback(null, false);
}
const resolve = getResolve({
alias: {
"external-false": false
}
});
if (request === "external-false") {
resolve(context, request, callback);
} else {
resolve(context, request, (err, resolved) => {
if (err) callback(err);
else callback(null, `var ${JSON.stringify(resolved)}`);
});
}
}
]
};

View File

@ -2,4 +2,7 @@ it("should allow functions as externals with promise and resolver", function ()
const result = require("external"); const result = require("external");
expect(result).toMatch(/^[a-z]:\\|\//i); expect(result).toMatch(/^[a-z]:\\|\//i);
expect(result).toMatch(/resolve.node_modules.external\.js$/); expect(result).toMatch(/resolve.node_modules.external\.js$/);
const result2 = require("external-promise");
expect(result2).toMatch(/^[a-z]:\\|\//i);
expect(result2).toMatch(/resolve.node_modules.external-promise\.js$/);
}); });

View File

View File

@ -5,7 +5,10 @@ module.exports = {
}, },
externals: [ externals: [
async ({ context, request, getResolve }) => { async ({ context, request, getResolve }) => {
if (request !== "external") return false; if (request !== "external" && request !== "external-promise") {
return false;
}
const resolve = getResolve(); const resolve = getResolve();
const resolved = await resolve(context, request); const resolved = await resolve(context, request);
return `var ${JSON.stringify(resolved)}`; return `var ${JSON.stringify(resolved)}`;

4
types.d.ts vendored
View File

@ -4791,7 +4791,7 @@ declare interface ExternalItemFunctionData {
| (( | ((
context: string, context: string,
request: string, request: string,
callback: (err?: Error, result?: string) => void callback: (err?: null | Error, result?: string | false) => void
) => void) ) => void)
| ((context: string, request: string) => Promise<string>); | ((context: string, request: string) => Promise<string>);
@ -9026,7 +9026,7 @@ declare interface ModuleFactoryCreateData {
declare interface ModuleFactoryCreateDataContextInfo { declare interface ModuleFactoryCreateDataContextInfo {
issuer: string; issuer: string;
issuerLayer?: null | string; issuerLayer?: null | string;
compiler: string; compiler?: string;
} }
declare interface ModuleFactoryResult { declare interface ModuleFactoryResult {
/** /**