ui
This commit is contained in:
+170
@@ -0,0 +1,170 @@
|
||||
# Changelog
|
||||
|
||||
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.
|
||||
|
||||
### [1.3.2](https://github.com/webpack-contrib/html-loader/compare/v1.3.1...v1.3.2) (2020-10-09)
|
||||
|
||||
### Chore
|
||||
|
||||
* update `schema-utils`
|
||||
|
||||
### [1.3.1](https://github.com/webpack-contrib/html-loader/compare/v1.3.0...v1.3.1) (2020-09-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* avoid removing redundant attributes ([#324](https://github.com/webpack-contrib/html-loader/issues/324)) ([ab299ac](https://github.com/webpack-contrib/html-loader/commit/ab299acd8784f96f9191988133fb74f72aaf89c8))
|
||||
|
||||
## [1.3.0](https://github.com/webpack-contrib/html-loader/compare/v1.2.1...v1.3.0) (2020-08-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* `...` syntax to extend default tag and attributes ([#317](https://github.com/webpack-contrib/html-loader/issues/317)) ([f02bb48](https://github.com/webpack-contrib/html-loader/commit/f02bb48a1124fc567b858234b1cf3d3e68bb6c76))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle `script` tags in SVG ([#315](https://github.com/webpack-contrib/html-loader/issues/315)) ([d021e42](https://github.com/webpack-contrib/html-loader/commit/d021e42fc830d36cf2ab3e9d483fd5723af23ba8))
|
||||
|
||||
### [1.2.1](https://github.com/webpack-contrib/html-loader/compare/v1.2.0...v1.2.1) (2020-08-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ignore template sources ([#314](https://github.com/webpack-contrib/html-loader/issues/314)) ([078d9bd](https://github.com/webpack-contrib/html-loader/commit/078d9bdc36d256d6a78ab0ae9b8ccddbef11393c))
|
||||
|
||||
## [1.2.0](https://github.com/webpack-contrib/html-loader/compare/v1.1.0...v1.2.0) (2020-08-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support SVG tags ([#302](https://github.com/webpack-contrib/html-loader/issues/302)) ([1acd204](https://github.com/webpack-contrib/html-loader/commit/1acd20448dbe976b883597b135bb8ac9e1b71d1a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not handle non standard `script` types ([ddad9f2](https://github.com/webpack-contrib/html-loader/commit/ddad9f2d6f5ab75fe2afd247bf55b1646c6e1c31))
|
||||
* inline syntax for sources ([#310](https://github.com/webpack-contrib/html-loader/issues/310)) ([c247cfa](https://github.com/webpack-contrib/html-loader/commit/c247cfa9ad66281b28aef5397c8c2d2786f05867))
|
||||
* linefeed characters in sources ([#311](https://github.com/webpack-contrib/html-loader/issues/311)) ([b8ee9ee](https://github.com/webpack-contrib/html-loader/commit/b8ee9ee0d60848e84e52fb117c1f3cdc2ebf08d7))
|
||||
* minimize is more safely ([#304](https://github.com/webpack-contrib/html-loader/issues/304)) ([03152b1](https://github.com/webpack-contrib/html-loader/commit/03152b1d3b807a287d84302f6a9987ceb22d395c))
|
||||
* perf ([#300](https://github.com/webpack-contrib/html-loader/issues/300)) ([d69f259](https://github.com/webpack-contrib/html-loader/commit/d69f259d2a6b4bc9ba9c163fd2d70989c3f3a6ff))
|
||||
|
||||
## [1.1.0](https://github.com/webpack-contrib/html-loader/compare/v1.0.0...v1.1.0) (2020-04-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added support for an async functions to the `preprocessor` option ([#272](https://github.com/webpack-contrib/html-loader/issues/272)) ([e59324b](https://github.com/webpack-contrib/html-loader/commit/e59324b929557a9e1da5e1713748351cf37efdb1))
|
||||
* allowed to handle an attribute without a tag ([#273](https://github.com/webpack-contrib/html-loader/issues/273)) ([c0d6d5b](https://github.com/webpack-contrib/html-loader/commit/c0d6d5b9338af219a5cd81779ee2bcd9254420ed))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* hash processing improved ([#275](https://github.com/webpack-contrib/html-loader/issues/275)) ([74d2c60](https://github.com/webpack-contrib/html-loader/commit/74d2c607c519bdaad995a51e74b6317c8cfaad14))
|
||||
|
||||
## [1.0.0](https://github.com/ryanclark/html-loader/compare/v0.5.5...v1.0.0) (2020-03-19)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* for parsing HTML now we use `htmlparser2` package
|
||||
* the `attrs` option was renamed to the `attributes` option
|
||||
* the `interpolate` option was removed, please consider migration on the [`preprocessor`](https://github.com/webpack-contrib/html-loader#preprocessor)
|
||||
* the `minimize` option is `true` by default in `production` mode. You need to list all options for `html-minifier` if you use `object` notation.
|
||||
* uppercase tags and uppercase attributes are handled by default
|
||||
* the `root` option was moved under the `attributes` option, please look at the [documentation](https://github.com/webpack-contrib/html-loader#object)
|
||||
* emit an error on broken HTML syntax when minimization is enabled
|
||||
* By default, now we process the following tags with attributes:
|
||||
* the `src` attribute of the `audio` tag
|
||||
* the `src` attribute of the `embed` tag
|
||||
* the `src` attribute of the `img` tag
|
||||
* the `srcset` attribute of the `img` tag
|
||||
* the `src` attribute of the `input` tag
|
||||
* the `href` attribute of the `link` tag (only for stylesheets)
|
||||
* the `data` attribute of the `object` tag
|
||||
* the `src` attribute of the `script` tag
|
||||
* the `src` attribute of the `source` tag
|
||||
* the `srcset` attribute of the `source` tag
|
||||
* the `src` attribute of the `track` tag
|
||||
* the `poster` attribute of the `video` tag
|
||||
* the `src` attribute of the `video` tag
|
||||
* the `attributes` option should be `Boolean` or `Object`, please look at the [documentation](https://github.com/webpack-contrib/html-loader#object)
|
||||
* the `exportAsDefault` option were removed in favor the `esModules` option
|
||||
* the `exportAsEs6Default` option were removed in favor the `esModules` option
|
||||
|
||||
### Features
|
||||
|
||||
* handle more tags and attributes
|
||||
* added the `preprocessor` option
|
||||
* added the `esModule` option
|
||||
* add the `rulFilter` option for filtering some of urls, please look at the [documentation](https://github.com/webpack-contrib/html-loader#urlfilter)
|
||||
* allow to setup how to handle and filter tags and attributes, please look at the [documentation](https://github.com/webpack-contrib/html-loader#list)
|
||||
* improve error reporting
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adding quotes when necessary for unquoted sources
|
||||
* do not handle empty attributes
|
||||
* escape `\u2028` and `\u2029` characters
|
||||
* handle only valid srcset tags
|
||||
* parser tags and attributes according spec
|
||||
* reduce `import`/`require` count
|
||||
* reduce size of generated modules
|
||||
* respect `#hash` in sources
|
||||
* support ES6 syntax in `script` tags when minimize
|
||||
* support ES6 import of urls
|
||||
|
||||
<a name="0.5.5"></a>
|
||||
## [0.5.5](https://github.com/webpack-contrib/html-loader/compare/v0.5.4...v0.5.5) (2018-01-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **index:** don't prepend `./` to the URL on `interpolate=require` (`options.interpolate`) ([#165](https://github.com/webpack-contrib/html-loader/issues/165)) ([9515410](https://github.com/webpack-contrib/html-loader/commit/9515410))
|
||||
|
||||
|
||||
|
||||
<a name="0.5.4"></a>
|
||||
## [0.5.4](https://github.com/webpack-contrib/html-loader/compare/v0.5.1...v0.5.4) (2018-01-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ignore attribute if `mailto:` is present ([#145](https://github.com/webpack-contrib/html-loader/issues/145)) ([4b13d4c](https://github.com/webpack-contrib/html-loader/commit/4b13d4c))
|
||||
* **index:** escape double quotes correctly (`options.interpolate`) ([#154](https://github.com/webpack-contrib/html-loader/issues/154)) ([1ef5de4](https://github.com/webpack-contrib/html-loader/commit/1ef5de4))
|
||||
|
||||
|
||||
<a name="0.5.1"></a>
|
||||
## [0.5.1](https://github.com/webpack/html-loader/compare/v0.5.0...v0.5.1) (2017-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Support for empty tags in tag-attribute matching ([#133](https://github.com/webpack/html-loader/issues/133)) ([6efa6de](https://github.com/webpack/html-loader/commit/6efa6de)), closes [#129](https://github.com/webpack/html-loader/issues/129)
|
||||
|
||||
|
||||
|
||||
<a name="0.5.0"></a>
|
||||
# [0.5.0](https://github.com/webpack/html-loader/compare/v0.4.3...v0.5.0) (2017-07-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for empty tags in `tag:attribute` matching ([#129](https://github.com/webpack/html-loader/issues/129)) ([70370dc](https://github.com/webpack/html-loader/commit/70370dc))
|
||||
|
||||
|
||||
<a name="0.4.5"></a>
|
||||
## [0.4.5](https://github.com/webpack/html-loader/compare/v0.4.3...v0.4.5) (2017-07-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* es6 default export ([fae0309](https://github.com/webpack/html-loader/commit/fae0309))
|
||||
* Handle es6 default export ([e04e969](https://github.com/webpack/html-loader/commit/e04e969))
|
||||
* **getOptions:** deprecation warn in loaderUtils ([#114](https://github.com/webpack/html-loader/issues/114)) ([3d47e98](https://github.com/webpack/html-loader/commit/3d47e98))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Adds exportAsDefault ([37d40d8](https://github.com/webpack/html-loader/commit/37d40d8))
|
||||
+20
@@ -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.
|
||||
+834
@@ -0,0 +1,834 @@
|
||||
<div align="center">
|
||||
<img width="200" height="200" src="https://worldvectorlogo.com/logos/html5.svg" alt="html-loader">
|
||||
<a href="https://github.com/webpack/webpack">
|
||||
<img width="200" height="200" vspace="" hspace="25" src="https://webpack.js.org/assets/icon-square-big.svg" alt="webpack">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
[![npm][npm]][npm-url]
|
||||
[![node][node]][node-url]
|
||||
[![deps][deps]][deps-url]
|
||||
[![tests][tests]][tests-url]
|
||||
[![coverage][cover]][cover-url]
|
||||
[![chat][chat]][chat-url]
|
||||
[![size][size]][size-url]
|
||||
|
||||
# html-loader
|
||||
|
||||
Exports HTML as string. HTML is minimized when the compiler demands.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To begin, you'll need to install `html-loader`:
|
||||
|
||||
```console
|
||||
npm install --save-dev html-loader
|
||||
```
|
||||
|
||||
Then add the plugin to your `webpack` config. For example:
|
||||
|
||||
**file.js**
|
||||
|
||||
```js
|
||||
import html from './file.html';
|
||||
```
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| :---------------------------------: | :-----------------: | :------------------------------------------: | :----------------------------------------------- |
|
||||
| **[`attributes`](#attributes)** | `{Boolean\|Object}` | `true` | Enables/Disables attributes handling |
|
||||
| **[`preprocessor`](#preprocessor)** | `{Function}` | `undefined` | Allows pre-processing of content before handling |
|
||||
| **[`minimize`](#minimize)** | `{Boolean\|Object}` | `true` in production mode, otherwise `false` | Tell `html-loader` to minimize HTML |
|
||||
| **[`esModule`](#esmodule)** | `{Boolean}` | `false` | Use ES modules syntax |
|
||||
|
||||
### `attributes`
|
||||
|
||||
Type: `Boolean|Object`
|
||||
Default: `true`
|
||||
|
||||
By default every loadable attributes (for example - `<img src="image.png">`) is imported (`const img = require('./image.png')` or `import img from "./image.png""`).
|
||||
You may need to specify loaders for images in your configuration (recommended `file-loader` or `url-loader`).
|
||||
|
||||
Supported tags and attributes:
|
||||
|
||||
- the `src` attribute of the `audio` tag
|
||||
- the `src` attribute of the `embed` tag
|
||||
- the `src` attribute of the `img` tag
|
||||
- the `srcset` attribute of the `img` tag
|
||||
- the `src` attribute of the `input` tag
|
||||
- the `href` attribute of the `link` tag (only for stylesheets)
|
||||
- the `data` attribute of the `object` tag
|
||||
- the `src` attribute of the `script` tag
|
||||
- the `href` attribute of the `script` tag
|
||||
- the `xlink:href` attribute of the `script` tag
|
||||
- the `src` attribute of the `source` tag
|
||||
- the `srcset` attribute of the `source` tag
|
||||
- the `src` attribute of the `track` tag
|
||||
- the `poster` attribute of the `video` tag
|
||||
- the `src` attribute of the `video` tag
|
||||
- the `xlink:href` attribute of the `image` tag
|
||||
- the `href` attribute of the `image` tag
|
||||
- the `xlink:href` attribute of the `use` tag
|
||||
- the `href` attribute of the `use` tag
|
||||
|
||||
#### `Boolean`
|
||||
|
||||
The `true` value enables processing of all default elements and attributes, the `false` disable processing of all attributes.
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
// Disables attributes processing
|
||||
attributes: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### `Object`
|
||||
|
||||
Allows you to specify which tags and attributes to process, filter them, filter urls and process sources starts with `/`.
|
||||
|
||||
For example:
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
attributes: {
|
||||
list: [
|
||||
// All default supported tags and attributes
|
||||
'...',
|
||||
{
|
||||
tag: 'img',
|
||||
attribute: 'data-src',
|
||||
type: 'src',
|
||||
},
|
||||
{
|
||||
tag: 'img',
|
||||
attribute: 'data-srcset',
|
||||
type: 'srcset',
|
||||
},
|
||||
],
|
||||
urlFilter: (attribute, value, resourcePath) => {
|
||||
// The `attribute` argument contains a name of the HTML attribute.
|
||||
// The `value` argument contains a value of the HTML attribute.
|
||||
// The `resourcePath` argument contains a path to the loaded HTML file.
|
||||
|
||||
if (/example\.pdf$/.test(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
root: '.',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### `list`
|
||||
|
||||
Type: `Array`
|
||||
Default: [supported tags and attributes](#attributes).
|
||||
|
||||
Allows to setup which tags and attributes to process and how, and the ability to filter some of them.
|
||||
|
||||
Using `...` syntax allows you to extend [default supported tags and attributes](#attributes).
|
||||
|
||||
For example:
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
attributes: {
|
||||
list: [
|
||||
// All default supported tags and attributes
|
||||
'...',
|
||||
{
|
||||
tag: 'img',
|
||||
attribute: 'data-src',
|
||||
type: 'src',
|
||||
},
|
||||
{
|
||||
tag: 'img',
|
||||
attribute: 'data-srcset',
|
||||
type: 'srcset',
|
||||
},
|
||||
{
|
||||
// Tag name
|
||||
tag: 'link',
|
||||
// Attribute name
|
||||
attribute: 'href',
|
||||
// Type of processing, can be `src` or `scrset`
|
||||
type: 'src',
|
||||
// Allow to filter some attributes
|
||||
filter: (tag, attribute, attributes, resourcePath) => {
|
||||
// The `tag` argument contains a name of the HTML tag.
|
||||
// The `attribute` argument contains a name of the HTML attribute.
|
||||
// The `attributes` argument contains all attributes of the tag.
|
||||
// The `resourcePath` argument contains a path to the loaded HTML file.
|
||||
|
||||
if (/my-html\.html$/.test(resourcePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!/stylesheet/i.test(attributes.rel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
attributes.type &&
|
||||
attributes.type.trim().toLowerCase() !== 'text/css'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
If the tag name is not specified it will process all the tags.
|
||||
|
||||
> You can use your custom filter to specify html elements to be processed.
|
||||
|
||||
For example:
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
attributes: {
|
||||
list: [
|
||||
{
|
||||
// Attribute name
|
||||
attribute: 'src',
|
||||
// Type of processing, can be `src` or `scrset`
|
||||
type: 'src',
|
||||
// Allow to filter some attributes (optional)
|
||||
filter: (tag, attribute, attributes, resourcePath) => {
|
||||
// The `tag` argument contains a name of the HTML tag.
|
||||
// The `attribute` argument contains a name of the HTML attribute.
|
||||
// The `attributes` argument contains all attributes of the tag.
|
||||
// The `resourcePath` argument contains a path to the loaded HTML file.
|
||||
|
||||
// choose all HTML tags except img tag
|
||||
return tag.toLowerCase() !== 'img';
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### `urlFilter`
|
||||
|
||||
Type: `Function`
|
||||
Default: `undefined`
|
||||
|
||||
Allow to filter urls. All filtered urls will not be resolved (left in the code as they were written).
|
||||
All non requestable sources (for example `<img src="javascript:void(0)">`) do not handle by default.
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
attributes: {
|
||||
urlFilter: (attribute, value, resourcePath) => {
|
||||
// The `attribute` argument contains a name of the HTML attribute.
|
||||
// The `value` argument contains a value of the HTML attribute.
|
||||
// The `resourcePath` argument contains a path to the loaded HTML file.
|
||||
|
||||
if (/example\.pdf$/.test(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### `root`
|
||||
|
||||
Type: `String`
|
||||
Default: `undefined`
|
||||
|
||||
For urls that start with a `/`, the default behavior is to not translate them.
|
||||
If a `root` query parameter is set, however, it will be prepended to the url and then translated.
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
attributes: {
|
||||
root: '.',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### `preprocessor`
|
||||
|
||||
Type: `Function`
|
||||
Default: `undefined`
|
||||
|
||||
Allows pre-processing of content before handling.
|
||||
|
||||
> ⚠ You should always return valid HTML
|
||||
|
||||
**file.hbs**
|
||||
|
||||
```hbs
|
||||
<div>
|
||||
<p>{{firstname}} {{lastname}}</p>
|
||||
<img src="image.png" alt="alt" />
|
||||
<div>
|
||||
```
|
||||
|
||||
#### `Function`
|
||||
|
||||
You can set the `preprocessor` option as a `Function` instance.
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
const Handlebars = require('handlebars');
|
||||
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.hbs$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
preprocessor: (content, loaderContext) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = Handlebars.compile(content)({
|
||||
firstname: 'Value',
|
||||
lastname: 'OtherValue',
|
||||
});
|
||||
} catch (error) {
|
||||
loaderContext.emitError(error);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
You can also set the `preprocessor` option as an asynchronous function instance.
|
||||
|
||||
For example:
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
const Handlebars = require('handlebars');
|
||||
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.hbs$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
preprocessor: async (content, loaderContext) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = await Handlebars.compile(content)({
|
||||
firstname: 'Value',
|
||||
lastname: 'OtherValue',
|
||||
});
|
||||
} catch (error) {
|
||||
await loaderContext.emitError(error);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### `minimize`
|
||||
|
||||
Type: `Boolean|Object`
|
||||
Default: `true` in production mode, otherwise `false`
|
||||
|
||||
Tell `html-loader` to minimize HTML.
|
||||
|
||||
#### `Boolean`
|
||||
|
||||
The enabled rules for minimizing by default are the following ones:
|
||||
|
||||
```js
|
||||
({
|
||||
caseSensitive: true,
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true,
|
||||
keepClosingSlash: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
removeComments: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
});
|
||||
```
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
minimize: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
#### `Object`
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
See [html-minifier-terser](https://github.com/DanielRuf/html-minifier-terser)'s documentation for more information on the available options.
|
||||
|
||||
The rules can be disabled using the following options in your `webpack.conf.js`
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
minimize: {
|
||||
removeComments: false,
|
||||
collapseWhitespace: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### `esModule`
|
||||
|
||||
Type: `Boolean`
|
||||
Default: `false`
|
||||
|
||||
By default, `html-loader` generates JS modules that use the CommonJS modules syntax.
|
||||
There are some cases in which using ES modules is beneficial, like in the case of [module concatenation](https://webpack.js.org/plugins/module-concatenation-plugin/) and [tree shaking](https://webpack.js.org/guides/tree-shaking/).
|
||||
|
||||
You can enable a ES module syntax using:
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
esModule: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### CDN
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.jpg$/, loader: 'file-loader' },
|
||||
{ test: /\.png$/, loader: 'url-loader' },
|
||||
],
|
||||
},
|
||||
output: {
|
||||
publicPath: 'http://cdn.example.com/[hash]/',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**file.html**
|
||||
|
||||
```html
|
||||
<img src="image.jpg" data-src="image2x.png" />
|
||||
```
|
||||
|
||||
**index.js**
|
||||
|
||||
```js
|
||||
require('html-loader!./file.html');
|
||||
|
||||
// => '<img src="http://cdn.example.com/49eba9f/a992ca.jpg" data-src="image2x.png">'
|
||||
```
|
||||
|
||||
```js
|
||||
require('html-loader?{"attributes":{"list":[{"tag":"img","attribute":"data-src","type":"src"}]}}!./file.html');
|
||||
|
||||
// => '<img src="image.jpg" data-src="data:image/png;base64,..." >'
|
||||
```
|
||||
|
||||
```js
|
||||
require('html-loader?{"attributes":{"list":[{"tag":"img","attribute":"src","type":"src"},{"tag":"img","attribute":"data-src","type":"src"}]}}!./file.html');
|
||||
|
||||
// => '<img src="http://cdn.example.com/49eba9f/a992ca.jpg" data-src="data:image/png;base64,..." >'
|
||||
```
|
||||
|
||||
```js
|
||||
require('html-loader?-attributes!./file.html');
|
||||
|
||||
// => '<img src="image.jpg" data-src="image2x.png" >'
|
||||
```
|
||||
|
||||
> :warning: `-attributes` sets `attributes: false`.
|
||||
|
||||
### Process `script` and `link` tags
|
||||
|
||||
**script.file.js**
|
||||
|
||||
```js
|
||||
console.log(document);
|
||||
```
|
||||
|
||||
**style.file.css**
|
||||
|
||||
```css
|
||||
a {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
**file.html**
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Title of the document</title>
|
||||
<link rel="stylesheet" type="text/css" href="./style.file.css" />
|
||||
</head>
|
||||
<body>
|
||||
Content of the document......
|
||||
<script src="./script.file.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
use: ['file-loader?name=[name].[ext]', 'extract-loader', 'html-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.js$/i,
|
||||
exclude: /\.file.js$/i,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.file.js$/i,
|
||||
loader: 'file-loader',
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
exclude: /\.file.css$/i,
|
||||
loader: 'css-loader',
|
||||
},
|
||||
{
|
||||
test: /\.file.css$/i,
|
||||
loader: 'file-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 'Root-relative' URLs
|
||||
|
||||
With the same configuration as in the CDN example:
|
||||
|
||||
**file.html**
|
||||
|
||||
```html
|
||||
<img src="/image.jpg" />
|
||||
```
|
||||
|
||||
**scripts.js**
|
||||
|
||||
```js
|
||||
require('html-loader!./file.html');
|
||||
|
||||
// => '<img src="/image.jpg">'
|
||||
```
|
||||
|
||||
**other-scripts.js**
|
||||
|
||||
```js
|
||||
require('html-loader?{"attributes":{"root":"."}}!./file.html');
|
||||
|
||||
// => '<img src="http://cdn.example.com/49eba9f/a992ca.jpg">'
|
||||
```
|
||||
|
||||
### Templating
|
||||
|
||||
You can use any template system. Below is an example for [handlebars](https://handlebarsjs.com/).
|
||||
|
||||
**file.hbs**
|
||||
|
||||
```hbs
|
||||
<div>
|
||||
<p>{{firstname}} {{lastname}}</p>
|
||||
<img src="image.png" alt="alt" />
|
||||
<div>
|
||||
```
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
const Handlebars = require('handlebars');
|
||||
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.hbs$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
preprocessor: (content, loaderContext) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = Handlebars.compile(content)({
|
||||
firstname: 'Value',
|
||||
lastname: 'OtherValue',
|
||||
});
|
||||
} catch (error) {
|
||||
loaderContext.emitError(error);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### PostHTML
|
||||
|
||||
You can use [PostHTML](https://github.com/posthtml/posthtml) without any additional loaders.
|
||||
|
||||
**file.html**
|
||||
|
||||
```html
|
||||
<img src="image.jpg" />
|
||||
```
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
const posthtml = require('posthtml');
|
||||
const posthtmlWebp = require('posthtml-webp');
|
||||
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.hbs$/i,
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
preprocessor: (content, loaderContext) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = posthtml().use(plugin).process(content, { sync: true });
|
||||
} catch (error) {
|
||||
loaderContext.emitError(error);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
return result.html;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Export into HTML files
|
||||
|
||||
A very common scenario is exporting the HTML into their own _.html_ file, to
|
||||
serve them directly instead of injecting with javascript. This can be achieved
|
||||
with a combination of 3 loaders:
|
||||
|
||||
- [file-loader](https://github.com/webpack/file-loader)
|
||||
- [extract-loader](https://github.com/peerigon/extract-loader)
|
||||
- html-loader
|
||||
|
||||
The html-loader will parse the URLs, require the images and everything you
|
||||
expect. The extract loader will parse the javascript back into a proper html
|
||||
file, ensuring images are required and point to proper path, and the file loader
|
||||
will write the _.html_ file for you. Example:
|
||||
|
||||
**webpack.config.js**
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.html$/i,
|
||||
use: ['file-loader?name=[name].[ext]', 'extract-loader', 'html-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Please take a moment to read our contributing guidelines if you haven't yet done so.
|
||||
|
||||
[CONTRIBUTING](./.github/CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
[npm]: https://img.shields.io/npm/v/html-loader.svg
|
||||
[npm-url]: https://npmjs.com/package/html-loader
|
||||
[node]: https://img.shields.io/node/v/html-loader.svg
|
||||
[node-url]: https://nodejs.org
|
||||
[deps]: https://david-dm.org/webpack-contrib/html-loader.svg
|
||||
[deps-url]: https://david-dm.org/webpack-contrib/html-loader
|
||||
[tests]: https://github.com/webpack-contrib/html-loader/workflows/html-loader/badge.svg
|
||||
[tests-url]: https://github.com/webpack-contrib/html-loader/actions
|
||||
[cover]: https://codecov.io/gh/webpack-contrib/html-loader/branch/master/graph/badge.svg
|
||||
[cover-url]: https://codecov.io/gh/webpack-contrib/html-loader
|
||||
[chat]: https://img.shields.io/badge/gitter-webpack%2Fwebpack-brightgreen.svg
|
||||
[chat-url]: https://gitter.im/webpack/webpack
|
||||
[size]: https://packagephobia.now.sh/badge?p=html-loader
|
||||
[size-url]: https://packagephobia.now.sh/result?p=html-loader
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
function getIndices(value) {
|
||||
const result = [];
|
||||
let index = value.indexOf('\n');
|
||||
|
||||
while (index !== -1) {
|
||||
result.push(index + 1);
|
||||
index = value.indexOf('\n', index + 1);
|
||||
}
|
||||
|
||||
result.push(value.length + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
function offsetToPosition(source, offset) {
|
||||
let index = -1;
|
||||
const indices = getIndices(source);
|
||||
const {
|
||||
length
|
||||
} = indices;
|
||||
|
||||
if (offset < 0) {
|
||||
return {};
|
||||
} // eslint-disable-next-line no-plusplus
|
||||
|
||||
|
||||
while (++index < length) {
|
||||
if (indices[index] > offset) {
|
||||
return {
|
||||
line: index + 1,
|
||||
column: offset - (indices[index - 1] || 0) + 1,
|
||||
offset
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
class HtmlSourceError extends Error {
|
||||
constructor(error, startIndex, endIndex, source) {
|
||||
super(error);
|
||||
this.name = 'HtmlSourceError';
|
||||
this.message = `${this.name}: ${this.message}`;
|
||||
this.startIndex = startIndex;
|
||||
this.endIndex = endIndex;
|
||||
this.source = source;
|
||||
const startPosition = offsetToPosition(source, this.startIndex);
|
||||
const endPosition = offsetToPosition(source, this.endIndex);
|
||||
this.message += ` (From line ${startPosition.line}, column ${startPosition.column}; to line ${endPosition.line}, column ${endPosition.column})`; // We don't need stack
|
||||
|
||||
this.stack = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.default = HtmlSourceError;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const loader = require('./index');
|
||||
|
||||
module.exports = loader.default;
|
||||
module.exports.raw = loader.raw;
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = loader;
|
||||
|
||||
var _loaderUtils = require("loader-utils");
|
||||
|
||||
var _schemaUtils = require("schema-utils");
|
||||
|
||||
var _plugins = require("./plugins");
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
var _options = _interopRequireDefault(require("./options.json"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
async function loader(content) {
|
||||
const rawOptions = (0, _loaderUtils.getOptions)(this);
|
||||
(0, _schemaUtils.validate)(_options.default, rawOptions, {
|
||||
name: 'HTML Loader',
|
||||
baseDataPath: 'options'
|
||||
});
|
||||
const options = (0, _utils.normalizeOptions)(rawOptions, this);
|
||||
|
||||
if (options.preprocessor) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
content = await options.preprocessor(content, this);
|
||||
}
|
||||
|
||||
const plugins = [];
|
||||
const errors = [];
|
||||
const imports = [];
|
||||
const replacements = [];
|
||||
|
||||
if (options.attributes) {
|
||||
plugins.push((0, _plugins.sourcePlugin)({
|
||||
urlHandler: url => (0, _loaderUtils.stringifyRequest)(this, url),
|
||||
attributes: options.attributes,
|
||||
resourcePath: this.resourcePath,
|
||||
imports,
|
||||
errors,
|
||||
replacements
|
||||
}));
|
||||
}
|
||||
|
||||
if (options.minimize) {
|
||||
plugins.push((0, _plugins.minimizerPlugin)({
|
||||
minimize: options.minimize,
|
||||
errors
|
||||
}));
|
||||
}
|
||||
|
||||
const {
|
||||
html
|
||||
} = (0, _utils.pluginRunner)(plugins).process(content);
|
||||
|
||||
for (const error of errors) {
|
||||
this.emitError(error instanceof Error ? error : new Error(error));
|
||||
}
|
||||
|
||||
const importCode = (0, _utils.getImportCode)(html, this, imports, options);
|
||||
const moduleCode = (0, _utils.getModuleCode)(html, replacements, options);
|
||||
const exportCode = (0, _utils.getExportCode)(html, options);
|
||||
return `${importCode}${moduleCode}${exportCode}`;
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"Attribute": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"attribute": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"type": {
|
||||
"enum": ["src", "srcset"]
|
||||
},
|
||||
"filter": {
|
||||
"instanceof": "Function"
|
||||
}
|
||||
},
|
||||
"required": ["attribute", "type"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"enum": ["..."]
|
||||
}
|
||||
]
|
||||
},
|
||||
"AttributeList": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Attribute"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"preprocessor": {
|
||||
"instanceof": "Function"
|
||||
},
|
||||
"attributes": {
|
||||
"anyOf": [
|
||||
{ "type": "boolean" },
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"list": {
|
||||
"$ref": "#/definitions/AttributeList"
|
||||
},
|
||||
"urlFilter": {
|
||||
"instanceof": "Function"
|
||||
},
|
||||
"root": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"minimize": {
|
||||
"anyOf": [{ "type": "boolean" }, { "type": "object" }]
|
||||
},
|
||||
"esModule": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "sourcePlugin", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _sourcePlugin.default;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "minimizerPlugin", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _minimizerPlugin.default;
|
||||
}
|
||||
});
|
||||
|
||||
var _sourcePlugin = _interopRequireDefault(require("./source-plugin"));
|
||||
|
||||
var _minimizerPlugin = _interopRequireDefault(require("./minimizer-plugin"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _htmlMinifierTerser = require("html-minifier-terser");
|
||||
|
||||
var _default = options => function process(html) {
|
||||
try {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
html = (0, _htmlMinifierTerser.minify)(html, options.minimize);
|
||||
} catch (error) {
|
||||
options.errors.push(error);
|
||||
}
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
exports.default = _default;
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _htmlparser = require("htmlparser2");
|
||||
|
||||
var _loaderUtils = require("loader-utils");
|
||||
|
||||
var _HtmlSourceError = _interopRequireDefault(require("../HtmlSourceError"));
|
||||
|
||||
var _utils = require("../utils");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var _default = options => function process(html) {
|
||||
const {
|
||||
list,
|
||||
urlFilter: maybeUrlFilter,
|
||||
root
|
||||
} = options.attributes;
|
||||
const sources = [];
|
||||
const urlFilter = (0, _utils.getFilter)(maybeUrlFilter, value => (0, _loaderUtils.isUrlRequest)(value, root));
|
||||
|
||||
const getAttribute = (tag, attribute, attributes, resourcePath) => {
|
||||
return list.find(element => {
|
||||
const foundTag = typeof element.tag === 'undefined' || typeof element.tag !== 'undefined' && element.tag.toLowerCase() === tag.toLowerCase();
|
||||
const foundAttribute = element.attribute.toLowerCase() === attribute.toLowerCase();
|
||||
const isNotFiltered = element.filter ? element.filter(tag, attribute, attributes, resourcePath) : true;
|
||||
return foundTag && foundAttribute && isNotFiltered;
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
resourcePath
|
||||
} = options;
|
||||
const parser = new _htmlparser.Parser({
|
||||
attributesMeta: {},
|
||||
|
||||
onattribute(name, value) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const endIndex = parser._tokenizer._index;
|
||||
const startIndex = endIndex - value.length;
|
||||
const unquoted = html[endIndex] !== '"' && html[endIndex] !== "'";
|
||||
this.attributesMeta[name] = {
|
||||
startIndex,
|
||||
unquoted
|
||||
};
|
||||
},
|
||||
|
||||
onopentag(tag, attributes) {
|
||||
Object.keys(attributes).forEach(attribute => {
|
||||
const value = attributes[attribute];
|
||||
const {
|
||||
startIndex: valueStartIndex,
|
||||
unquoted
|
||||
} = this.attributesMeta[attribute];
|
||||
const foundAttribute = getAttribute(tag, attribute, attributes, resourcePath);
|
||||
|
||||
if (!foundAttribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
type
|
||||
} = foundAttribute; // eslint-disable-next-line default-case
|
||||
|
||||
switch (type) {
|
||||
case 'src':
|
||||
{
|
||||
let source;
|
||||
|
||||
try {
|
||||
source = (0, _utils.parseSrc)(value);
|
||||
} catch (error) {
|
||||
options.errors.push(new _HtmlSourceError.default(`Bad value for attribute "${attribute}" on element "${tag}": ${error.message}`, parser.startIndex, parser.endIndex, html));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(0, _utils.isUrlRequestable)(source.value, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startIndex = valueStartIndex + source.startIndex;
|
||||
const endIndex = startIndex + source.value.length;
|
||||
sources.push({
|
||||
name: attribute,
|
||||
value: source.value,
|
||||
unquoted,
|
||||
startIndex,
|
||||
endIndex
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'srcset':
|
||||
{
|
||||
let sourceSet;
|
||||
|
||||
try {
|
||||
sourceSet = (0, _utils.parseSrcset)(value);
|
||||
} catch (error) {
|
||||
options.errors.push(new _HtmlSourceError.default(`Bad value for attribute "${attribute}" on element "${tag}": ${error.message}`, parser.startIndex, parser.endIndex, html));
|
||||
return;
|
||||
}
|
||||
|
||||
sourceSet.forEach(sourceItem => {
|
||||
const {
|
||||
source
|
||||
} = sourceItem;
|
||||
const startIndex = valueStartIndex + source.startIndex;
|
||||
const endIndex = startIndex + source.value.length;
|
||||
|
||||
if (!(0, _utils.isUrlRequestable)(source.value, root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sources.push({
|
||||
name: attribute,
|
||||
value: source.value,
|
||||
unquoted,
|
||||
startIndex,
|
||||
endIndex
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Need improve
|
||||
// case 'include': {
|
||||
// let source;
|
||||
//
|
||||
// // eslint-disable-next-line no-underscore-dangle
|
||||
// if (parser._tokenizer._state === 4) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// source = parseSrc(value);
|
||||
// } catch (error) {
|
||||
// options.errors.push(
|
||||
// new HtmlSourceError(
|
||||
// `Bad value for attribute "${attribute}" on element "${tag}": ${error.message}`,
|
||||
// parser.startIndex,
|
||||
// parser.endIndex,
|
||||
// html
|
||||
// )
|
||||
// );
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (!urlFilter(attribute, source.value, resourcePath)) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// const { startIndex } = parser;
|
||||
// const closingTag = html
|
||||
// .slice(startIndex - 1)
|
||||
// .match(
|
||||
// new RegExp(`<s*${tag}[^>]*>(?:.*?)</${tag}[^<>]*>`, 's')
|
||||
// );
|
||||
//
|
||||
// if (!closingTag) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// const endIndex = startIndex + closingTag[0].length;
|
||||
// const importItem = getImportItem(source.value);
|
||||
// const replacementItem = getReplacementItem(importItem);
|
||||
//
|
||||
// sources.push({ replacementItem, startIndex, endIndex });
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
});
|
||||
this.attributesMeta = {};
|
||||
},
|
||||
|
||||
onerror(error) {
|
||||
options.errors.push(error);
|
||||
}
|
||||
|
||||
}, {
|
||||
decodeEntities: false,
|
||||
lowerCaseTags: false,
|
||||
lowerCaseAttributeNames: false,
|
||||
recognizeCDATA: true,
|
||||
recognizeSelfClosing: true
|
||||
});
|
||||
parser.write(html);
|
||||
parser.end();
|
||||
const imports = new Map();
|
||||
const replacements = new Map();
|
||||
let offset = 0;
|
||||
|
||||
for (const source of sources) {
|
||||
const {
|
||||
name,
|
||||
value,
|
||||
unquoted,
|
||||
startIndex,
|
||||
endIndex
|
||||
} = source;
|
||||
let normalizedUrl = value;
|
||||
let prefix = '';
|
||||
const queryParts = normalizedUrl.split('!');
|
||||
|
||||
if (queryParts.length > 1) {
|
||||
normalizedUrl = queryParts.pop();
|
||||
prefix = queryParts.join('!');
|
||||
}
|
||||
|
||||
normalizedUrl = (0, _utils.normalizeUrl)(normalizedUrl);
|
||||
|
||||
if (!urlFilter(name, value, resourcePath)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
|
||||
let hash;
|
||||
const indexHash = normalizedUrl.lastIndexOf('#');
|
||||
|
||||
if (indexHash >= 0) {
|
||||
hash = normalizedUrl.substr(indexHash, indexHash);
|
||||
normalizedUrl = normalizedUrl.substr(0, indexHash);
|
||||
}
|
||||
|
||||
const request = (0, _utils.requestify)(normalizedUrl, root);
|
||||
const newUrl = prefix ? `${prefix}!${request}` : request;
|
||||
const importKey = newUrl;
|
||||
let importName = imports.get(importKey);
|
||||
|
||||
if (!importName) {
|
||||
importName = `___HTML_LOADER_IMPORT_${imports.size}___`;
|
||||
imports.set(importKey, importName);
|
||||
options.imports.push({
|
||||
importName,
|
||||
source: options.urlHandler(newUrl)
|
||||
});
|
||||
}
|
||||
|
||||
const replacementKey = JSON.stringify({
|
||||
newUrl,
|
||||
unquoted,
|
||||
hash
|
||||
});
|
||||
let replacementName = replacements.get(replacementKey);
|
||||
|
||||
if (!replacementName) {
|
||||
replacementName = `___HTML_LOADER_REPLACEMENT_${replacements.size}___`;
|
||||
replacements.set(replacementKey, replacementName);
|
||||
options.replacements.push({
|
||||
replacementName,
|
||||
importName,
|
||||
hash,
|
||||
unquoted
|
||||
});
|
||||
} // eslint-disable-next-line no-param-reassign
|
||||
|
||||
|
||||
html = html.slice(0, startIndex + offset) + replacementName + html.slice(endIndex + offset);
|
||||
offset += startIndex + replacementName.length - endIndex;
|
||||
}
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
exports.default = _default;
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function (url, options) {
|
||||
if (!options) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
options = {};
|
||||
} // eslint-disable-next-line no-underscore-dangle, no-param-reassign
|
||||
|
||||
|
||||
url = url && url.__esModule ? url.default : url;
|
||||
|
||||
if (typeof url !== 'string') {
|
||||
return url;
|
||||
}
|
||||
|
||||
if (options.hash) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
url += options.hash;
|
||||
}
|
||||
|
||||
if (options.maybeNeedQuotes && /[\t\n\f\r "'=<>`]/.test(url)) {
|
||||
return "\"".concat(url, "\"");
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
+655
@@ -0,0 +1,655 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.parseSrcset = parseSrcset;
|
||||
exports.parseSrc = parseSrc;
|
||||
exports.normalizeUrl = normalizeUrl;
|
||||
exports.requestify = requestify;
|
||||
exports.isUrlRequestable = isUrlRequestable;
|
||||
exports.normalizeOptions = normalizeOptions;
|
||||
exports.pluginRunner = pluginRunner;
|
||||
exports.getFilter = getFilter;
|
||||
exports.getImportCode = getImportCode;
|
||||
exports.getModuleCode = getModuleCode;
|
||||
exports.getExportCode = getExportCode;
|
||||
|
||||
var _loaderUtils = require("loader-utils");
|
||||
|
||||
function isASCIIWhitespace(character) {
|
||||
return (// Horizontal tab
|
||||
character === '\u0009' || // New line
|
||||
character === '\u000A' || // Form feed
|
||||
character === '\u000C' || // Carriage return
|
||||
character === '\u000D' || // Space
|
||||
character === '\u0020'
|
||||
);
|
||||
} // (Don't use \s, to avoid matching non-breaking space)
|
||||
// eslint-disable-next-line no-control-regex
|
||||
|
||||
|
||||
const regexLeadingSpaces = /^[ \t\n\r\u000c]+/; // eslint-disable-next-line no-control-regex
|
||||
|
||||
const regexLeadingCommasOrSpaces = /^[, \t\n\r\u000c]+/; // eslint-disable-next-line no-control-regex
|
||||
|
||||
const regexLeadingNotSpaces = /^[^ \t\n\r\u000c]+/;
|
||||
const regexTrailingCommas = /[,]+$/;
|
||||
const regexNonNegativeInteger = /^\d+$/; // ( Positive or negative or unsigned integers or decimals, without or without exponents.
|
||||
// Must include at least one digit.
|
||||
// According to spec tests any decimal point must be followed by a digit.
|
||||
// No leading plus sign is allowed.)
|
||||
// https://html.spec.whatwg.org/multipage/infrastructure.html#valid-floating-point-number
|
||||
|
||||
const regexFloatingPoint = /^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
|
||||
|
||||
function parseSrcset(input) {
|
||||
// 1. Let input be the value passed to this algorithm.
|
||||
const inputLength = input.length;
|
||||
let url;
|
||||
let descriptors;
|
||||
let currentDescriptor;
|
||||
let state;
|
||||
let c; // 2. Let position be a pointer into input, initially pointing at the start
|
||||
// of the string.
|
||||
|
||||
let position = 0;
|
||||
let startUrlPosition; // eslint-disable-next-line consistent-return
|
||||
|
||||
function collectCharacters(regEx) {
|
||||
let chars;
|
||||
const match = regEx.exec(input.substring(position));
|
||||
|
||||
if (match) {
|
||||
[chars] = match;
|
||||
position += chars.length;
|
||||
return chars;
|
||||
}
|
||||
} // 3. Let candidates be an initially empty source set.
|
||||
|
||||
|
||||
const candidates = []; // 4. Splitting loop: Collect a sequence of characters that are space
|
||||
// characters or U+002C COMMA characters. If any U+002C COMMA characters
|
||||
// were collected, that is a parse error.
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
|
||||
while (true) {
|
||||
collectCharacters(regexLeadingCommasOrSpaces); // 5. If position is past the end of input, return candidates and abort these steps.
|
||||
|
||||
if (position >= inputLength) {
|
||||
if (candidates.length === 0) {
|
||||
throw new Error('Must contain one or more image candidate strings');
|
||||
} // (we're done, this is the sole return path)
|
||||
|
||||
|
||||
return candidates;
|
||||
} // 6. Collect a sequence of characters that are not space characters,
|
||||
// and let that be url.
|
||||
|
||||
|
||||
startUrlPosition = position;
|
||||
url = collectCharacters(regexLeadingNotSpaces); // 7. Let descriptors be a new empty list.
|
||||
|
||||
descriptors = []; // 8. If url ends with a U+002C COMMA character (,), follow these substeps:
|
||||
// (1). Remove all trailing U+002C COMMA characters from url. If this removed
|
||||
// more than one character, that is a parse error.
|
||||
|
||||
if (url.slice(-1) === ',') {
|
||||
url = url.replace(regexTrailingCommas, ''); // (Jump ahead to step 9 to skip tokenization and just push the candidate).
|
||||
|
||||
parseDescriptors();
|
||||
} // Otherwise, follow these substeps:
|
||||
else {
|
||||
tokenize();
|
||||
} // 16. Return to the step labeled splitting loop.
|
||||
|
||||
}
|
||||
/**
|
||||
* Tokenizes descriptor properties prior to parsing
|
||||
* Returns undefined.
|
||||
*/
|
||||
|
||||
|
||||
function tokenize() {
|
||||
// 8.1. Descriptor tokenizer: Skip whitespace
|
||||
collectCharacters(regexLeadingSpaces); // 8.2. Let current descriptor be the empty string.
|
||||
|
||||
currentDescriptor = ''; // 8.3. Let state be in descriptor.
|
||||
|
||||
state = 'in descriptor'; // eslint-disable-next-line no-constant-condition
|
||||
|
||||
while (true) {
|
||||
// 8.4. Let c be the character at position.
|
||||
c = input.charAt(position); // Do the following depending on the value of state.
|
||||
// For the purpose of this step, "EOF" is a special character representing
|
||||
// that position is past the end of input.
|
||||
// In descriptor
|
||||
|
||||
if (state === 'in descriptor') {
|
||||
// Do the following, depending on the value of c:
|
||||
// Space character
|
||||
// If current descriptor is not empty, append current descriptor to
|
||||
// descriptors and let current descriptor be the empty string.
|
||||
// Set state to after descriptor.
|
||||
if (isASCIIWhitespace(c)) {
|
||||
if (currentDescriptor) {
|
||||
descriptors.push(currentDescriptor);
|
||||
currentDescriptor = '';
|
||||
state = 'after descriptor';
|
||||
}
|
||||
} // U+002C COMMA (,)
|
||||
// Advance position to the next character in input. If current descriptor
|
||||
// is not empty, append current descriptor to descriptors. Jump to the step
|
||||
// labeled descriptor parser.
|
||||
else if (c === ',') {
|
||||
position += 1;
|
||||
|
||||
if (currentDescriptor) {
|
||||
descriptors.push(currentDescriptor);
|
||||
}
|
||||
|
||||
parseDescriptors();
|
||||
return;
|
||||
} // U+0028 LEFT PARENTHESIS (()
|
||||
// Append c to current descriptor. Set state to in parens.
|
||||
else if (c === '\u0028') {
|
||||
currentDescriptor += c;
|
||||
state = 'in parens';
|
||||
} // EOF
|
||||
// If current descriptor is not empty, append current descriptor to
|
||||
// descriptors. Jump to the step labeled descriptor parser.
|
||||
else if (c === '') {
|
||||
if (currentDescriptor) {
|
||||
descriptors.push(currentDescriptor);
|
||||
}
|
||||
|
||||
parseDescriptors();
|
||||
return; // Anything else
|
||||
// Append c to current descriptor.
|
||||
} else {
|
||||
currentDescriptor += c;
|
||||
}
|
||||
} // In parens
|
||||
else if (state === 'in parens') {
|
||||
// U+0029 RIGHT PARENTHESIS ())
|
||||
// Append c to current descriptor. Set state to in descriptor.
|
||||
if (c === ')') {
|
||||
currentDescriptor += c;
|
||||
state = 'in descriptor';
|
||||
} // EOF
|
||||
// Append current descriptor to descriptors. Jump to the step labeled
|
||||
// descriptor parser.
|
||||
else if (c === '') {
|
||||
descriptors.push(currentDescriptor);
|
||||
parseDescriptors();
|
||||
return;
|
||||
} // Anything else
|
||||
// Append c to current descriptor.
|
||||
else {
|
||||
currentDescriptor += c;
|
||||
}
|
||||
} // After descriptor
|
||||
else if (state === 'after descriptor') {
|
||||
// Do the following, depending on the value of c:
|
||||
if (isASCIIWhitespace(c)) {// Space character: Stay in this state.
|
||||
} // EOF: Jump to the step labeled descriptor parser.
|
||||
else if (c === '') {
|
||||
parseDescriptors();
|
||||
return;
|
||||
} // Anything else
|
||||
// Set state to in descriptor. Set position to the previous character in input.
|
||||
else {
|
||||
state = 'in descriptor';
|
||||
position -= 1;
|
||||
}
|
||||
} // Advance position to the next character in input.
|
||||
|
||||
|
||||
position += 1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Adds descriptor properties to a candidate, pushes to the candidates array
|
||||
* @return undefined
|
||||
*/
|
||||
// Declared outside of the while loop so that it's only created once.
|
||||
|
||||
|
||||
function parseDescriptors() {
|
||||
// 9. Descriptor parser: Let error be no.
|
||||
let pError = false; // 10. Let width be absent.
|
||||
// 11. Let density be absent.
|
||||
// 12. Let future-compat-h be absent. (We're implementing it now as h)
|
||||
|
||||
let w;
|
||||
let d;
|
||||
let h;
|
||||
let i;
|
||||
const candidate = {};
|
||||
let desc;
|
||||
let lastChar;
|
||||
let value;
|
||||
let intVal;
|
||||
let floatVal; // 13. For each descriptor in descriptors, run the appropriate set of steps
|
||||
// from the following list:
|
||||
|
||||
for (i = 0; i < descriptors.length; i++) {
|
||||
desc = descriptors[i];
|
||||
lastChar = desc[desc.length - 1];
|
||||
value = desc.substring(0, desc.length - 1);
|
||||
intVal = parseInt(value, 10);
|
||||
floatVal = parseFloat(value); // If the descriptor consists of a valid non-negative integer followed by
|
||||
// a U+0077 LATIN SMALL LETTER W character
|
||||
|
||||
if (regexNonNegativeInteger.test(value) && lastChar === 'w') {
|
||||
// If width and density are not both absent, then let error be yes.
|
||||
if (w || d) {
|
||||
pError = true;
|
||||
} // Apply the rules for parsing non-negative integers to the descriptor.
|
||||
// If the result is zero, let error be yes.
|
||||
// Otherwise, let width be the result.
|
||||
|
||||
|
||||
if (intVal === 0) {
|
||||
pError = true;
|
||||
} else {
|
||||
w = intVal;
|
||||
}
|
||||
} // If the descriptor consists of a valid floating-point number followed by
|
||||
// a U+0078 LATIN SMALL LETTER X character
|
||||
else if (regexFloatingPoint.test(value) && lastChar === 'x') {
|
||||
// If width, density and future-compat-h are not all absent, then let error
|
||||
// be yes.
|
||||
if (w || d || h) {
|
||||
pError = true;
|
||||
} // Apply the rules for parsing floating-point number values to the descriptor.
|
||||
// If the result is less than zero, let error be yes. Otherwise, let density
|
||||
// be the result.
|
||||
|
||||
|
||||
if (floatVal < 0) {
|
||||
pError = true;
|
||||
} else {
|
||||
d = floatVal;
|
||||
}
|
||||
} // If the descriptor consists of a valid non-negative integer followed by
|
||||
// a U+0068 LATIN SMALL LETTER H character
|
||||
else if (regexNonNegativeInteger.test(value) && lastChar === 'h') {
|
||||
// If height and density are not both absent, then let error be yes.
|
||||
if (h || d) {
|
||||
pError = true;
|
||||
} // Apply the rules for parsing non-negative integers to the descriptor.
|
||||
// If the result is zero, let error be yes. Otherwise, let future-compat-h
|
||||
// be the result.
|
||||
|
||||
|
||||
if (intVal === 0) {
|
||||
pError = true;
|
||||
} else {
|
||||
h = intVal;
|
||||
} // Anything else, Let error be yes.
|
||||
|
||||
} else {
|
||||
pError = true;
|
||||
}
|
||||
} // 15. If error is still no, then append a new image source to candidates whose
|
||||
// URL is url, associated with a width width if not absent and a pixel
|
||||
// density density if not absent. Otherwise, there is a parse error.
|
||||
|
||||
|
||||
if (!pError) {
|
||||
candidate.source = {
|
||||
value: url,
|
||||
startIndex: startUrlPosition
|
||||
};
|
||||
|
||||
if (w) {
|
||||
candidate.width = {
|
||||
value: w
|
||||
};
|
||||
}
|
||||
|
||||
if (d) {
|
||||
candidate.density = {
|
||||
value: d
|
||||
};
|
||||
}
|
||||
|
||||
if (h) {
|
||||
candidate.height = {
|
||||
value: h
|
||||
};
|
||||
}
|
||||
|
||||
candidates.push(candidate);
|
||||
} else {
|
||||
throw new Error(`Invalid srcset descriptor found in '${input}' at '${desc}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseSrc(input) {
|
||||
if (!input) {
|
||||
throw new Error('Must be non-empty');
|
||||
}
|
||||
|
||||
let startIndex = 0;
|
||||
let value = input;
|
||||
|
||||
while (isASCIIWhitespace(value.substring(0, 1))) {
|
||||
startIndex += 1;
|
||||
value = value.substring(1, value.length);
|
||||
}
|
||||
|
||||
while (isASCIIWhitespace(value.substring(value.length - 1, value.length))) {
|
||||
value = value.substring(0, value.length - 1);
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
throw new Error('Must be non-empty');
|
||||
}
|
||||
|
||||
return {
|
||||
value,
|
||||
startIndex
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeUrl(url) {
|
||||
return decodeURIComponent(url).replace(/[\t\n\r]/g, '');
|
||||
}
|
||||
|
||||
function requestify(url, root) {
|
||||
return (0, _loaderUtils.urlToRequest)(url, root);
|
||||
}
|
||||
|
||||
function isUrlRequestable(url, root) {
|
||||
return (0, _loaderUtils.isUrlRequest)(url, root);
|
||||
}
|
||||
|
||||
function isProductionMode(loaderContext) {
|
||||
return loaderContext.mode === 'production' || !loaderContext.mode;
|
||||
}
|
||||
|
||||
const defaultMinimizerOptions = {
|
||||
caseSensitive: true,
|
||||
// `collapseBooleanAttributes` is not always safe, since this can break CSS attribute selectors and not safe for XHTML
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true,
|
||||
keepClosingSlash: true,
|
||||
// We need ability to use cssnano, or setup own function without extra dependencies
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
// `minifyURLs` is unsafe, because we can't guarantee what the base URL is
|
||||
// `removeAttributeQuotes` is not safe in some rare cases, also HTML spec recommends against doing this
|
||||
removeComments: true,
|
||||
// `removeEmptyAttributes` is not safe, can affect certain style or script behavior, look at https://github.com/webpack-contrib/html-loader/issues/323
|
||||
// `removeRedundantAttributes` is not safe, can affect certain style or script behavior, look at https://github.com/webpack-contrib/html-loader/issues/323
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true // `useShortDoctype` is not safe for XHTML
|
||||
|
||||
};
|
||||
|
||||
function getMinimizeOption(rawOptions, loaderContext) {
|
||||
if (typeof rawOptions.minimize === 'undefined') {
|
||||
return isProductionMode(loaderContext) ? defaultMinimizerOptions : false;
|
||||
}
|
||||
|
||||
if (typeof rawOptions.minimize === 'boolean') {
|
||||
return rawOptions.minimize === true ? defaultMinimizerOptions : false;
|
||||
}
|
||||
|
||||
return rawOptions.minimize;
|
||||
}
|
||||
|
||||
function getAttributeValue(attributes, name) {
|
||||
const lowercasedAttributes = Object.keys(attributes).reduce((keys, k) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
keys[k.toLowerCase()] = k;
|
||||
return keys;
|
||||
}, {});
|
||||
return attributes[lowercasedAttributes[name.toLowerCase()]];
|
||||
}
|
||||
|
||||
function scriptFilter(tag, attribute, attributes) {
|
||||
if (attributes.type) {
|
||||
const type = getAttributeValue(attributes, 'type').trim().toLowerCase();
|
||||
|
||||
if (type !== 'module' && type !== 'text/javascript' && type !== 'application/javascript') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const defaultAttributes = [{
|
||||
tag: 'audio',
|
||||
attribute: 'src',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'embed',
|
||||
attribute: 'src',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'img',
|
||||
attribute: 'src',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'img',
|
||||
attribute: 'srcset',
|
||||
type: 'srcset'
|
||||
}, {
|
||||
tag: 'input',
|
||||
attribute: 'src',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'link',
|
||||
attribute: 'href',
|
||||
type: 'src',
|
||||
filter: (tag, attribute, attributes) => {
|
||||
if (!/stylesheet/i.test(getAttributeValue(attributes, 'rel'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attributes.type && getAttributeValue(attributes, 'type').trim().toLowerCase() !== 'text/css') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}, {
|
||||
tag: 'object',
|
||||
attribute: 'data',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'script',
|
||||
attribute: 'src',
|
||||
type: 'src',
|
||||
filter: scriptFilter
|
||||
}, // Using href with <script> is described here: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
|
||||
{
|
||||
tag: 'script',
|
||||
attribute: 'href',
|
||||
type: 'src',
|
||||
filter: scriptFilter
|
||||
}, {
|
||||
tag: 'script',
|
||||
attribute: 'xlink:href',
|
||||
type: 'src',
|
||||
filter: scriptFilter
|
||||
}, {
|
||||
tag: 'source',
|
||||
attribute: 'src',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'source',
|
||||
attribute: 'srcset',
|
||||
type: 'srcset'
|
||||
}, {
|
||||
tag: 'track',
|
||||
attribute: 'src',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'video',
|
||||
attribute: 'poster',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'video',
|
||||
attribute: 'src',
|
||||
type: 'src'
|
||||
}, // SVG
|
||||
{
|
||||
tag: 'image',
|
||||
attribute: 'xlink:href',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'image',
|
||||
attribute: 'href',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'use',
|
||||
attribute: 'xlink:href',
|
||||
type: 'src'
|
||||
}, {
|
||||
tag: 'use',
|
||||
attribute: 'href',
|
||||
type: 'src'
|
||||
}];
|
||||
|
||||
function smartMergeSources(array, factory) {
|
||||
if (typeof array === 'undefined') {
|
||||
return factory();
|
||||
}
|
||||
|
||||
const newArray = [];
|
||||
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const item = array[i];
|
||||
|
||||
if (item === '...') {
|
||||
const items = factory();
|
||||
|
||||
if (typeof items !== 'undefined') {
|
||||
// eslint-disable-next-line no-shadow
|
||||
for (const item of items) {
|
||||
newArray.push(item);
|
||||
}
|
||||
}
|
||||
} else if (typeof newArray !== 'undefined') {
|
||||
newArray.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
function getAttributesOption(rawOptions) {
|
||||
if (typeof rawOptions.attributes === 'undefined') {
|
||||
return {
|
||||
list: defaultAttributes
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof rawOptions.attributes === 'boolean') {
|
||||
return rawOptions.attributes === true ? {
|
||||
list: defaultAttributes
|
||||
} : false;
|
||||
}
|
||||
|
||||
const sources = smartMergeSources(rawOptions.attributes.list, () => defaultAttributes);
|
||||
return {
|
||||
list: sources,
|
||||
urlFilter: rawOptions.attributes.urlFilter,
|
||||
root: rawOptions.attributes.root
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeOptions(rawOptions, loaderContext) {
|
||||
return {
|
||||
preprocessor: rawOptions.preprocessor,
|
||||
attributes: getAttributesOption(rawOptions),
|
||||
minimize: getMinimizeOption(rawOptions, loaderContext),
|
||||
esModule: typeof rawOptions.esModule === 'undefined' ? false : rawOptions.esModule
|
||||
};
|
||||
}
|
||||
|
||||
function pluginRunner(plugins) {
|
||||
return {
|
||||
process: content => {
|
||||
const result = {};
|
||||
|
||||
for (const plugin of plugins) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
content = plugin(content, result);
|
||||
}
|
||||
|
||||
result.html = content;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getFilter(filter, defaultFilter = null) {
|
||||
return (attribute, value, resourcePath) => {
|
||||
if (defaultFilter && !defaultFilter(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof filter === 'function') {
|
||||
return filter(attribute, value, resourcePath);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
const GET_SOURCE_FROM_IMPORT_NAME = '___HTML_LOADER_GET_SOURCE_FROM_IMPORT___';
|
||||
|
||||
function getImportCode(html, loaderContext, imports, options) {
|
||||
if (imports.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const stringifiedHelperRequest = (0, _loaderUtils.stringifyRequest)(loaderContext, require.resolve('./runtime/getUrl.js'));
|
||||
let code = options.esModule ? `import ${GET_SOURCE_FROM_IMPORT_NAME} from ${stringifiedHelperRequest};\n` : `var ${GET_SOURCE_FROM_IMPORT_NAME} = require(${stringifiedHelperRequest});\n`;
|
||||
|
||||
for (const item of imports) {
|
||||
const {
|
||||
importName,
|
||||
source
|
||||
} = item;
|
||||
code += options.esModule ? `import ${importName} from ${source};\n` : `var ${importName} = require(${source});\n`;
|
||||
}
|
||||
|
||||
return `// Imports\n${code}`;
|
||||
}
|
||||
|
||||
function getModuleCode(html, replacements) {
|
||||
let code = JSON.stringify(html) // Invalid in JavaScript but valid HTML
|
||||
.replace(/[\u2028\u2029]/g, str => str === '\u2029' ? '\\u2029' : '\\u2028');
|
||||
let replacersCode = '';
|
||||
|
||||
for (const item of replacements) {
|
||||
const {
|
||||
importName,
|
||||
replacementName,
|
||||
unquoted,
|
||||
hash
|
||||
} = item;
|
||||
const getUrlOptions = [].concat(hash ? [`hash: ${JSON.stringify(hash)}`] : []).concat(unquoted ? 'maybeNeedQuotes: true' : []);
|
||||
const preparedOptions = getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(', ')} }` : '';
|
||||
replacersCode += `var ${replacementName} = ${GET_SOURCE_FROM_IMPORT_NAME}(${importName}${preparedOptions});\n`;
|
||||
code = code.replace(new RegExp(replacementName, 'g'), () => `" + ${replacementName} + "`);
|
||||
}
|
||||
|
||||
return `// Module\n${replacersCode}var code = ${code};\n`;
|
||||
}
|
||||
|
||||
function getExportCode(html, options) {
|
||||
if (options.esModule) {
|
||||
return `// Exports\nexport default code;`;
|
||||
}
|
||||
|
||||
return `// Exports\nmodule.exports = code;`;
|
||||
}
|
||||
+20
@@ -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.
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
<div align="center">
|
||||
<a href="http://json-schema.org">
|
||||
<img width="160" height="160"
|
||||
src="https://raw.githubusercontent.com/webpack-contrib/schema-utils/master/.github/assets/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>
|
||||
</div>
|
||||
|
||||
[![npm][npm]][npm-url]
|
||||
[![node][node]][node-url]
|
||||
[![deps][deps]][deps-url]
|
||||
[![tests][tests]][tests-url]
|
||||
[![coverage][cover]][cover-url]
|
||||
[![chat][chat]][chat-url]
|
||||
[![size][size]][size-url]
|
||||
|
||||
# schema-utils
|
||||
|
||||
Package for validate options in loaders and plugins.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To begin, you'll need to install `schema-utils`:
|
||||
|
||||
```console
|
||||
npm install schema-utils
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
**schema.json**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"option": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
import schema from "./path/to/schema.json";
|
||||
import { validate } from "schema-utils";
|
||||
|
||||
const options = { option: true };
|
||||
const configuration = { name: "Loader Name/Plugin Name/Name" };
|
||||
|
||||
validate(schema, options, configuration);
|
||||
```
|
||||
|
||||
### `schema`
|
||||
|
||||
Type: `String`
|
||||
|
||||
JSON schema.
|
||||
|
||||
Simple example of schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "This is description of option.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
### `options`
|
||||
|
||||
Type: `Object`
|
||||
|
||||
Object with options.
|
||||
|
||||
```js
|
||||
import schema from "./path/to/schema.json";
|
||||
import { validate } from "schema-utils";
|
||||
|
||||
const options = { foo: "bar" };
|
||||
|
||||
validate(schema, { name: 123 }, { name: "MyPlugin" });
|
||||
```
|
||||
|
||||
### `configuration`
|
||||
|
||||
Allow to configure validator.
|
||||
|
||||
There is an alternative method to configure the `name` and`baseDataPath` options via the `title` property in the schema.
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "My Loader options",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "This is description of option.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
The last word used for the `baseDataPath` option, other words used for the `name` option.
|
||||
Based on the example above the `name` option equals `My Loader`, the `baseDataPath` option equals `options`.
|
||||
|
||||
#### `name`
|
||||
|
||||
Type: `Object`
|
||||
Default: `"Object"`
|
||||
|
||||
Allow to setup name in validation errors.
|
||||
|
||||
```js
|
||||
import schema from "./path/to/schema.json";
|
||||
import { validate } from "schema-utils";
|
||||
|
||||
const options = { foo: "bar" };
|
||||
|
||||
validate(schema, options, { name: "MyPlugin" });
|
||||
```
|
||||
|
||||
```shell
|
||||
Invalid configuration object. MyPlugin has been initialised using a configuration object that does not match the API schema.
|
||||
- configuration.optionName should be a integer.
|
||||
```
|
||||
|
||||
#### `baseDataPath`
|
||||
|
||||
Type: `String`
|
||||
Default: `"configuration"`
|
||||
|
||||
Allow to setup base data path in validation errors.
|
||||
|
||||
```js
|
||||
import schema from "./path/to/schema.json";
|
||||
import { validate } from "schema-utils";
|
||||
|
||||
const options = { foo: "bar" };
|
||||
|
||||
validate(schema, options, { name: "MyPlugin", baseDataPath: "options" });
|
||||
```
|
||||
|
||||
```shell
|
||||
Invalid options object. MyPlugin has been initialised using an options object that does not match the API schema.
|
||||
- options.optionName should be a integer.
|
||||
```
|
||||
|
||||
#### `postFormatter`
|
||||
|
||||
Type: `Function`
|
||||
Default: `undefined`
|
||||
|
||||
Allow to reformat errors.
|
||||
|
||||
```js
|
||||
import schema from "./path/to/schema.json";
|
||||
import { validate } from "schema-utils";
|
||||
|
||||
const options = { foo: "bar" };
|
||||
|
||||
validate(schema, options, {
|
||||
name: "MyPlugin",
|
||||
postFormatter: (formattedError, error) => {
|
||||
if (error.keyword === "type") {
|
||||
return `${formattedError}\nAdditional Information.`;
|
||||
}
|
||||
|
||||
return formattedError;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```shell
|
||||
Invalid options object. MyPlugin has been initialized using an options object that does not match the API schema.
|
||||
- options.optionName should be a integer.
|
||||
Additional Information.
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
**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 { validate } from "schema-utils";
|
||||
|
||||
import schema from "path/to/schema.json";
|
||||
|
||||
function loader(src, map) {
|
||||
const options = getOptions(this);
|
||||
|
||||
validate(schema, options, {
|
||||
name: "Loader Name",
|
||||
baseDataPath: "options",
|
||||
});
|
||||
|
||||
// Code...
|
||||
}
|
||||
|
||||
export default loader;
|
||||
```
|
||||
|
||||
### `Plugin`
|
||||
|
||||
```js
|
||||
import { validate } from "schema-utils";
|
||||
|
||||
import schema from "path/to/schema.json";
|
||||
|
||||
class Plugin {
|
||||
constructor(options) {
|
||||
validate(schema, options, {
|
||||
name: "Plugin Name",
|
||||
baseDataPath: "options",
|
||||
});
|
||||
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
apply(compiler) {
|
||||
// Code...
|
||||
}
|
||||
}
|
||||
|
||||
export default Plugin;
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Please take a moment to read our contributing guidelines if you haven't yet done so.
|
||||
|
||||
[CONTRIBUTING](./.github/CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
[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/schema-utils.svg
|
||||
[deps-url]: https://david-dm.org/webpack/schema-utils
|
||||
[tests]: https://github.com/webpack/schema-utils/workflows/schema-utils/badge.svg
|
||||
[tests-url]: https://github.com/webpack/schema-utils/actions
|
||||
[cover]: https://codecov.io/gh/webpack/schema-utils/branch/master/graph/badge.svg
|
||||
[cover-url]: https://codecov.io/gh/webpack/schema-utils
|
||||
[chat]: https://badges.gitter.im/webpack/webpack.svg
|
||||
[chat-url]: https://gitter.im/webpack/webpack
|
||||
[size]: https://packagephobia.com/badge?p=schema-utils
|
||||
[size-url]: https://packagephobia.com/result?p=schema-utils
|
||||
Generated
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
export default ValidationError;
|
||||
export type JSONSchema6 = import("json-schema").JSONSchema6;
|
||||
export type JSONSchema7 = import("json-schema").JSONSchema7;
|
||||
export type Schema = import("./validate").Schema;
|
||||
export type ValidationErrorConfiguration =
|
||||
import("./validate").ValidationErrorConfiguration;
|
||||
export type PostFormatter = import("./validate").PostFormatter;
|
||||
export type SchemaUtilErrorObject = import("./validate").SchemaUtilErrorObject;
|
||||
declare class ValidationError extends Error {
|
||||
/**
|
||||
* @param {Array<SchemaUtilErrorObject>} errors
|
||||
* @param {Schema} schema
|
||||
* @param {ValidationErrorConfiguration} configuration
|
||||
*/
|
||||
constructor(
|
||||
errors: Array<SchemaUtilErrorObject>,
|
||||
schema: Schema,
|
||||
configuration?: ValidationErrorConfiguration
|
||||
);
|
||||
/** @type {Array<SchemaUtilErrorObject>} */
|
||||
errors: Array<SchemaUtilErrorObject>;
|
||||
/** @type {Schema} */
|
||||
schema: Schema;
|
||||
/** @type {string} */
|
||||
headerName: string;
|
||||
/** @type {string} */
|
||||
baseDataPath: string;
|
||||
/** @type {PostFormatter | null} */
|
||||
postFormatter: PostFormatter | null;
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {Schema}
|
||||
*/
|
||||
getSchemaPart(path: string): Schema;
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {boolean} logic
|
||||
* @param {Array<Object>} prevSchemas
|
||||
* @returns {string}
|
||||
*/
|
||||
formatSchema(
|
||||
schema: Schema,
|
||||
logic?: boolean,
|
||||
prevSchemas?: Array<Object>
|
||||
): string;
|
||||
/**
|
||||
* @param {Schema=} schemaPart
|
||||
* @param {(boolean | Array<string>)=} additionalPath
|
||||
* @param {boolean=} needDot
|
||||
* @param {boolean=} logic
|
||||
* @returns {string}
|
||||
*/
|
||||
getSchemaPartText(
|
||||
schemaPart?: Schema | undefined,
|
||||
additionalPath?: (boolean | Array<string>) | undefined,
|
||||
needDot?: boolean | undefined,
|
||||
logic?: boolean | undefined
|
||||
): string;
|
||||
/**
|
||||
* @param {Schema=} schemaPart
|
||||
* @returns {string}
|
||||
*/
|
||||
getSchemaPartDescription(schemaPart?: Schema | undefined): string;
|
||||
/**
|
||||
* @param {SchemaUtilErrorObject} error
|
||||
* @returns {string}
|
||||
*/
|
||||
formatValidationError(error: SchemaUtilErrorObject): string;
|
||||
/**
|
||||
* @param {Array<SchemaUtilErrorObject>} errors
|
||||
* @returns {string}
|
||||
*/
|
||||
formatValidationErrors(errors: Array<SchemaUtilErrorObject>): string;
|
||||
}
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import { validate } from "./validate";
|
||||
import { ValidationError } from "./validate";
|
||||
export { validate, ValidationError };
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
export default addAbsolutePathKeyword;
|
||||
export type Ajv = import("ajv").Ajv;
|
||||
export type ValidateFunction = import("ajv").ValidateFunction;
|
||||
export type SchemaUtilErrorObject = import("../validate").SchemaUtilErrorObject;
|
||||
/**
|
||||
*
|
||||
* @param {Ajv} ajv
|
||||
* @returns {Ajv}
|
||||
*/
|
||||
declare function addAbsolutePathKeyword(ajv: Ajv): Ajv;
|
||||
Generated
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
export = Range;
|
||||
/**
|
||||
* @typedef {[number, boolean]} RangeValue
|
||||
*/
|
||||
/**
|
||||
* @callback RangeValueCallback
|
||||
* @param {RangeValue} rangeValue
|
||||
* @returns {boolean}
|
||||
*/
|
||||
declare class Range {
|
||||
/**
|
||||
* @param {"left" | "right"} side
|
||||
* @param {boolean} exclusive
|
||||
* @returns {">" | ">=" | "<" | "<="}
|
||||
*/
|
||||
static getOperator(
|
||||
side: "left" | "right",
|
||||
exclusive: boolean
|
||||
): ">" | ">=" | "<" | "<=";
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @param {boolean} exclusive is range exclusive
|
||||
* @returns {string}
|
||||
*/
|
||||
static formatRight(value: number, logic: boolean, exclusive: boolean): string;
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @param {boolean} exclusive is range exclusive
|
||||
* @returns {string}
|
||||
*/
|
||||
static formatLeft(value: number, logic: boolean, exclusive: boolean): string;
|
||||
/**
|
||||
* @param {number} start left side value
|
||||
* @param {number} end right side value
|
||||
* @param {boolean} startExclusive is range exclusive from left side
|
||||
* @param {boolean} endExclusive is range exclusive from right side
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @returns {string}
|
||||
*/
|
||||
static formatRange(
|
||||
start: number,
|
||||
end: number,
|
||||
startExclusive: boolean,
|
||||
endExclusive: boolean,
|
||||
logic: boolean
|
||||
): string;
|
||||
/**
|
||||
* @param {Array<RangeValue>} values
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @return {RangeValue} computed value and it's exclusive flag
|
||||
*/
|
||||
static getRangeValue(values: Array<RangeValue>, logic: boolean): RangeValue;
|
||||
/** @type {Array<RangeValue>} */
|
||||
_left: Array<RangeValue>;
|
||||
/** @type {Array<RangeValue>} */
|
||||
_right: Array<RangeValue>;
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean=} exclusive
|
||||
*/
|
||||
left(value: number, exclusive?: boolean | undefined): void;
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean=} exclusive
|
||||
*/
|
||||
right(value: number, exclusive?: boolean | undefined): void;
|
||||
/**
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @return {string} "smart" range string representation
|
||||
*/
|
||||
format(logic?: boolean): string;
|
||||
}
|
||||
declare namespace Range {
|
||||
export { RangeValue, RangeValueCallback };
|
||||
}
|
||||
type RangeValue = [number, boolean];
|
||||
type RangeValueCallback = (rangeValue: RangeValue) => boolean;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
export function stringHints(schema: Schema, logic: boolean): string[];
|
||||
export function numberHints(schema: Schema, logic: boolean): string[];
|
||||
export type Schema = import("../validate").Schema;
|
||||
Generated
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
export type JSONSchema4 = import("json-schema").JSONSchema4;
|
||||
export type JSONSchema6 = import("json-schema").JSONSchema6;
|
||||
export type JSONSchema7 = import("json-schema").JSONSchema7;
|
||||
export type ErrorObject = import("ajv").ErrorObject;
|
||||
export type Extend = {
|
||||
formatMinimum?: number | undefined;
|
||||
formatMaximum?: number | undefined;
|
||||
formatExclusiveMinimum?: boolean | undefined;
|
||||
formatExclusiveMaximum?: boolean | undefined;
|
||||
link?: string | undefined;
|
||||
};
|
||||
export type Schema = (JSONSchema4 | JSONSchema6 | JSONSchema7) & Extend;
|
||||
export type SchemaUtilErrorObject = ErrorObject & {
|
||||
children?: Array<ErrorObject>;
|
||||
};
|
||||
export type PostFormatter = (
|
||||
formattedError: string,
|
||||
error: SchemaUtilErrorObject
|
||||
) => string;
|
||||
export type ValidationErrorConfiguration = {
|
||||
name?: string | undefined;
|
||||
baseDataPath?: string | undefined;
|
||||
postFormatter?: PostFormatter | undefined;
|
||||
};
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {Array<object> | object} options
|
||||
* @param {ValidationErrorConfiguration=} configuration
|
||||
* @returns {void}
|
||||
*/
|
||||
export function validate(
|
||||
schema: Schema,
|
||||
options: Array<object> | object,
|
||||
configuration?: ValidationErrorConfiguration | undefined
|
||||
): void;
|
||||
import ValidationError from "./ValidationError";
|
||||
export { ValidationError };
|
||||
Generated
Vendored
+1271
File diff suppressed because it is too large
Load Diff
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
validate,
|
||||
ValidationError
|
||||
} = require("./validate");
|
||||
|
||||
module.exports = {
|
||||
validate,
|
||||
ValidationError
|
||||
};
|
||||
Generated
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
/** @typedef {import("ajv").Ajv} Ajv */
|
||||
|
||||
/** @typedef {import("ajv").ValidateFunction} ValidateFunction */
|
||||
|
||||
/** @typedef {import("../validate").SchemaUtilErrorObject} SchemaUtilErrorObject */
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {object} schema
|
||||
* @param {string} data
|
||||
* @returns {SchemaUtilErrorObject}
|
||||
*/
|
||||
function errorMessage(message, schema, data) {
|
||||
return {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undefined
|
||||
dataPath: undefined,
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undefined
|
||||
schemaPath: undefined,
|
||||
keyword: "absolutePath",
|
||||
params: {
|
||||
absolutePath: data
|
||||
},
|
||||
message,
|
||||
parentSchema: schema
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {boolean} shouldBeAbsolute
|
||||
* @param {object} schema
|
||||
* @param {string} data
|
||||
* @returns {SchemaUtilErrorObject}
|
||||
*/
|
||||
|
||||
|
||||
function getErrorFor(shouldBeAbsolute, schema, data) {
|
||||
const message = shouldBeAbsolute ? `The provided value ${JSON.stringify(data)} is not an absolute path!` : `A relative path is expected. However, the provided value ${JSON.stringify(data)} is an absolute path!`;
|
||||
return errorMessage(message, schema, data);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {Ajv} ajv
|
||||
* @returns {Ajv}
|
||||
*/
|
||||
|
||||
|
||||
function addAbsolutePathKeyword(ajv) {
|
||||
ajv.addKeyword("absolutePath", {
|
||||
errors: true,
|
||||
type: "string",
|
||||
|
||||
compile(schema, parentSchema) {
|
||||
/** @type {ValidateFunction} */
|
||||
const callback = data => {
|
||||
let passes = true;
|
||||
const isExclamationMarkPresent = data.includes("!");
|
||||
|
||||
if (isExclamationMarkPresent) {
|
||||
callback.errors = [errorMessage(`The provided value ${JSON.stringify(data)} contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.`, parentSchema, data)];
|
||||
passes = false;
|
||||
} // ?:[A-Za-z]:\\ - Windows absolute path
|
||||
// \\\\ - Windows network absolute path
|
||||
// \/ - Unix-like OS absolute path
|
||||
|
||||
|
||||
const isCorrectAbsolutePath = schema === /^(?:[A-Za-z]:(\\|\/)|\\\\|\/)/.test(data);
|
||||
|
||||
if (!isCorrectAbsolutePath) {
|
||||
callback.errors = [getErrorFor(schema, parentSchema, data)];
|
||||
passes = false;
|
||||
}
|
||||
|
||||
return passes;
|
||||
};
|
||||
|
||||
callback.errors = [];
|
||||
return callback;
|
||||
}
|
||||
|
||||
});
|
||||
return ajv;
|
||||
}
|
||||
|
||||
var _default = addAbsolutePathKeyword;
|
||||
exports.default = _default;
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @typedef {[number, boolean]} RangeValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback RangeValueCallback
|
||||
* @param {RangeValue} rangeValue
|
||||
* @returns {boolean}
|
||||
*/
|
||||
class Range {
|
||||
/**
|
||||
* @param {"left" | "right"} side
|
||||
* @param {boolean} exclusive
|
||||
* @returns {">" | ">=" | "<" | "<="}
|
||||
*/
|
||||
static getOperator(side, exclusive) {
|
||||
if (side === "left") {
|
||||
return exclusive ? ">" : ">=";
|
||||
}
|
||||
|
||||
return exclusive ? "<" : "<=";
|
||||
}
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @param {boolean} exclusive is range exclusive
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
|
||||
static formatRight(value, logic, exclusive) {
|
||||
if (logic === false) {
|
||||
return Range.formatLeft(value, !logic, !exclusive);
|
||||
}
|
||||
|
||||
return `should be ${Range.getOperator("right", exclusive)} ${value}`;
|
||||
}
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @param {boolean} exclusive is range exclusive
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
|
||||
static formatLeft(value, logic, exclusive) {
|
||||
if (logic === false) {
|
||||
return Range.formatRight(value, !logic, !exclusive);
|
||||
}
|
||||
|
||||
return `should be ${Range.getOperator("left", exclusive)} ${value}`;
|
||||
}
|
||||
/**
|
||||
* @param {number} start left side value
|
||||
* @param {number} end right side value
|
||||
* @param {boolean} startExclusive is range exclusive from left side
|
||||
* @param {boolean} endExclusive is range exclusive from right side
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
|
||||
static formatRange(start, end, startExclusive, endExclusive, logic) {
|
||||
let result = "should be";
|
||||
result += ` ${Range.getOperator(logic ? "left" : "right", logic ? startExclusive : !startExclusive)} ${start} `;
|
||||
result += logic ? "and" : "or";
|
||||
result += ` ${Range.getOperator(logic ? "right" : "left", logic ? endExclusive : !endExclusive)} ${end}`;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @param {Array<RangeValue>} values
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @return {RangeValue} computed value and it's exclusive flag
|
||||
*/
|
||||
|
||||
|
||||
static getRangeValue(values, logic) {
|
||||
let minMax = logic ? Infinity : -Infinity;
|
||||
let j = -1;
|
||||
const predicate = logic ?
|
||||
/** @type {RangeValueCallback} */
|
||||
([value]) => value <= minMax :
|
||||
/** @type {RangeValueCallback} */
|
||||
([value]) => value >= minMax;
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (predicate(values[i])) {
|
||||
[minMax] = values[i];
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (j > -1) {
|
||||
return values[j];
|
||||
}
|
||||
|
||||
return [Infinity, true];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
/** @type {Array<RangeValue>} */
|
||||
this._left = [];
|
||||
/** @type {Array<RangeValue>} */
|
||||
|
||||
this._right = [];
|
||||
}
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean=} exclusive
|
||||
*/
|
||||
|
||||
|
||||
left(value, exclusive = false) {
|
||||
this._left.push([value, exclusive]);
|
||||
}
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean=} exclusive
|
||||
*/
|
||||
|
||||
|
||||
right(value, exclusive = false) {
|
||||
this._right.push([value, exclusive]);
|
||||
}
|
||||
/**
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @return {string} "smart" range string representation
|
||||
*/
|
||||
|
||||
|
||||
format(logic = true) {
|
||||
const [start, leftExclusive] = Range.getRangeValue(this._left, logic);
|
||||
const [end, rightExclusive] = Range.getRangeValue(this._right, !logic);
|
||||
|
||||
if (!Number.isFinite(start) && !Number.isFinite(end)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const realStart = leftExclusive ? start + 1 : start;
|
||||
const realEnd = rightExclusive ? end - 1 : end; // e.g. 5 < x < 7, 5 < x <= 6, 6 <= x <= 6
|
||||
|
||||
if (realStart === realEnd) {
|
||||
return `should be ${logic ? "" : "!"}= ${realStart}`;
|
||||
} // e.g. 4 < x < ∞
|
||||
|
||||
|
||||
if (Number.isFinite(start) && !Number.isFinite(end)) {
|
||||
return Range.formatLeft(start, logic, leftExclusive);
|
||||
} // e.g. ∞ < x < 4
|
||||
|
||||
|
||||
if (!Number.isFinite(start) && Number.isFinite(end)) {
|
||||
return Range.formatRight(end, logic, rightExclusive);
|
||||
}
|
||||
|
||||
return Range.formatRange(start, end, leftExclusive, rightExclusive, logic);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Range;
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
"use strict";
|
||||
|
||||
const Range = require("./Range");
|
||||
/** @typedef {import("../validate").Schema} Schema */
|
||||
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {boolean} logic
|
||||
* @return {string[]}
|
||||
*/
|
||||
|
||||
|
||||
module.exports.stringHints = function stringHints(schema, logic) {
|
||||
const hints = [];
|
||||
let type = "string";
|
||||
const currentSchema = { ...schema
|
||||
};
|
||||
|
||||
if (!logic) {
|
||||
const tmpLength = currentSchema.minLength;
|
||||
const tmpFormat = currentSchema.formatMinimum;
|
||||
const tmpExclusive = currentSchema.formatExclusiveMaximum;
|
||||
currentSchema.minLength = currentSchema.maxLength;
|
||||
currentSchema.maxLength = tmpLength;
|
||||
currentSchema.formatMinimum = currentSchema.formatMaximum;
|
||||
currentSchema.formatMaximum = tmpFormat;
|
||||
currentSchema.formatExclusiveMaximum = !currentSchema.formatExclusiveMinimum;
|
||||
currentSchema.formatExclusiveMinimum = !tmpExclusive;
|
||||
}
|
||||
|
||||
if (typeof currentSchema.minLength === "number") {
|
||||
if (currentSchema.minLength === 1) {
|
||||
type = "non-empty string";
|
||||
} else {
|
||||
const length = Math.max(currentSchema.minLength - 1, 0);
|
||||
hints.push(`should be longer than ${length} character${length > 1 ? "s" : ""}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof currentSchema.maxLength === "number") {
|
||||
if (currentSchema.maxLength === 0) {
|
||||
type = "empty string";
|
||||
} else {
|
||||
const length = currentSchema.maxLength + 1;
|
||||
hints.push(`should be shorter than ${length} character${length > 1 ? "s" : ""}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSchema.pattern) {
|
||||
hints.push(`should${logic ? "" : " not"} match pattern ${JSON.stringify(currentSchema.pattern)}`);
|
||||
}
|
||||
|
||||
if (currentSchema.format) {
|
||||
hints.push(`should${logic ? "" : " not"} match format ${JSON.stringify(currentSchema.format)}`);
|
||||
}
|
||||
|
||||
if (currentSchema.formatMinimum) {
|
||||
hints.push(`should be ${currentSchema.formatExclusiveMinimum ? ">" : ">="} ${JSON.stringify(currentSchema.formatMinimum)}`);
|
||||
}
|
||||
|
||||
if (currentSchema.formatMaximum) {
|
||||
hints.push(`should be ${currentSchema.formatExclusiveMaximum ? "<" : "<="} ${JSON.stringify(currentSchema.formatMaximum)}`);
|
||||
}
|
||||
|
||||
return [type].concat(hints);
|
||||
};
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {boolean} logic
|
||||
* @return {string[]}
|
||||
*/
|
||||
|
||||
|
||||
module.exports.numberHints = function numberHints(schema, logic) {
|
||||
const hints = [schema.type === "integer" ? "integer" : "number"];
|
||||
const range = new Range();
|
||||
|
||||
if (typeof schema.minimum === "number") {
|
||||
range.left(schema.minimum);
|
||||
}
|
||||
|
||||
if (typeof schema.exclusiveMinimum === "number") {
|
||||
range.left(schema.exclusiveMinimum, true);
|
||||
}
|
||||
|
||||
if (typeof schema.maximum === "number") {
|
||||
range.right(schema.maximum);
|
||||
}
|
||||
|
||||
if (typeof schema.exclusiveMaximum === "number") {
|
||||
range.right(schema.exclusiveMaximum, true);
|
||||
}
|
||||
|
||||
const rangeFormat = range.format(logic);
|
||||
|
||||
if (rangeFormat) {
|
||||
hints.push(rangeFormat);
|
||||
}
|
||||
|
||||
if (typeof schema.multipleOf === "number") {
|
||||
hints.push(`should${logic ? "" : " not"} be multiple of ${schema.multipleOf}`);
|
||||
}
|
||||
|
||||
return hints;
|
||||
};
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.validate = validate;
|
||||
Object.defineProperty(exports, "ValidationError", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _ValidationError.default;
|
||||
}
|
||||
});
|
||||
|
||||
var _absolutePath = _interopRequireDefault(require("./keywords/absolutePath"));
|
||||
|
||||
var _ValidationError = _interopRequireDefault(require("./ValidationError"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
// Use CommonJS require for ajv libs so TypeScript consumers aren't locked into esModuleInterop (see #110).
|
||||
const Ajv = require("ajv");
|
||||
|
||||
const ajvKeywords = require("ajv-keywords");
|
||||
/** @typedef {import("json-schema").JSONSchema4} JSONSchema4 */
|
||||
|
||||
/** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
|
||||
|
||||
/** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */
|
||||
|
||||
/** @typedef {import("ajv").ErrorObject} ErrorObject */
|
||||
|
||||
/**
|
||||
* @typedef {Object} Extend
|
||||
* @property {number=} formatMinimum
|
||||
* @property {number=} formatMaximum
|
||||
* @property {boolean=} formatExclusiveMinimum
|
||||
* @property {boolean=} formatExclusiveMaximum
|
||||
* @property {string=} link
|
||||
*/
|
||||
|
||||
/** @typedef {(JSONSchema4 | JSONSchema6 | JSONSchema7) & Extend} Schema */
|
||||
|
||||
/** @typedef {ErrorObject & { children?: Array<ErrorObject>}} SchemaUtilErrorObject */
|
||||
|
||||
/**
|
||||
* @callback PostFormatter
|
||||
* @param {string} formattedError
|
||||
* @param {SchemaUtilErrorObject} error
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ValidationErrorConfiguration
|
||||
* @property {string=} name
|
||||
* @property {string=} baseDataPath
|
||||
* @property {PostFormatter=} postFormatter
|
||||
*/
|
||||
|
||||
|
||||
const ajv = new Ajv({
|
||||
allErrors: true,
|
||||
verbose: true,
|
||||
$data: true
|
||||
});
|
||||
ajvKeywords(ajv, ["instanceof", "formatMinimum", "formatMaximum", "patternRequired"]); // Custom keywords
|
||||
|
||||
(0, _absolutePath.default)(ajv);
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {Array<object> | object} options
|
||||
* @param {ValidationErrorConfiguration=} configuration
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function validate(schema, options, configuration) {
|
||||
let errors = [];
|
||||
|
||||
if (Array.isArray(options)) {
|
||||
errors = Array.from(options, nestedOptions => validateObject(schema, nestedOptions));
|
||||
errors.forEach((list, idx) => {
|
||||
const applyPrefix =
|
||||
/**
|
||||
* @param {SchemaUtilErrorObject} error
|
||||
*/
|
||||
error => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
error.dataPath = `[${idx}]${error.dataPath}`;
|
||||
|
||||
if (error.children) {
|
||||
error.children.forEach(applyPrefix);
|
||||
}
|
||||
};
|
||||
|
||||
list.forEach(applyPrefix);
|
||||
});
|
||||
errors = errors.reduce((arr, items) => {
|
||||
arr.push(...items);
|
||||
return arr;
|
||||
}, []);
|
||||
} else {
|
||||
errors = validateObject(schema, options);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new _ValidationError.default(errors, schema, configuration);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {Array<object> | object} options
|
||||
* @returns {Array<SchemaUtilErrorObject>}
|
||||
*/
|
||||
|
||||
|
||||
function validateObject(schema, options) {
|
||||
const compiledSchema = ajv.compile(schema);
|
||||
const valid = compiledSchema(options);
|
||||
if (valid) return [];
|
||||
return compiledSchema.errors ? filterErrors(compiledSchema.errors) : [];
|
||||
}
|
||||
/**
|
||||
* @param {Array<ErrorObject>} errors
|
||||
* @returns {Array<SchemaUtilErrorObject>}
|
||||
*/
|
||||
|
||||
|
||||
function filterErrors(errors) {
|
||||
/** @type {Array<SchemaUtilErrorObject>} */
|
||||
let newErrors = [];
|
||||
|
||||
for (const error of
|
||||
/** @type {Array<SchemaUtilErrorObject>} */
|
||||
errors) {
|
||||
const {
|
||||
dataPath
|
||||
} = error;
|
||||
/** @type {Array<SchemaUtilErrorObject>} */
|
||||
|
||||
let children = [];
|
||||
newErrors = newErrors.filter(oldError => {
|
||||
if (oldError.dataPath.includes(dataPath)) {
|
||||
if (oldError.children) {
|
||||
children = children.concat(oldError.children.slice(0));
|
||||
} // eslint-disable-next-line no-undefined, no-param-reassign
|
||||
|
||||
|
||||
oldError.children = undefined;
|
||||
children.push(oldError);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (children.length) {
|
||||
error.children = children;
|
||||
}
|
||||
|
||||
newErrors.push(error);
|
||||
}
|
||||
|
||||
return newErrors;
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"name": "schema-utils",
|
||||
"version": "3.1.1",
|
||||
"description": "webpack Validation Utils",
|
||||
"license": "MIT",
|
||||
"repository": "webpack/schema-utils",
|
||||
"author": "webpack Contrib (https://github.com/webpack-contrib)",
|
||||
"homepage": "https://github.com/webpack/schema-utils",
|
||||
"bugs": "https://github.com/webpack/schema-utils/issues",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "declarations/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run build -- -w",
|
||||
"clean": "del-cli dist declarations",
|
||||
"prebuild": "npm run clean",
|
||||
"build:types": "tsc --declaration --emitDeclarationOnly --outDir declarations && prettier \"declarations/**/*.ts\" --write",
|
||||
"build:code": "cross-env NODE_ENV=production babel src -d dist --copy-files",
|
||||
"build": "npm-run-all -p \"build:**\"",
|
||||
"commitlint": "commitlint --from=master",
|
||||
"security": "npm audit --production",
|
||||
"fmt:check": "prettier \"{**/*,*}.{js,json,md,yml,css,ts}\" --list-different",
|
||||
"lint:js": "eslint --cache .",
|
||||
"lint:types": "tsc --pretty --noEmit",
|
||||
"lint": "npm-run-all lint:js lint:types fmt:check",
|
||||
"fmt": "npm run fmt:check -- --write",
|
||||
"fix:js": "npm run lint:js -- --fix",
|
||||
"fix": "npm-run-all fix:js fmt",
|
||||
"test:only": "cross-env NODE_ENV=test jest",
|
||||
"test:watch": "npm run test:only -- --watch",
|
||||
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
|
||||
"pretest": "npm run lint",
|
||||
"test": "npm run test:coverage",
|
||||
"prepare": "npm run build && husky install",
|
||||
"release": "standard-version"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"declarations"
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.8",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.14.3",
|
||||
"@babel/core": "^7.14.6",
|
||||
"@babel/preset-env": "^7.14.7",
|
||||
"@commitlint/cli": "^12.1.4",
|
||||
"@commitlint/config-conventional": "^12.1.4",
|
||||
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
|
||||
"babel-jest": "^27.0.6",
|
||||
"cross-env": "^7.0.3",
|
||||
"del": "^6.0.0",
|
||||
"del-cli": "^3.0.1",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"husky": "^6.0.0",
|
||||
"jest": "^27.0.6",
|
||||
"lint-staged": "^11.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.3.2",
|
||||
"standard-version": "^9.3.1",
|
||||
"typescript": "^4.3.5",
|
||||
"webpack": "^5.45.1"
|
||||
},
|
||||
"keywords": [
|
||||
"webpack"
|
||||
]
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"name": "html-loader",
|
||||
"version": "1.3.2",
|
||||
"description": "Html loader module for webpack",
|
||||
"license": "MIT",
|
||||
"repository": "webpack-contrib/html-loader",
|
||||
"author": "Tobias Koppers @sokra",
|
||||
"homepage": "https://github.com/webpack-contrib/html-loader",
|
||||
"bugs": "https://github.com/webpack-contrib/html-loader/issues",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"main": "dist/cjs.js",
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run build -- -w",
|
||||
"clean": "del-cli dist",
|
||||
"validate:runtime": "es-check es5 \"dist/runtime/**/*.js\"",
|
||||
"prebuild": "npm run clean",
|
||||
"build": "cross-env NODE_ENV=production babel src -d dist --copy-files",
|
||||
"postbuild": "npm run validate:runtime",
|
||||
"commitlint": "commitlint --from=master",
|
||||
"security": "npm audit",
|
||||
"lint:prettier": "prettier --list-different .",
|
||||
"lint:js": "eslint --cache .",
|
||||
"lint": "npm-run-all -l -p \"lint:**\"",
|
||||
"test:only": "cross-env NODE_ENV=test jest",
|
||||
"test:watch": "npm run test:only -- --watch",
|
||||
"test:coverage": "npm run test:only -- --coverage",
|
||||
"pretest": "npm run lint",
|
||||
"test": "npm run test:coverage",
|
||||
"prepare": "npm run build",
|
||||
"release": "standard-version",
|
||||
"defaults": "webpack-defaults"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"webpack": "^4.0.0 || ^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"html-minifier-terser": "^5.1.1",
|
||||
"htmlparser2": "^4.1.0",
|
||||
"loader-utils": "^2.0.0",
|
||||
"schema-utils": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.11.6",
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"@commitlint/cli": "^11.0.0",
|
||||
"@commitlint/config-conventional": "^11.0.0",
|
||||
"@webpack-contrib/defaults": "^6.3.0",
|
||||
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
|
||||
"babel-jest": "^26.5.2",
|
||||
"cross-env": "^7.0.2",
|
||||
"del": "^6.0.0",
|
||||
"del-cli": "^3.0.1",
|
||||
"es-check": "^5.1.1",
|
||||
"eslint": "^7.10.0",
|
||||
"eslint-config-prettier": "^6.12.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"file-loader": "^6.1.1",
|
||||
"handlebars": "^4.7.6",
|
||||
"husky": "^4.3.0",
|
||||
"jest": "^26.5.2",
|
||||
"lint-staged": "^10.4.0",
|
||||
"memfs": "^3.2.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"posthtml": "^0.13.3",
|
||||
"posthtml-webp": "^2.1.0",
|
||||
"prettier": "^2.1.2",
|
||||
"standard-version": "^9.0.0",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^4.44.2"
|
||||
},
|
||||
"keywords": [
|
||||
"webpack",
|
||||
"html",
|
||||
"loader"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user