diff --git a/declarations/WebpackOptions.d.ts b/declarations/WebpackOptions.d.ts index ecdc73bfa..3de26661a 100644 --- a/declarations/WebpackOptions.d.ts +++ b/declarations/WebpackOptions.d.ts @@ -3175,7 +3175,7 @@ export interface ExternalItemFunctionData { | (( context: string, request: string, - callback: (err?: Error, result?: string) => void + callback: (err?: Error | null, result?: string | false) => void ) => void) | ((context: string, request: string) => Promise); /** diff --git a/lib/ExternalModuleFactoryPlugin.js b/lib/ExternalModuleFactoryPlugin.js index d7b9e0bbe..c5c4f3fb2 100644 --- a/lib/ExternalModuleFactoryPlugin.js +++ b/lib/ExternalModuleFactoryPlugin.js @@ -15,10 +15,13 @@ const ImportDependency = require("./dependencies/ImportDependency"); const { resolveByProperty, cachedSetProperty } = require("./util/cleverMerge"); /** @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("./Compilation").DepConstructor} DepConstructor */ /** @typedef {import("./ExternalModule").DependencyMeta} DependencyMeta */ /** @typedef {import("./Module")} Module */ +/** @typedef {import("./ModuleFactory").IssuerLayer} IssuerLayer */ /** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */ const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9-]+ /; @@ -27,7 +30,7 @@ const EMPTY_RESOLVE_OPTIONS = {}; // TODO webpack 6 remove this const callDeprecatedExternals = util.deprecate( /** - * @param {TODO} externalsFunction externals function + * @param {EXPECTED_FUNCTION} externalsFunction externals function * @param {string} context context * @param {string} request request * @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" ); +/** @typedef {ExternalItemObjectKnown & ExternalItemObjectUnknown} ExternalItemObject */ + +/** + * @template {ExternalItemObject} T + * @typedef {WeakMap>>} ExternalWeakCache + */ + +/** @type {ExternalWeakCache} */ const cache = new WeakMap(); /** - * @template {object} T - * @param {T} obj obj - * @param {TODO} layer layer - * @returns {Omit} result + * @param {ExternalItemObject} obj obj + * @param {IssuerLayer} layer layer + * @returns {Omit} result */ const resolveLayer = (obj, layer) => { - let map = cache.get(/** @type {object} */ (obj)); + let map = cache.get(obj); if (map === undefined) { map = new Map(); - cache.set(/** @type {object} */ (obj), map); + cache.set(obj, map); } else { const cacheEntry = map.get(layer); if (cacheEntry !== undefined) return cacheEntry; @@ -275,8 +285,7 @@ class ExternalModuleFactoryPlugin { context, request, resolveContext, - /** @type {TODO} */ - (callback) + callback ); } else { return new Promise((resolve, reject) => { @@ -302,7 +311,8 @@ class ExternalModuleFactoryPlugin { } else if (typeof externals === "object") { const resolvedExternals = resolveLayer( externals, - contextInfo.issuerLayer + /** @type {IssuerLayer} */ + (contextInfo.issuerLayer) ); if ( Object.prototype.hasOwnProperty.call( diff --git a/lib/ModuleFactory.js b/lib/ModuleFactory.js index 4dc7b862d..26cb9a94a 100644 --- a/lib/ModuleFactory.js +++ b/lib/ModuleFactory.js @@ -18,11 +18,13 @@ * @property {boolean=} cacheable allow to use the unsafe cache */ +/** @typedef {string | null} IssuerLayer */ + /** * @typedef {object} ModuleFactoryCreateDataContextInfo * @property {string} issuer - * @property {string | null=} issuerLayer - * @property {string} compiler + * @property {IssuerLayer=} issuerLayer + * @property {string=} compiler */ /** diff --git a/schemas/WebpackOptions.json b/schemas/WebpackOptions.json index 82d7660da..a26fba7c4 100644 --- a/schemas/WebpackOptions.json +++ b/schemas/WebpackOptions.json @@ -1206,7 +1206,7 @@ "getResolve": { "description": "Get a resolve function with the current resolver options.", "instanceof": "Function", - "tsType": "((options?: ResolveOptions) => ((context: string, request: string, callback: (err?: Error, result?: string) => void) => void) | ((context: string, request: string) => Promise))" + "tsType": "((options?: ResolveOptions) => ((context: string, request: string, callback: (err?: Error | null, result?: string | false) => void) => void) | ((context: string, request: string) => Promise))" }, "request": { "description": "The request as written by the user in the require/import expression/statement.", diff --git a/test/configCases/externals/resolve-callback/index.js b/test/configCases/externals/resolve-callback/index.js new file mode 100644 index 000000000..94596ebbd --- /dev/null +++ b/test/configCases/externals/resolve-callback/index.js @@ -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('{}'); +}); diff --git a/test/configCases/externals/resolve-callback/node_modules/external-false.js b/test/configCases/externals/resolve-callback/node_modules/external-false.js new file mode 100644 index 000000000..e69de29bb diff --git a/test/configCases/externals/resolve-callback/node_modules/external.js b/test/configCases/externals/resolve-callback/node_modules/external.js new file mode 100644 index 000000000..e69de29bb diff --git a/test/configCases/externals/resolve-callback/webpack.config.js b/test/configCases/externals/resolve-callback/webpack.config.js new file mode 100644 index 000000000..05c5ea60f --- /dev/null +++ b/test/configCases/externals/resolve-callback/webpack.config.js @@ -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)}`); + }); + } + } + ] +}; diff --git a/test/configCases/externals/resolve/index.js b/test/configCases/externals/resolve/index.js index 941c59e9b..2b43aa03e 100644 --- a/test/configCases/externals/resolve/index.js +++ b/test/configCases/externals/resolve/index.js @@ -2,4 +2,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.node_modules.external\.js$/); + const result2 = require("external-promise"); + expect(result2).toMatch(/^[a-z]:\\|\//i); + expect(result2).toMatch(/resolve.node_modules.external-promise\.js$/); }); diff --git a/test/configCases/externals/resolve/node_modules/external-promise.js b/test/configCases/externals/resolve/node_modules/external-promise.js new file mode 100644 index 000000000..e69de29bb diff --git a/test/configCases/externals/resolve/webpack.config.js b/test/configCases/externals/resolve/webpack.config.js index fc61b5b07..96db94acd 100644 --- a/test/configCases/externals/resolve/webpack.config.js +++ b/test/configCases/externals/resolve/webpack.config.js @@ -5,7 +5,10 @@ module.exports = { }, externals: [ async ({ context, request, getResolve }) => { - if (request !== "external") return false; + if (request !== "external" && request !== "external-promise") { + return false; + } + const resolve = getResolve(); const resolved = await resolve(context, request); return `var ${JSON.stringify(resolved)}`; diff --git a/types.d.ts b/types.d.ts index ca2d4f9d1..2b624c452 100644 --- a/types.d.ts +++ b/types.d.ts @@ -4791,7 +4791,7 @@ declare interface ExternalItemFunctionData { | (( context: string, request: string, - callback: (err?: Error, result?: string) => void + callback: (err?: null | Error, result?: string | false) => void ) => void) | ((context: string, request: string) => Promise); @@ -9026,7 +9026,7 @@ declare interface ModuleFactoryCreateData { declare interface ModuleFactoryCreateDataContextInfo { issuer: string; issuerLayer?: null | string; - compiler: string; + compiler?: string; } declare interface ModuleFactoryResult { /**