1 | # Webpack Assets Manifest
|
2 |
|
3 | [![Build Status](https://github.com/webdeveric/webpack-assets-manifest/workflows/Node.js%20CI/badge.svg)](https://github.com/webdeveric/webpack-assets-manifest/actions)
|
4 | [![codecov](https://codecov.io/gh/webdeveric/webpack-assets-manifest/branch/master/graph/badge.svg)](https://codecov.io/gh/webdeveric/webpack-assets-manifest)
|
5 | [![dependencies Status](https://david-dm.org/webdeveric/webpack-assets-manifest/status.svg)](https://david-dm.org/webdeveric/webpack-assets-manifest)
|
6 | [![devDependencies Status](https://david-dm.org/webdeveric/webpack-assets-manifest/dev-status.svg)](https://david-dm.org/webdeveric/webpack-assets-manifest?type=dev)
|
7 |
|
8 | This webpack plugin will generate a JSON file that matches the original filename with the hashed version.
|
9 |
|
10 | ## Installation
|
11 |
|
12 | ```shell
|
13 | npm install webpack-assets-manifest --save-dev
|
14 | ```
|
15 |
|
16 | ## New in version 5
|
17 |
|
18 | * Compatible with webpack 5 only (5.1+ required).
|
19 | * Supports finding [asset modules](https://webpack.js.org/guides/asset-modules/).
|
20 | * Updated options schema to prevent additional properties. This helps with catching typos in option names.
|
21 | * :warning: Updated default value of the `output` option to be `assets-manifest.json`.
|
22 | This is to prevent confusion when working with [Web app manifests](https://developer.mozilla.org/en-US/docs/Web/Manifest) or [WebExtension manifests](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json).
|
23 |
|
24 | ## New in version 4
|
25 |
|
26 | * Requires Node 10+.
|
27 | * Compatible with webpack 4 only (4.40+ required).
|
28 | * Added options: [`enabled`](#enabled), [`entrypointsUseAssets`](#entrypointsUseAssets), [`contextRelativeKeys`](#contextRelativeKeys).
|
29 | * Updated [`writeToDisk`](#writeToDisk) option to default to `auto`.
|
30 | * Use lock files for various operations.
|
31 | * `done` hook is now an `AsyncSeriesHook`.
|
32 | * :warning: The structure of the `entrypoints` data has been updated to include `preload` and `prefetch` assets. Assets for an entrypoint are now included in an `assets` property under the entrypoint.
|
33 |
|
34 | Example:
|
35 |
|
36 | ```json
|
37 | {
|
38 | "entrypoints": {
|
39 | "main": {
|
40 | "assets": {
|
41 | "css": [
|
42 | "main.css"
|
43 | ],
|
44 | "js": [
|
45 | "main.js"
|
46 | ]
|
47 | },
|
48 | "prefetch": {
|
49 | "js": [
|
50 | "prefetch.js"
|
51 | ]
|
52 | },
|
53 | "preload": {
|
54 | "js": [
|
55 | "preload.js"
|
56 | ]
|
57 | }
|
58 | }
|
59 | }
|
60 | }
|
61 | ```
|
62 |
|
63 | ## Usage
|
64 |
|
65 | In your webpack config, require the plugin then add an instance to the `plugins` array.
|
66 |
|
67 | ```js
|
68 | const path = require('path');
|
69 | const WebpackAssetsManifest = require('webpack-assets-manifest');
|
70 |
|
71 | module.exports = {
|
72 | entry: {
|
73 | // Your entry points
|
74 | },
|
75 | output: {
|
76 | path: path.join( __dirname, 'dist' ),
|
77 | filename: '[name]-[hash].js',
|
78 | chunkFilename: '[id]-[chunkhash].js',
|
79 | },
|
80 | module: {
|
81 | // Your loader rules go here.
|
82 | },
|
83 | plugins: [
|
84 | new WebpackAssetsManifest({
|
85 | // Options go here
|
86 | }),
|
87 | ],
|
88 | };
|
89 | ```
|
90 |
|
91 | ## Sample output
|
92 |
|
93 | ```json
|
94 | {
|
95 | "main.js": "main-9c68d5e8de1b810a80e4.js",
|
96 | "main.css": "main-9c68d5e8de1b810a80e4.css",
|
97 | "images/logo.svg": "images/logo-b111da4f34cefce092b965ebc1078ee3.svg"
|
98 | }
|
99 | ```
|
100 |
|
101 | ---
|
102 |
|
103 | ## Options ([read the schema](src/options-schema.json))
|
104 |
|
105 | ### `enabled`
|
106 |
|
107 | Type: `boolean`
|
108 |
|
109 | Default: `true`
|
110 |
|
111 | Is the plugin enabled?
|
112 |
|
113 | ### `output`
|
114 |
|
115 | Type: `string`
|
116 |
|
117 | Default: `assets-manifest.json`
|
118 |
|
119 | This is where to save the manifest file relative to your webpack `output.path`.
|
120 |
|
121 | ### `assets`
|
122 |
|
123 | Type: `object`
|
124 |
|
125 | Default: `{}`
|
126 |
|
127 | Data is stored in this object.
|
128 |
|
129 | #### Sharing data
|
130 |
|
131 | You can share data between instances by passing in your own object in the `assets` option.
|
132 |
|
133 | This is useful in [multi-compiler mode](https://github.com/webpack/webpack/tree/master/examples/multi-compiler).
|
134 |
|
135 | ```js
|
136 | const data = Object.create(null);
|
137 |
|
138 | const manifest1 = new WebpackAssetsManifest({
|
139 | assets: data,
|
140 | });
|
141 |
|
142 | const manifest2 = new WebpackAssetsManifest({
|
143 | assets: data,
|
144 | });
|
145 | ```
|
146 |
|
147 | ### `contextRelativeKeys`
|
148 |
|
149 | Type: `boolean`
|
150 |
|
151 | Default: `false`
|
152 |
|
153 | Keys are relative to the compiler context.
|
154 |
|
155 | ### `space`
|
156 |
|
157 | Type: `int`
|
158 |
|
159 | Default: `2`
|
160 |
|
161 | Number of spaces to use for pretty printing.
|
162 |
|
163 | ### `replacer`
|
164 |
|
165 | Type: `null`, `function`, or `array`
|
166 |
|
167 | Default: `null`
|
168 |
|
169 | [Replacer reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter)
|
170 |
|
171 | You'll probably want to use the `transform` hook instead.
|
172 |
|
173 | ### `fileExtRegex`
|
174 |
|
175 | Type: `regex`
|
176 |
|
177 | Default: `/\.\w{2,4}\.(?:map|gz)$|\.\w+$/i`
|
178 |
|
179 | This is the regular expression used to find file extensions. You'll probably never need to change this.
|
180 |
|
181 | ### `writeToDisk`
|
182 |
|
183 | Type: `boolean`, `string`
|
184 |
|
185 | Default: `'auto'`
|
186 |
|
187 | Write the manifest to disk using `fs`.
|
188 |
|
189 | :warning: If you're using another language for your site and you're using `webpack-dev-server` to process your assets during development,
|
190 | you should set `writeToDisk: true` and provide an absolute path in `output` so the manifest file is actually written to disk and not kept only in memory.
|
191 |
|
192 | ### `sortManifest`
|
193 |
|
194 | Type: `boolean`, `function`
|
195 |
|
196 | Default: `true`
|
197 |
|
198 | The manifest is sorted alphabetically by default. You can turn off sorting by setting `sortManifest: false`.
|
199 |
|
200 | If you want more control over how the manifest is sorted, you can provide your own
|
201 | [comparison function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters).
|
202 | See the [sorted](examples/sorted.js) example.
|
203 |
|
204 | ```js
|
205 | new WebpackAssetsManifest({
|
206 | sortManifest(a, b) {
|
207 | // Return -1, 0, or 1
|
208 | }
|
209 | });
|
210 | ```
|
211 |
|
212 | ### `merge`
|
213 |
|
214 | Type: `boolean`, `string`
|
215 |
|
216 | Default: `false`
|
217 |
|
218 | If the `output` file already exists and you'd like to add to it, use `merge: true`.
|
219 | The default behavior is to use the existing keys/values without modification.
|
220 |
|
221 | ```js
|
222 | new WebpackAssetsManifest({
|
223 | output: '/path/to/manifest.json',
|
224 | merge: true
|
225 | });
|
226 | ```
|
227 |
|
228 | If you need to customize during merge, use `merge: 'customize'`.
|
229 |
|
230 | If you want to know if `customize` was called when merging with an existing manifest, you can check `manifest.isMerging`.
|
231 |
|
232 | ```js
|
233 | new WebpackAssetsManifest({
|
234 | merge: 'customize',
|
235 | customize(entry, original, manifest, asset) {
|
236 | if ( manifest.isMerging ) {
|
237 | // Do something
|
238 | }
|
239 | },
|
240 | }),
|
241 | ```
|
242 |
|
243 | ### `publicPath`
|
244 |
|
245 | Type: `string`, `function`, `boolean`,
|
246 |
|
247 | Default: `null`
|
248 |
|
249 | When using `publicPath: true`, your webpack config `output.publicPath` will be used as the value prefix.
|
250 |
|
251 | ```js
|
252 | const manifest = new WebpackAssetsManifest({
|
253 | publicPath: true,
|
254 | });
|
255 | ```
|
256 |
|
257 | When using a string, it will be the value prefix. One common use is to prefix your CDN URL.
|
258 |
|
259 | ```js
|
260 | const manifest = new WebpackAssetsManifest({
|
261 | publicPath: '//cdn.example.com',
|
262 | });
|
263 | ```
|
264 |
|
265 | If you'd like to have more control, use a function. See the [custom CDN](examples/custom-cdn.js) example.
|
266 |
|
267 | ```js
|
268 | const manifest = new WebpackAssetsManifest({
|
269 | publicPath(filename, manifest)
|
270 | {
|
271 | // customize filename here
|
272 | return filename;
|
273 | }
|
274 | });
|
275 | ```
|
276 |
|
277 | ### `entrypoints`
|
278 |
|
279 | Type: `boolean`
|
280 |
|
281 | Default: `false`
|
282 |
|
283 | Include `compilation.entrypoints` in the manifest file.
|
284 |
|
285 | ### `entrypointsKey`
|
286 |
|
287 | Type: `string`, `boolean`
|
288 |
|
289 | Default: `entrypoints`
|
290 |
|
291 | If this is set to `false`, the `entrypoints` will be added to the root of the manifest.
|
292 |
|
293 | ### `entrypointsUseAssets`
|
294 |
|
295 | Type: `boolean`
|
296 |
|
297 | Default: `false`
|
298 |
|
299 | Entrypoint data should use the value from `assets`, which means the values could be customized and not just a `string` file path.
|
300 | This new option defaults to `false` so the new behavior is opt-in.
|
301 |
|
302 | ### `integrity`
|
303 |
|
304 | Type: `boolean`
|
305 |
|
306 | Default: `false`
|
307 |
|
308 | Include the [subresource integrity hash](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity).
|
309 |
|
310 | ### `integrityHashes`
|
311 |
|
312 | Type: `array`
|
313 |
|
314 | Default: `[ 'sha256', 'sha384', 'sha512' ]`
|
315 |
|
316 | Hash algorithms to use when generating SRI. For browsers, the currently the allowed integrity hashes are `sha256`, `sha384`, and `sha512`.
|
317 |
|
318 | Other hash algorithms can be used if your target environment is not a browser.
|
319 | If you were to create a tool to audit your S3 buckets for
|
320 | [data integrity](https://aws.amazon.com/premiumsupport/knowledge-center/data-integrity-s3/),
|
321 | you could use something like this [example](examples/aws-s3-data-integrity.js) to record the `md5` hashes.
|
322 |
|
323 | ### `integrityPropertyName`
|
324 |
|
325 | Type: `string`
|
326 |
|
327 | Default: `integrity`
|
328 |
|
329 | This is the property that will be set on each entry in `compilation.assets`, which will then be available during `customize`.
|
330 | It is customizable so that you can have multiple instances of this plugin and not have them overwrite the `currentAsset.integrity` property.
|
331 |
|
332 | You'll probably only need to change this if you're using multiple instances of this plugin to create different manifests.
|
333 |
|
334 | ### `apply`
|
335 |
|
336 | Type: `function`
|
337 |
|
338 | Default: `null`
|
339 |
|
340 | Callback to run after setup is complete.
|
341 |
|
342 | ### `customize`
|
343 |
|
344 | Type: `function`
|
345 |
|
346 | Default: `null`
|
347 |
|
348 | Callback to customize each entry in the manifest.
|
349 |
|
350 | You can use this to customize entry names for example. In the sample below, we adjust `img` keys so that it's easier to use them with a template engine:
|
351 |
|
352 | ```javascript
|
353 | new WebpackAssetsManifest({
|
354 | customize(entry) {
|
355 | if (entry.key.startsWith('img/')) {
|
356 | return { key: entry.key.split('img/')[1], value: entry.value };
|
357 | }
|
358 |
|
359 | return o;
|
360 | }
|
361 | }
|
362 | ```
|
363 |
|
364 | The function is called per each entry and provides you a way to intercept and rewrite each object. The result is then merged into a whole manifest.
|
365 |
|
366 | [View the example](examples/customized.js) to see what else you can do with this function.
|
367 |
|
368 | ### `transform`
|
369 |
|
370 | Type: `function`
|
371 |
|
372 | Default: `null`
|
373 |
|
374 | Callback to transform the entire manifest.
|
375 |
|
376 | ### `done`
|
377 |
|
378 | Type: `function`
|
379 |
|
380 | Default: `null`
|
381 |
|
382 | Callback to run after the compilation is done and the manifest has been written.
|
383 |
|
384 | ---
|
385 |
|
386 | ### Hooks
|
387 |
|
388 | This plugin is using hooks from [Tapable](https://github.com/webpack/tapable/).
|
389 |
|
390 | The `apply`, `customize`, `transform`, and `done` options are automatically tapped into the appropriate hook.
|
391 |
|
392 | | Name | Type | Callback signature |
|
393 | | ---- | ---- | --------- |
|
394 | | `apply` | `SyncHook` | `function(manifest){}` |
|
395 | | `customize` | `SyncWaterfallHook` | `function(entry, original, manifest, asset){}` |
|
396 | | `transform` | `SyncWaterfallHook` | `function(assets, manifest){}` |
|
397 | | `done` | `AsyncSeriesHook` | `async function(manifest, stats){}` |
|
398 | | `options` | `SyncWaterfallHook` | `function(options){}` |
|
399 | | `afterOptions` | `SyncHook` | `function(options){}` |
|
400 |
|
401 | #### Tapping into hooks
|
402 |
|
403 | Tap into a hook by calling the `tap` method on the hook as shown below.
|
404 |
|
405 | If you want more control over exactly what gets added to your manifest, then use the `customize` and `transform` hooks.
|
406 | See the [customized](examples/customized.js) and [transformed](examples/transformed.js) examples.
|
407 |
|
408 | ```js
|
409 | const manifest = new WebpackAssetsManifest();
|
410 |
|
411 | manifest.hooks.apply.tap('YourPluginName', function(manifest) {
|
412 | // Do something here
|
413 | manifest.set('some-key', 'some-value');
|
414 | });
|
415 |
|
416 | manifest.hooks.customize.tap('YourPluginName', function(entry, original, manifest, asset) {
|
417 | // customize entry here
|
418 | return entry;
|
419 | });
|
420 |
|
421 | manifest.hooks.transform.tap('YourPluginName', function(assets, manifest) {
|
422 | // customize assets here
|
423 | return assets;
|
424 | });
|
425 |
|
426 | manifest.hooks.options.tap('YourPluginName', function(options) {
|
427 | // customize options here
|
428 | return options;
|
429 | });
|
430 |
|
431 | manifest.hooks.done.tap('YourPluginName', function(manifest, stats) {
|
432 | console.log(`The manifest has been written to ${manifest.getOutputPath()}`);
|
433 | console.log(`${manifest}`);
|
434 | });
|
435 |
|
436 | manifest.hooks.done.tapPromise('YourPluginName', async (manifest, stats) => {
|
437 | await yourAsyncOperation();
|
438 | });
|
439 | ```
|
440 |
|
441 | These hooks can also be set by passing them in the constructor options.
|
442 |
|
443 | ```js
|
444 | new WebpackAssetsManifest({
|
445 | done(manifest, stats) {
|
446 | console.log(`The manifest has been written to ${manifest.getOutputPath()}`);
|
447 | console.log(`${manifest}`);
|
448 | }
|
449 | });
|
450 | ```
|
451 |
|
452 | ## Manifest methods
|
453 |
|
454 | If the manifest instance is passed to a hook, you can use the following methods to manage what goes into the manifest.
|
455 |
|
456 | - `has(key)`
|
457 | - `get(key)`
|
458 | - `set(key, value)`
|
459 | - `setRaw(key, value)`
|
460 | - `delete(key)`
|
461 |
|
462 | If you want to write the manifest to another location, you can use `writeTo(destination)`.
|
463 |
|
464 | ```js
|
465 | new WebpackAssetsManifest({
|
466 | async done(manifest) {
|
467 | await manifest.writeTo('/some/other/path/assets-manifest.json');
|
468 | }
|
469 | });
|
470 | ```
|