diff --git a/declarations/plugins/ManifestPlugin.d.ts b/declarations/plugins/ManifestPlugin.d.ts new file mode 100644 index 000000000..3af25d7bf --- /dev/null +++ b/declarations/plugins/ManifestPlugin.d.ts @@ -0,0 +1,38 @@ +/* + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn fix:special` to update + */ + +/** + * A function that receives the manifest object and returns the manifest string. + */ +export type HandlerFunction = (manifest: ManifestObject) => string; +/** + * Maps asset identifiers to their manifest entries. + */ +export type ManifestObject = Record; + +export interface ManifestPluginOptions { + /** + * Specifies the filename of the output file on disk. By default the plugin will emit `manifest.json` inside the 'output.path' directory. + */ + filename?: string; + /** + * A function that receives the manifest object and returns the manifest string. + */ + handler?: HandlerFunction; +} +/** + * Describes a manifest entry that links the emitted path to the producing asset. + */ +export interface ManifestItem { + /** + * The compilation asset that produced this manifest entry. + */ + asset?: import("../../lib/Compilation").Asset; + /** + * The public path recorded in the manifest for this asset. + */ + filePath: string; +} diff --git a/examples/manifest-plugin/README.md b/examples/manifest-plugin/README.md new file mode 100644 index 000000000..dff6c9055 --- /dev/null +++ b/examples/manifest-plugin/README.md @@ -0,0 +1,149 @@ +This example demonstrates how to use webpack internal ManifestPlugin. + +# example.js + +```js +import("./baz"); +``` + +# foo.txt + +```js +foo +``` + +# bar.txt + +```js +bar +``` + +# baz.js + +```js +import foo from "./foo.txt"; +import bar from "./bar.txt"; + +export default foo + bar; +``` + +# webpack.config.js + +```javascript +"use strict"; + +const webpack = require("../../"); + +/** @type {webpack.Configuration} */ +module.exports = { + devtool: "source-map", + module: { + rules: [ + { + test: /foo.txt/, + type: "asset/resource" + }, + { + test: /bar.txt/, + use: require.resolve("file-loader") + } + ] + }, + plugins: [ + new webpack.ManifestPlugin({ + filename: "manifest.json" + }), + new webpack.ManifestPlugin({ + filename: "manifest.yml", + handler(manifest) { + let _manifest = ""; + for (const key in manifest) { + if (key === "manifest.json") continue; + _manifest += `- ${key}: '${manifest[key].filePath}'\n`; + } + return _manifest; + } + }) + ] +}; +``` + +# dist/manifest.json + +```json +{ + "output.js.map": "dist/output.js.map", + "main.js": "dist/output.js", + "bar.txt": "dist/a0145fafc7fab801e574631452de554b.txt", + "foo.txt": "dist/3ee037f347c64cc372ad.txt", + "1.output.js.map": "dist/1.output.js.map", + "1.output.js": "dist/1.output.js" +} +``` + +# dist/manifest.yml + +```yml +- output.js.map: 'dist/output.js.map' +- main.js: 'dist/output.js' +- bar.txt: 'dist/a0145fafc7fab801e574631452de554b.txt' +- foo.txt: 'dist/3ee037f347c64cc372ad.txt' +- 1.output.js.map: 'dist/1.output.js.map' +- 1.output.js: 'dist/1.output.js' +``` + +# Info + +## Unoptimized + +``` +assets by path *.js 11.9 KiB + asset output.js 9.61 KiB [emitted] (name: main) 1 related asset + asset 1.output.js 2.3 KiB [emitted] 1 related asset +assets by path *.txt 8 bytes + asset 3ee037f347c64cc372ad.txt 4 bytes [emitted] [immutable] [from: foo.txt] + asset a0145fafc7fab801e574631452de554b.txt 4 bytes [emitted] [immutable] [from: bar.txt] +asset manifest.json 260 bytes [emitted] +asset manifest.yml 240 bytes [emitted] +chunk (runtime: main) output.js (main) 17 bytes (javascript) 5.48 KiB (runtime) [entry] [rendered] + > ./example.js main + runtime modules 5.48 KiB 8 modules + ./example.js 17 bytes [built] [code generated] + [used exports unknown] + entry ./example.js main +chunk (runtime: main) 1.output.js 207 bytes (javascript) 4 bytes (asset) [rendered] + > ./baz ./example.js 1:0-15 + dependent modules 122 bytes (javascript) 4 bytes (asset) [dependent] 2 modules + ./baz.js 85 bytes [built] [code generated] + [exports: default] + [used exports unknown] + import() ./baz ./example.js 1:0-15 +webpack X.X.X compiled successfully +``` + +## Production mode + +``` +assets by path *.js 2.17 KiB + asset output.js 1.94 KiB [emitted] [minimized] (name: main) 1 related asset + asset 293.output.js 237 bytes [emitted] [minimized] 1 related asset +assets by path *.txt 8 bytes + asset 3ee037f347c64cc372ad.txt 4 bytes [emitted] [immutable] [from: foo.txt] + asset a0145fafc7fab801e574631452de554b.txt 4 bytes [emitted] [immutable] [from: bar.txt] +asset manifest.json 268 bytes [emitted] +asset manifest.yml 248 bytes [emitted] +chunk (runtime: main) 293.output.js 4 bytes (asset) 249 bytes (javascript) [rendered] + > ./baz ./example.js 1:0-15 + ./baz.js + 2 modules 207 bytes [built] [code generated] + [exports: default] + import() ./baz ./example.js 1:0-15 + ./foo.txt 4 bytes (asset) 42 bytes (javascript) [built] [code generated] + [no exports] +chunk (runtime: main) output.js (main) 17 bytes (javascript) 5.48 KiB (runtime) [entry] [rendered] + > ./example.js main + runtime modules 5.48 KiB 8 modules + ./example.js 17 bytes [built] [code generated] + [no exports used] + entry ./example.js main +webpack X.X.X compiled successfully +``` diff --git a/examples/manifest-plugin/bar.txt b/examples/manifest-plugin/bar.txt new file mode 100644 index 000000000..5716ca598 --- /dev/null +++ b/examples/manifest-plugin/bar.txt @@ -0,0 +1 @@ +bar diff --git a/examples/manifest-plugin/baz.js b/examples/manifest-plugin/baz.js new file mode 100644 index 000000000..5a75046c3 --- /dev/null +++ b/examples/manifest-plugin/baz.js @@ -0,0 +1,4 @@ +import foo from "./foo.txt"; +import bar from "./bar.txt"; + +export default foo + bar; diff --git a/examples/manifest-plugin/build.js b/examples/manifest-plugin/build.js new file mode 100644 index 000000000..2e93fe5a3 --- /dev/null +++ b/examples/manifest-plugin/build.js @@ -0,0 +1 @@ +require("../build-common"); diff --git a/examples/manifest-plugin/example.js b/examples/manifest-plugin/example.js new file mode 100644 index 000000000..bb59c7083 --- /dev/null +++ b/examples/manifest-plugin/example.js @@ -0,0 +1 @@ +import("./baz"); diff --git a/examples/manifest-plugin/foo.txt b/examples/manifest-plugin/foo.txt new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/examples/manifest-plugin/foo.txt @@ -0,0 +1 @@ +foo diff --git a/examples/manifest-plugin/template.md b/examples/manifest-plugin/template.md new file mode 100644 index 000000000..a791ca330 --- /dev/null +++ b/examples/manifest-plugin/template.md @@ -0,0 +1,57 @@ +This example demonstrates how to use webpack internal ManifestPlugin. + +# example.js + +```js +_{{example.js}}_ +``` + +# foo.txt + +```js +_{{foo.txt}}_ +``` + +# bar.txt + +```js +_{{bar.txt}}_ +``` + +# baz.js + +```js +_{{baz.js}}_ +``` + +# webpack.config.js + +```javascript +_{{webpack.config.js}}_ +``` + +# dist/manifest.json + +```json +_{{dist/manifest.json}}_ +``` + +# dist/manifest.yml + +```yml +_{{dist/manifest.yml}}_ +``` + +# Info + +## Unoptimized + +``` +_{{stdout}}_ +``` + +## Production mode + +``` +_{{production:stdout}}_ +``` diff --git a/examples/manifest-plugin/webpack.config.js b/examples/manifest-plugin/webpack.config.js new file mode 100644 index 000000000..a3d1a8ce7 --- /dev/null +++ b/examples/manifest-plugin/webpack.config.js @@ -0,0 +1,36 @@ +"use strict"; + +const webpack = require("../../"); + +/** @type {webpack.Configuration} */ +module.exports = { + devtool: "source-map", + module: { + rules: [ + { + test: /foo.txt/, + type: "asset/resource" + }, + { + test: /bar.txt/, + use: require.resolve("file-loader") + } + ] + }, + plugins: [ + new webpack.ManifestPlugin({ + filename: "manifest.json" + }), + new webpack.ManifestPlugin({ + filename: "manifest.yml", + handler(manifest) { + let _manifest = ""; + for (const key in manifest) { + if (key === "manifest.json") continue; + _manifest += `- ${key}: '${manifest[key].filePath}'\n`; + } + return _manifest; + } + }) + ] +}; diff --git a/lib/ManifestPlugin.js b/lib/ManifestPlugin.js new file mode 100644 index 000000000..37a640895 --- /dev/null +++ b/lib/ManifestPlugin.js @@ -0,0 +1,176 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Haijie Xie @hai-x +*/ + +"use strict"; + +const path = require("path"); +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("..").StatsCompilation} StatsCompilation */ +/** @typedef {import("./Chunk")} Chunk */ +/** @typedef {import("./Compilation").Asset} Asset */ +/** @typedef {import("./Module")} Module */ +/** @typedef {import("./NormalModule")} NormalModule */ +/** @typedef {import("./config/defaults").WebpackOptionsNormalizedWithDefaults} WebpackOptions */ + +/** @typedef {import("../declarations/plugins/ManifestPlugin").ManifestPluginOptions} ManifestPluginOptions */ +/** @typedef {import("../declarations/plugins/ManifestPlugin").ManifestObject} ManifestObject */ +/** @typedef {import("../declarations/plugins/ManifestPlugin").ManifestItem} ManifestItem */ + +const PLUGIN_NAME = "ManifestPlugin"; + +const validate = createSchemaValidation( + require("../schemas/plugins/ManifestPlugin.check"), + () => require("../schemas/plugins/ManifestPlugin.json"), + { + name: "ManifestPlugin", + baseDataPath: "options" + } +); + +/** + * @param {string} filename filename + * @returns {string} extname + */ +const extname = (filename) => { + const replaced = filename.replace(/\?.*/, ""); + const split = replaced.split("."); + const last = split.pop(); + if (!last) return ""; + return last && /^(gz|br|map)$/i.test(last) ? `${split.pop()}.${last}` : last; +}; + +class ManifestPlugin { + /** + * @param {ManifestPluginOptions} options options + */ + constructor(options) { + validate(options); + + /** @type {Required} */ + this.options = { + filename: "manifest.json", + handler: (manifest) => this._handleManifest(manifest), + ...options + }; + } + + /** + * @param {ManifestObject} manifest manifest object + * @returns {string} manifest content + */ + _handleManifest(manifest) { + return JSON.stringify( + Object.keys(manifest).reduce((acc, cur) => { + acc[cur] = manifest[cur].filePath; + return acc; + }, /** @type {Record} */ ({})), + null, + 2 + ); + } + + /** + * Apply the plugin + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => { + compilation.hooks.processAssets.tap( + { + name: PLUGIN_NAME, + stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE + }, + () => { + const assets = compilation.getAssets(); + const hashDigestLength = compilation.outputOptions.hashDigestLength; + const publicPath = compilation.getPath( + compilation.outputOptions.publicPath + ); + + /** @type {Set} */ + const added = new Set(); + /** @type {ManifestObject} */ + const manifest = {}; + + /** + * @param {string} name name + * @returns {string} hash removed name + */ + const removeHash = (name) => { + // Handles hashes that match configured `hashDigestLength` + // i.e. index.XXXX.html -> index.html (html-webpack-plugin) + if (hashDigestLength <= 0) return name; + const reg = new RegExp( + `(\\.[a-f0-9]{${hashDigestLength},32})(?=\\.)`, + "gi" + ); + return name.replace(reg, ""); + }; + + /** + * @param {string} file file + * @param {((file: string) => string)=} namer namer + * @returns {void} + */ + const handleFile = (file, namer) => { + if (added.has(file)) return; + added.add(file); + + let name = namer ? namer(file) : file; + const asset = compilation.getAsset(file); + if (asset && asset.info.sourceFilename) { + name = path.join( + path.dirname(file), + path.basename(asset.info.sourceFilename) + ); + } + manifest[removeHash(name)] = { + filePath: publicPath + ? publicPath + + (publicPath.endsWith("/") ? `${file}` : `/${file}`) + : file, + asset + }; + }; + + for (const chunk of compilation.chunks) { + if (chunk instanceof HotUpdateChunk) continue; + + const chunkName = chunk.name; + for (const auxiliaryFile of chunk.auxiliaryFiles) { + handleFile(auxiliaryFile, (file) => path.basename(file)); + } + for (const file of chunk.files) { + handleFile(file, (file) => { + if (chunkName) return `${chunkName}.${extname(file)}`; + return file; + }); + } + } + + for (const asset of assets) { + if (asset.info.hotModuleReplacement) { + continue; + } + handleFile(asset.name); + } + + compilation.emitAsset( + this.options.filename, + new RawSource(this.options.handler(manifest)) + ); + } + ); + }); + } +} + +module.exports = ManifestPlugin; diff --git a/lib/index.js b/lib/index.js index a8ef0a82d..b655320ff 100644 --- a/lib/index.js +++ b/lib/index.js @@ -355,6 +355,9 @@ module.exports = mergeExports(fn, { get Stats() { return require("./Stats"); }, + get ManifestPlugin() { + return require("./ManifestPlugin"); + }, get Template() { return require("./Template"); }, diff --git a/schemas/plugins/ManifestPlugin.check.d.ts b/schemas/plugins/ManifestPlugin.check.d.ts new file mode 100644 index 000000000..706a74d49 --- /dev/null +++ b/schemas/plugins/ManifestPlugin.check.d.ts @@ -0,0 +1,7 @@ +/* + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn fix:special` to update + */ +declare const check: (options: import("../../declarations/plugins/ManifestPlugin").ManifestPluginOptions) => boolean; +export = check; diff --git a/schemas/plugins/ManifestPlugin.check.js b/schemas/plugins/ManifestPlugin.check.js new file mode 100644 index 000000000..1258beaf3 --- /dev/null +++ b/schemas/plugins/ManifestPlugin.check.js @@ -0,0 +1,6 @@ +/* + * This file was automatically generated. + * DO NOT MODIFY BY HAND. + * Run `yarn fix:special` to update + */ +const r=/^(?:[A-Za-z]:[\\/]|\\\\|\/)/;function e(t,{instancePath:n="",parentData:a,parentDataProperty:s,rootData:o=t}={}){let i=null,l=0;if(0===l){if(!t||"object"!=typeof t||Array.isArray(t))return e.errors=[{params:{type:"object"}}],!1;{const n=l;for(const r in t)if("filename"!==r&&"handler"!==r)return e.errors=[{params:{additionalProperty:r}}],!1;if(n===l){if(void 0!==t.filename){let n=t.filename;const a=l;if(l===a){if("string"!=typeof n)return e.errors=[{params:{type:"string"}}],!1;if(n.includes("!")||!1!==r.test(n))return e.errors=[{params:{}}],!1;if(n.length<1)return e.errors=[{params:{}}],!1}var f=a===l}else f=!0;if(f)if(void 0!==t.handler){const r=l,n=l;let a=!1,s=null;const o=l;if(!(t.handler instanceof Function)){const r={params:{}};null===i?i=[r]:i.push(r),l++}if(o===l&&(a=!0,s=0),!a){const r={params:{passingSchemas:s}};return null===i?i=[r]:i.push(r),l++,e.errors=i,!1}l=n,null!==i&&(n?i.length=n:i=null),f=r===l}else f=!0}}}return e.errors=i,0===l}module.exports=e,module.exports.default=e; \ No newline at end of file diff --git a/schemas/plugins/ManifestPlugin.json b/schemas/plugins/ManifestPlugin.json new file mode 100644 index 000000000..de261b769 --- /dev/null +++ b/schemas/plugins/ManifestPlugin.json @@ -0,0 +1,51 @@ +{ + "definitions": { + "HandlerFunction": { + "description": "A function that receives the manifest object and returns the manifest string.", + "instanceof": "Function", + "tsType": "(manifest: ManifestObject) => string" + }, + "ManifestItem": { + "description": "Describes a manifest entry that links the emitted path to the producing asset.", + "type": "object", + "additionalProperties": false, + "properties": { + "asset": { + "description": "The compilation asset that produced this manifest entry.", + "tsType": "import('../../lib/Compilation').Asset" + }, + "filePath": { + "description": "The public path recorded in the manifest for this asset.", + "type": "string" + } + }, + "required": ["filePath"] + }, + "ManifestObject": { + "description": "Maps asset identifiers to their manifest entries.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ManifestItem" + }, + "tsType": "Record" + } + }, + "title": "ManifestPluginOptions", + "type": "object", + "additionalProperties": false, + "properties": { + "filename": { + "description": "Specifies the filename of the output file on disk. By default the plugin will emit `manifest.json` inside the 'output.path' directory.", + "type": "string", + "absolutePath": false, + "minLength": 1 + }, + "handler": { + "oneOf": [ + { + "$ref": "#/definitions/HandlerFunction" + } + ] + } + } +} diff --git a/test/configCases/plugins/manifest-plugin/file.txt b/test/configCases/plugins/manifest-plugin/file.txt new file mode 100644 index 000000000..f73f3093f --- /dev/null +++ b/test/configCases/plugins/manifest-plugin/file.txt @@ -0,0 +1 @@ +file diff --git a/test/configCases/plugins/manifest-plugin/index-2.js b/test/configCases/plugins/manifest-plugin/index-2.js new file mode 100644 index 000000000..24ed9700a --- /dev/null +++ b/test/configCases/plugins/manifest-plugin/index-2.js @@ -0,0 +1,30 @@ +import fs from "fs"; +import path from "path"; +import url from "../../asset-modules/_images/file.png"; + +import(/* webpackChunkName: 'file' */ "./file.txt?foo"); + +it("should emit manifest with expected entries and paths with function publicPath", () => { + expect(url).toEqual("/dist/file-loader.png"); + + const manifest = JSON.parse( + fs.readFileSync(path.resolve(__dirname, "bar.json"), "utf-8") + ); + + const keys = Object.keys(manifest).sort(); + expect(keys).toEqual( + [ + "file.js", + "file.txt?foo", + "main.js", + "third.party.js", + "file.png" + ].sort() + ); + + expect(manifest["main.js"]).toMatch(/\/dist\/bundle1\.js/); + expect(manifest["file.js"]).toMatch(/\/dist\/file\.[a-f0-9]+\.js/); + expect(manifest["file.txt?foo"]).toMatch(/\/dist\/file\.[a-f0-9]+\.txt\?foo/); + expect(manifest["third.party.js"]).toBe("/dist/third.party.js"); + expect(manifest["file.png"]).toBe("/dist/file-loader.png"); +}); diff --git a/test/configCases/plugins/manifest-plugin/index.js b/test/configCases/plugins/manifest-plugin/index.js new file mode 100644 index 000000000..c033985a0 --- /dev/null +++ b/test/configCases/plugins/manifest-plugin/index.js @@ -0,0 +1,30 @@ +import fs from "fs"; +import path from "path"; +import url from "../../asset-modules/_images/file.png"; + +import(/* webpackChunkName: 'file' */ "./file.txt?foo"); + +it("should emit manifest with expected entries and paths with string publicPath", () => { + expect(url).toEqual("/app/file-loader.png"); + + const manifest = JSON.parse( + fs.readFileSync(path.resolve(__dirname, "foo.json"), "utf-8") + ); + + const keys = Object.keys(manifest).sort(); + expect(keys).toEqual( + [ + "file.js", + "file.txt?foo", + "main.js", + "third.party.js", + "file.png" + ].sort() + ); + + expect(manifest["main.js"]).toMatch(/\/app\/bundle0\.js/); + expect(manifest["file.js"]).toMatch(/\/app\/file\.[a-f0-9]+\.js/); + expect(manifest["file.txt?foo"]).toMatch(/\/app\/file\.[a-f0-9]+\.txt\?foo/); + expect(manifest["third.party.js"]).toBe("/app/third.party.js"); + expect(manifest["file.png"]).toBe("/app/file-loader.png"); +}); diff --git a/test/configCases/plugins/manifest-plugin/infrastructure-log.js b/test/configCases/plugins/manifest-plugin/infrastructure-log.js new file mode 100644 index 000000000..3ed655eb2 --- /dev/null +++ b/test/configCases/plugins/manifest-plugin/infrastructure-log.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = [ + // each time returns different OriginalSource in webpack.config.js:33 + // this prevents hit in inmemory cache + /^Pack got invalid because of write to: RealContentHashPlugin|analyse|third.party.js$/, + /^Pack got invalid because of write to: RealContentHashPlugin|analyse|third.party.js$/ +]; diff --git a/test/configCases/plugins/manifest-plugin/webpack.config.js b/test/configCases/plugins/manifest-plugin/webpack.config.js new file mode 100644 index 000000000..5aad1590f --- /dev/null +++ b/test/configCases/plugins/manifest-plugin/webpack.config.js @@ -0,0 +1,96 @@ +"use strict"; + +const { RawSource } = require("webpack-sources"); +const webpack = require("../../../../"); + +/** @typedef {import("../../../../lib/Compiler")} Compiler */ + +class CopyPlugin { + /** + * Apply the plugin + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + const hookOptions = { + name: "MockCopyPlugin", + stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS + }; + + compiler.hooks.thisCompilation.tap(hookOptions, (compilation) => { + compilation.hooks.processAssets.tap(hookOptions, () => { + const output = "// some compilation result\n"; + compilation.emitAsset("third.party.js", new RawSource(output)); + }); + }); + } +} + +/** @type {import("../../../../").Configuration[]} */ +module.exports = [ + { + node: { + __dirname: false, + __filename: false + }, + output: { + publicPath: "/app/", + chunkFilename: "[name].[contenthash].js", + assetModuleFilename: "[name].[contenthash][ext][query]" + }, + plugins: [ + new CopyPlugin(), + new webpack.ManifestPlugin({ + filename: "foo.json" + }) + ], + module: { + rules: [ + { + test: /\.txt$/, + type: "asset/resource" + }, + { + test: /\.png$/, + loader: "file-loader", + options: { + name: "file-loader.[ext]" + } + } + ] + } + }, + { + entry: "./index-2.js", + node: { + __dirname: false, + __filename: false + }, + output: { + publicPath: (_data) => "/dist/", + chunkFilename: "[name].[contenthash].js", + assetModuleFilename: "[name].[contenthash][ext][query]" + }, + plugins: [ + new CopyPlugin(), + new webpack.ManifestPlugin({ + filename: "bar.json" + }) + ], + module: { + rules: [ + { + test: /\.txt$/, + type: "asset/resource" + }, + { + test: /\.png$/, + loader: "file-loader", + options: { + name: "file-loader.[ext]" + } + } + ] + } + } +]; diff --git a/test/configCases/process-assets/html-plugin/infrastructure-log.js b/test/configCases/process-assets/html-plugin/infrastructure-log.js index c519ba1a3..c375cd598 100644 --- a/test/configCases/process-assets/html-plugin/infrastructure-log.js +++ b/test/configCases/process-assets/html-plugin/infrastructure-log.js @@ -1,7 +1,7 @@ "use strict"; module.exports = [ - // each time returns different OriginalSource in webpack.config.js:78 + // each time returns different OriginalSource in webpack.config.js:108 // this prevents hit in inmemory cache /^Pack got invalid because of write to: RealContentHashPlugin|analyse|index\.html$/ ]; diff --git a/types.d.ts b/types.d.ts index 2935c9928..7386857db 100644 --- a/types.d.ts +++ b/types.d.ts @@ -9927,6 +9927,44 @@ declare interface MakeDirectoryOptions { recursive?: boolean; mode?: string | number; } + +/** + * Describes a manifest entry that links the emitted path to the producing asset. + */ +declare interface ManifestItem { + /** + * The compilation asset that produced this manifest entry. + */ + asset?: Asset; + + /** + * The public path recorded in the manifest for this asset. + */ + filePath: string; +} +declare interface ManifestObject { + [index: string]: ManifestItem; +} +declare class ManifestPlugin { + constructor(options: ManifestPluginOptions); + options: Required; + + /** + * Apply the plugin + */ + apply(compiler: Compiler): void; +} +declare interface ManifestPluginOptions { + /** + * Specifies the filename of the output file on disk. By default the plugin will emit `manifest.json` inside the 'output.path' directory. + */ + filename?: string; + + /** + * A function that receives the manifest object and returns the manifest string. + */ + handler?: (manifest: ManifestObject) => string; +} declare interface MapOptions { /** * need columns? @@ -19344,6 +19382,7 @@ declare namespace exports { EntryPlugin as SingleEntryPlugin, SourceMapDevToolPlugin, Stats, + ManifestPlugin, Template, WatchIgnorePlugin, WebpackError,