| | |
| | | const { RawSource } = require("webpack-sources"); |
| | | const Compilation = require("./Compilation"); |
| | | const HotUpdateChunk = require("./HotUpdateChunk"); |
| | | const createSchemaValidation = require("./util/create-schema-validation"); |
| | | |
| | | /** @typedef {import("./Compiler")} Compiler */ |
| | | /** @typedef {import("./Chunk")} Chunk */ |
| | |
| | | /** @typedef {import("../declarations/plugins/ManifestPlugin").ManifestEntrypoint} ManifestEntrypoint */ |
| | | /** @typedef {import("../declarations/plugins/ManifestPlugin").ManifestItem} ManifestItem */ |
| | | |
| | | /** @typedef {(item: ManifestItem) => boolean} Filter */ |
| | | /** @typedef {(manifest: ManifestObject) => ManifestObject} Generate */ |
| | | /** @typedef {(manifest: ManifestObject) => string} Serialize */ |
| | | |
| | | const PLUGIN_NAME = "ManifestPlugin"; |
| | | |
| | | const validate = createSchemaValidation( |
| | | require("../schemas/plugins/ManifestPlugin.check"), |
| | | () => require("../schemas/plugins/ManifestPlugin.json"), |
| | | { |
| | | name: "ManifestPlugin", |
| | | baseDataPath: "options" |
| | | } |
| | | ); |
| | | |
| | | /** |
| | | * Returns extname. |
| | | * @param {string} filename filename |
| | | * @returns {string} extname |
| | | */ |
| | |
| | | const split = replaced.split("."); |
| | | const last = split.pop(); |
| | | if (!last) return ""; |
| | | return last && /^(gz|br|map)$/i.test(last) ? `${split.pop()}.${last}` : last; |
| | | return last && /^(?:gz|br|map)$/i.test(last) |
| | | ? `${split.pop()}.${last}` |
| | | : last; |
| | | }; |
| | | |
| | | const DEFAULT_PREFIX = "[publicpath]"; |
| | | const DEFAULT_FILENAME = "manifest.json"; |
| | | |
| | | class ManifestPlugin { |
| | | /** |
| | | * Creates an instance of ManifestPlugin. |
| | | * @param {ManifestPluginOptions} options options |
| | | */ |
| | | constructor(options) { |
| | | validate(options); |
| | | |
| | | /** @type {ManifestPluginOptions & Required<Omit<ManifestPluginOptions, "filter" | "generate">>} */ |
| | | this.options = { |
| | | filename: "manifest.json", |
| | | prefix: "[publicpath]", |
| | | entrypoints: true, |
| | | serialize: (manifest) => JSON.stringify(manifest, null, 2), |
| | | ...options |
| | | }; |
| | | constructor(options = {}) { |
| | | /** @type {ManifestPluginOptions} */ |
| | | this.options = options; |
| | | } |
| | | |
| | | /** |
| | | * Apply the plugin |
| | | * Applies the plugin by registering its hooks on the compiler. |
| | | * @param {Compiler} compiler the compiler instance |
| | | * @returns {void} |
| | | */ |
| | | apply(compiler) { |
| | | compiler.hooks.validate.tap(PLUGIN_NAME, () => { |
| | | compiler.validate( |
| | | () => require("../schemas/plugins/ManifestPlugin.json"), |
| | | this.options, |
| | | { |
| | | name: "ManifestPlugin", |
| | | baseDataPath: "options" |
| | | }, |
| | | (options) => require("../schemas/plugins/ManifestPlugin.check")(options) |
| | | ); |
| | | }); |
| | | |
| | | const entrypoints = |
| | | this.options.entrypoints !== undefined ? this.options.entrypoints : true; |
| | | const serialize = |
| | | this.options.serialize || |
| | | ((manifest) => JSON.stringify(manifest, null, 2)); |
| | | |
| | | compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => { |
| | | compilation.hooks.processAssets.tap( |
| | | { |
| | |
| | | ); |
| | | |
| | | /** |
| | | * Creates a hash reg exp. |
| | | * @param {string | string[]} value value |
| | | * @returns {RegExp} regexp to remove hash |
| | | */ |
| | |
| | | ); |
| | | |
| | | /** |
| | | * Removes the provided name from the manifest plugin. |
| | | * @param {string} name name |
| | | * @param {AssetInfo | null} info asset info |
| | | * @returns {string} hash removed name |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Returns chunk name or chunk id. |
| | | * @param {Chunk} chunk chunk |
| | | * @returns {ChunkName | ChunkId} chunk name or chunk id |
| | | */ |
| | |
| | | /** @type {ManifestObject} */ |
| | | let manifest = {}; |
| | | |
| | | if (this.options.entrypoints) { |
| | | if (entrypoints) { |
| | | /** @type {ManifestObject["entrypoints"]} */ |
| | | const entrypoints = {}; |
| | | |
| | | for (const [name, entrypoint] of compilation.entrypoints) { |
| | | /** @type {string[]} */ |
| | | const imports = []; |
| | | |
| | | for (const chunk of entrypoint.chunks) { |
| | |
| | | const added = new Set(); |
| | | |
| | | /** |
| | | * Processes the provided file. |
| | | * @param {string} file file |
| | | * @param {string=} usedName usedName |
| | | * @returns {void} |
| | |
| | | // Fallback for unofficial plugins, just remove hash from filename |
| | | removeHash(file, asset.info); |
| | | |
| | | const prefix = this.options.prefix.replace( |
| | | const prefix = (this.options.prefix || DEFAULT_PREFIX).replace( |
| | | /\[publicpath\]/gi, |
| | | () => (publicPath === "auto" ? "/" : publicPath) |
| | | ); |
| | |
| | | } |
| | | |
| | | compilation.emitAsset( |
| | | this.options.filename, |
| | | new RawSource(this.options.serialize(manifest)), |
| | | this.options.filename || DEFAULT_FILENAME, |
| | | new RawSource(serialize(manifest)), |
| | | { manifest: true } |
| | | ); |
| | | } |