WIP - add extractor, generate snippet_data

This commit is contained in:
Stefan Fejes
2019-08-20 15:52:05 +02:00
parent 88084d3d30
commit cc8f1d8a7a
37396 changed files with 4588842 additions and 133 deletions

21
node_modules/webpack-assets-manifest/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Eric King
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,134 @@
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
<a name="1.0.0"></a>
# [1.0.0](https://github.com/webpack-contrib/schema-utils/compare/v0.4.7...v1.0.0) (2018-08-07)
### Features
* **src:** add support for custom error messages ([#33](https://github.com/webpack-contrib/schema-utils/issues/33)) ([1cbe4ef](https://github.com/webpack-contrib/schema-utils/commit/1cbe4ef))
<a name="0.4.7"></a>
## [0.4.7](https://github.com/webpack-contrib/schema-utils/compare/v0.4.6...v0.4.7) (2018-08-07)
### Bug Fixes
* **src:** `node >= v4.0.0` support ([#32](https://github.com/webpack-contrib/schema-utils/issues/32)) ([cb13dd4](https://github.com/webpack-contrib/schema-utils/commit/cb13dd4))
<a name="0.4.6"></a>
## [0.4.6](https://github.com/webpack-contrib/schema-utils/compare/v0.4.5...v0.4.6) (2018-08-06)
### Bug Fixes
* **package:** remove lockfile ([#28](https://github.com/webpack-contrib/schema-utils/issues/28)) ([69f1a81](https://github.com/webpack-contrib/schema-utils/commit/69f1a81))
* **package:** remove unnecessary `webpack` dependency ([#26](https://github.com/webpack-contrib/schema-utils/issues/26)) ([532eaa5](https://github.com/webpack-contrib/schema-utils/commit/532eaa5))
<a name="0.4.5"></a>
## [0.4.5](https://github.com/webpack-contrib/schema-utils/compare/v0.4.4...v0.4.5) (2018-02-13)
### Bug Fixes
* **CHANGELOG:** update broken links ([4483b9f](https://github.com/webpack-contrib/schema-utils/commit/4483b9f))
* **package:** update broken links ([f2494ba](https://github.com/webpack-contrib/schema-utils/commit/f2494ba))
<a name="0.4.4"></a>
## [0.4.4](https://github.com/webpack-contrib/schema-utils/compare/v0.4.3...v0.4.4) (2018-02-13)
### Bug Fixes
* **package:** update `dependencies` ([#22](https://github.com/webpack-contrib/schema-utils/issues/22)) ([3aecac6](https://github.com/webpack-contrib/schema-utils/commit/3aecac6))
<a name="0.4.3"></a>
## [0.4.3](https://github.com/webpack-contrib/schema-utils/compare/v0.4.2...v0.4.3) (2017-12-14)
### Bug Fixes
* **validateOptions:** throw `err` instead of `process.exit(1)` ([#17](https://github.com/webpack-contrib/schema-utils/issues/17)) ([c595eda](https://github.com/webpack-contrib/schema-utils/commit/c595eda))
* **ValidationError:** never return `this` in the ctor ([#16](https://github.com/webpack-contrib/schema-utils/issues/16)) ([c723791](https://github.com/webpack-contrib/schema-utils/commit/c723791))
<a name="0.4.2"></a>
## [0.4.2](https://github.com/webpack-contrib/schema-utils/compare/v0.4.1...v0.4.2) (2017-11-09)
### Bug Fixes
* **validateOptions:** catch `ValidationError` and handle it internally ([#15](https://github.com/webpack-contrib/schema-utils/issues/15)) ([9c5ef5e](https://github.com/webpack-contrib/schema-utils/commit/9c5ef5e))
<a name="0.4.1"></a>
## [0.4.1](https://github.com/webpack-contrib/schema-utils/compare/v0.4.0...v0.4.1) (2017-11-03)
### Bug Fixes
* **ValidationError:** use `Error.captureStackTrace` for `err.stack` handling ([#14](https://github.com/webpack-contrib/schema-utils/issues/14)) ([a6fb974](https://github.com/webpack-contrib/schema-utils/commit/a6fb974))
<a name="0.4.0"></a>
# [0.4.0](https://github.com/webpack-contrib/schema-utils/compare/v0.3.0...v0.4.0) (2017-10-28)
### Features
* add support for `typeof`, `instanceof` (`{Function\|RegExp}`) ([#10](https://github.com/webpack-contrib/schema-utils/issues/10)) ([9f01816](https://github.com/webpack-contrib/schema-utils/commit/9f01816))
<a name="0.3.0"></a>
# [0.3.0](https://github.com/webpack-contrib/schema-utils/compare/v0.2.1...v0.3.0) (2017-04-29)
### Features
* add ValidationError ([#8](https://github.com/webpack-contrib/schema-utils/issues/8)) ([d48f0fb](https://github.com/webpack-contrib/schema-utils/commit/d48f0fb))
<a name="0.2.1"></a>
## [0.2.1](https://github.com/webpack-contrib/schema-utils/compare/v0.2.0...v0.2.1) (2017-03-13)
### Bug Fixes
* Include .babelrc to `files` ([28f0363](https://github.com/webpack-contrib/schema-utils/commit/28f0363))
* Include source to `files` ([43b0f2f](https://github.com/webpack-contrib/schema-utils/commit/43b0f2f))
<a name="0.2.0"></a>
# [0.2.0](https://github.com/webpack-contrib/schema-utils/compare/v0.1.0...v0.2.0) (2017-03-12)
<a name="0.1.0"></a>
# 0.1.0 (2017-03-07)
### Features
* **validations:** add validateOptions module ([ae9b47b](https://github.com/webpack-contrib/schema-utils/commit/ae9b47b))
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

View File

@ -0,0 +1,20 @@
Copyright JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,149 @@
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![deps][deps]][deps-url]
[![test][test]][test-url]
[![coverage][cover]][cover-url]
[![chat][chat]][chat-url]
<div align="center">
<a href="http://json-schema.org">
<img width="160" height="160"
src="https://raw.githubusercontent.com/webpack-contrib/schema-utils/master/docs/logo.png">
</a>
<a href="https://github.com/webpack/webpack">
<img width="200" height="200"
src="https://webpack.js.org/assets/icon-square-big.svg">
</a>
<h1>Schema Utils</h1>
</div>
<h2 align="center">Install</h2>
```bash
npm i schema-utils
```
<h2 align="center">Usage</h2>
### `validateOptions`
**`schema.json`**
```js
{
"type": "object",
"properties": {
// Options...
},
"additionalProperties": false
}
```
#### Error Messages (Custom)
**`schema.json`**
```js
{
"type": "object",
"properties": {
"option": {
"type": [ "boolean" ]
}
},
// Overrides the default err.message for option
"errorMessage": {
"option": "should be {Boolean} (https:/github.com/org/repo#anchor)"
}
"additionalProperties": false
}
```
```js
import schema from 'path/to/schema.json'
import validateOptions from 'schema-utils'
validateOptions(schema, options, 'Loader/Plugin Name')
```
<h2 align="center">Examples</h2>
**schema.json**
```json
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"test": {
"anyOf": [
{ "type": "array" },
{ "type": "string" },
{ "instanceof": "RegExp" }
]
},
"transform": {
"instanceof": "Function"
},
"sourceMap": {
"type": "boolean"
}
},
"additionalProperties": false
}
```
### `Loader`
```js
import { getOptions } from 'loader-utils'
import validateOptions from 'schema-utils'
import schema from 'path/to/schema.json'
function loader (src, map) {
const options = getOptions(this) || {}
validateOptions(schema, options, 'Loader Name')
// Code...
}
```
### `Plugin`
```js
import validateOptions from 'schema-utils'
import schema from 'path/to/schema.json'
class Plugin {
constructor (options) {
validateOptions(schema, options, 'Plugin Name')
this.options = options
}
apply (compiler) {
// Code...
}
}
```
[npm]: https://img.shields.io/npm/v/schema-utils.svg
[npm-url]: https://npmjs.com/package/schema-utils
[node]: https://img.shields.io/node/v/schema-utils.svg
[node-url]: https://nodejs.org
[deps]: https://david-dm.org/webpack-contrib/schema-utils.svg
[deps-url]: https://david-dm.org/webpack-contrib/schema-utils
[test]: http://img.shields.io/travis/webpack-contrib/schema-utils.svg
[test-url]: https://travis-ci.org/webpack-contrib/schema-utils
[cover]: https://codecov.io/gh/webpack-contrib/schema-utils/branch/master/graph/badge.svg
[cover-url]: https://codecov.io/gh/webpack-contrib/schema-utils
[chat]: https://img.shields.io/badge/gitter-webpack%2Fwebpack-brightgreen.svg
[chat-url]: https://gitter.im/webpack/webpack

View File

@ -0,0 +1,74 @@
{
"_from": "schema-utils@^1.0.0",
"_id": "schema-utils@1.0.0",
"_inBundle": false,
"_integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
"_location": "/webpack-assets-manifest/schema-utils",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "schema-utils@^1.0.0",
"name": "schema-utils",
"escapedName": "schema-utils",
"rawSpec": "^1.0.0",
"saveSpec": null,
"fetchSpec": "^1.0.0"
},
"_requiredBy": [
"/webpack-assets-manifest"
],
"_resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
"_shasum": "0b79a93204d7b600d4b2850d1f66c2a34951c770",
"_spec": "schema-utils@^1.0.0",
"_where": "/Users/stefanfejes/Projects/30-seconds-of-python-code/node_modules/webpack-assets-manifest",
"author": {
"name": "webpack Contrib",
"url": "https://github.com/webpack-contrib"
},
"bugs": {
"url": "https://github.com/webpack-contrib/schema-utils/issues"
},
"bundleDependencies": false,
"dependencies": {
"ajv": "^6.1.0",
"ajv-errors": "^1.0.0",
"ajv-keywords": "^3.1.0"
},
"deprecated": false,
"description": "webpack Validation Utils",
"devDependencies": {
"@commitlint/cli": "^7.0.0",
"@commitlint/config-conventional": "^7.0.0",
"@webpack-contrib/eslint-config-webpack": "^2.0.0",
"del-cli": "^1.0.0",
"eslint": "^5.0.0",
"eslint-plugin-import": "^2.0.0",
"eslint-plugin-prettier": "^2.0.0",
"jest": "^21.0.0",
"prettier": "^1.0.0",
"standard-version": "^4.0.0"
},
"engines": {
"node": ">= 4"
},
"files": [
"src"
],
"homepage": "https://github.com/webpack-contrib/schema-utils",
"license": "MIT",
"main": "src/index.js",
"name": "schema-utils",
"repository": {
"type": "git",
"url": "git+https://github.com/webpack-contrib/schema-utils.git"
},
"scripts": {
"clean": "del-cli coverage",
"commits": "commitlint --from $(git rev-list --tags --max-count=1)",
"lint": "eslint --cache src test",
"release": "npm run commits && standard-version",
"test": "jest --env node --verbose --coverage"
},
"version": "1.0.0"
}

View File

@ -0,0 +1,30 @@
/* eslint-disable
strict,
no-param-reassign
*/
'use strict';
class ValidationError extends Error {
constructor(errors, name) {
super();
this.name = 'ValidationError';
this.message = `${name || ''} Invalid Options\n\n`;
this.errors = errors.map((err) => {
err.dataPath = err.dataPath.replace(/\//g, '.');
return err;
});
this.errors.forEach((err) => {
this.message += `options${err.dataPath} ${err.message}\n`;
});
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = ValidationError;

View File

@ -0,0 +1,9 @@
/* eslint-disable
strict
*/
'use strict';
const validateOptions = require('./validateOptions');
module.exports = validateOptions;

View File

@ -0,0 +1,38 @@
/* eslint-disable
strict,
no-param-reassign
*/
'use strict';
const fs = require('fs');
const path = require('path');
const Ajv = require('ajv');
const errors = require('ajv-errors');
const keywords = require('ajv-keywords');
const ValidationError = require('./ValidationError');
const ajv = new Ajv({
allErrors: true,
jsonPointers: true,
});
errors(ajv);
keywords(ajv, ['instanceof', 'typeof']);
const validateOptions = (schema, options, name) => {
if (typeof schema === 'string') {
schema = fs.readFileSync(path.resolve(schema), 'utf8');
schema = JSON.parse(schema);
}
if (!ajv.validate(schema, options)) {
throw new ValidationError(ajv.errors, name);
}
return true;
};
module.exports = validateOptions;

105
node_modules/webpack-assets-manifest/package.json generated vendored Normal file
View File

@ -0,0 +1,105 @@
{
"_from": "webpack-assets-manifest@^3.0.2",
"_id": "webpack-assets-manifest@3.1.1",
"_inBundle": false,
"_integrity": "sha512-JV9V2QKc5wEWQptdIjvXDUL1ucbPLH2f27toAY3SNdGZp+xSaStAgpoMcvMZmqtFrBc9a5pTS1058vxyMPOzRQ==",
"_location": "/webpack-assets-manifest",
"_phantomChildren": {
"ajv": "6.10.2",
"ajv-errors": "1.0.1",
"ajv-keywords": "3.4.1"
},
"_requested": {
"type": "range",
"registry": true,
"raw": "webpack-assets-manifest@^3.0.2",
"name": "webpack-assets-manifest",
"escapedName": "webpack-assets-manifest",
"rawSpec": "^3.0.2",
"saveSpec": null,
"fetchSpec": "^3.0.2"
},
"_requiredBy": [
"/gatsby-plugin-netlify"
],
"_resolved": "https://registry.npmjs.org/webpack-assets-manifest/-/webpack-assets-manifest-3.1.1.tgz",
"_shasum": "39bbc3bf2ee57fcd8ba07cda51c9ba4a3c6ae1de",
"_spec": "webpack-assets-manifest@^3.0.2",
"_where": "/Users/stefanfejes/Projects/30-seconds-of-python-code/node_modules/gatsby-plugin-netlify",
"author": {
"name": "Eric King",
"email": "eric@webdeveric.com",
"url": "http://webdeveric.com/"
},
"bugs": {
"url": "https://github.com/webdeveric/webpack-assets-manifest/issues"
},
"bundleDependencies": false,
"dependencies": {
"chalk": "^2.0",
"lodash.get": "^4.0",
"lodash.has": "^4.0",
"mkdirp": "^0.5",
"schema-utils": "^1.0.0",
"tapable": "^1.0.0",
"webpack-sources": "^1.0.0"
},
"deprecated": false,
"description": "This Webpack plugin will generate a JSON file that matches the original filename with the hashed version.",
"devDependencies": {
"chai": "^4.2.0",
"chai-spies": "^1.0.0",
"css-loader": "^1.0.1",
"eslint": "^5.9.0",
"eslint-config-webdeveric": "^0.3",
"file-loader": "^2.0.0",
"fs-extra": "^7.0.1",
"istanbul": "^0.4",
"jsdoc": "^3.0",
"memory-fs": "^0.4.1",
"mini-css-extract-plugin": "^0.4.4",
"mocha": "^5.2.0",
"rimraf": "^2.0",
"superagent": "^3.8.3",
"webpack": "^4.25.1",
"webpack-dev-server": "^3.1.10"
},
"engines": {
"node": ">=6.11.5"
},
"files": [
"src"
],
"homepage": "https://github.com/webdeveric/webpack-assets-manifest",
"keywords": [
"webpack-assets-manifest",
"webpack-plugin",
"webpack",
"plugin",
"assets",
"manifest",
"json",
"subresource",
"integrity",
"sri"
],
"license": "MIT",
"main": "src/WebpackAssetsManifest.js",
"name": "webpack-assets-manifest",
"peerDependencies": {
"webpack": ">=4.4.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/webdeveric/webpack-assets-manifest.git"
},
"scripts": {
"doc": "jsdoc -c ./jsdoc.json",
"lint": "eslint --fix src test",
"prepublishOnly": "npm run lint && npm test",
"test": "mocha --colors",
"test:coverage": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec",
"test:report": "istanbul cover ./node_modules/mocha/bin/_mocha -- -R spec"
},
"version": "3.1.1"
}

378
node_modules/webpack-assets-manifest/readme.md generated vendored Normal file
View File

@ -0,0 +1,378 @@
# Webpack Assets Manifest
[![Build Status](https://travis-ci.org/webdeveric/webpack-assets-manifest.svg?branch=master)](https://travis-ci.org/webdeveric/webpack-assets-manifest)
[![codecov](https://codecov.io/gh/webdeveric/webpack-assets-manifest/branch/master/graph/badge.svg)](https://codecov.io/gh/webdeveric/webpack-assets-manifest)
[![dependencies Status](https://david-dm.org/webdeveric/webpack-assets-manifest/status.svg)](https://david-dm.org/webdeveric/webpack-assets-manifest)
[![devDependencies Status](https://david-dm.org/webdeveric/webpack-assets-manifest/dev-status.svg)](https://david-dm.org/webdeveric/webpack-assets-manifest?type=dev)
This Webpack plugin will generate a JSON file that matches the original filename with the hashed version.
## Installation
:warning: Starting with version 2, this plugin works with Webpack 4+. Version 3.1 requires Webpack 4.4+.
```shell
npm install webpack-assets-manifest --save-dev
```
If you're using Webpack 3 or below, you'll need to install version 1.
```shell
npm install webpack-assets-manifest@1 --save-dev
```
## New in version 3
* Added [hooks](#hooks).
* Added [examples](examples/).
* Added options:
* [`integrity`](#integrity)
* [`integrityHashes`](#integrityhashes)
* [`entrypoints`](#entrypoints)
* [`entrypointsKey`](#entrypointskey)
* Updated `customize` callback arguments. See [customized](examples/customized.js) example.
* Removed `contextRelativeKeys` option.
## Usage
In your webpack config, require the plugin then add an instance to the `plugins` array.
```js
const path = require('path');
const WebpackAssetsManifest = require('webpack-assets-manifest');
module.exports = {
entry: {
// Your entry points
},
output: {
path: path.join( __dirname, 'dist' ),
filename: '[name]-[hash].js',
chunkFilename: '[id]-[chunkhash].js',
},
module: {
// Your loader rules go here.
},
plugins: [
new WebpackAssetsManifest({
// Options go here
}),
],
};
```
## Sample output
```json
{
"main.js": "main-9c68d5e8de1b810a80e4.js",
"main.css": "main-9c68d5e8de1b810a80e4.css",
"images/logo.svg": "images/logo-b111da4f34cefce092b965ebc1078ee3.svg"
}
```
---
## Options ([read the schema](src/options-schema.json))
### `output`
Type: `string`
Default: `manifest.json`
This is where to save the manifest file relative to your webpack `output.path`.
### `assets`
Type: `object`
Default: `{}`
Data is stored in this object.
#### Sharing data
You can share data between instances by passing in your own object in the `assets` option.
This is useful in [multi-compiler mode](https://github.com/webpack/webpack/tree/master/examples/multi-compiler).
```js
const data = Object.create(null);
const manifest1 = new WebpackAssetsManifest({
assets: data,
});
const manifest2 = new WebpackAssetsManifest({
assets: data,
});
```
### `space`
Type: `int`
Default: `2`
Number of spaces to use for pretty printing.
### `replacer`
Type: `null`, `function`, or `array`
Default: `null`
[Replacer reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter)
You'll probably want to use the `transform` hook instead.
### `fileExtRegex`
Type: `regex`
Default: `/\.\w{2,4}\.(?:map|gz)$|\.\w+$/i`
This is the regular expression used to find file extensions. You'll probably never need to change this.
### `writeToDisk`
Type: `boolean`
Default: `false`
Write the manifest to disk using `fs` during `afterEmit`.
:warning: If you're using another language for your site and you're using `webpack-dev-server` to process your assets during development,
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.
### `sortManifest`
Type: `boolean`, `function`
Default: `true`
The manifest is sorted alphabetically by default. You can turn off sorting by setting `sortManifest: false`.
If you want more control over how the manifest is sorted, you can provide your own
[comparison function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters).
See the [sorted](examples/sorted.js) example.
```js
new WebpackAssetsManifest({
sortManifest(a, b) {
// Return -1, 0, or 1
}
});
```
### `merge`
Type: `boolean`, `string`
Default: `false`
If the `output` file already exists and you'd like to add to it, use `merge: true`.
The default behavior is to use the existing keys/values without modification.
```js
new WebpackAssetsManifest({
output: '/path/to/manifest.json',
merge: true
});
```
If you need to customize during merge, use `merge: 'customize'`.
If you want to know if `customize` was called when merging with an existing manifest, you can check `manifest.isMerging`.
```js
new WebpackAssetsManifest({
merge: 'customize',
customize(entry, original, manifest, asset) {
if ( manifest.isMerging ) {
// Do something
}
},
}),
```
### `publicPath`
Type: `string`, `function`, `boolean`,
Default: `null`
When using `publicPath: true`, your webpack config `output.publicPath` will be used as the value prefix.
```js
const manifest = new WebpackAssetsManifest({
publicPath: true,
});
```
When using a string, it will be the value prefix. One common use is to prefix your CDN URL.
```js
const manifest = new WebpackAssetsManifest({
publicPath: '//cdn.example.com',
});
```
If you'd like to have more control, use a function. See the [custom CDN](examples/custom-cdn.js) example.
```js
const manifest = new WebpackAssetsManifest({
publicPath(filename, manifest)
{
// customize filename here
return filename;
}
});
```
### `entrypoints`
Type: `boolean`
Default: `false`
Include `compilation.entrypoints` in the manifest file.
### `entrypointsKey`
Type: `string`, `boolean`
Default: `entrypoints`
If this is set to `false`, the `entrypoints` will be added to the root of the manifest.
### `integrity`
Type: `boolean`
Default: `false`
Include the [subresource integrity hash](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity).
### `integrityHashes`
Type: `array`
Default: `[ 'sha256', 'sha384', 'sha512' ]`
Hash algorithms to use when generating SRI. For browsers, the currently the allowed integrity hashes are `sha256`, `sha384`, and `sha512`.
Other hash algorithms can be used if your target environment is not a browser.
If you were to create a tool to audit your S3 buckets for
[data integrity](https://aws.amazon.com/premiumsupport/knowledge-center/data-integrity-s3/),
you could use something like this [example](examples/aws-s3-data-integrity.js) to record the `md5` hashes.
### `integrityPropertyName`
Type: `string`
Default: `integrity`
This is the property that will be set on each entry in `compilation.assets`, which will then be available during `customize`.
It is customizable so that you can have multiple instances of this plugin and not have them overwrite the `currentAsset.integrity` property.
You'll probably only need to change this if you're using multiple instances of this plugin to create different manifests.
### `apply`
Type: `function`
Default: `null`
Callback to run after setup is complete.
### `customize`
Type: `function`
Default: `null`
Callback to customize each entry in the manifest.
### `transform`
Type: `function`
Default: `null`
Callback to transform the entire manifest.
### `done`
Type: `function`
Default: `null`
Callback to run after the compilation is done and the manifest has been written.
---
### Hooks
This plugin is using hooks from [Tapable](https://github.com/webpack/tapable/).
The `apply`, `customize`, `transform`, and `done` options are automatically tapped into the appropriate hook.
| Name | Type | Callback signature |
| ---- | ---- | --------- |
| `apply` | `SyncHook` | `function(manifest){}` |
| `customize` | `SyncWaterfallHook` | `function(entry, original, manifest, asset){}` |
| `transform` | `SyncWaterfallHook` | `function(assets, manifest){}` |
| `done` | `SyncHook` | `function(manifest, stats){}` |
| `options` | `SyncWaterfallHook` | `function(options){}` |
| `afterOptions` | `SyncHook` | `function(options){}` |
#### Tapping into hooks
Tap into a hook by calling the `tap` method on the hook as shown below.
If you want more control over exactly what gets added to your manifest, then use the `customize` and `transform` hooks.
See the [customized](examples/customized.js) and [transformed](examples/transformed.js) examples.
```js
const manifest = new WebpackAssetsManifest();
manifest.hooks.apply.tap('YourPluginName', function(manifest) {
// Do something here
manifest.set('some-key', 'some-value');
});
manifest.hooks.customize.tap('YourPluginName', function(entry, original, manifest, asset) {
// customize entry here
return entry;
});
manifest.hooks.transform.tap('YourPluginName', function(assets, manifest) {
// customize assets here
return assets;
});
manifest.hooks.options.tap('YourPluginName', function(options) {
// customize options here
return options;
});
manifest.hooks.done.tap('YourPluginName', function(manifest, stats) {
console.log(`The manifest has been written to ${manifest.getOutputPath()}`);
console.log(`${manifest}`);
});
```
These hooks can also be set by passing them in the constructor options.
```js
new WebpackAssetsManifest({
done(manifest, stats) {
console.log(`The manifest has been written to ${manifest.getOutputPath()}`);
console.log(`${manifest}`);
}
});
```
If the manifest instance is passed to a hook, you can use `has(key)`, `get(key)`, `set(key, value)`, `setRaw(key, value)`,and `delete(key)` methods to manage what goes into the manifest.

View File

@ -0,0 +1,670 @@
/**
* Webpack Assets Manifest
*
* @author Eric King <eric@webdeveric.com>
*/
'use strict';
const fs = require('fs');
const url = require('url');
const path = require('path');
const get = require('lodash.get');
const has = require('lodash.has');
const validateOptions = require('schema-utils');
const { SyncHook, SyncWaterfallHook } = require('tapable');
const { RawSource } = require('webpack-sources');
const optionsSchema = require('./options-schema.json');
const {
maybeArrayWrap,
filterHashes,
getSRIHash,
warn,
varType,
getSortedObject,
} = require('./helpers.js');
const IS_MERGING = Symbol('isMerging');
const PLUGIN_NAME = 'WebpackAssetsManifest';
class WebpackAssetsManifest
{
/**
* @param {object} options - configuration options
* @constructor
*/
constructor(options = {})
{
/**
* This is using hooks from {@link https://github.com/webpack/tapable Tapable}.
*/
this.hooks = {
apply: new SyncHook([ 'manifest' ]),
customize: new SyncWaterfallHook([ 'entry', 'original', 'manifest', 'asset' ]),
transform: new SyncWaterfallHook([ 'assets', 'manifest' ]),
done: new SyncHook([ 'manifest', 'stats' ]),
options: new SyncWaterfallHook([ 'options' ]),
afterOptions: new SyncHook([ 'options' ]),
};
this.hooks.transform.tap(PLUGIN_NAME, assets => {
const { sortManifest } = this.options;
return sortManifest ? getSortedObject(
assets,
typeof sortManifest === 'function' ? sortManifest.bind(this) : undefined
) : assets;
});
this.hooks.afterOptions.tap(PLUGIN_NAME, options => {
this.options = Object.assign( this.defaultOptions, options );
this.options.integrityHashes = filterHashes( this.options.integrityHashes );
validateOptions(optionsSchema, this.options, PLUGIN_NAME);
// Copy over any entries that may have been added to the manifest before apply() was called.
// If the same key exists in assets and options.assets, options.assets should be used.
this.assets = Object.assign(this.options.assets, this.assets, this.options.assets);
if ( this.options.hasOwnProperty('contextRelativeKeys') ) {
warn('contextRelativeKeys has been removed. Please use the customize hook instead.');
}
[ 'apply', 'customize', 'transform', 'done' ].forEach( hookName => {
if ( typeof this.options[ hookName ] === 'function' ) {
this.hooks[ hookName ].tap(`${PLUGIN_NAME}.option.${hookName}`, this.options[ hookName ] );
}
});
});
this.options = Object.assign( this.defaultOptions, options );
// This is what gets JSON stringified
this.assets = this.options.assets;
// hashed filename : original filename
this.assetNames = new Map();
// This is passed to the customize() hook
this.currentAsset = null;
// The Webpack compiler instance
this.compiler = null;
// compilation stats
this.stats = null;
// This is used to identify hot module replacement files
this.hmrRegex = null;
// Is a merge happening?
this[ IS_MERGING ] = false;
}
/**
* Hook into the Webpack compiler
*
* @param {object} compiler - The Webpack compiler object
*/
apply(compiler)
{
this.compiler = compiler;
// Allow hooks to modify options
this.options = this.hooks.options.call(this.options);
// Ensure options contain defaults and are valid
this.hooks.afterOptions.call(this.options);
const { output: { filename, hotUpdateChunkFilename } } = compiler.options;
if ( filename !== hotUpdateChunkFilename && typeof hotUpdateChunkFilename === 'string' ) {
this.hmrRegex = new RegExp(
hotUpdateChunkFilename
.replace(/\./g, '\\.')
.replace(/\[[a-z]+(:\d+)?\]/gi, (m, n) => (n ? `.{${n.substr(1)}}` : '.+')) + '$',
'i'
);
}
// compilation.assets contains the results of the build
compiler.hooks.compilation.tap(PLUGIN_NAME, this.handleCompilation.bind(this));
// Add manifest.json to compiler.assets
compiler.hooks.emit.tapAsync(PLUGIN_NAME, this.handleEmit.bind(this));
// Use fs to write the manifest.json to disk if `options.writeToDisk` is true
compiler.hooks.afterEmit.tapPromise(PLUGIN_NAME, this.handleAfterEmit.bind(this));
// The compilation has finished
compiler.hooks.done.tap(PLUGIN_NAME, stats => this.hooks.done.call(this, stats));
// Setup is complete.
this.hooks.apply.call(this);
}
/**
* Get the default options.
*
* @return {object}
*/
get defaultOptions()
{
return {
assets: Object.create(null),
output: 'manifest.json',
replacer: null, // Its easier to use the transform hook instead.
space: 2,
writeToDisk: false,
fileExtRegex: /\.\w{2,4}\.(?:map|gz)$|\.\w+$/i,
sortManifest: true,
merge: false,
publicPath: null,
// Hooks
apply: null, // After setup is complete
customize: null, // Customize each entry in the manifest
transform: null, // Transform the entire manifest
done: null, // Compilation is done and the manifest has been written
// Include `compilation.entrypoints` in the manifest file
entrypoints: false,
entrypointsKey: 'entrypoints',
// https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
integrity: false,
integrityHashes: [ 'sha256', 'sha384', 'sha512' ],
integrityPropertyName: 'integrity',
};
}
/**
* Determine if the manifest data is currently being merged.
*
* @return {boolean}
*/
get isMerging()
{
return this[ IS_MERGING ];
}
/**
* Get the file extension.
*
* @param {string} filename
* @return {string}
*/
getExtension(filename)
{
if (! filename || typeof filename !== 'string') {
return '';
}
filename = filename.split(/[?#]/)[0];
if (this.options.fileExtRegex) {
const ext = filename.match(this.options.fileExtRegex);
return ext && ext.length ? ext[ 0 ] : '';
}
return path.extname(filename);
}
/**
* Replace backslash with forward slash.
*
* @return {string}
*/
fixKey(key)
{
return typeof key === 'string' ? key.replace( /\\/g, '/' ) : key;
}
/**
* Determine if the filename matches the HMR filename pattern.
*
* @return {boolean}
*/
isHMR(filename)
{
return this.hmrRegex ? this.hmrRegex.test( filename ) : false;
}
/**
* Add item to assets without modifying the key or value.
*
* @param {string} key
* @param {string} value
* @return {object} this
*/
setRaw(key, value)
{
this.assets[ key ] = value;
return this;
}
/**
* Add an item to the manifest.
*
* @param {string} key
* @param {string} value
* @return {object} this
*/
set(key, value)
{
if ( this.isMerging && this.options.merge !== 'customize' ) {
// Do not fix the key if merging since it should already be correct.
return this.setRaw(key, value);
}
const fixedKey = this.fixKey(key);
const publicPath = this.getPublicPath( value );
const entry = this.hooks.customize.call(
{
key: fixedKey,
value: publicPath,
},
{
key,
value,
},
this,
this.currentAsset
);
// Allow the entry to be skipped
if ( entry === false ) {
return this;
}
// Use the customized values
if ( varType(entry) === 'Object' ) {
let { key = fixedKey, value = publicPath } = entry;
// If the integrity should be returned but the entry value was
// not customized lets do that now so it includes both.
if ( value === publicPath && this.options.integrity ) {
value = {
src: value,
integrity: get(this, `currentAsset.${this.options.integrityPropertyName}`, ''),
};
}
return this.setRaw( key, value );
}
warn.once(`Unexpected customize() return type: ${varType(entry)}`);
return this.setRaw( fixedKey, publicPath );
}
/**
* Determine if an item exist in the manifest.
*
* @param {string} key
* @return {boolean}
*/
has(key)
{
return has(this.assets, key) || has(this.assets, this.fixKey(key));
}
/**
* Get an item from the manifest.
*
* @param {string} key
* @param {string} defaultValue - Defaults to empty string
* @return {*}
*/
get(key, defaultValue = '')
{
return this.assets[ key ] || this.assets[ this.fixKey(key) ] || defaultValue;
}
/**
* Delete an item from the manifest.
*
* @param {string} key
* @return {boolean}
*/
delete(key)
{
if ( has(this.assets, key) ) {
return (delete this.assets[ key ]);
}
key = this.fixKey(key);
if ( has(this.assets, key) ) {
return (delete this.assets[ key ]);
}
return false;
}
/**
* Process compilation assets.
*
* @param {object} assets - Assets by chunk name
* @return {object}
*/
processAssetsByChunkName(assets)
{
Object.keys(assets).forEach( chunkName => {
maybeArrayWrap( assets[ chunkName ] )
.filter( f => ! this.isHMR(f) ) // Remove hot module replacement files
.forEach( filename => {
this.assetNames.set( filename, chunkName + this.getExtension( filename ) );
});
});
return this.assetNames;
}
/**
* Get the data for `JSON.stringify()`.
*
* @return {object}
*/
toJSON()
{
// This is the last chance to modify the data before the manifest file gets created.
return this.hooks.transform.call(this.assets, this);
}
/**
* `JSON.stringify()` the manifest.
*
* @return {string}
*/
toString()
{
return JSON.stringify(this, this.options.replacer, this.options.space) || '{}';
}
/**
* Merge data if the output file already exists
*/
maybeMerge()
{
if ( this.options.merge ) {
try {
this[ IS_MERGING ] = true;
const data = JSON.parse(fs.readFileSync(this.getOutputPath()));
for ( const key in data ) {
if ( ! this.has(key) ) {
this.set(key, data[ key ]);
}
}
} catch (err) { // eslint-disable-line
} finally {
this[ IS_MERGING ] = false;
}
}
}
/**
* @param {object} entrypoints from a compilation
*/
getEntrypointFilesGroupedByExtension( entrypoints )
{
const files = Object.create(null);
const removeHMR = f => ! this.isHMR(f);
const groupFilesByExtension = (files, file) => {
const ext = this.getExtension(file).replace(/^\.+/, '').toLowerCase();
files[ ext ] = files[ ext ] || [];
files[ ext ].push( this.getPublicPath( file ) );
return files;
};
for ( const [ name, entrypoint ] of entrypoints ) {
files[ name ] = entrypoint
.getFiles()
.filter( removeHMR )
.reduce( groupFilesByExtension, Object.create(null) );
}
return files;
}
/**
* Handle the `emit` event
*
* @param {object} compilation - the Webpack compilation object
* @param {Function} callback
*/
handleEmit(compilation, callback)
{
this.stats = compilation.getStats().toJson();
this.processAssetsByChunkName( this.stats.assetsByChunkName );
for ( const [ hashedFile, filename ] of this.assetNames ) {
this.currentAsset = compilation.assets[ hashedFile ];
// `integrity` may have already been set by another plugin, like `webpack-subresource-integrity`.
// Only generate the SRI hash if `integrity` is not found.
if ( this.options.integrity && this.currentAsset && ! this.currentAsset[ this.options.integrityPropertyName ] ) {
this.currentAsset[ this.options.integrityPropertyName ] = getSRIHash( this.options.integrityHashes, this.currentAsset.source() );
}
this.set( filename, hashedFile );
this.currentAsset = null;
}
if ( this.options.entrypoints ) {
const entrypoints = this.getEntrypointFilesGroupedByExtension( compilation.entrypoints );
if ( this.options.entrypointsKey === false ) {
for ( const key in entrypoints ) {
this.setRaw( key, entrypoints[ key ] );
}
} else {
this.setRaw( this.options.entrypointsKey, entrypoints );
}
}
this.maybeMerge();
const output = this.getManifestPath(
compilation,
this.inDevServer() ?
path.basename( this.getOutputPath() ) :
path.relative( compilation.compiler.outputPath, this.getOutputPath() )
);
compilation.assets[ output ] = new RawSource(this.toString());
callback();
}
/**
* Get the parsed output path. [hash] is supported.
*
* @param {object} compilation - the Webpack compilation object
* @param {string} filename
* @return {string}
*/
getManifestPath(compilation, filename)
{
return compilation.getPath( filename, { chunk: { name: 'manifest' }, filename: 'manifest.json' } );
}
/**
* Write to disk using `fs`.
*
* This is likely only needed if you're using webpack-dev-server
* and you don't want to keep the manifest file only in memory.
*
* @param {object} compilation - the Webpack compilation object
*/
handleAfterEmit(compilation)
{
// Reset the internal mapping of hashed name to original name after every compilation.
this.assetNames.clear();
if ( ! this.options.writeToDisk ) {
return Promise.resolve();
}
return new Promise( (resolve, reject) => {
const output = this.getManifestPath( compilation, this.getOutputPath() );
require('mkdirp')(
path.dirname(output),
err => {
if ( err ) {
reject( err );
return;
}
fs.writeFile( output, this.toString(), resolve );
}
);
});
}
/**
* Record asset names
*
* @param {object} loaderContext
* @param {object} module
*/
handleNormalModuleLoader(loaderContext, module)
{
const { emitFile } = loaderContext;
loaderContext.emitFile = (name, content, sourceMap) => {
if ( ! this.assetNames.has( name ) ) {
const originalName = path.join(
path.dirname(name),
path.basename(module.userRequest)
);
this.assetNames.set(name, originalName);
}
return emitFile.call(module, name, content, sourceMap);
};
}
/**
* Hook into the compilation object
*
* @param {object} compilation - the Webpack compilation object
*/
handleCompilation(compilation)
{
compilation.hooks.normalModuleLoader.tap(PLUGIN_NAME, this.handleNormalModuleLoader.bind(this));
}
/**
* Determine if webpack-dev-server is being used
*
* @return {boolean}
*/
inDevServer()
{
if ( process.argv.some( arg => arg.includes('webpack-dev-server') ) ) {
return true;
}
return has(this, 'compiler.outputFileSystem') && this.compiler.outputFileSystem.constructor.name === 'MemoryFileSystem';
}
/**
* Get the file system path to the manifest
*
* @return {string} path to manifest file
*/
getOutputPath()
{
if ( ! this.compiler ) {
return '';
}
if ( path.isAbsolute( this.options.output ) ) {
return this.options.output;
}
if ( this.inDevServer() ) {
let outputPath = get( this, 'compiler.options.devServer.outputPath', get( this, 'compiler.outputPath', '/' ) );
if ( outputPath === '/' ) {
warn.once('Please use an absolute path in options.output when using webpack-dev-server.');
outputPath = get( this, 'compiler.context', process.cwd() );
}
return path.resolve( outputPath, this.options.output );
}
return path.resolve( this.compiler.outputPath, this.options.output );
}
/**
* Get the public path for the filename
*
* @param {string} filePath
*/
getPublicPath(filename)
{
if ( typeof filename === 'string' ) {
const { publicPath } = this.options;
if ( typeof publicPath === 'function' ) {
return publicPath( filename, this );
}
if ( typeof publicPath === 'string' ) {
return url.resolve( publicPath, filename );
}
if ( publicPath === true ) {
return url.resolve(
get( this, 'compiler.options.output.publicPath', '' ),
filename
);
}
}
return filename;
}
/**
* Get a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler|Proxy} for the manifest.
* This allows you to use `[]` to manage entries.
*
* @param {boolean} raw - Should the proxy use `setRaw` instead of `set`?
* @return {Proxy}
*/
getProxy(raw = false)
{
const setMethod = raw ? 'setRaw' : 'set';
return new Proxy(this, {
has(target, property) {
return target.has(property);
},
get(target, property) {
return target.get(property) || undefined;
},
set(target, property, value) {
return target[ setMethod ](property, value).has(property);
},
deleteProperty(target, property) {
return target.delete(property);
},
});
}
}
module.exports = WebpackAssetsManifest;

117
node_modules/webpack-assets-manifest/src/helpers.js generated vendored Normal file
View File

@ -0,0 +1,117 @@
const crypto = require('crypto');
const chalk = require('chalk');
/**
* Display a warning message.
*
* @param {string} message
*/
function warn( message )
{
if ( message in warn.cache ) {
return;
}
const prefix = chalk.hex('#CC4A8B')('WARNING:');
console.warn(chalk`${prefix} ${message}`);
}
warn.cache = Object.create(null);
/**
* Display a warning message once.
*
* @param {string} message
*/
warn.once = function( message ) {
warn( message );
warn.cache[ message ] = true;
};
/**
* @param {*} data
* @return {array}
*/
function maybeArrayWrap( data )
{
return Array.isArray( data ) ? data : [ data ];
}
/**
* Filter out invalid hash algorithms.
*
* @param {array} hashes
* @return {array} Valid hash algorithms
*/
function filterHashes( hashes )
{
const validHashes = crypto.getHashes();
return hashes.filter( hash => {
if ( validHashes.includes(hash) ) {
return true;
}
warn(chalk`{blueBright ${hash}} is not a supported hash algorithm`);
return false;
});
}
/**
* See {@link https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity|Subresource Integrity} at MDN
*
* @param {array} hashes - The algorithms you want to use when hashing `content`
* @param {string} content - File contents you want to hash
* @return {string} SRI hash
*/
function getSRIHash( hashes, content )
{
return Array.isArray( hashes ) ? hashes.map( hash => {
const integrity = crypto.createHash(hash).update(content, 'utf-8').digest('base64');
return `${hash}-${integrity}`;
}).join(' ') : '';
}
/**
* Get the data type of an argument.
*
* @param {*} v - Some variable
* @return {string} Data type
*/
function varType( v )
{
const [ , type ] = Object.prototype.toString.call( v ).match(/\[object\s(\w+)\]/);
return type;
}
/**
* Get an object sorted by keys.
*
* @param {object} object
* @param {function} compareFunction
* @return {object}
*/
function getSortedObject(object, compareFunction)
{
const keys = Object.keys(object);
keys.sort( compareFunction );
return keys.reduce(
(sorted, key) => (sorted[ key ] = object[ key ], sorted),
Object.create(null)
);
}
module.exports = {
maybeArrayWrap,
filterHashes,
getSRIHash,
warn,
varType,
getSortedObject,
};

View File

@ -0,0 +1,147 @@
{
"title": "Webpack Assets Manifest options schema",
"description": "Webpack Assets Manifest options",
"type": "object",
"properties": {
"assets": {
"type": "object",
"default": {}
},
"output": {
"type": "string",
"default": "manifest.json"
},
"replacer": {
"oneOf": [
{
"$ref": "#/definitions/functionOrNull"
},
{
"type": "array"
}
]
},
"space": {
"type": "integer",
"multipleOf": 1.0,
"minimum": 0,
"default": 2
},
"writeToDisk": {
"type": "boolean",
"default": false
},
"fileExtRegex": {
"oneOf": [
{
"instanceof": "RegExp"
},
{
"type": "null"
},
{
"const": false
}
]
},
"sortManifest": {
"default": true,
"oneOf": [
{
"type": "boolean"
},
{
"instanceof": "Function"
}
]
},
"merge": {
"default": false,
"oneOf": [
{
"type": "boolean"
},
{
"const": "customize"
}
]
},
"publicPath": {
"default": null,
"oneOf": [
{
"type": "string"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"instanceof": "Function"
}
]
},
"apply": {
"$ref": "#/definitions/functionOrNull"
},
"customize": {
"$ref": "#/definitions/functionOrNull"
},
"transform": {
"$ref": "#/definitions/functionOrNull"
},
"done": {
"$ref": "#/definitions/functionOrNull"
},
"entrypoints": {
"type": "boolean",
"default": false
},
"entrypointsKey": {
"default": "entrypoints",
"oneOf": [
{
"type": "string"
},
{
"const": false
}
]
},
"integrity": {
"type": "boolean",
"default": false
},
"integrityHashes": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"sha256",
"sha384",
"sha512"
]
},
"integrityPropertyName": {
"type": "string",
"minLength": 1,
"default": "integrity"
}
},
"definitions": {
"functionOrNull": {
"default": null,
"oneOf": [
{
"instanceof": "Function"
},
{
"type": "null"
}
]
}
}
}