webpack/lib/container/OverridablesPlugin.js

163 lines
4.6 KiB
JavaScript
Raw Normal View History

2020-02-20 17:52:47 +08:00
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const validateOptions = require("schema-utils");
const schema = require("../../schemas/plugins/container/OverridablesPlugin.json");
2020-02-20 17:52:47 +08:00
const ModuleNotFoundError = require("../ModuleNotFoundError");
const RuntimeGlobals = require("../RuntimeGlobals");
const {
toConstantDependency,
evaluateToString
} = require("../javascript/JavascriptParserHelpers");
const LazySet = require("../util/LazySet");
2020-02-20 17:52:47 +08:00
const OverridableModule = require("./OverridableModule");
const OverridableOriginalDependency = require("./OverridableOriginalDependency");
const OverridablesRuntimeModule = require("./OverridablesRuntimeModule");
2020-02-25 04:12:45 +08:00
const parseOptions = require("./parseOptions");
2020-02-20 17:52:47 +08:00
/** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */
/** @typedef {import("../../declarations/plugins/container/OverridablesPlugin").OverridablesPluginOptions} OverridablesPluginOptions */
2020-02-20 17:52:47 +08:00
/** @typedef {import("../Compiler")} Compiler */
const PLUGIN_NAME = "OverridablesPlugin";
2020-02-20 17:52:47 +08:00
class OverridablesPlugin {
/**
* @param {OverridablesPluginOptions} options options
*/
2020-02-20 17:52:47 +08:00
constructor(options) {
if (typeof options !== "string") {
validateOptions(schema, options, { name: "Overridables Plugin" });
}
this._overridables = parseOptions(options);
2020-02-20 17:52:47 +08:00
}
2020-02-20 17:52:47 +08:00
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
2020-02-20 17:52:47 +08:00
* @returns {void}
*/
apply(compiler) {
compiler.hooks.thisCompilation.tap(
PLUGIN_NAME,
2020-02-20 17:52:47 +08:00
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
OverridableOriginalDependency,
normalModuleFactory
2020-02-20 17:52:47 +08:00
);
const resolvedOverridables = new Map();
const resolveContext = {
/** @type {LazySet<string>} */
fileDependencies: new LazySet(),
/** @type {LazySet<string>} */
contextDependencies: new LazySet(),
/** @type {LazySet<string>} */
missingDependencies: new LazySet()
};
2020-02-20 17:52:47 +08:00
const promise = Promise.all(
this._overridables.map(([key, request]) => {
2020-02-20 17:52:47 +08:00
const resolver = compilation.resolverFactory.get("normal");
return new Promise((resolve, reject) => {
resolver.resolve(
{},
compiler.context,
request,
resolveContext,
2020-02-20 17:52:47 +08:00
(err, result) => {
if (err) {
compilation.errors.push(
new ModuleNotFoundError(null, err, {
name: `overridable ${key}`
})
);
return resolve();
}
resolvedOverridables.set(result, key);
resolve();
}
);
});
})
).then(() => {
compilation.contextDependencies.addAll(
resolveContext.contextDependencies
);
compilation.fileDependencies.addAll(resolveContext.fileDependencies);
compilation.missingDependencies.addAll(
resolveContext.missingDependencies
);
});
2020-02-20 17:52:47 +08:00
normalModuleFactory.hooks.afterResolve.tapAsync(
PLUGIN_NAME,
2020-02-20 17:52:47 +08:00
(resolveData, callback) => {
// wait for resolving to be complete
promise.then(() => callback());
}
);
normalModuleFactory.hooks.module.tap(
PLUGIN_NAME,
(module, createData, resolveData) => {
if (
resolveData.dependencies[0] instanceof
OverridableOriginalDependency
) {
return;
}
2020-02-20 17:52:47 +08:00
const key = resolvedOverridables.get(createData.resource);
if (key !== undefined) {
return new OverridableModule(
resolveData.context,
resolveData.request,
key
);
}
2020-02-20 17:52:47 +08:00
}
);
2020-02-27 05:32:48 +08:00
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.ensureChunkHandlers)
.tap(PLUGIN_NAME, (chunk, set) => {
2020-02-20 17:52:47 +08:00
set.add(RuntimeGlobals.module);
2020-02-25 04:12:45 +08:00
set.add(RuntimeGlobals.moduleFactories);
2020-02-20 17:52:47 +08:00
set.add(RuntimeGlobals.hasOwnProperty);
compilation.addRuntimeModule(
chunk,
new OverridablesRuntimeModule()
);
2020-02-27 05:32:48 +08:00
});
const handler = parser => {
parser.hooks.expression
.for("__webpack_override__")
.tap(
PLUGIN_NAME,
toConstantDependency(
parser,
`Object.assign.bind(Object, ${RuntimeGlobals.overrides})`,
[RuntimeGlobals.overrides]
)
);
parser.hooks.evaluateTypeof
.for("__webpack_override__")
.tap(PLUGIN_NAME, evaluateToString("function"));
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("APIPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("APIPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("APIPlugin", handler);
2020-02-20 17:52:47 +08:00
}
);
}
}
module.exports = OverridablesPlugin;