code:
mini-css-extract-plugin

[![npm][npm]][npm-url]
[![node][node]][node-url]
[![tests][tests]][tests-url]
[![coverage][cover]][cover-url]
[![discussion][discussion]][discussion-url]
[![size][size]][size-url]

# mini-css-extract-plugin

This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.

It builds on top of a new webpack v5 feature and requires webpack 5 to work.

Compared to the extract-text-webpack-plugin:

- Async loading
- No duplicate compilation (performance)
- Easier to use
- Specific to CSS

## Getting Started

To begin, you'll need to install `mini-css-extract-plugin`:

```console
npm install --save-dev mini-css-extract-plugin
```

or

```console
yarn add -D mini-css-extract-plugin
```

or

```console
pnpm add -D mini-css-extract-plugin
```

It's recommended to combine `mini-css-extract-plugin` with the [`css-loader`](https://github.com/webpack-contrib/css-loader)

Then add the loader and the plugin to your `webpack` config. For example:

**style.css**

```css
body {
  background: green;
}
```

**component.js**

```js
import "./style.css";
```

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

> **Warning**
>
> Note that if you import CSS from your webpack entrypoint or import styles in the [initial](https://webpack.js.org/concepts/under-the-hood/#chunks) chunk, `mini-css-extract-plugin` will not load this CSS into the page. Please use [`html-webpack-plugin`](https://github.com/jantimon/html-webpack-plugin) for automatic generation `link` tags or create `index.html` file with `link` tag.

> **Warning**
>
> Source maps works only for `source-map`/`nosources-source-map`/`hidden-nosources-source-map`/`hidden-source-map` values because CSS only supports source maps with the `sourceMappingURL` comment (i.e. `//# sourceMappingURL=style.css.map`). If you need set `devtool` to another value you can enable source maps generation for extracted CSS using [`sourceMap: true`](https://github.com/webpack-contrib/css-loader#sourcemap) for `css-loader`.

## Options

### Plugin Options

- **[`filename`](#filename)**
- **[`chunkFilename`](#chunkFilename)**
- **[`ignoreOrder`](#ignoreOrder)**
- **[`insert`](#insert)**
- **[`attributes`](#attributes)**
- **[`linkType`](#linkType)**
- **[`runtime`](#runtime)**
- **[`experimentalUseImportModule`](#experimentalUseImportModule)**

#### `filename`

Type:

```ts
type filename =
  | string
  | ((pathData: PathData, assetInfo?: AssetInfo) => string);
```

Default: `[name].css`

This option determines the name of each output CSS file.

Works like [`output.filename`](https://webpack.js.org/configuration/output/#outputfilename)

#### `chunkFilename`

Type:

```ts
type chunkFilename =
  | string
  | ((pathData: PathData, assetInfo?: AssetInfo) => string);
```

Default: `based on filename`

> Specifying `chunkFilename` as a `function` is only available in webpack@5

This option determines the name of non-entry chunk files.

Works like [`output.chunkFilename`](https://webpack.js.org/configuration/output/#outputchunkfilename)

#### `ignoreOrder`

Type:

```ts
type ignoreOrder = boolean;
```

Default: `false`

Remove Order Warnings.
See [examples](#remove-order-warnings) below for details.

#### `insert`

Type:

```ts
type insert = string | ((linkTag: HTMLLinkElement) => void);
```

Default: `document.head.appendChild(linkTag);`

Inserts the `link` tag at the given position for [non-initial (async)](https://webpack.js.org/concepts/under-the-hood/#chunks) CSS chunks

> **Warning**
>
> Only for [non-initial (async)](https://webpack.js.org/concepts/under-the-hood/#chunks) chunks.

By default, the `mini-css-extract-plugin` appends styles (`<link>` elements) to `document.head` of the current `window`.

However in some circumstances it might be necessary to have finer control over the append target or even delay `link` elements insertion.
For example this is the case when you asynchronously load styles for an application that runs inside of an iframe.
In such cases `insert` can be configured to be a function or a custom selector.

If you target an [iframe](https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement) make sure that the parent document has sufficient access rights to reach into the frame document and append elements to it.

##### `string`

Allows to setup custom [query selector](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector).
A new `<link>` element will be inserted after the found item.

**webpack.config.js**

```js
new MiniCssExtractPlugin({
  insert: "#some-element",
});
```

A new `<link>` element will be inserted after the element with id `some-element`.

##### `function`

Allows to override default behavior and insert styles at any position.

> ⚠ Do not forget that this code will run in the browser alongside your application. Since not all browsers support latest ECMA features like `let`, `const`, `arrow function expression` and etc we recommend you to use only ECMA 5 features and syntax.

> > ⚠ The `insert` function is serialized to string and passed to the plugin. This means that it won't have access to the scope of the webpack configuration module.

**webpack.config.js**

```js
new MiniCssExtractPlugin({
  insert: function (linkTag) {
    var reference = document.querySelector("#some-element");
    if (reference) {
      reference.parentNode.insertBefore(linkTag, reference);
    }
  },
});
```

A new `<link>` element will be inserted before the element with id `some-element`.

#### `attributes`

Type:

```ts
type attributes = Record<string, string>};
```

Default: `{}`

> **Warning**
>
> Only for [non-initial (async)](https://webpack.js.org/concepts/under-the-hood/#chunks) chunks.

If defined, the `mini-css-extract-plugin` will attach given attributes with their values on `<link>` element.

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      attributes: {
        id: "target",
        "data-target": "example",
      },
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

> **Note**
>
> It's only applied to dynamically loaded css chunks, if you want to modify link attributes inside html file, please using [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin)

#### `linkType`

Type:

```ts
type linkType = string | boolean;
```

Default: `text/css`

This option allows loading asynchronous chunks with a custom link type, such as `<link type="text/css" ...>`.

##### `string`

Possible values: `text/css`

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      linkType: "text/css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

##### `boolean`

`false` disables the link `type` attribute

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      linkType: false,
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

#### `runtime`

Type:

```ts
type runtime = boolean;
```

Default: `true`

Allows to enable/disable the runtime generation.
CSS will be still extracted and can be used for a custom loading methods.
For example, you can use [assets-webpack-plugin](https://github.com/ztoben/assets-webpack-plugin) to retrieve them then use your own runtime code to download assets when needed.

`false` to skip.

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      runtime: false,
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

#### `experimentalUseImportModule`

Type:

```ts
type experimentalUseImportModule = boolean;
```

Default: `undefined`

Enabled by default if not explicitly enabled (i.e. `true` and `false` allow you to explicitly control this option) and new API is available (at least webpack `5.52.0` is required).
Boolean values are available since version `5.33.2`, but you need to enable `experiments.executeModule` (not required from webpack `5.52.0`).

Use a new webpack API to execute modules instead of child compilers.
This improves performance and memory usage a lot.

When combined with `experiments.layers`, this adds a `layer` option to the loader options to specify the layer of the css execution.

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // You don't need this for `>= 5.52.0` due to the fact that this is enabled by default
      // Required only for `>= 5.33.2 & <= 5.52.0`
      // Not available/unsafe for `<= 5.33.2`
      experimentalUseImportModule: true,
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

### Loader Options

- **[`publicPath`](#publicPath)**
- **[`emit`](#emit)**
- **[`esModule`](#esModule)**
- **[`defaultExport`](#defaultExport)**

#### `publicPath`

Type:

```ts
type publicPath =
  | string
  | ((resourcePath: string, rootContext: string) => string);
```

Default: the `publicPath` in `webpackOptions.output`

Specifies a custom public path for the external resources like images, files, etc inside `CSS`.
Works like [`output.publicPath`](https://webpack.js.org/configuration/output/#outputpublicpath)

##### `string`

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: "/public/path/to/",
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

##### `function`

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: (resourcePath, context) => {
                return path.relative(path.dirname(resourcePath), context) + "/";
              },
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

#### `emit`

Type:

```ts
type emit = boolean;
```

Default: `true`

If true, emits a file (writes a file to the filesystem). If false, the plugin will extract the CSS but **will not** emit the file.
It is often useful to disable this option for server-side packages.

#### `esModule`

Type:

```ts
type esModule = boolean;
```

Default: `true`

By default, `mini-css-extract-plugin` generates JS modules that use the ES 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 CommonJS syntax using:

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: false,
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

#### `defaultExport`

Type:

```ts
type defaultExport = boolean;
```

Default: `false`

> **Note**
>
> This option will work only when you set `namedExport` to `true` in `css-loader`

By default, `mini-css-extract-plugin` generates JS modules based on the `esModule` and `namedExport` options in `css-loader`.
Using the `esModule` and `namedExport` options will allow you to better optimize your code.
If you set `esModule: true` and `namedExport: true` for `css-loader` `mini-css-extract-plugin` will generate **only** a named export.
Our official recommendation is to use only named export for better future compatibility.
But for some applications, it is not easy to quickly rewrite the code from the default export to a named export.

In case you need both default and named exports, you can enable this option:

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              defaultExport: true,
            },
          },
          {
            loader: "css-loader",
            esModule: true,
            modules: {
              namedExport: true,
            },
          },
        ],
      },
    ],
  },
};
```

## Examples

### Recommended

For `production` builds it's recommended to extract the CSS from your bundle being able to use parallel loading of CSS/JS resources later on.
This can be achieved by using the `mini-css-extract-plugin`, because it creates separate css files.
For `development` mode (including `webpack-dev-server`) you can use [style-loader](https://github.com/webpack-contrib/style-loader), because it injects CSS into the DOM using multiple  and works faster.

> Do not use `style-loader` and `mini-css-extract-plugin` together.

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== "production";

module.exports = {
  module: {
    rules: [
      {
        // If you enable `experiments.css` or `experiments.futureDefaults`, please uncomment line below
        // type: "javascript/auto",
        test: /\.(sa|sc|c)ss$/,
        use: [
          devMode ? "style-loader" : MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader",
          "sass-loader",
        ],
      },
    ],
  },
  plugins: [].concat(devMode ? [] : [new MiniCssExtractPlugin()]),
};
```

### Minimal example

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // all options are optional
      filename: "[name].css",
      chunkFilename: "[id].css",
      ignoreOrder: false, // Enable to remove warnings about conflicting order
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              // you can specify a publicPath here
              // by default it uses publicPath in webpackOptions.output
              publicPath: "../",
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

### Named export for CSS Modules

> ⚠ Names of locals are converted to `camelCase`.

> ⚠ It is not allowed to use JavaScript reserved words in css class names.

> ⚠ Options `esModule` and `modules.namedExport` in `css-loader` should be enabled.

**styles.css**

```css
.foo-baz {
  color: red;
}
.bar {
  color: blue;
}
```

**index.js**

```js
import { fooBaz, bar } from "./styles.css";

console.log(fooBaz, bar);
```

You can enable a ES module named export using:

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          {
            loader: "css-loader",
            options: {
              esModule: true,
              modules: {
                namedExport: true,
                localIdentName: "foo__[name]__[local]",
              },
            },
          },
        ],
      },
    ],
  },
};
```

### The `publicPath` option as function

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: (resourcePath, context) => {
                // publicPath is the relative path of the resource to the context
                // e.g. for ./css/admin/main.css the publicPath will be ../../
                // while for ./css/main.css the publicPath will be ../
                return path.relative(path.dirname(resourcePath), context) + "/";
              },
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

### Advanced configuration example

This plugin should not be used with `style-loader` in the loaders chain.

Here is an example to have both HMR in `development` and your styles extracted in a file for `production` builds.

(Loaders options left out for clarity, adapt accordingly to your needs.)

You should not use `HotModuleReplacementPlugin` plugin if you are using a `webpack-dev-server`.
`webpack-dev-server` enables / disables HMR using `hot` option.

**webpack.config.js**

```js
const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== "production";

const plugins = [
  new MiniCssExtractPlugin({
    // Options similar to the same options in webpackOptions.output
    // both options are optional
    filename: devMode ? "[name].css" : "[name].[contenthash].css",
    chunkFilename: devMode ? "[id].css" : "[id].[contenthash].css",
  }),
];
if (devMode) {
  // only enable hot in development
  plugins.push(new webpack.HotModuleReplacementPlugin());
}

module.exports = {
  plugins,
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader",
          "sass-loader",
        ],
      },
    ],
  },
};
```

### Hot Module Reloading (HMR)

> **Note**
>
> HMR is automatically supported in webpack 5. No need to configure it. Skip the following:

The `mini-css-extract-plugin` supports hot reloading of actual css files in development.
Some options are provided to enable HMR of both standard stylesheets and locally scoped CSS or CSS modules.
Below is an example configuration of mini-css for HMR use with CSS modules.

You should not use `HotModuleReplacementPlugin` plugin if you are using a `webpack-dev-server`.
`webpack-dev-server` enables / disables HMR using `hot` option.

**webpack.config.js**

```js
const webpack = require("webpack");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const plugins = [
  new MiniCssExtractPlugin({
    // Options similar to the same options in webpackOptions.output
    // both options are optional
    filename: devMode ? "[name].css" : "[name].[contenthash].css",
    chunkFilename: devMode ? "[id].css" : "[id].[contenthash].css",
  }),
];
if (devMode) {
  // only enable hot in development
  plugins.push(new webpack.HotModuleReplacementPlugin());
}

module.exports = {
  plugins,
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {},
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

### Minimizing For Production

To minify the output, use a plugin like [css-minimizer-webpack-plugin](https://github.com/webpack-contrib/css-minimizer-webpack-plugin).

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
  optimization: {
    minimizer: [
      // For webpack@5 you can use the `...` syntax to extend existing minimizers (i.e. `terser-webpack-plugin`), uncomment the next line
      // `...`,
      new CssMinimizerPlugin(),
    ],
  },
};
```

This will enable CSS optimization only in production mode. If you want to run it also in development set the `optimization.minimize` option to true.

### Using preloaded or inlined CSS

The runtime code detects already added CSS via `<link>` or `<style>` tag.
This can be useful when injecting CSS on server-side for Server-Side-Rendering.
The `href` of the `<link>` tag has to match the URL that will be used for loading the CSS chunk.
The `data-href` attribute can be used for `<link>` and `<style>` too.
When inlining CSS `data-href` must be used.

### Extracting all CSS in a single file

The CSS can be extracted in one CSS file using `optimization.splitChunks.cacheGroups`.

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: "styles",
          type: "css/mini-extract",
          chunks: "all",
          enforce: true,
        },
      },
    },
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

Note that `type` should be used instead of `test` in Webpack 5, or else an extra `.js` file can be generated besides the `.css` file. This is because `test` doesn't know which modules should be dropped (in this case, it won't detect that `.js` should be dropped).

### Extracting CSS based on entry

You may also extract the CSS based on the webpack entry name.
This is especially useful if you import routes dynamically but want to keep your CSS bundled according to entry.
This also prevents the CSS duplication issue one had with the ExtractTextPlugin.

```js
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: {
    foo: path.resolve(__dirname, "src/foo"),
    bar: path.resolve(__dirname, "src/bar"),
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        fooStyles: {
          type: "css/mini-extract",
          name: "styles_foo",
          chunks: (chunk) => {
            return chunk.name === "foo";
          },
          enforce: true,
        },
        barStyles: {
          type: "css/mini-extract",
          name: "styles_bar",
          chunks: (chunk) => {
            return chunk.name === "bar";
          },
          enforce: true,
        },
      },
    },
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

### Filename Option as function

With the `filename` option you can use chunk data to customize the filename.
This is particularly useful when dealing with multiple entry points and wanting to get more control out of the filename for a given entry point/chunk.
In the example below, we'll use `filename` to output the generated css into a different directory.

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: ({ chunk }) => `${chunk.name.replace("/js/", "/css/")}.css`,
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

### Long Term Caching

For long term caching use `filename: "[contenthash].css"`. Optionally add `[name]`.

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash].css",
      chunkFilename: "[id].[contenthash].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

### Remove Order Warnings

For projects where css ordering has been mitigated through consistent use of scoping or naming conventions, such as [CSS Modules](https://github.com/css-modules/css-modules), the css order warnings can be disabled by setting the ignoreOrder flag to true for the plugin.

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      ignoreOrder: true,
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
};
```

### Multiple Themes

**webpack.config.js**

```js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: "./src/index.js",
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        oneOf: [
          {
            resourceQuery: "?dark",
            use: [
              MiniCssExtractPlugin.loader,
              "css-loader",
              {
                loader: "sass-loader",
                options: {
                  additionalData: `@use 'dark-theme/vars' as vars;`,
                },
              },
            ],
          },
          {
            use: [
              MiniCssExtractPlugin.loader,
              "css-loader",
              {
                loader: "sass-loader",
                options: {
                  additionalData: `@use 'light-theme/vars' as vars;`,
                },
              },
            ],
          },
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      attributes: {
        id: "theme",
      },
    }),
  ],
};
```

**src/index.js**

```js
import "./style.scss";

let theme = "light";
const themes = {};

themes[theme] = document.querySelector("#theme");

async function loadTheme(newTheme) {
  // eslint-disable-next-line no-console
  console.log(`CHANGE THEME - ${newTheme}`);

  const themeElement = document.querySelector("#theme");

  if (themeElement) {
    themeElement.remove();
  }

  if (themes[newTheme]) {
    // eslint-disable-next-line no-console
    console.log(`THEME ALREADY LOADED - ${newTheme}`);

    document.head.appendChild(themes[newTheme]);

    return;
  }

  if (newTheme === "dark") {
    // eslint-disable-next-line no-console
    console.log(`LOADING THEME - ${newTheme}`);

    import(/* webpackChunkName: "dark" */ "./style.scss?dark").then(() => {
      themes[newTheme] = document.querySelector("#theme");

      // eslint-disable-next-line no-console
      console.log(`LOADED - ${newTheme}`);
    });
  }
}

document.onclick = () => {
  if (theme === "light") {
    theme = "dark";
  } else {
    theme = "light";
  }

  loadTheme(theme);
};
```

**src/dark-theme/\_vars.scss**

```scss
$background: black;
```

**src/light-theme/\_vars.scss**

```scss
$background: white;
```

**src/styles.scss**

```scss
body {
  background-color: vars.$background;
}
```

**public/index.html**

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Document</title>
    <link id="theme" rel="stylesheet" type="text/css" href="./main.css" />
  </head>
  <body>
    <script src="./main.js"></script>
  </body>
</html>
```

### Media Query Plugin

If you'd like to extract the media queries from the extracted CSS (so mobile users don't need to load desktop or tablet specific CSS anymore) you should use one of the following plugins:

- [Media Query Plugin](https://github.com/SassNinja/media-query-plugin)
- [Media Query Splitting Plugin](https://github.com/mike-diamond/media-query-splitting-plugin)

## Hooks

The mini-css-extract-plugin provides hooks to extend it to your needs.

### beforeTagInsert

`SyncWaterfallHook`

Called before inject the insert code for link tag. Should return a string

```javascript
MiniCssExtractPlugin.getCompilationHooks(compilation).beforeTagInsert.tap(
  "changeHref",
  (source, varNames) =>
    Template.asString([
      source,
      `${varNames.tag}.setAttribute("href", "https://github.com/webpack-contrib/mini-css-extract-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/mini-css-extract-plugin.svg
[npm-url]: https://npmjs.com/package/mini-css-extract-plugin
[node]: https://img.shields.io/node/v/mini-css-extract-plugin.svg
[node-url]: https://nodejs.org
[tests]: https://github.com/webpack-contrib/mini-css-extract-plugin/workflows/mini-css-extract-plugin/badge.svg
[tests-url]: https://github.com/webpack-contrib/mini-css-extract-plugin/actions
[cover]: https://codecov.io/gh/webpack-contrib/mini-css-extract-plugin/branch/master/graph/badge.svg
[cover-url]: https://codecov.io/gh/webpack-contrib/mini-css-extract-plugin
[discussion]: https://img.shields.io/github/discussions/webpack/webpack
[discussion-url]: https://github.com/webpack/webpack/discussions
[size]: https://packagephobia.now.sh/badge?p=mini-css-extract-plugin
[size-url]: https://packagephobia.now.sh/result?p=mini-css-extract-plugin


code:
"use strict";

/* eslint-env browser */
/*
  eslint-disable
  no-console,
  func-names
*/

/** @typedef {any} TODO */

var normalizeUrl = require("./normalize-url");
var srcByModuleId = Object.create(null);
var noDocument = typeof document === "undefined";
var forEach = Array.prototype.forEach;

/**
 * @param {function} fn
 * @param {number} time
 * @returns {(function(): void)|*}
 */
function debounce(fn, time) {
  var timeout = 0;
  return function () {
    // @ts-ignore
    var self = this;
    // eslint-disable-next-line prefer-rest-params
    var args = arguments;
    var functionCall = function functionCall() {
      return fn.apply(self, args);
    };
    clearTimeout(timeout);

    // @ts-ignore
    timeout = setTimeout(functionCall, time);
  };
}
function noop() {}

/**
 * @param {TODO} moduleId
 * @returns {TODO}
 */
function getCurrentScriptUrl(moduleId) {
  var src = srcByModuleId[moduleId];
  if (!src) {
    if (document.currentScript) {
      src = ( /** @type {HTMLScriptElement} */document.currentScript).src;
    } else {
      var scripts = document.getElementsByTagName("script");
      var lastScriptTag = scripts[scripts.length - 1];
      if (lastScriptTag) {
        src = lastScriptTag.src;
      }
    }
    srcByModuleId[moduleId] = src;
  }

  /**
   * @param {string} fileMap
   * @returns {null | string[]}
   */
  return function (fileMap) {
    if (!src) {
      return null;
    }
    var splitResult = src.split(/([^\\/]+)\.js$/);
    var filename = splitResult && splitResult[1];
    if (!filename) {
      return [src.replace(".js", ".css")];
    }
    if (!fileMap) {
      return [src.replace(".js", ".css")];
    }
    return fileMap.split(",").map(function (mapRule) {
      var reg = new RegExp("".concat(filename, "\\.js$"), "g");
      return normalizeUrl(src.replace(reg, "".concat(mapRule.replace(/{fileName}/g, filename), ".css")));
    });
  };
}

/**
 * @param {TODO} el
 * @param {string} [url]
 */
function updateCss(el, url) {
  if (!url) {
    if (!el.href) {
      return;
    }

    // eslint-disable-next-line
    url = el.href.split("?")[0];
  }
  if (!isUrlRequest( /** @type {string} */url)) {
    return;
  }
  if (el.isLoaded === false) {
    // We seem to be about to replace a css link that hasn't loaded yet.
    // We're probably changing the same file more than once.
    return;
  }
  if (!url || !(url.indexOf(".css") > -1)) {
    return;
  }

  // eslint-disable-next-line no-param-reassign
  el.visited = true;
  var newEl = el.cloneNode();
  newEl.isLoaded = false;
  newEl.addEventListener("load", function () {
    if (newEl.isLoaded) {
      return;
    }
    newEl.isLoaded = true;
    el.parentNode.removeChild(el);
  });
  newEl.addEventListener("error", function () {
    if (newEl.isLoaded) {
      return;
    }
    newEl.isLoaded = true;
    el.parentNode.removeChild(el);
  });
  newEl.href = "".concat(url, "?").concat(Date.now());
  if (el.nextSibling) {
    el.parentNode.insertBefore(newEl, el.nextSibling);
  } else {
    el.parentNode.appendChild(newEl);
  }
}

/**
 * @param {string} href
 * @param {TODO} src
 * @returns {TODO}
 */
function getReloadUrl(href, src) {
  var ret;

  // eslint-disable-next-line no-param-reassign
  href = normalizeUrl(href);
  src.some(
  /**
   * @param {string} url
   */
  // eslint-disable-next-line array-callback-return
  function (url) {
    if (href.indexOf(src) > -1) {
      ret = url;
    }
  });
  return ret;
}

/**
 * @param {string} [src]
 * @returns {boolean}
 */
function reloadStyle(src) {
  if (!src) {
    return false;
  }
  var elements = document.querySelectorAll("link");
  var loaded = false;
  forEach.call(elements, function (el) {
    if (!el.href) {
      return;
    }
    var url = getReloadUrl(el.href, src);
    if (!isUrlRequest(url)) {
      return;
    }
    if (el.visited === true) {
      return;
    }
    if (url) {
      updateCss(el, url);
      loaded = true;
    }
  });
  return loaded;
}
function reloadAll() {
  var elements = document.querySelectorAll("link");
  forEach.call(elements, function (el) {
    if (el.visited === true) {
      return;
    }
    updateCss(el);
  });
}

/**
 * @param {string} url
 * @returns {boolean}
 */
function isUrlRequest(url) {
  // An URL is not an request if

  // It is not http or https
  if (!/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url)) {
    return false;
  }
  return true;
}

/**
 * @param {TODO} moduleId
 * @param {TODO} options
 * @returns {TODO}
 */
module.exports = function (moduleId, options) {
  if (noDocument) {
    console.log("no window.document found, will not HMR CSS");
    return noop;
  }
  var getScriptSrc = getCurrentScriptUrl(moduleId);
  function update() {
    var src = getScriptSrc(options.filename);
    var reloaded = reloadStyle(src);
    if (options.locals) {
      console.log("[HMR] Detected local css modules. Reload all css");
      reloadAll();
      return;
    }
    if (reloaded) {
      console.log("[HMR] css reload %s", src.join(" "));
    } else {
      console.log("[HMR] Reload all css");
      reloadAll();
    }
  }
  return debounce(update, 50);
};

license: mit


code:
"use strict";

/* eslint-disable */

/**
 * @param {string[]} pathComponents
 * @returns {string}
 */
function normalizeUrl(pathComponents) {
  return pathComponents.reduce(function (accumulator, item) {
    switch (item) {
      case "..":
        accumulator.pop();
        break;
      case ".":
        break;
      default:
        accumulator.push(item);
    }
    return accumulator;
  }, /** @type {string[]} */[]).join("/");
}

/**
 * @param {string} urlString
 * @returns {string}
 */
module.exports = function (urlString) {
  urlString = urlString.trim();
  if (/^data:/i.test(urlString)) {
    return urlString;
  }
  var protocol = urlString.indexOf("//") !== -1 ? urlString.split("//")[0] + "//" : "";
  var components = urlString.replace(new RegExp(protocol, "i"), "").split("/");
  var host = components[0].toLowerCase().replace(/\.$/, "");
  components[0] = "";
  var path = normalizeUrl(components);
  return protocol + host + path;
};


title: Mini CSS Extract Plugin Loader options

type: object

type: string

instanceof: Function

description: Specifies a custom public path for the external resources like images, files, etc inside CSS.

type: boolean

description: If true, emits a file (writes a file to the filesystem). If false, the plugin will extract the CSS but will not emit the file

description: Generates JS modules that use the ES modules syntax.

description: Duplicate the named export with CSS modules locals to the default export (only when `esModules: true` for css-loader).


code:
"use strict";

const path = require("path");
const {
  findModuleById,
  evalModuleCode,
  AUTO_PUBLIC_PATH,
  ABSOLUTE_PUBLIC_PATH,
  BASE_URI,
  SINGLE_DOT_PATH_SEGMENT,
  stringifyRequest,
  stringifyLocal
} = require("./utils");
const schema = require("./loader-options.json");
const MiniCssExtractPlugin = require("./index");

/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
/** @typedef {import("webpack").Compiler} Compiler */
/** @typedef {import("webpack").Compilation} Compilation */
/** @typedef {import("webpack").Chunk} Chunk */
/** @typedef {import("webpack").Module} Module */
/** @typedef {import("webpack").sources.Source} Source */
/** @typedef {import("webpack").AssetInfo} AssetInfo */
/** @typedef {import("webpack").NormalModule} NormalModule */
/** @typedef {import("./index.js").LoaderOptions} LoaderOptions */
/** @typedef {{ [key: string]: string | function }} Locals */

/** @typedef {any} TODO */

/**
 * @typedef {Object} Dependency
 * @property {string} identifier
 * @property {string | null} context
 * @property {Buffer} content
 * @property {string} media
 * @property {string} [supports]
 * @property {string} [layer]
 * @property {Buffer} [sourceMap]
 */

/**
 * @param {string} content
 * @param {{ loaderContext: import("webpack").LoaderContext<LoaderOptions>, options: LoaderOptions, locals: Locals | undefined }} context
 * @returns {string}
 */
function hotLoader(content, context) {
  const localsJsonString = JSON.stringify(JSON.stringify(context.locals));
  return `${content}
    if(module.hot) {
      (function() {
        var localsJsonString = ${localsJsonString};
        // ${Date.now()}
        var cssReload = require(${stringifyRequest(context.loaderContext, path.join(__dirname, "hmr/hotModuleReplacement.js"))})(module.id, ${JSON.stringify(context.options)});
        // only invalidate when locals change
        if (
          module.hot.data &&
          module.hot.data.value &&
          module.hot.data.value !== localsJsonString
        ) {
          module.hot.invalidate();
        } else {
          module.hot.accept();
        }
        module.hot.dispose(function(data) {
          data.value = localsJsonString;
          cssReload();
        });
      })();
    }
  `;
}

/**
 * @this {import("webpack").LoaderContext<LoaderOptions>}
 * @param {string} request
 */
function pitch(request) {
  if (this._compiler && this._compiler.options && this._compiler.options.experiments && this._compiler.options.experiments.css && this._module && (this._module.type === "css" || this._module.type === "css/auto" || this._module.type === "css/global" || this._module.type === "css/module")) {
    this.emitWarning(new Error('You can\'t use `experiments.css` (`experiments.futureDefaults` enable built-in CSS support by default) and `mini-css-extract-plugin` together, please set `experiments.css` to `false` or set `{ type: "javascript/auto" }` for rules with `mini-css-extract-plugin` in your webpack config (now `mini-css-extract-plugin` does nothing).'));
    return;
  }

  // @ts-ignore
  const options = this.getOptions( /** @type {Schema} */schema);
  const emit = typeof options.emit !== "undefined" ? options.emit : true;
  const callback = this.async();
  const optionsFromPlugin = /** @type {TODO} */this[MiniCssExtractPlugin.pluginSymbol];
  if (!optionsFromPlugin) {
    callback(new Error("You forgot to add 'mini-css-extract-plugin' plugin (i.e. `{ plugins: [new MiniCssExtractPlugin()] }`), please read https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started"));
    return;
  }
  const {
    webpack
  } = /** @type {Compiler} */this._compiler;

  /**
   * @param {TODO} originalExports
   * @param {Compilation} [compilation]
   * @param {{ [name: string]: Source }} [assets]
   * @param {Map<string, AssetInfo>} [assetsInfo]
   * @returns {void}
   */
  const handleExports = (originalExports, compilation, assets, assetsInfo) => {
    /** @type {Locals | undefined} */
    let locals;
    let namedExport;
    const esModule = typeof options.esModule !== "undefined" ? options.esModule : true;

    /**
     * @param {Dependency[] | [null, object][]} dependencies
     */
    const addDependencies = dependencies => {
      if (!Array.isArray(dependencies) && dependencies != null) {
        throw new Error(`Exported value was not extracted as an array: ${JSON.stringify(dependencies)}`);
      }
      const identifierCountMap = new Map();
      let lastDep;
      for (const dependency of dependencies) {
        if (!( /** @type {Dependency} */dependency.identifier) || !emit) {
          // eslint-disable-next-line no-continue
          continue;
        }
        const count = identifierCountMap.get( /** @type {Dependency} */dependency.identifier) || 0;
        const CssDependency = MiniCssExtractPlugin.getCssDependency(webpack);

        /** @type {NormalModule} */
        this._module.addDependency(lastDep = new CssDependency( /** @type {Dependency} */
        dependency, /** @type {Dependency} */
        dependency.context, count));
        identifierCountMap.set( /** @type {Dependency} */
        dependency.identifier, count + 1);
      }
      if (lastDep && assets) {
        lastDep.assets = assets;
        lastDep.assetsInfo = assetsInfo;
      }
    };
    try {
      // eslint-disable-next-line no-underscore-dangle
      const exports = originalExports.__esModule ? originalExports.default : originalExports;
      namedExport =
      // eslint-disable-next-line no-underscore-dangle
      originalExports.__esModule && (!originalExports.default || !("locals" in originalExports.default));
      if (namedExport) {
        Object.keys(originalExports).forEach(key => {
          if (key !== "default") {
            if (!locals) {
              locals = {};
            }

            /** @type {Locals} */
            locals[key] = originalExports[key];
          }
        });
      } else {
        locals = exports && exports.locals;
      }

      /** @type {Dependency[] | [null, object][]} */
      let dependencies;
      if (!Array.isArray(exports)) {
        dependencies = [[null, exports]];
      } else {
        dependencies = exports.map(([id, content, media, sourceMap, supports, layer]) => {
          let identifier = id;
          let context;
          if (compilation) {
            const module = /** @type {Module} */
            findModuleById(compilation, id);
            identifier = module.identifier();
            ({
              context
            } = module);
          } else {
            // TODO check if this context is used somewhere
            context = this.rootContext;
          }
          return {
            identifier,
            context,
            content: Buffer.from(content),
            media,
            supports,
            layer,
            sourceMap: sourceMap ? Buffer.from(JSON.stringify(sourceMap)) :
            // eslint-disable-next-line no-undefined
            undefined
          };
        });
      }
      addDependencies(dependencies);
    } catch (e) {
      callback( /** @type {Error} */e);
      return;
    }
    const result = function makeResult() {
      if (locals) {
        if (namedExport) {
          const identifiers = Array.from(function* generateIdentifiers() {
            let identifierId = 0;
            for (const key of Object.keys(locals)) {
              identifierId += 1;
              yield [`_${identifierId.toString(16)}`, key];
            }
          }());
          const localsString = identifiers.map(([id, key]) => `\nvar ${id} = ${stringifyLocal( /** @type {Locals} */locals[key])};`).join("");
          const exportsString = `export { ${identifiers.map(([id, key]) => `${id} as ${JSON.stringify(key)}`).join(", ")} }`;
          const defaultExport = typeof options.defaultExport !== "undefined" ? options.defaultExport : false;
          return defaultExport ? `${localsString}\n${exportsString}\nexport default { ${identifiers.map(([id, key]) => `${JSON.stringify(key)}: ${id}`).join(", ")} }\n` : `${localsString}\n${exportsString}\n`;
        }
        return `\n${esModule ? "export default" : "module.exports = "} ${JSON.stringify(locals)};`;
      } else if (esModule) {
        return "\nexport {};";
      }
      return "";
    }();
    let resultSource = `// extracted by ${MiniCssExtractPlugin.pluginName}`;

    // only attempt hotreloading if the css is actually used for something other than hash values
    resultSource += this.hot && emit ? hotLoader(result, {
      loaderContext: this,
      options,
      locals
    }) : result;
    callback(null, resultSource);
  };
  let {
    publicPath
  } = /** @type {Compilation} */
  this._compilation.outputOptions;
  if (typeof options.publicPath === "string") {
    // eslint-disable-next-line prefer-destructuring
    publicPath = options.publicPath;
  } else if (typeof options.publicPath === "function") {
    publicPath = options.publicPath(this.resourcePath, this.rootContext);
  }
  if (publicPath === "auto") {
    publicPath = AUTO_PUBLIC_PATH;
  }
  if (typeof optionsFromPlugin.experimentalUseImportModule === "undefined" && typeof this.importModule === "function" || optionsFromPlugin.experimentalUseImportModule) {
    if (!this.importModule) {
      callback(new Error("You are using 'experimentalUseImportModule' but 'this.importModule' is not available in loader context. You need to have at least webpack 5.33.2."));
      return;
    }
    let publicPathForExtract;
    if (typeof publicPath === "string") {
      const isAbsolutePublicPath = /^[a-zA-Z][a-zA-Z\d+\-.]*?:/.test(publicPath);
      publicPathForExtract = isAbsolutePublicPath ? publicPath : `${ABSOLUTE_PUBLIC_PATH}${publicPath.replace(/\./g, SINGLE_DOT_PATH_SEGMENT)}`;
    } else {
      publicPathForExtract = publicPath;
    }
    this.importModule(`${this.resourcePath}.webpack[javascript/auto]!=!!!${request}`, {
      layer: options.layer,
      publicPath: ( /** @type {string} */publicPathForExtract),
      baseUri: `${BASE_URI}/`
    },
    /**
     * @param {Error | null | undefined} error
     * @param {object} exports
     */
    (error, exports) => {
      if (error) {
        callback(error);
        return;
      }
      handleExports(exports);
    });
    return;
  }
  const loaders = this.loaders.slice(this.loaderIndex + 1);
  this.addDependency(this.resourcePath);
  const childFilename = "*";
  const outputOptions = {
    filename: childFilename,
    publicPath
  };
  const childCompiler = /** @type {Compilation} */
  this._compilation.createChildCompiler(`${MiniCssExtractPlugin.pluginName} ${request}`, outputOptions);

  // The templates are compiled and executed by NodeJS - similar to server side rendering
  // Unfortunately this causes issues as some loaders require an absolute URL to support ES Modules
  // The following config enables relative URL support for the child compiler
  childCompiler.options.module = {
    ...childCompiler.options.module
  };
  childCompiler.options.module.parser = {
    ...childCompiler.options.module.parser
  };
  childCompiler.options.module.parser.javascript = {
    ...childCompiler.options.module.parser.javascript,
    url: "relative"
  };
  const {
    NodeTemplatePlugin
  } = webpack.node;
  const {
    NodeTargetPlugin
  } = webpack.node;

  // @ts-ignore
  new NodeTemplatePlugin(outputOptions).apply(childCompiler);
  new NodeTargetPlugin().apply(childCompiler);
  const {
    EntryOptionPlugin
  } = webpack;
  const {
    library: {
      EnableLibraryPlugin
    }
  } = webpack;
  new EnableLibraryPlugin("commonjs2").apply(childCompiler);
  EntryOptionPlugin.applyEntryOption(childCompiler, this.context, {
    child: {
      library: {
        type: "commonjs2"
      },
      import: [`!!${request}`]
    }
  });
  const {
    LimitChunkCountPlugin
  } = webpack.optimize;
  new LimitChunkCountPlugin({
    maxChunks: 1
  }).apply(childCompiler);
  const {
    NormalModule
  } = webpack;
  childCompiler.hooks.thisCompilation.tap(`${MiniCssExtractPlugin.pluginName} loader`,
  /**
   * @param {Compilation} compilation
   */
  compilation => {
    const normalModuleHook = NormalModule.getCompilationHooks(compilation).loader;
    normalModuleHook.tap(`${MiniCssExtractPlugin.pluginName} loader`, (loaderContext, module) => {
      if (module.request === request) {
        // eslint-disable-next-line no-param-reassign
        module.loaders = loaders.map(loader => {
          return {
            type: null,
            loader: loader.path,
            options: loader.options,
            ident: loader.ident
          };
        });
      }
    });
  });

  /** @type {string | Buffer} */
  let source;
  childCompiler.hooks.compilation.tap(MiniCssExtractPlugin.pluginName,
  /**
   * @param {Compilation} compilation
   */
  compilation => {
    compilation.hooks.processAssets.tap(MiniCssExtractPlugin.pluginName, () => {
      source = compilation.assets[childFilename] && compilation.assets[childFilename].source();

      // Remove all chunk assets
      compilation.chunks.forEach(chunk => {
        chunk.files.forEach(file => {
          compilation.deleteAsset(file);
        });
      });
    });
  });
  childCompiler.runAsChild((error, entries, compilation) => {
    if (error) {
      callback(error);
      return;
    }
    if ( /** @type {Compilation} */compilation.errors.length > 0) {
      callback( /** @type {Compilation} */compilation.errors[0]);
      return;
    }

    /** @type {{ [name: string]: Source }} */
    const assets = Object.create(null);
    /** @type {Map<string, AssetInfo>} */
    const assetsInfo = new Map();
    for (const asset of /** @type {Compilation} */compilation.getAssets()) {
      assets[asset.name] = asset.source;
      assetsInfo.set(asset.name, asset.info);
    }

    /** @type {Compilation} */
    compilation.fileDependencies.forEach(dep => {
      this.addDependency(dep);
    }, this);

    /** @type {Compilation} */
    compilation.contextDependencies.forEach(dep => {
      this.addContextDependency(dep);
    }, this);
    if (!source) {
      callback(new Error("Didn't get a result from child compiler"));
      return;
    }
    let originalExports;
    try {
      originalExports = evalModuleCode(this, source, request);
    } catch (e) {
      callback( /** @type {Error} */e);
      return;
    }
    handleExports(originalExports, compilation, assets, assetsInfo);
  });
}

/**
 * @this {import("webpack").LoaderContext<LoaderOptions>}
 * @param {string} content
 */
// eslint-disable-next-line consistent-return
function loader(content) {
  if (this._compiler && this._compiler.options && this._compiler.options.experiments && this._compiler.options.experiments.css && this._module && (this._module.type === "css" || this._module.type === "css/auto" || this._module.type === "css/global" || this._module.type === "css/module")) {
    return content;
  }
}
module.exports = loader;
module.exports.pitch = pitch;
module.exports.hotLoader = hotLoader;


title: Mini CSS Extract Plugin options

description: This option determines the name of each output CSS file.

description: This option determines the name of non-entry chunk files.

description: Enable the experimental importModule approach instead of using child compilers. This uses less memory and is faster.

description: Remove Order Warnings.

description: Inserts the `link` tag at the given position for non-initial (async) (https://webpack.js.org/concepts/under-the-hood/#chunks) CSS chunks.

description: Adds custom attributes to the `link` tag for non-initial (async) (https://webpack.js.org/concepts/under-the-hood/#chunks) CSS chunks.

description: This option allows loading asynchronous chunks with a custom link type

description: Enabled/Disables runtime generation. CSS will be still extracted and can be used for a custom loading methods.


code:
"use strict";

const NativeModule = require("module");
const path = require("path");

/** @typedef {import("webpack").Compilation} Compilation */
/** @typedef {import("webpack").Module} Module */
/** @typedef {import("webpack").LoaderContext<any>} LoaderContext */

/**
 * @returns {boolean}
 */
function trueFn() {
  return true;
}

/**
 * @param {Compilation} compilation
 * @param {string | number} id
 * @returns {null | Module}
 */
function findModuleById(compilation, id) {
  const {
    modules,
    chunkGraph
  } = compilation;
  for (const module of modules) {
    const moduleId = typeof chunkGraph !== "undefined" ? chunkGraph.getModuleId(module) : module.id;
    if (moduleId === id) {
      return module;
    }
  }
  return null;
}

/**
 * @param {LoaderContext} loaderContext
 * @param {string | Buffer} code
 * @param {string} filename
 * @returns {object}
 */
function evalModuleCode(loaderContext, code, filename) {
  // @ts-ignore
  const module = new NativeModule(filename, loaderContext);

  // @ts-ignore
  module.paths = NativeModule._nodeModulePaths(loaderContext.context); // eslint-disable-line no-underscore-dangle
  module.filename = filename;
  // @ts-ignore
  module._compile(code, filename); // eslint-disable-line no-underscore-dangle

  return module.exports;
}

/**
 * @param {string} a
 * @param {string} b
 * @returns {0 | 1 | -1}
 */
function compareIds(a, b) {
  if (typeof a !== typeof b) {
    return typeof a < typeof b ? -1 : 1;
  }
  if (a < b) {
    return -1;
  }
  if (a > b) {
    return 1;
  }
  return 0;
}

/**
 * @param {Module} a
 * @param {Module} b
 * @returns {0 | 1 | -1}
 */
function compareModulesByIdentifier(a, b) {
  return compareIds(a.identifier(), b.identifier());
}
const MODULE_TYPE = "css/mini-extract";
const AUTO_PUBLIC_PATH = "__mini_css_extract_plugin_public_path_auto__";
const ABSOLUTE_PUBLIC_PATH = "webpack:///mini-css-extract-plugin/";
const BASE_URI = "webpack://";
const SINGLE_DOT_PATH_SEGMENT = "__mini_css_extract_plugin_single_dot_path_segment__";

/**
 * @param {string} str
 * @returns {boolean}
 */
function isAbsolutePath(str) {
  return path.posix.isAbsolute(str) || path.win32.isAbsolute(str);
}
const RELATIVE_PATH_REGEXP = /^\.\.?[/\\]/;

/**
 * @param {string} str
 * @returns {boolean}
 */
function isRelativePath(str) {
  return RELATIVE_PATH_REGEXP.test(str);
}

// TODO simplify for the next major release
/**
 * @param {LoaderContext} loaderContext
 * @param {string} request
 * @returns {string}
 */
function stringifyRequest(loaderContext, request) {
  if (typeof loaderContext.utils !== "undefined" && typeof loaderContext.utils.contextify === "function") {
    return JSON.stringify(loaderContext.utils.contextify(loaderContext.context || loaderContext.rootContext, request));
  }
  const splitted = request.split("!");
  const {
    context
  } = loaderContext;
  return JSON.stringify(splitted.map(part => {
    // First, separate singlePath from query, because the query might contain paths again
    const splittedPart = part.match(/^(.*?)(\?.*)/);
    const query = splittedPart ? splittedPart[2] : "";
    let singlePath = splittedPart ? splittedPart[1] : part;
    if (isAbsolutePath(singlePath) && context) {
      singlePath = path.relative(context, singlePath);
      if (isAbsolutePath(singlePath)) {
        // If singlePath still matches an absolute path, singlePath was on a different drive than context.
        // In this case, we leave the path platform-specific without replacing any separators.
        // @see https://github.com/webpack/loader-utils/pull/14
        return singlePath + query;
      }
      if (isRelativePath(singlePath) === false) {
        // Ensure that the relative path starts at least with ./ otherwise it would be a request into the modules directory (like node_modules).
        singlePath = `./${singlePath}`;
      }
    }
    return singlePath.replace(/\\/g, "/") + query;
  }).join("!"));
}

/**
 * @param {string} filename
 * @param {string} outputPath
 * @param {boolean} enforceRelative
 * @returns {string}
 */
function getUndoPath(filename, outputPath, enforceRelative) {
  let depth = -1;
  let append = "";

  // eslint-disable-next-line no-param-reassign
  outputPath = outputPath.replace(/[\\/]$/, "");
  for (const part of filename.split(/[/\\]+/)) {
    if (part === "..") {
      if (depth > -1) {
        // eslint-disable-next-line no-plusplus
        depth--;
      } else {
        const i = outputPath.lastIndexOf("/");
        const j = outputPath.lastIndexOf("\\");
        const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j);
        if (pos < 0) {
          return `${outputPath}/`;
        }
        append = `${outputPath.slice(pos + 1)}/${append}`;

        // eslint-disable-next-line no-param-reassign
        outputPath = outputPath.slice(0, pos);
      }
    } else if (part !== ".") {
      // eslint-disable-next-line no-plusplus
      depth++;
    }
  }
  return depth > 0 ? `${"../".repeat(depth)}${append}` : enforceRelative ? `./${append}` : append;
}

/**
 *
 * @param {string | function} value
 * @returns {string}
 */
function stringifyLocal(value) {
  return typeof value === "function" ? value.toString() : JSON.stringify(value);
}

/**
 * @param {string} str string
 * @returns {string} string
 */
const toSimpleString = str => {
  if (`${+str}` === str) {
    return str;
  }
  return JSON.stringify(str);
};

/**
 * @param {string} str string
 * @returns {string} quoted meta
 */
const quoteMeta = str => str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&");

/**
 * @param {Array<string>} items items
 * @returns {string} common prefix
 */
const getCommonPrefix = items => {
  let prefix = items[0];
  for (let i = 1; i < items.length; i++) {
    const item = items[i];
    for (let p = 0; p < prefix.length; p++) {
      if (item[p] !== prefix[p]) {
        prefix = prefix.slice(0, p);
        break;
      }
    }
  }
  return prefix;
};

/**
 * @param {Array<string>} items items
 * @returns {string} common suffix
 */
const getCommonSuffix = items => {
  let suffix = items[0];
  for (let i = 1; i < items.length; i++) {
    const item = items[i];
    for (let p = item.length - 1, s = suffix.length - 1; s >= 0; p--, s--) {
      if (item[p] !== suffix[s]) {
        suffix = suffix.slice(s + 1);
        break;
      }
    }
  }
  return suffix;
};

/**
 * @param {Set<string>} itemsSet items set
 * @param {(str: string) => string | false} getKey get key function
 * @param {(str: Array<string>) => boolean} condition condition
 * @returns {Array<Array<string>>} list of common items
 */
const popCommonItems = (itemsSet, getKey, condition) => {
  /** @type {Map<string, Array<string>>} */
  const map = new Map();
  for (const item of itemsSet) {
    const key = getKey(item);
    if (key) {
      let list = map.get(key);
      if (list === undefined) {
        /** @type {Array<string>} */
        list = [];
        map.set(key, list);
      }
      list.push(item);
    }
  }

  /** @type {Array<Array<string>>} */
  const result = [];
  for (const list of map.values()) {
    if (condition(list)) {
      for (const item of list) {
        itemsSet.delete(item);
      }
      result.push(list);
    }
  }
  return result;
};

/**
 * @param {Array<string>} itemsArr array of items
 * @returns {string} regexp
 */
const itemsToRegexp = itemsArr => {
  if (itemsArr.length === 1) {
    return quoteMeta(itemsArr[0]);
  }

  /** @type {Array<string>} */
  const finishedItems = [];

  // merge single char items: (a|b|c|d|ef) => ([abcd]|ef)
  let countOfSingleCharItems = 0;
  for (const item of itemsArr) {
    if (item.length === 1) {
      // eslint-disable-next-line no-plusplus
      countOfSingleCharItems++;
    }
  }

  // special case for only single char items
  if (countOfSingleCharItems === itemsArr.length) {
    return `[${quoteMeta(itemsArr.sort().join(""))}]`;
  }
  const items = new Set(itemsArr.sort());
  if (countOfSingleCharItems > 2) {
    let singleCharItems = "";
    for (const item of items) {
      if (item.length === 1) {
        singleCharItems += item;
        items.delete(item);
      }
    }
    finishedItems.push(`[${quoteMeta(singleCharItems)}]`);
  }

  // special case for 2 items with common prefix/suffix
  if (finishedItems.length === 0 && items.size === 2) {
    const prefix = getCommonPrefix(itemsArr);
    const suffix = getCommonSuffix(itemsArr.map(item => item.slice(prefix.length)));
    if (prefix.length > 0 || suffix.length > 0) {
      return `${quoteMeta(prefix)}${itemsToRegexp(itemsArr.map(i => i.slice(prefix.length, -suffix.length || undefined)))}${quoteMeta(suffix)}`;
    }
  }

  // special case for 2 items with common suffix
  if (finishedItems.length === 0 && items.size === 2) {
    /** @type {Iterator<string>} */
    const it = items[Symbol.iterator]();
    const a = it.next().value;
    const b = it.next().value;
    if (a.length > 0 && b.length > 0 && a.slice(-1) === b.slice(-1)) {
      return `${itemsToRegexp([a.slice(0, -1), b.slice(0, -1)])}${quoteMeta(a.slice(-1))}`;
    }
  }

  // find common prefix: (a1|a2|a3|a4|b5) => (a(1|2|3|4)|b5)
  const prefixed = popCommonItems(items, item => item.length >= 1 ? item[0] : false, list => {
    if (list.length >= 3) return true;
    if (list.length <= 1) return false;
    return list[0][1] === list[1][1];
  });
  for (const prefixedItems of prefixed) {
    const prefix = getCommonPrefix(prefixedItems);
    finishedItems.push(`${quoteMeta(prefix)}${itemsToRegexp(prefixedItems.map(i => i.slice(prefix.length)))}`);
  }

  // find common suffix: (a1|b1|c1|d1|e2) => ((a|b|c|d)1|e2)
  const suffixed = popCommonItems(items, item => item.length >= 1 ? item.slice(-1) : false, list => {
    if (list.length >= 3) return true;
    if (list.length <= 1) return false;
    return list[0].slice(-2) === list[1].slice(-2);
  });
  for (const suffixedItems of suffixed) {
    const suffix = getCommonSuffix(suffixedItems);
    finishedItems.push(`${itemsToRegexp(suffixedItems.map(i => i.slice(0, -suffix.length)))}${quoteMeta(suffix)}`);
  }

  // TODO further optimize regexp, i. e.
  // use ranges: (1|2|3|4|a) => [1-4a]
  const conditional = finishedItems.concat(Array.from(items, quoteMeta));
  if (conditional.length === 1) return conditional[0];
  return `(${conditional.join("|")})`;
};

/**
 * @param {string[]} positiveItems positive items
 * @param {string[]} negativeItems negative items
 * @returns {function(string): string} a template function to determine the value at runtime
 */
const compileBooleanMatcherFromLists = (positiveItems, negativeItems) => {
  if (positiveItems.length === 0) {
    return () => "false";
  }
  if (negativeItems.length === 0) {
    return () => "true";
  }
  if (positiveItems.length === 1) {
    return value => `${toSimpleString(positiveItems[0])} == ${value}`;
  }
  if (negativeItems.length === 1) {
    return value => `${toSimpleString(negativeItems[0])} != ${value}`;
  }
  const positiveRegexp = itemsToRegexp(positiveItems);
  const negativeRegexp = itemsToRegexp(negativeItems);
  if (positiveRegexp.length <= negativeRegexp.length) {
    return value => `/^${positiveRegexp}$/.test(${value})`;
  }
  return value => `!/^${negativeRegexp}$/.test(${value})`;
};

// TODO simplify in the next major release and use it from webpack
/**
 * @param {Record<string|number, boolean>} map value map
 * @returns {boolean|(function(string): string)} true/false, when unconditionally true/false, or a template function to determine the value at runtime
 */
const compileBooleanMatcher = map => {
  const positiveItems = Object.keys(map).filter(i => map[i]);
  const negativeItems = Object.keys(map).filter(i => !map[i]);
  if (positiveItems.length === 0) {
    return false;
  }
  if (negativeItems.length === 0) {
    return true;
  }
  return compileBooleanMatcherFromLists(positiveItems, negativeItems);
};
module.exports = {
  trueFn,
  findModuleById,
  evalModuleCode,
  compareModulesByIdentifier,
  MODULE_TYPE,
  AUTO_PUBLIC_PATH,
  ABSOLUTE_PUBLIC_PATH,
  BASE_URI,
  SINGLE_DOT_PATH_SEGMENT,
  stringifyRequest,
  stringifyLocal,
  getUndoPath,
  compileBooleanMatcher
};


code:
# ajv-keywords

Custom JSON-Schema keywords for [Ajv](https://github.com/epoberezkin/ajv) validator

[![build](https://github.com/ajv-validator/ajv-keywords/workflows/build/badge.svg)](https://github.com/ajv-validator/ajv-keywords/actions?query=workflow%3Abuild)
[![npm](https://img.shields.io/npm/v/ajv-keywords.svg)](https://www.npmjs.com/package/ajv-keywords)
[![npm downloads](https://img.shields.io/npm/dm/ajv-keywords.svg)](https://www.npmjs.com/package/ajv-keywords)
[![coverage](https://coveralls.io/repos/github/ajv-validator/ajv-keywords/badge.svg?branch=master)](https://coveralls.io/github/ajv-validator/ajv-keywords?branch=master)
[![gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv)

**Please note**: This readme file is for [ajv-keywords v5.0.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v5.0.0) that should be used with [ajv v8](https://github.com/ajv-validator/ajv).

[ajv-keywords v3](https://github.com/ajv-validator/ajv-keywords/tree/v3) should be used with [ajv v6](https://github.com/ajv-validator/ajv/tree/v6).

## Contents

- [Install](#install)
- [Usage](#usage)
- [Keywords](#keywords)
  - [Types](#types)
    - [typeof](#typeof)
    - [instanceof](#instanceof)\+
  - [Keywords for numbers](#keywords-for-numbers)
    - [range and exclusiveRange](#range-and-exclusiverange)
  - [Keywords for strings](#keywords-for-strings)
    - [regexp](#regexp)
    - [transform](#transform)\*
  - [Keywords for arrays](#keywords-for-arrays)
    - [uniqueItemProperties](#uniqueitemproperties)\+
  - [Keywords for objects](#keywords-for-objects)
    - [allRequired](#allrequired)
    - [anyRequired](#anyrequired)
    - [oneRequired](#onerequired)
    - [patternRequired](#patternrequired)
    - [prohibited](#prohibited)
    - [deepProperties](#deepproperties)
    - [deepRequired](#deeprequired)
    - [dynamicDefaults](#dynamicdefaults)\*\+
  - [Keywords for all types](#keywords-for-all-types)
    - [select/selectCases/selectDefault](#selectselectcasesselectdefault)
- [Security contact](#security-contact)
- [Open-source software support](#open-source-software-support)
- [License](#license)

\* - keywords that modify data
\+ - keywords that are not supported in [standalone validation code](https://github.com/ajv-validator/ajv/blob/master/docs/standalone.md)

## Install

To install version 4 to use with [Ajv v7](https://github.com/ajv-validator/ajv):

```
npm install ajv-keywords
```

## Usage

To add all available keywords:

```javascript
const Ajv = require("ajv")
const ajv = new Ajv()
require("ajv-keywords")(ajv)

ajv.validate({instanceof: "RegExp"}, /.*/) // true
ajv.validate({instanceof: "RegExp"}, ".*") // false
```

To add a single keyword:

```javascript
require("ajv-keywords")(ajv, "instanceof")
```

To add multiple keywords:

```javascript
require("ajv-keywords")(ajv, ["typeof", "instanceof"])
```

To add a single keyword directly (to avoid adding unused code):

```javascript
require("ajv-keywords/dist/keywords/select")(ajv, opts)
```

To add all keywords via Ajv options:

```javascript
const ajv = new Ajv({keywords: require("ajv-keywords/dist/definitions")(opts)})
```

To add one or several keywords via options:

```javascript
const ajv = new Ajv({
  keywords: [
    require("ajv-keywords/dist/definitions/typeof")(),
    require("ajv-keywords/dist/definitions/instanceof")(),
    // select exports an array of 3 definitions - see "select" in docs
    ...require("ajv-keywords/dist/definitions/select")(opts),
  ],
})
```

`opts` is an optional object with a property `defaultMeta` - URI of meta-schema to use for keywords that use subschemas (`select` and `deepProperties`). The default is `"http://json-schema.org/schema"`.

## Keywords

### Types

#### `typeof`

Based on JavaScript `typeof` operation.

The value of the keyword should be a string (`"undefined"`, `"string"`, `"number"`, `"object"`, `"function"`, `"boolean"` or `"symbol"`) or an array of strings.

To pass validation the result of `typeof` operation on the value should be equal to the string (or one of the strings in the array).

```javascript
ajv.validate({typeof: "undefined"}, undefined) // true
ajv.validate({typeof: "undefined"}, null) // false
ajv.validate({typeof: ["undefined", "object"]}, null) // true
```

#### `instanceof`

Based on JavaScript `instanceof` operation.

The value of the keyword should be a string (`"Object"`, `"Array"`, `"Function"`, `"Number"`, `"String"`, `"Date"`, `"RegExp"` or `"Promise"`) or an array of strings.

To pass validation the result of `data instanceof ...` operation on the value should be true:

```javascript
ajv.validate({instanceof: "Array"}, []) // true
ajv.validate({instanceof: "Array"}, {}) // false
ajv.validate({instanceof: ["Array", "Function"]}, function () {}) // true
```

You can add your own constructor function to be recognised by this keyword:

```javascript
class MyClass {}
const instanceofDef = require("ajv-keywords/dist/definitions/instanceof")
instanceofDef.CONSTRUCTORS.MyClass = MyClass
ajv.validate({instanceof: "MyClass"}, new MyClass()) // true
```

**Please note**: currently `instanceof` is not supported in [standalone validation code](https://github.com/ajv-validator/ajv/blob/master/docs/standalone.md) - it has to be implemented as [`code` keyword](https://github.com/ajv-validator/ajv/blob/master/docs/keywords.md#define-keyword-with-code-generation-function) to support it (PR is welcome).

### Keywords for numbers

#### `range` and `exclusiveRange`

Syntax sugar for the combination of minimum and maximum keywords (or exclusiveMinimum and exclusiveMaximum), also fails schema compilation if there are no numbers in the range.

The value of these keywords must be an array consisting of two numbers, the second must be greater or equal than the first one.

If the validated value is not a number the validation passes, otherwise to pass validation the value should be greater (or equal) than the first number and smaller (or equal) than the second number in the array.

```javascript
const schema = {type: "number", range: [1, 3]}
ajv.validate(schema, 1) // true
ajv.validate(schema, 2) // true
ajv.validate(schema, 3) // true
ajv.validate(schema, 0.99) // false
ajv.validate(schema, 3.01) // false

const schema = {type: "number", exclusiveRange: [1, 3]}
ajv.validate(schema, 1.01) // true
ajv.validate(schema, 2) // true
ajv.validate(schema, 2.99) // true
ajv.validate(schema, 1) // false
ajv.validate(schema, 3) // false
```

### Keywords for strings

#### `regexp`

This keyword allows to use regular expressions with flags in schemas, and also without `"u"` flag when needed (the standard `pattern` keyword does not support flags and implies the presence of `"u"` flag).

This keyword applies only to strings. If the data is not a string, the validation succeeds.

The value of this keyword can be either a string (the result of `regexp.toString()`) or an object with the properties `pattern` and `flags` (the same strings that should be passed to RegExp constructor).

```javascript
const schema = {
  type: "object",
  properties: {
    foo: {type: "string", regexp: "/foo/i"},
    bar: {type: "string", regexp: {pattern: "bar", flags: "i"}},
  },
}

const validData = {
  foo: "Food",
  bar: "Barmen",
}

const invalidData = {
  foo: "fog",
  bar: "bad",
}
```

#### `transform`

This keyword allows a string to be modified during validation.

This keyword applies only to strings. If the data is not a string, the `transform` keyword is ignored.

A standalone string cannot be modified, i.e. `data = 'a'; ajv.validate(schema, data);`, because strings are passed by value

**Supported transformations:**

- `trim`: remove whitespace from start and end
- `trimStart`/`trimLeft`: remove whitespace from start
- `trimEnd`/`trimRight`: remove whitespace from end
- `toLowerCase`: convert to lower case
- `toUpperCase`: convert to upper case
- `toEnumCase`: change string case to be equal to one of `enum` values in the schema

Transformations are applied in the order they are listed.

Note: `toEnumCase` requires that all allowed values are unique when case insensitive.

**Example: multiple transformations**

```javascript
require("ajv-keywords")(ajv, "transform")

const schema = {
  type: "array",
  items: {
    type: "string",
    transform: ["trim", "toLowerCase"],
  },
}

const data = ["  MixCase  "]
ajv.validate(schema, data)
console.log(data) // ['mixcase']
```

**Example: `enumcase`**

```javascript
require("ajv-keywords")(ajv, ["transform"])

const schema = {
  type: "array",
  items: {
    type: "string",
    transform: ["trim", "toEnumCase"],
    enum: ["pH"],
  },
}

const data = ["ph", " Ph", "PH", "pH "]
ajv.validate(schema, data)
console.log(data) // ['pH','pH','pH','pH']
```

### Keywords for arrays

#### `uniqueItemProperties`

The keyword allows to check that some properties in array items are unique.

This keyword applies only to arrays. If the data is not an array, the validation succeeds.

The value of this keyword must be an array of strings - property names that should have unique values across all items.

```javascript
const schema = {
  type: "array",
  uniqueItemProperties: ["id", "name"],
}

const validData = [{id: 1}, {id: 2}, {id: 3}]

const invalidData1 = [
  {id: 1},
  {id: 1}, // duplicate "id"
  {id: 3},
]

const invalidData2 = [
  {id: 1, name: "taco"},
  {id: 2, name: "taco"}, // duplicate "name"
  {id: 3, name: "salsa"},
]
```

This keyword is contributed by [@blainesch](https://github.com/blainesch).

**Please note**: currently `uniqueItemProperties` is not supported in [standalone validation code](https://github.com/ajv-validator/ajv/blob/master/docs/standalone.md) - it has to be implemented as [`code` keyword](https://github.com/ajv-validator/ajv/blob/master/docs/keywords.md#define-keyword-with-code-generation-function) to support it (PR is welcome).

### Keywords for objects

#### `allRequired`

This keyword allows to require the presence of all properties used in `properties` keyword in the same schema object.

This keyword applies only to objects. If the data is not an object, the validation succeeds.

The value of this keyword must be boolean.

If the value of the keyword is `false`, the validation succeeds.

If the value of the keyword is `true`, the validation succeeds if the data contains all properties defined in `properties` keyword (in the same schema object).

If the `properties` keyword is not present in the same schema object, schema compilation will throw exception.

```javascript
const schema = {
  type: "object",
  properties: {
    foo: {type: "number"},
    bar: {type: "number"},
  },
  allRequired: true,
}

const validData = {foo: 1, bar: 2}
const alsoValidData = {foo: 1, bar: 2, baz: 3}

const invalidDataList = [{}, {foo: 1}, {bar: 2}]
```

#### `anyRequired`

This keyword allows to require the presence of any (at least one) property from the list.

This keyword applies only to objects. If the data is not an object, the validation succeeds.

The value of this keyword must be an array of strings, each string being a property name. For data object to be valid at least one of the properties in this array should be present in the object.

```javascript
const schema = {
  type: "object",
  anyRequired: ["foo", "bar"],
}

const validData = {foo: 1}
const alsoValidData = {foo: 1, bar: 2}

const invalidDataList = [{}, {baz: 3}]
```

#### `oneRequired`

This keyword allows to require the presence of only one property from the list.

This keyword applies only to objects. If the data is not an object, the validation succeeds.

The value of this keyword must be an array of strings, each string being a property name. For data object to be valid exactly one of the properties in this array should be present in the object.

```javascript
const schema = {
  type: "object",
  oneRequired: ["foo", "bar"],
}

const validData = {foo: 1}
const alsoValidData = {bar: 2, baz: 3}

const invalidDataList = [{}, {baz: 3}, {foo: 1, bar: 2}]
```

#### `patternRequired`

This keyword allows to require the presence of properties that match some pattern(s).

This keyword applies only to objects. If the data is not an object, the validation succeeds.

The value of this keyword should be an array of strings, each string being a regular expression. For data object to be valid each regular expression in this array should match at least one property name in the data object.

If the array contains multiple regular expressions, more than one expression can match the same property name.

```javascript
const schema = {
  type: "object",
  patternRequired: ["f.*o", "b.*r"],
}

const validData = {foo: 1, bar: 2}
const alsoValidData = {foobar: 3}

const invalidDataList = [{}, {foo: 1}, {bar: 2}]
```

#### `prohibited`

This keyword allows to prohibit that any of the properties in the list is present in the object.

This keyword applies only to objects. If the data is not an object, the validation succeeds.

The value of this keyword should be an array of strings, each string being a property name. For data object to be valid none of the properties in this array should be present in the object.

```javascript
const schema = {
  type: "object",
  prohibited: ["foo", "bar"],
}

const validData = {baz: 1}
const alsoValidData = {}

const invalidDataList = [{foo: 1}, {bar: 2}, {foo: 1, bar: 2}]
```

**Please note**: `{prohibited: ['foo', 'bar']}` is equivalent to `{not: {anyRequired: ['foo', 'bar']}}` (i.e. it has the same validation result for any data).

#### `deepProperties`

This keyword allows to validate deep properties (identified by JSON pointers).

This keyword applies only to objects. If the data is not an object, the validation succeeds.

The value should be an object, where keys are JSON pointers to the data, starting from the current position in data, and the values are JSON schemas. For data object to be valid the value of each JSON pointer should be valid according to the corresponding schema.

```javascript
const schema = {
  type: "object",
  deepProperties: {
    "/users/1/role": {enum: ["admin"]},
  },
}

const validData = {
  users: [
    {},
    {
      id: 123,
      role: "admin",
    },
  ],
}

const alsoValidData = {
  users: {
    1: {
      id: 123,
      role: "admin",
    },
  },
}

const invalidData = {
  users: [
    {},
    {
      id: 123,
      role: "user",
    },
  ],
}

const alsoInvalidData = {
  users: {
    1: {
      id: 123,
      role: "user",
    },
  },
}
```

#### `deepRequired`

This keyword allows to check that some deep properties (identified by JSON pointers) are available.

This keyword applies only to objects. If the data is not an object, the validation succeeds.

The value should be an array of JSON pointers to the data, starting from the current position in data. For data object to be valid each JSON pointer should be some existing part of the data.

```javascript
const schema = {
  type: "object",
  deepRequired: ["/users/1/role"],
}

const validData = {
  users: [
    {},
    {
      id: 123,
      role: "admin",
    },
  ],
}

const invalidData = {
  users: [
    {},
    {
      id: 123,
    },
  ],
}
```

See [json-schema-org/json-schema-spec#203](https://github.com/json-schema-org/json-schema-spec/issues/203#issue-197211916) for an example of the equivalent schema without `deepRequired` keyword.

### Keywords for all types

#### `select`/`selectCases`/`selectDefault`

**Please note**: these keywords are deprecated. It is recommended to use OpenAPI [discriminator](https://ajv.js.org/json-schema.html#discriminator) keyword supported by Ajv v8 instead of `select`.

These keywords allow to choose the schema to validate the data based on the value of some property in the validated data.

These keywords must be present in the same schema object (`selectDefault` is optional).

The value of `select` keyword should be a [\$data reference](https://github.com/ajv-validator/ajv/blob/master/docs/validation.md#data-reference) that points to any primitive JSON type (string, number, boolean or null) in the data that is validated. You can also use a constant of primitive type as the value of this keyword (e.g., for debugging purposes).

The value of `selectCases` keyword must be an object where each property name is a possible string representation of the value of `select` keyword and each property value is a corresponding schema (from draft-06 it can be boolean) that must be used to validate the data.

The value of `selectDefault` keyword is a schema (also can be boolean) that must be used to validate the data in case `selectCases` has no key equal to the stringified value of `select` keyword.

The validation succeeds in one of the following cases:

- the validation of data using selected schema succeeds,
- none of the schemas is selected for validation,
- the value of select is undefined (no property in the data that the data reference points to).

If `select` value (in data) is not a primitive type the validation fails.

This keyword correctly tracks evaluated properties and items to work with `unevaluatedProperties` and `unevaluatedItems` keywords - only properties and items from the subschema that was used (one of `selectCases` subschemas or `selectDefault` subschema) are marked as evaluated.

**Please note**: these keywords require Ajv `$data` option to support [\$data reference](https://github.com/ajv-validator/ajv/blob/master/docs/validation.md#data-reference).

```javascript
require("ajv-keywords")(ajv, "select")

const schema = {
  type: "object",
  required: ["kind"],
  properties: {
    kind: {type: "string"},
  },
  select: {$data: "0/kind"},
  selectCases: {
    foo: {
      required: ["foo"],
      properties: {
        kind: {},
        foo: {type: "string"},
      },
      additionalProperties: false,
    },
    bar: {
      required: ["bar"],
      properties: {
        kind: {},
        bar: {type: "number"},
      },
      additionalProperties: false,
    },
  },
  selectDefault: {
    propertyNames: {
      not: {enum: ["foo", "bar"]},
    },
  },
}

const validDataList = [
  {kind: "foo", foo: "any"},
  {kind: "bar", bar: 1},
  {kind: "anything_else", not_bar_or_foo: "any value"},
]

const invalidDataList = [
  {kind: "foo"}, // no property foo
  {kind: "bar"}, // no property bar
  {kind: "foo", foo: "any", another: "any value"}, // additional property
  {kind: "bar", bar: 1, another: "any value"}, // additional property
  {kind: "anything_else", foo: "any"}, // property foo not allowed
  {kind: "anything_else", bar: 1}, // property bar not allowed
]
```

#### `dynamicDefaults`

This keyword allows to assign dynamic defaults to properties, such as timestamps, unique IDs etc.

This keyword only works if `useDefaults` options is used and not inside `anyOf` keywords etc., in the same way as [default keyword treated by Ajv](https://github.com/epoberezkin/ajv#assigning-defaults).

The keyword should be added on the object level. Its value should be an object with each property corresponding to a property name, in the same way as in standard `properties` keyword. The value of each property can be:

- an identifier of dynamic default function (a string)
- an object with properties `func` (an identifier) and `args` (an object with parameters that will be passed to this function during schema compilation - see examples).

The properties used in `dynamicDefaults` should not be added to `required` keyword in the same schema (or validation will fail), because unlike `default` this keyword is processed after validation.

There are several predefined dynamic default functions:

- `"timestamp"` - current timestamp in milliseconds
- `"datetime"` - current date and time as string (ISO, valid according to `date-time` format)
- `"date"` - current date as string (ISO, valid according to `date` format)
- `"time"` - current time as string (ISO, valid according to `time` format)
- `"random"` - pseudo-random number in [0, 1) interval
- `"randomint"` - pseudo-random integer number. If string is used as a property value, the function will randomly return 0 or 1. If object `{ func: 'randomint', args: { max: N } }` is used then the default will be an integer number in [0, N) interval.
- `"seq"` - sequential integer number starting from 0. If string is used as a property value, the default sequence will be used. If object `{ func: 'seq', args: { name: 'foo'} }` is used then the sequence with name `"foo"` will be used. Sequences are global, even if different ajv instances are used.

```javascript
const schema = {
  type: "object",
  dynamicDefaults: {
    ts: "datetime",
    r: {func: "randomint", args: {max: 100}},
    id: {func: "seq", args: {name: "id"}},
  },
  properties: {
    ts: {
      type: "string",
      format: "date-time",
    },
    r: {
      type: "integer",
      minimum: 0,
      exclusiveMaximum: 100,
    },
    id: {
      type: "integer",
      minimum: 0,
    },
  },
}

const data = {}
ajv.validate(data) // true
data // { ts: '2016-12-01T22:07:28.829Z', r: 25, id: 0 }

const data1 = {}
ajv.validate(data1) // true
data1 // { ts: '2016-12-01T22:07:29.832Z', r: 68, id: 1 }

ajv.validate(data1) // true
data1 // didn't change, as all properties were defined
```

When using the `useDefaults` option value `"empty"`, properties and items equal to `null` or `""` (empty string) will be considered missing and assigned defaults. Use `allOf` [compound keyword](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#compound-keywords) to execute `dynamicDefaults` before validation.

```javascript
const schema = {
  type: "object",
  allOf: [
    {
      dynamicDefaults: {
        ts: "datetime",
        r: {func: "randomint", args: {min: 5, max: 100}},
        id: {func: "seq", args: {name: "id"}},
      },
    },
    {
      properties: {
        ts: {
          type: "string",
        },
        r: {
          type: "number",
          minimum: 5,
          exclusiveMaximum: 100,
        },
        id: {
          type: "integer",
          minimum: 0,
        },
      },
    },
  ],
}

const data = {ts: "", r: null}
ajv.validate(data) // true
data // { ts: '2016-12-01T22:07:28.829Z', r: 25, id: 0 }
```

You can add your own dynamic default function to be recognised by this keyword:

```javascript
const uuid = require("uuid")

const def = require("ajv-keywords/dist/definitions/dynamicDefaults")
def.DEFAULTS.uuid = () => uuid.v4

const schema = {
  dynamicDefaults: {id: "uuid"},
  properties: {id: {type: "string", format: "uuid"}},
}

const data = {}
ajv.validate(schema, data) // true
data // { id: 'a1183fbe-697b-4030-9bcc-cfeb282a9150' };

const data1 = {}
ajv.validate(schema, data1) // true
data1 // { id: '5b008de7-1669-467a-a5c6-70fa244d7209' }
```

You also can define dynamic default that accept parameters, e.g. version of uuid:

```javascript
const uuid = require("uuid")

function getUuid(args) {
  const version = "v" + ((arvs && args.v) || "4")
  return uuid[version]
}

const def = require("ajv-keywords/dist/definitions/dynamicDefaults")
def.DEFAULTS.uuid = getUuid

const schema = {
  dynamicDefaults: {
    id1: "uuid", // v4
    id2: {func: "uuid", v: 4}, // v4
    id3: {func: "uuid", v: 1}, // v1
  },
}
```

**Please note**: dynamic default functions are differentiated by the number of parameters they have (`function.length`). Functions that do not expect default must have one non-optional argument so that `function.length` > 0.

`dynamicDefaults` is not supported in [standalone validation code](https://github.com/ajv-validator/ajv/blob/master/docs/standalone.md).

## Security contact

To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.

Please do NOT report security vulnerabilities via GitHub issues.

## Open-source software support

Ajv-keywords is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv-keywords?utm_source=npm-ajv-keywords&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers.

## License

[MIT](https://github.com/epoberezkin/ajv-keywords/blob/master/LICENSE)


code:
import type { MacroKeywordDefinition } from "ajv";
import type { GetDefinition } from "./_types";
declare type RangeKwd = "range" | "exclusiveRange";
export default function getRangeDef(keyword: RangeKwd): GetDefinition<MacroKeywordDefinition>;
export {};


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function getRangeDef(keyword) {
    return () => ({
        keyword,
        type: "number",
        schemaType: "array",
        macro: function ([min, max]) {
            validateRangeSchema(min, max);
            return keyword === "range"
                ? { minimum: min, maximum: max }
                : { exclusiveMinimum: min, exclusiveMaximum: max };
        },
        metaSchema: {
            type: "array",
            minItems: 2,
            maxItems: 2,
            items: { type: "number" },
        },
    });
    function validateRangeSchema(min, max) {
        if (min > max || (keyword === "exclusiveRange" && min === max)) {
            throw new Error("There are no numbers in range");
        }
    }
}
exports.default = getRangeDef;
//# sourceMappingURL=_range.js.map


code:
import type { MacroKeywordDefinition } from "ajv";
import type { GetDefinition } from "./_types";
declare type RequiredKwd = "anyRequired" | "oneRequired";
export default function getRequiredDef(keyword: RequiredKwd): GetDefinition<MacroKeywordDefinition>;
export {};


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function getRequiredDef(keyword) {
    return () => ({
        keyword,
        type: "object",
        schemaType: "array",
        macro(schema) {
            if (schema.length === 0)
                return true;
            if (schema.length === 1)
                return { required: schema };
            const comb = keyword === "anyRequired" ? "anyOf" : "oneOf";
            return { [comb]: schema.map((p) => ({ required: [p] })) };
        },
        metaSchema: {
            type: "array",
            items: { type: "string" },
        },
    });
}
exports.default = getRequiredDef;
//# sourceMappingURL=_required.js.map


code:
import type { KeywordDefinition } from "ajv";
export interface DefinitionOptions {
    defaultMeta?: string | boolean;
}
export declare type GetDefinition<T extends KeywordDefinition> = (opts?: DefinitionOptions) => T;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=_types.js.map


code:
import type { DefinitionOptions } from "./_types";
import type { SchemaObject, KeywordCxt, Name } from "ajv";
export declare function metaSchemaRef({ defaultMeta }?: DefinitionOptions): SchemaObject;
export declare function usePattern({ gen, it: { opts } }: KeywordCxt, pattern: string, flags?: string): Name;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePattern = exports.metaSchemaRef = void 0;
const codegen_1 = require("ajv/dist/compile/codegen");
const META_SCHEMA_ID = "http://json-schema.org/schema";
function metaSchemaRef({ defaultMeta } = {}) {
    return defaultMeta === false ? {} : { $ref: defaultMeta || META_SCHEMA_ID };
}
exports.metaSchemaRef = metaSchemaRef;
function usePattern({ gen, it: { opts } }, pattern, flags = opts.unicodeRegExp ? "u" : "") {
    const rx = new RegExp(pattern, flags);
    return gen.scopeValue("pattern", {
        key: rx.toString(),
        ref: rx,
        code: (0, codegen_1._) `new RegExp(${pattern}, ${flags})`,
    });
}
exports.usePattern = usePattern;
//# sourceMappingURL=_util.js.map


code:
import type { MacroKeywordDefinition } from "ajv";
export default function getDef(): MacroKeywordDefinition;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function getDef() {
    return {
        keyword: "allRequired",
        type: "object",
        schemaType: "boolean",
        macro(schema, parentSchema) {
            if (!schema)
                return true;
            const required = Object.keys(parentSchema.properties);
            if (required.length === 0)
                return true;
            return { required };
        },
        dependencies: ["properties"],
    };
}
exports.default = getDef;
module.exports = getDef;
//# sourceMappingURL=allRequired.js.map


code:
import type { MacroKeywordDefinition } from "ajv";
import type { GetDefinition } from "./_types";
declare const getDef: GetDefinition<MacroKeywordDefinition>;
export default getDef;


code:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const _required_1 = __importDefault(require("./_required"));
const getDef = (0, _required_1.default)("anyRequired");
exports.default = getDef;
module.exports = getDef;
//# sourceMappingURL=anyRequired.js.map


code:
import type { MacroKeywordDefinition } from "ajv";
import type { DefinitionOptions } from "./_types";
export default function getDef(opts?: DefinitionOptions): MacroKeywordDefinition;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const _util_1 = require("./_util");
function getDef(opts) {
    return {
        keyword: "deepProperties",
        type: "object",
        schemaType: "object",
        macro: function (schema) {
            const allOf = [];
            for (const pointer in schema)
                allOf.push(getSchema(pointer, schema[pointer]));
            return { allOf };
        },
        metaSchema: {
            type: "object",
            propertyNames: { type: "string", format: "json-pointer" },
            additionalProperties: (0, _util_1.metaSchemaRef)(opts),
        },
    };
}
exports.default = getDef;
function getSchema(jsonPointer, schema) {
    const segments = jsonPointer.split("/");
    const rootSchema = {};
    let pointerSchema = rootSchema;
    for (let i = 1; i < segments.length; i++) {
        let segment = segments[i];
        const isLast = i === segments.length - 1;
        segment = unescapeJsonPointer(segment);
        const properties = (pointerSchema.properties = {});
        let items;
        if (/[0-9]+/.test(segment)) {
            let count = +segment;
            items = pointerSchema.items = [];
            pointerSchema.type = ["object", "array"];
            while (count--)
                items.push({});
        }
        else {
            pointerSchema.type = "object";
        }
        pointerSchema = isLast ? schema : {};
        properties[segment] = pointerSchema;
        if (items)
            items.push(pointerSchema);
    }
    return rootSchema;
}
function unescapeJsonPointer(str) {
    return str.replace(/~1/g, "/").replace(/~0/g, "~");
}
module.exports = getDef;
//# sourceMappingURL=deepProperties.js.map


code:
import type { CodeKeywordDefinition } from "ajv";
export default function getDef(): CodeKeywordDefinition;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const codegen_1 = require("ajv/dist/compile/codegen");
function getDef() {
    return {
        keyword: "deepRequired",
        type: "object",
        schemaType: "array",
        code(ctx) {
            const { schema, data } = ctx;
            const props = schema.map((jp) => (0, codegen_1._) `(${getData(jp)}) === undefined`);
            ctx.fail((0, codegen_1.or)(...props));
            function getData(jsonPointer) {
                if (jsonPointer === "")
                    throw new Error("empty JSON pointer not allowed");
                const segments = jsonPointer.split("/");
                let x = data;
                const xs = segments.map((s, i) => i ? (x = (0, codegen_1._) `${x}${(0, codegen_1.getProperty)(unescapeJPSegment(s))}`) : x);
                return (0, codegen_1.and)(...xs);
            }
        },
        metaSchema: {
            type: "array",
            items: { type: "string", format: "json-pointer" },
        },
    };
}
exports.default = getDef;
function unescapeJPSegment(s) {
    return s.replace(/~1/g, "/").replace(/~0/g, "~");
}
module.exports = getDef;
//# sourceMappingURL=deepRequired.js.map


code:
import type { FuncKeywordDefinition } from "ajv";
export declare type DynamicDefaultFunc = (args?: Record<string, any>) => () => any;
declare const DEFAULTS: Record<string, DynamicDefaultFunc | undefined>;
declare const getDef: (() => FuncKeywordDefinition) & {
    DEFAULTS: typeof DEFAULTS;
};
export default getDef;