webpack
Version:
Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
184 lines (171 loc) • 5.14 kB
JavaScript
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
;
const util = require("util");
const ExternalModule = require("./ExternalModule");
/** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
/** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9]+ /;
// TODO webpack 6 remove this
const callDeprecatedExternals = util.deprecate(
(externalsFunction, context, request, cb) => {
externalsFunction.call(null, context, request, cb);
},
"The externals-function should be defined like ({context, request}, cb) => { ... }",
"DEP_WEBPACK_EXTERNALS_FUNCTION_PARAMETERS"
);
class ExternalModuleFactoryPlugin {
/**
* @param {string | undefined} type default external type
* @param {Externals} externals externals config
*/
constructor(type, externals) {
this.type = type;
this.externals = externals;
}
/**
* @param {NormalModuleFactory} normalModuleFactory the normal module factory
* @returns {void}
*/
apply(normalModuleFactory) {
const globalType = this.type;
normalModuleFactory.hooks.factorize.tapAsync(
"ExternalModuleFactoryPlugin",
(data, callback) => {
const context = data.context;
const dependency = data.dependencies[0];
/**
* @param {string|string[]|boolean|Record<string, string|string[]>} value the external config
* @param {string|undefined} type type of external
* @param {function(Error=, ExternalModule=): void} callback callback
* @returns {void}
*/
const handleExternal = (value, type, callback) => {
if (value === false) {
// Not externals, fallback to original factory
return callback();
}
/** @type {string | string[] | Record<string, string|string[]>} */
let externalConfig;
if (value === true) {
externalConfig = dependency.request;
} else {
externalConfig = value;
}
// When no explicit type is specified, extract it from the externalConfig
if (type === undefined) {
if (
typeof externalConfig === "string" &&
UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
) {
const idx = externalConfig.indexOf(" ");
type = externalConfig.substr(0, idx);
externalConfig = externalConfig.substr(idx + 1);
} else if (
Array.isArray(externalConfig) &&
externalConfig.length > 0 &&
UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig[0])
) {
const firstItem = externalConfig[0];
const idx = firstItem.indexOf(" ");
type = firstItem.substr(0, idx);
externalConfig = [
firstItem.substr(idx + 1),
...externalConfig.slice(1)
];
}
}
callback(
null,
new ExternalModule(
externalConfig,
type || globalType,
dependency.request
)
);
};
/**
* @param {Externals} externals externals config
* @param {function(Error=, ExternalModule=): void} callback callback
* @returns {void}
*/
const handleExternals = (externals, callback) => {
if (typeof externals === "string") {
if (externals === dependency.request) {
return handleExternal(dependency.request, undefined, callback);
}
} else if (Array.isArray(externals)) {
let i = 0;
const next = () => {
let asyncFlag;
const handleExternalsAndCallback = (err, module) => {
if (err) return callback(err);
if (!module) {
if (asyncFlag) {
asyncFlag = false;
return;
}
return next();
}
callback(null, module);
};
do {
asyncFlag = true;
if (i >= externals.length) return callback();
handleExternals(externals[i++], handleExternalsAndCallback);
} while (!asyncFlag);
asyncFlag = false;
};
next();
return;
} else if (externals instanceof RegExp) {
if (externals.test(dependency.request)) {
return handleExternal(dependency.request, undefined, callback);
}
} else if (typeof externals === "function") {
const cb = (err, value, type) => {
if (err) return callback(err);
if (value !== undefined) {
handleExternal(value, type, callback);
} else {
callback();
}
};
if (externals.length === 3) {
// TODO webpack 6 remove this
callDeprecatedExternals(
externals,
context,
dependency.request,
cb
);
} else {
externals(
{
context,
request: dependency.request
},
cb
);
}
return;
} else if (
typeof externals === "object" &&
Object.prototype.hasOwnProperty.call(externals, dependency.request)
) {
return handleExternal(
externals[dependency.request],
undefined,
callback
);
}
callback();
};
handleExternals(this.externals, callback);
}
);
}
}
module.exports = ExternalModuleFactoryPlugin;