webpack/lib/asset/AssetModulesPlugin.js

251 lines
7.3 KiB
JavaScript
Raw Normal View History

/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Yuta Hiroto @hiroppy
*/
"use strict";
const {
ASSET_MODULE_TYPE_RESOURCE,
ASSET_MODULE_TYPE_INLINE,
ASSET_MODULE_TYPE,
ASSET_MODULE_TYPE_SOURCE
} = require("../ModuleTypeConstants");
const { cleverMerge } = require("../util/cleverMerge");
const { compareModulesByIdOrIdentifier } = require("../util/comparators");
const createSchemaValidation = require("../util/create-schema-validation");
const memoize = require("../util/memoize");
/** @typedef {import("webpack-sources").Source} Source */
2024-08-09 23:42:37 +08:00
/** @typedef {import("../../declarations/WebpackOptions").AssetParserOptions} AssetParserOptions */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Module")} Module */
2024-08-09 23:42:37 +08:00
/** @typedef {import("../Module").BuildInfo} BuildInfo */
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
/**
* @param {string} name name of definitions
* @returns {TODO} definition
*/
const getSchema = name => {
const { definitions } = require("../../schemas/WebpackOptions.json");
return {
definitions,
oneOf: [{ $ref: `#/definitions/${name}` }]
};
};
const generatorValidationOptions = {
name: "Asset Modules Plugin",
baseDataPath: "generator"
};
const validateGeneratorOptions = {
asset: createSchemaValidation(
require("../../schemas/plugins/asset/AssetGeneratorOptions.check.js"),
() => getSchema("AssetGeneratorOptions"),
generatorValidationOptions
),
"asset/resource": createSchemaValidation(
require("../../schemas/plugins/asset/AssetResourceGeneratorOptions.check.js"),
() => getSchema("AssetResourceGeneratorOptions"),
generatorValidationOptions
),
"asset/inline": createSchemaValidation(
require("../../schemas/plugins/asset/AssetInlineGeneratorOptions.check.js"),
() => getSchema("AssetInlineGeneratorOptions"),
generatorValidationOptions
)
2020-01-23 18:27:12 +08:00
};
const validateParserOptions = createSchemaValidation(
require("../../schemas/plugins/asset/AssetParserOptions.check.js"),
() => getSchema("AssetParserOptions"),
{
name: "Asset Modules Plugin",
baseDataPath: "parser"
}
);
const getAssetGenerator = memoize(() => require("./AssetGenerator"));
const getAssetParser = memoize(() => require("./AssetParser"));
const getAssetSourceParser = memoize(() => require("./AssetSourceParser"));
const getAssetSourceGenerator = memoize(() =>
require("./AssetSourceGenerator")
);
const type = ASSET_MODULE_TYPE;
const plugin = "AssetModulesPlugin";
2019-07-16 19:33:45 +08:00
class AssetModulesPlugin {
/**
2020-04-23 16:48:36 +08:00
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
plugin,
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.createParser
.for(ASSET_MODULE_TYPE)
.tap(plugin, parserOptions => {
validateParserOptions(parserOptions);
parserOptions = cleverMerge(
2024-08-09 23:42:37 +08:00
/** @type {AssetParserOptions} */
(compiler.options.module.parser.asset),
parserOptions
);
2019-11-18 00:49:48 +08:00
let dataUrlCondition = parserOptions.dataUrlCondition;
if (!dataUrlCondition || typeof dataUrlCondition === "object") {
dataUrlCondition = {
maxSize: 8096,
...dataUrlCondition
};
}
const AssetParser = getAssetParser();
return new AssetParser(dataUrlCondition);
});
normalModuleFactory.hooks.createParser
.for(ASSET_MODULE_TYPE_INLINE)
2024-08-09 23:42:37 +08:00
.tap(plugin, _parserOptions => {
const AssetParser = getAssetParser();
return new AssetParser(true);
});
normalModuleFactory.hooks.createParser
.for(ASSET_MODULE_TYPE_RESOURCE)
2024-08-09 23:42:37 +08:00
.tap(plugin, _parserOptions => {
const AssetParser = getAssetParser();
return new AssetParser(false);
});
2019-11-26 21:05:07 +08:00
normalModuleFactory.hooks.createParser
.for(ASSET_MODULE_TYPE_SOURCE)
2024-08-09 23:42:37 +08:00
.tap(plugin, _parserOptions => {
const AssetSourceParser = getAssetSourceParser();
return new AssetSourceParser();
});
for (const type of [
ASSET_MODULE_TYPE,
ASSET_MODULE_TYPE_INLINE,
ASSET_MODULE_TYPE_RESOURCE
]) {
normalModuleFactory.hooks.createGenerator
.for(type)
.tap(plugin, generatorOptions => {
validateGeneratorOptions[type](generatorOptions);
2024-07-31 06:15:03 +08:00
let dataUrl;
if (type !== ASSET_MODULE_TYPE_RESOURCE) {
2020-01-29 21:48:46 +08:00
dataUrl = generatorOptions.dataUrl;
if (!dataUrl || typeof dataUrl === "object") {
dataUrl = {
encoding: undefined,
2020-01-29 21:48:46 +08:00
mimetype: undefined,
...dataUrl
};
}
}
2024-07-31 06:15:03 +08:00
let filename;
let publicPath;
let outputPath;
if (type !== ASSET_MODULE_TYPE_INLINE) {
2020-01-29 21:48:46 +08:00
filename = generatorOptions.filename;
2021-03-16 02:03:19 +08:00
publicPath = generatorOptions.publicPath;
outputPath = generatorOptions.outputPath;
}
const AssetGenerator = getAssetGenerator();
return new AssetGenerator(
compilation.moduleGraph,
dataUrl,
filename,
2021-03-16 02:03:19 +08:00
publicPath,
outputPath,
generatorOptions.emit !== false
);
});
}
2019-11-26 21:05:07 +08:00
normalModuleFactory.hooks.createGenerator
.for(ASSET_MODULE_TYPE_SOURCE)
.tap(plugin, () => {
const AssetSourceGenerator = getAssetSourceGenerator();
2024-10-12 20:22:36 +08:00
return new AssetSourceGenerator(compilation.moduleGraph);
});
compilation.hooks.renderManifest.tap(plugin, (result, options) => {
const { chunkGraph } = compilation;
2020-07-17 20:57:11 +08:00
const { chunk, codeGenerationResults } = options;
const modules = chunkGraph.getOrderedChunkModulesIterableBySourceType(
chunk,
ASSET_MODULE_TYPE,
compareModulesByIdOrIdentifier(chunkGraph)
);
if (modules) {
for (const module of modules) {
try {
const codeGenResult = codeGenerationResults.get(
module,
chunk.runtime
);
2024-08-09 23:42:37 +08:00
const buildInfo = /** @type {BuildInfo} */ (module.buildInfo);
const data =
/** @type {NonNullable<CodeGenerationResult["data"]>} */
(codeGenResult.data);
const errored = module.getNumberOfErrors() > 0;
result.push({
2024-08-09 23:42:37 +08:00
render: () =>
/** @type {Source} */ (codeGenResult.sources.get(type)),
filename: errored
? module.nameForCondition()
: buildInfo.filename || data.get("filename"),
2024-08-09 23:42:37 +08:00
info: buildInfo.assetInfo || data.get("assetInfo"),
auxiliary: true,
identifier: `assetModule${chunkGraph.getModuleId(module)}`,
hash: errored
? chunkGraph.getModuleHash(module, chunk.runtime)
: buildInfo.fullContentHash || data.get("fullContentHash")
});
2024-07-31 15:37:05 +08:00
} catch (err) {
/** @type {Error} */ (err).message +=
2024-01-14 09:41:34 +08:00
`\nduring rendering of asset ${module.identifier()}`;
2024-07-31 15:37:05 +08:00
throw err;
}
}
}
return result;
});
compilation.hooks.prepareModuleExecution.tap(
"AssetModulesPlugin",
(options, context) => {
const { codeGenerationResult } = options;
const source = codeGenerationResult.sources.get(ASSET_MODULE_TYPE);
if (source === undefined) return;
2024-08-09 23:42:37 +08:00
const data =
/** @type {NonNullable<CodeGenerationResult["data"]>} */
(codeGenerationResult.data);
context.assets.set(data.get("filename"), {
source,
2024-08-09 23:42:37 +08:00
info: data.get("assetInfo")
});
}
);
}
);
}
}
module.exports = AssetModulesPlugin;