code:
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![deps][deps]][deps-url]
[![tests][tests]][tests-url]
[![coverage][cover]][cover-url]
[![chat][chat]][chat-url]
[![size][size]][size-url]

# webpack-dev-middleware

An express-style development middleware for use with [webpack](https://webpack.js.org)
bundles and allows for serving of the files emitted from webpack.
This should be used for **development only**.

Some of the benefits of using this middleware include:

- No files are written to disk, rather it handles files in memory
- If files changed in watch mode, the middleware delays requests until compiling
  has completed.
- Supports hot module reload (HMR).

## Getting Started

First thing's first, install the module:

```console
npm install webpack-dev-middleware --save-dev
```

_Note: We do not recommend installing this module globally._

## Usage

```js
const webpack = require("webpack");
const middleware = require("webpack-dev-middleware");
const compiler = webpack({
  // webpack options
});
const express = require("express");
const app = express();

app.use(
  middleware(compiler, {
    // webpack-dev-middleware options
  })
);

app.listen(3000, () => console.log("Example app listening on port 3000!"));
```

See [below](#other-servers) for an example of use with fastify.

## Options

|                    Name                     |           Type            |                    Default                    | Description                                                                                |
| :-----------------------------------------: | :-----------------------: | :-------------------------------------------: | :------------------------------------------------------------------------------------------------------------------- |
|          **[`methods`](#methods)**          |          `Array`          |              `[ 'GET', 'HEAD' ]`              | Allows to pass the list of HTTP request methods accepted by the middleware                                           |
|          **[`headers`](#headers)**          | `Array\|Object\|Function` |                  `undefined`                  | Allows to pass custom HTTP headers on each request.                                                                  |
|            **[`index`](#index)**            |     `Boolean\|String`     |                 `index.html`                  | If `false` (but not `undefined`), the server will not respond to requests to the root URL.                           |
|        **[`mimeTypes`](#mimetypes)**        |         `Object`          |                  `undefined`                  | Allows to register custom mime types or extension mappings.                                                          |
|       **[`publicPath`](#publicpath)**       |         `String`          |  `output.publicPath` (from a configuration)   | The public path that the middleware is bound to.                                                                     |
|            **[`stats`](#stats)**            | `Boolean\|String\|Object` |        `stats` (from a configuration)         | Stats options object or preset name.                                                                                |
| **[`serverSideRender`](#serversiderender)** |         `Boolean`         |                  `undefined`                  | Instructs the module to enable or disable the server-side rendering mode.                                            |
|      **[`writeToDisk`](#writetodisk)**      |    `Boolean\|Function`    |                    `false`                    | Instructs the module to write files to the configured location on disk as specified in your `webpack` configuration. |
| **[`outputFileSystem`](#outputfilesystem)** |         `Object`          | [`memfs`](https://github.com/streamich/memfs) | Set the default file system which will be used by webpack as primary destination of generated files.                 |

The middleware accepts an `options` Object. The following is a property reference for the Object.

### methods

Type: `Array`
Default: `[ 'GET', 'HEAD' ]`

This property allows a user to pass the list of HTTP request methods accepted by the middleware\*\*.

### headers

Type: `Array|Object|Function`
Default: `undefined`

This property allows a user to pass custom HTTP headers on each request.
eg. `{ "X-Custom-Header": "yes" }`

or

```js
webpackDevMiddleware(compiler, {
  headers: () => {
    return {
      "Last-Modified": new Date(),
    };
  },
});
```

or

```js
webpackDevMiddleware(compiler, {
  headers: (req, res, context) => {
    res.setHeader("Last-Modified", new Date());
  },
});
```

or

```js
webpackDevMiddleware(compiler, {
  headers: [
    {
      key: "X-custom-header"
      value: "foo"
    },
    {
      key: "Y-custom-header",
      value: "bar"
    }
  ]
  },
});
```

or

```js
webpackDevMiddleware(compiler, {
  headers: () => [
    {
      key: "X-custom-header"
      value: "foo"
    },
    {
      key: "Y-custom-header",
      value: "bar"
    }
  ]
  },
});
```

### index

Type: `Boolean|String`
Default: `index.html`

If `false` (but not `undefined`), the server will not respond to requests to the root URL.

### mimeTypes

Type: `Object`
Default: `undefined`

This property allows a user to register custom mime types or extension mappings.
eg. `mimeTypes: { phtml: 'text/html' }`.

Please see the documentation for [`mime-types`](https://github.com/jshttp/mime-types) for more information.

### publicPath

Type: `String`
Default: `output.publicPath` (from a configuration)

The public path that the middleware is bound to.

_Best Practice: use the same `publicPath` defined in your webpack config. For more information about `publicPath`, please see [the webpack documentation](https://webpack.js.org/guides/public-path)._

### stats

Type: `Boolean|String|Object`
Default: `stats` (from a configuration)

Stats options object or preset name.

### serverSideRender

Type: `Boolean`
Default: `undefined`

Instructs the module to enable or disable the server-side rendering mode.
Please see [Server-Side Rendering](#server-side-rendering) for more information.

### writeToDisk

Type: `Boolean|Function`
Default: `false`

If `true`, the option will instruct the module to write files to the configured location on disk as specified in your `webpack` config file.
_Setting `writeToDisk: true` won't change the behavior of the `webpack-dev-middleware`, and bundle files accessed through the browser will still be served from memory._
This option provides the same capabilities as the [`WriteFilePlugin`](https://github.com/gajus/write-file-webpack-plugin/pulls).

This option also accepts a `Function` value, which can be used to filter which files are written to disk.
The function follows the same premise as [`Array#filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) in which a return value of `false` _will not_ write the file, and a return value of `true` _will_ write the file to disk. eg.

```js
const webpack = require("webpack");
const configuration = {
  /* Webpack configuration */
};
const compiler = webpack(configuration);

middleware(compiler, {
  writeToDisk: (filePath) => {
    return /superman\.css$/.test(filePath);
  },
});
```

### outputFileSystem

Type: `Object`
Default: [memfs](https://github.com/streamich/memfs)

Set the default file system which will be used by webpack as primary destination of generated files.
This option isn't affected by the [writeToDisk](#writeToDisk) option.

You have to provide `.join()` and `mkdirp` method to the `outputFileSystem` instance manually for compatibility with `webpack@4`.

This can be done simply by using `path.join`:

```js
const webpack = require("webpack");
const path = require("path");
const myOutputFileSystem = require("my-fs");
const mkdirp = require("mkdirp");

myOutputFileSystem.join = path.join.bind(path); // no need to bind
myOutputFileSystem.mkdirp = mkdirp.bind(mkdirp); // no need to bind

const compiler = webpack({
  /* Webpack configuration */
});

middleware(compiler, { outputFileSystem: myOutputFileSystem });
```

## API

`webpack-dev-middleware` also provides convenience methods that can be use to
interact with the middleware at runtime:

### `close(callback)`

Instructs `webpack-dev-middleware` instance to stop watching for file changes.

#### Parameters

##### `callback`

Type: `Function`
Required: `No`

A function executed once the middleware has stopped watching.

```js
const express = require("express");
const webpack = require("webpack");
const compiler = webpack({
  /* Webpack configuration */
});
const middleware = require("webpack-dev-middleware");
const instance = middleware(compiler);

const app = new express();

app.use(instance);

setTimeout(() => {
  // Says `webpack` to stop watch changes
  instance.close();
}, 1000);
```

### `invalidate(callback)`

Instructs `webpack-dev-middleware` instance to recompile the bundle, e.g. after a change to the configuration.

#### Parameters

##### `callback`

Type: `Function`
Required: `No`

A function executed once the middleware has invalidated.

```js
const express = require("express");
const webpack = require("webpack");
const compiler = webpack({
  /* Webpack configuration */
});
const middleware = require("webpack-dev-middleware");
const instance = middleware(compiler);

const app = new express();

app.use(instance);

setTimeout(() => {
  // After a short delay the configuration is changed and a banner plugin is added to the config
  new webpack.BannerPlugin("A new banner").apply(compiler);

  // Recompile the bundle with the banner plugin:
  instance.invalidate();
}, 1000);
```

### `waitUntilValid(callback)`

Executes a callback function when the compiler bundle is valid, typically after
compilation.

#### Parameters

##### `callback`

Type: `Function`
Required: `No`

A function executed when the bundle becomes valid.
If the bundle is valid at the time of calling, the callback is executed immediately.

```js
const express = require("express");
const webpack = require("webpack");
const compiler = webpack({
  /* Webpack configuration */
});
const middleware = require("webpack-dev-middleware");
const instance = middleware(compiler);

const app = new express();

app.use(instance);

instance.waitUntilValid(() => {
  console.log("Package is in a valid state");
});
```

### `getFilenameFromUrl(url)`

Get filename from URL.

#### Parameters

##### `url`

Type: `String`
Required: `Yes`

URL for the requested file.

```js
const express = require("express");
const webpack = require("webpack");
const compiler = webpack({
  /* Webpack configuration */
});
const middleware = require("webpack-dev-middleware");
const instance = middleware(compiler);

const app = new express();

app.use(instance);

instance.waitUntilValid(() => {
  const filename = instance.getFilenameFromUrl("/bundle.js");

  console.log(`Filename is ${filename}`);
});
```

## Known Issues

### Multiple Successive Builds

Watching will frequently cause multiple compilations
as the bundle changes during compilation. This is due in part to cross-platform
differences in file watchers, so that webpack doesn't loose file changes when
watched files change rapidly. If you run into this situation, please make use of
the [`TimeFixPlugin`](https://github.com/egoist/time-fix-plugin).

## Server-Side Rendering

_Note: this feature is experimental and may be removed or changed completely in the future._

In order to develop an app using server-side rendering, we need access to the
[`stats`](https://github.com/webpack/docs/wiki/node.js-api#stats), which is
generated with each build.

With server-side rendering enabled, `webpack-dev-middleware` sets the `stats` to `res.locals.webpack.devMiddleware.context.stats`
and the filesystem to `res.locals.webpack.devMiddleware.context.outputFileSystem` before invoking the next middleware,
allowing a developer to render the page body and manage the response to clients.

_Note: Requests for bundle files will still be handled by
`webpack-dev-middleware` and all requests will be pending until the build
process is finished with server-side rendering enabled._

Example Implementation:

```js
const express = require("express");
const webpack = require("webpack");
const compiler = webpack({
  /* Webpack configuration */
});
const isObject = require("is-object");
const middleware = require("webpack-dev-middleware");

const app = new express();

// This function makes server rendering of asset references consistent with different webpack chunk/entry configurations
function normalizeAssets(assets) {
  if (isObject(assets)) {
    return Object.values(assets);
  }

  return Array.isArray(assets) ? assets : [assets];
}

app.use(middleware(compiler, { serverSideRender: true }));

// The following middleware would not be invoked until the latest build is finished.
app.use((req, res) => {
  const { devMiddleware } = res.locals.webpack;
  const outputFileSystem = devMiddleware.context.outputFileSystem;
  const jsonWebpackStats = devMiddleware.context.stats.toJson();
  const { assetsByChunkName, outputPath } = jsonWebpackStats;

  // Then use `assetsByChunkName` for server-side rendering
  // For example, if you have only one main chunk:
  res.send(`
<html>
  <head>
    <title>My App</title>
    <style>
    ${normalizeAssets(assetsByChunkName.main)
      .filter((path) => path.endsWith(".css"))
      .map((path) => outputFileSystem.readFileSync(path.join(outputPath, path)))
      .join("\n")}
    </style>
  </head>
  <body>
    <div id="root"></div>
    ${normalizeAssets(assetsByChunkName.main)
      .filter((path) => path.endsWith(".js"))
      .map((path) => `<script src="${path}"></script>`)
      .join("\n")}
  </body>
</html>
  `);
});
```

## Support

We do our best to keep Issues in the repository focused on bugs, features, and
needed modifications to the code for the module. Because of that, we ask users
with general support, "how-to", or "why isn't this working" questions to try one
of the other support channels that are available.

Your first-stop-shop for support for webpack-dev-server should by the excellent
[documentation][docs-url] for the module. If you see an opportunity for improvement
of those docs, please head over to the [webpack.js.org repo][wjo-url] and open a
pull request.

From there, we encourage users to visit the [webpack Gitter chat][chat-url] and
talk to the fine folks there. If your quest for answers comes up dry in chat,
head over to [StackOverflow][stack-url] and do a quick search or open a new
question. Remember; It's always much easier to answer questions that include your
`webpack.config.js` and relevant files!

If you're twitter-savvy you can tweet [#webpack][hash-url] with your question
and someone should be able to reach out and lend a hand.

If you have discovered a :bug:, have a feature suggestion, or would like to see
a modification, please feel free to create an issue on Github. _Note: The issue
template isn't optional, so please be sure not to remove it, and please fill it
out completely._

## Other servers

Examples of use with other servers will follow here.

### Fastify

Fastify interop will require the use of `fastify-express` instead of `middie` for providing middleware support. As the authors of `fastify-express` recommend, this should only be used as a stopgap while full Fastify support is worked on.

```js
const fastify = require("fastify")();
const webpack = require("webpack");
const webpackConfig = require("./webpack.config.js");
const devMiddleware = require("webpack-dev-middleware");

const compiler = webpack(webpackConfig);
const { publicPath } = webpackConfig.output;

(async () => {
  await fastify.register(require("fastify-express"));
  await fastify.use(devMiddleware(compiler, { publicPath }));
  await fastify.listen(3000);
})();
```

## Contributing

Please take a moment to read our contributing guidelines if you haven't yet done so.

[CONTRIBUTING](./CONTRIBUTING.md)

## License

[MIT](./LICENSE)

[npm]: https://img.shields.io/npm/v/webpack-dev-middleware.svg
[npm-url]: https://npmjs.com/package/webpack-dev-middleware
[node]: https://img.shields.io/node/v/webpack-dev-middleware.svg
[node-url]: https://nodejs.org
[deps]: https://david-dm.org/webpack/webpack-dev-middleware.svg
[deps-url]: https://david-dm.org/webpack/webpack-dev-middleware
[tests]: https://github.com/webpack/webpack-dev-middleware/workflows/webpack-dev-middleware/badge.svg
[tests-url]: https://github.com/webpack/webpack-dev-middleware/actions
[cover]: https://codecov.io/gh/webpack/webpack-dev-middleware/branch/master/graph/badge.svg
[cover-url]: https://codecov.io/gh/webpack/webpack-dev-middleware
[chat]: https://badges.gitter.im/webpack/webpack.svg
[chat-url]: https://gitter.im/webpack/webpack
[size]: https://packagephobia.com/badge?p=webpack-dev-middleware
[size-url]: https://packagephobia.com/result?p=webpack-dev-middleware
[docs-url]: https://webpack.js.org/guides/development/#using-webpack-dev-middleware
[hash-url]: https://twitter.com/search?q=webpack
[middleware-url]: https://github.com/webpack/webpack-dev-middleware
[stack-url]: https://stackoverflow.com/questions/tagged/webpack-dev-middleware
[wjo-url]: https://github.com/webpack/webpack.js.org


code:
"use strict";

const {
  validate
} = require("schema-utils");

const mime = require("mime-types");

const middleware = require("./middleware");

const getFilenameFromUrl = require("./utils/getFilenameFromUrl");

const setupHooks = require("./utils/setupHooks");

const setupWriteToDisk = require("./utils/setupWriteToDisk");

const setupOutputFileSystem = require("./utils/setupOutputFileSystem");

const ready = require("./utils/ready");

const schema = require("./options.json");

const noop = () => {};
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */

/** @typedef {import("webpack").Compiler} Compiler */

/** @typedef {import("webpack").MultiCompiler} MultiCompiler */

/** @typedef {import("webpack").Configuration} Configuration */

/** @typedef {import("webpack").Stats} Stats */

/** @typedef {import("webpack").MultiStats} MultiStats */

/**
 * @typedef {Object} ExtendedServerResponse
 * @property {{ webpack?: { devMiddleware?: Context<IncomingMessage, ServerResponse> } }} [locals]
 */

/** @typedef {import("http").IncomingMessage} IncomingMessage */

/** @typedef {import("http").ServerResponse & ExtendedServerResponse} ServerResponse */

/**
 * @callback NextFunction
 * @param {any} [err]
 * @return {void}
 */

/**
 * @typedef {NonNullable<Configuration["watchOptions"]>} WatchOptions
 */

/**
 * @typedef {Compiler["watching"]} Watching
 */

/**
 * @typedef {ReturnType<Compiler["watch"]>} MultiWatching
 */

/**
 * @typedef {Compiler["outputFileSystem"] & { createReadStream?: import("fs").createReadStream, statSync?: import("fs").statSync, lstat?: import("fs").lstat, readFileSync?: import("fs").readFileSync }} OutputFileSystem
 */

/** @typedef {ReturnType<Compiler["getInfrastructureLogger"]>} Logger */

/**
 * @callback Callback
 * @param {Stats | MultiStats} [stats]
 */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @typedef {Object} Context
 * @property {boolean} state
 * @property {Stats | MultiStats | undefined} stats
 * @property {Callback[]} callbacks
 * @property {Options<Request, Response>} options
 * @property {Compiler | MultiCompiler} compiler
 * @property {Watching | MultiWatching} watching
 * @property {Logger} logger
 * @property {OutputFileSystem} outputFileSystem
 */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @typedef {Record<string, string | number> | Array<{ key: string, value: number | string }> | ((req: Request, res: Response, context: Context<Request, Response>) =>  void | undefined | Record<string, string | number>) | undefined} Headers
 */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @typedef {Object} Options
 * @property {{[key: string]: string}} [mimeTypes]
 * @property {boolean | ((targetPath: string) => boolean)} [writeToDisk]
 * @property {string} [methods]
 * @property {Headers<Request, Response>} [headers]
 * @property {NonNullable<Configuration["output"]>["publicPath"]} [publicPath]
 * @property {Configuration["stats"]} [stats]
 * @property {boolean} [serverSideRender]
 * @property {OutputFileSystem} [outputFileSystem]
 * @property {boolean | string} [index]
 */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @callback Middleware
 * @param {Request} req
 * @param {Response} res
 * @param {NextFunction} next
 * @return {Promise<void>}
 */

/**
 * @callback GetFilenameFromUrl
 * @param {string} url
 * @returns {string | undefined}
 */

/**
 * @callback WaitUntilValid
 * @param {Callback} callback
 */

/**
 * @callback Invalidate
 * @param {Callback} callback
 */

/**
 * @callback Close
 * @param {(err: Error | null | undefined) => void} callback
 */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @typedef {Object} AdditionalMethods
 * @property {GetFilenameFromUrl} getFilenameFromUrl
 * @property {WaitUntilValid} waitUntilValid
 * @property {Invalidate} invalidate
 * @property {Close} close
 * @property {Context<Request, Response>} context
 */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @typedef {Middleware<Request, Response> & AdditionalMethods<Request, Response>} API
 */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {Compiler | MultiCompiler} compiler
 * @param {Options<Request, Response>} [options]
 * @returns {API<Request, Response>}
 */

function wdm(compiler, options = {}) {
  validate(
  /** @type {Schema} */
  schema, options, {
    name: "Dev Middleware",
    baseDataPath: "options"
  });
  const {
    mimeTypes
  } = options;

  if (mimeTypes) {
    const {
      types
    } = mime; // mimeTypes from user provided options should take priority
    // over existing, known types
    // @ts-ignore

    mime.types = { ...types,
      ...mimeTypes
    };
  }
  /**
   * @type {Context<Request, Response>}
   */

  const context = {
    state: false,
    // eslint-disable-next-line no-undefined
    stats: undefined,
    callbacks: [],
    options,
    compiler,
    // @ts-ignore
    // eslint-disable-next-line no-undefined
    watching: undefined,
    logger: compiler.getInfrastructureLogger("webpack-dev-middleware"),
    // @ts-ignore
    // eslint-disable-next-line no-undefined
    outputFileSystem: undefined
  };
  setupHooks(context);

  if (options.writeToDisk) {
    setupWriteToDisk(context);
  }

  setupOutputFileSystem(context); // Start watching

  if (
  /** @type {Compiler} */
  context.compiler.watching) {
    context.watching =
    /** @type {Compiler} */
    context.compiler.watching;
  } else {
    /**
     * @type {WatchOptions | WatchOptions[]}
     */
    let watchOptions;
    /**
     * @param {Error | null | undefined} error
     */

    const errorHandler = error => {
      if (error) {
        // TODO: improve that in future
        // For example - `writeToDisk` can throw an error and right now it is ends watching.
        // We can improve that and keep watching active, but it is require API on webpack side.
        // Let's implement that in webpack@5 because it is rare case.
        context.logger.error(error);
      }
    };

    if (Array.isArray(
    /** @type {MultiCompiler} */
    context.compiler.compilers)) {
      watchOptions =
      /** @type {MultiCompiler} */
      context.compiler.compilers.map(
      /**
       * @param {Compiler} childCompiler
       * @returns {WatchOptions}
       */
      childCompiler => childCompiler.options.watchOptions || {});
      context.watching =
      /** @type {MultiWatching} */
      context.compiler.watch(
      /** @type {WatchOptions}} */
      watchOptions, errorHandler);
    } else {
      watchOptions =
      /** @type {Compiler} */
      context.compiler.options.watchOptions || {};
      context.watching =
      /** @type {Watching} */
      context.compiler.watch(watchOptions, errorHandler);
    }
  }

  const instance =
  /** @type {API<Request, Response>} */
  middleware(context); // API

  /** @type {API<Request, Response>} */

  instance.getFilenameFromUrl =
  /**
   * @param {string} url
   * @returns {string|undefined}
   */
  url => getFilenameFromUrl(context, url);
  /** @type {API<Request, Response>} */

  instance.waitUntilValid = (callback = noop) => {
    ready(context, callback);
  };
  /** @type {API<Request, Response>} */

  instance.invalidate = (callback = noop) => {
    ready(context, callback);
    context.watching.invalidate();
  };
  /** @type {API<Request, Response>} */

  instance.close = (callback = noop) => {
    context.watching.close(callback);
  };
  /** @type {API<Request, Response>} */

  instance.context = context;
  return instance;
}

module.exports = wdm;

license: mit


code:
"use strict";

const path = require("path");

const mime = require("mime-types");

const parseRange = require("range-parser");

const getFilenameFromUrl = require("./utils/getFilenameFromUrl");

const {
  getHeaderNames,
  getHeaderFromRequest,
  getHeaderFromResponse,
  setHeaderForResponse,
  setStatusCode,
  send,
  sendError
} = require("./utils/compatibleAPI");

const ready = require("./utils/ready");
/** @typedef {import("./index.js").NextFunction} NextFunction */

/** @typedef {import("./index.js").IncomingMessage} IncomingMessage */

/** @typedef {import("./index.js").ServerResponse} ServerResponse */

/**
 * @param {string} type
 * @param {number} size
 * @param {import("range-parser").Range} [range]
 * @returns {string}
 */

function getValueContentRangeHeader(type, size, range) {
  return `${type} ${range ? `${range.start}-${range.end}` : "*"}/${size}`;
}
/**
 * @param {string | number} title
 * @param {string} body
 * @returns {string}
 */

function createHtmlDocument(title, body) {
  return `${"<!DOCTYPE html>\n" + '<html lang="en">\n' + "<head>\n" + '<meta charset="utf-8">\n' + "<title>"}${title}</title>\n` + `</head>\n` + `<body>\n` + `<pre>${body}</pre>\n` + `</body>\n` + `</html>\n`;
}

const BYTES_RANGE_REGEXP = /^ *bytes/i;
/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {import("./index.js").Context<Request, Response>} context
 * @return {import("./index.js").Middleware<Request, Response>}
 */

function wrapper(context) {
  return async function middleware(req, res, next) {
    const acceptedMethods = context.options.methods || ["GET", "HEAD"]; // fixes #282. credit @cexoso. in certain edge situations res.locals is undefined.
    // eslint-disable-next-line no-param-reassign

    res.locals = res.locals || {};

    if (req.method && !acceptedMethods.includes(req.method)) {
      await goNext();
      return;
    }

    ready(context, processRequest, req);

    async function goNext() {
      if (!context.options.serverSideRender) {
        return next();
      }

      return new Promise(resolve => {
        ready(context, () => {
          /** @type {any} */
          // eslint-disable-next-line no-param-reassign
          res.locals.webpack = {
            devMiddleware: context
          };
          resolve(next());
        }, req);
      });
    }

    async function processRequest() {
      /** @type {import("./utils/getFilenameFromUrl").Extra} */
      const extra = {};
      const filename = getFilenameFromUrl(context,
      /** @type {string} */
      req.url, extra);

      if (!filename) {
        await goNext();
        return;
      }

      if (extra.errorCode) {
        if (extra.errorCode === 403) {
          context.logger.error(`Malicious path "${filename}".`);
        }

        sendError(req, res, extra.errorCode);
        return;
      }

      let {
        headers
      } = context.options;

      if (typeof headers === "function") {
        // @ts-ignore
        headers = headers(req, res, context);
      }
      /**
       * @type {{key: string, value: string | number}[]}
       */

      const allHeaders = [];

      if (typeof headers !== "undefined") {
        if (!Array.isArray(headers)) {
          // eslint-disable-next-line guard-for-in
          for (const name in headers) {
            // @ts-ignore
            allHeaders.push({
              key: name,
              value: headers[name]
            });
          }

          headers = allHeaders;
        }

        headers.forEach(
        /**
         * @param {{key: string, value: any}} header
         */
        header => {
          setHeaderForResponse(res, header.key, header.value);
        });
      }

      if (!getHeaderFromResponse(res, "Content-Type")) {
        // content-type name(like application/javascript; charset=utf-8) or false
        const contentType = mime.contentType(path.extname(filename)); // Only set content-type header if media type is known
        // https://tools.ietf.org/html/rfc7231#section-3.1.1.5

        if (contentType) {
          setHeaderForResponse(res, "Content-Type", contentType);
        }
      }

      if (!getHeaderFromResponse(res, "Accept-Ranges")) {
        setHeaderForResponse(res, "Accept-Ranges", "bytes");
      }

      const rangeHeader = getHeaderFromRequest(req, "range");
      let start;
      let end;

      if (rangeHeader && BYTES_RANGE_REGEXP.test(rangeHeader)) {
        const size = await new Promise(resolve => {
          /** @type {import("fs").lstat} */
          context.outputFileSystem.lstat(filename, (error, stats) => {
            if (error) {
              context.logger.error(error);
              return;
            }

            resolve(stats.size);
          });
        });
        const parsedRanges = parseRange(size, rangeHeader, {
          combine: true
        });

        if (parsedRanges === -1) {
          const message = "Unsatisfiable range for 'Range' header.";
          context.logger.error(message);
          const existingHeaders = getHeaderNames(res);

          for (let i = 0; i < existingHeaders.length; i++) {
            res.removeHeader(existingHeaders[i]);
          }

          setStatusCode(res, 416);
          setHeaderForResponse(res, "Content-Range", getValueContentRangeHeader("bytes", size));
          setHeaderForResponse(res, "Content-Type", "text/html; charset=utf-8");
          const document = createHtmlDocument(416, `Error: ${message}`);
          const byteLength = Buffer.byteLength(document);
          setHeaderForResponse(res, "Content-Length", Buffer.byteLength(document));
          send(req, res, document, byteLength);
          return;
        } else if (parsedRanges === -2) {
          context.logger.error("A malformed 'Range' header was provided. A regular response will be sent for this request.");
        } else if (parsedRanges.length > 1) {
          context.logger.error("A 'Range' header with multiple ranges was provided. Multiple ranges are not supported, so a regular response will be sent for this request.");
        }

        if (parsedRanges !== -2 && parsedRanges.length === 1) {
          // Content-Range
          setStatusCode(res, 206);
          setHeaderForResponse(res, "Content-Range", getValueContentRangeHeader("bytes", size,
          /** @type {import("range-parser").Ranges} */
          parsedRanges[0]));
          [{
            start,
            end
          }] = parsedRanges;
        }
      }

      const isFsSupportsStream = typeof context.outputFileSystem.createReadStream === "function";
      let bufferOtStream;
      let byteLength;

      try {
        if (typeof start !== "undefined" && typeof end !== "undefined" && isFsSupportsStream) {
          bufferOtStream =
          /** @type {import("fs").createReadStream} */
          context.outputFileSystem.createReadStream(filename, {
            start,
            end
          });
          byteLength = end - start + 1;
        } else {
          bufferOtStream =
          /** @type {import("fs").readFileSync} */
          context.outputFileSystem.readFileSync(filename);
          ({
            byteLength
          } = bufferOtStream);
        }
      } catch (_ignoreError) {
        await goNext();
        return;
      }

      send(req, res, bufferOtStream, byteLength);
    }
  };
}

module.exports = wrapper;


type: object

description: Allows a user to register custom mime types or extension mappings.

description: Allows to write generated files on disk.

type: boolean

instanceof: Function

description: Allows to pass the list of HTTP request methods accepted by the middleware.

type: array

type: string

description: key of header.

description: value of header.

description: Allows to pass custom HTTP headers on each request

description: The `publicPath` specifies the public URL address of the output files when referenced in a browser.

description: Stats options object or preset name.

enum: none summary errors-only errors-warnings minimal normal detailed verbose

description: Instructs the module to enable or disable the server-side rendering mode.

description: Set the default file system which will be used by webpack as primary destination of generated files.

description: Allows to serve an index of the directory.


code:
"use strict";

/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */

/** @typedef {import("../index.js").ServerResponse} ServerResponse */

/**
 * @typedef {Object} ExpectedRequest
 * @property {(name: string) => string | undefined} get
 */

/**
 * @typedef {Object} ExpectedResponse
 * @property {(name: string) => string | string[] | undefined} get
 * @property {(name: string, value: number | string | string[]) => void} set
 * @property {(status: number) => void} status
 * @property {(data: any) => void} send
 */

/**
 * @template {ServerResponse} Response
 * @param {Response} res
 * @returns {string[]}
 */
function getHeaderNames(res) {
  if (typeof res.getHeaderNames !== "function") {
    // @ts-ignore
    // eslint-disable-next-line no-underscore-dangle
    return Object.keys(res._headers || {});
  }

  return res.getHeaderNames();
}
/**
 * @template {IncomingMessage} Request
 * @param {Request} req
 * @param {string} name
 * @returns {string | undefined}
 */

function getHeaderFromRequest(req, name) {
  // Express API
  if (typeof
  /** @type {Request & ExpectedRequest} */
  req.get === "function") {
    return (
      /** @type {Request & ExpectedRequest} */
      req.get(name)
    );
  } // Node.js API
  // @ts-ignore

  return req.headers[name];
}
/**
 * @template {ServerResponse} Response
 * @param {Response} res
 * @param {string} name
 * @returns {number | string | string[] | undefined}
 */

function getHeaderFromResponse(res, name) {
  // Express API
  if (typeof
  /** @type {Response & ExpectedResponse} */
  res.get === "function") {
    return (
      /** @type {Response & ExpectedResponse} */
      res.get(name)
    );
  } // Node.js API

  return res.getHeader(name);
}
/**
 * @template {ServerResponse} Response
 * @param {Response} res
 * @param {string} name
 * @param {number | string | string[]} value
 * @returns {void}
 */

function setHeaderForResponse(res, name, value) {
  // Express API
  if (typeof
  /** @type {Response & ExpectedResponse} */
  res.set === "function") {
    /** @type {Response & ExpectedResponse} */
    res.set(name, typeof value === "number" ? String(value) : value);
    return;
  } // Node.js API

  res.setHeader(name, value);
}
/**
 * @template {ServerResponse} Response
 * @param {Response} res
 * @param {number} code
 */

function setStatusCode(res, code) {
  if (typeof
  /** @type {Response & ExpectedResponse} */
  res.status === "function") {
    /** @type {Response & ExpectedResponse} */
    res.status(code);
    return;
  } // eslint-disable-next-line no-param-reassign

  res.statusCode = code;
}
/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {Request} req
 * @param {Response} res
 * @param {string | Buffer | import("fs").ReadStream} bufferOtStream
 * @param {number} byteLength
 */

function send(req, res, bufferOtStream, byteLength) {
  if (typeof
  /** @type {import("fs").ReadStream} */
  bufferOtStream.pipe === "function") {
    setHeaderForResponse(res, "Content-Length", byteLength);

    if (req.method === "HEAD") {
      res.end();
      return;
    }
    /** @type {import("fs").ReadStream} */

    bufferOtStream.pipe(res);
    return;
  }

  if (typeof
  /** @type {Response & ExpectedResponse} */
  res.send === "function") {
    /** @type {Response & ExpectedResponse} */
    res.send(bufferOtStream);
    return;
  } // Only Node.js API used

  res.setHeader("Content-Length", byteLength);

  if (req.method === "HEAD") {
    res.end();
  } else {
    res.end(bufferOtStream);
  }
}
/**
 * @template {ServerResponse} Response
 * @param {Response} res
 */

function clearHeadersForResponse(res) {
  const headers = getHeaderNames(res);

  for (let i = 0; i < headers.length; i++) {
    res.removeHeader(headers[i]);
  }
}

const matchHtmlRegExp = /["'&<>]/;
/**
 * @param {string} string raw HTML
 * @returns {string} escaped HTML
 */

function escapeHtml(string) {
  const str = `${string}`;
  const match = matchHtmlRegExp.exec(str);

  if (!match) {
    return str;
  }

  let escape;
  let html = "";
  let index = 0;
  let lastIndex = 0;

  for (({
    index
  } = match); index < str.length; index++) {
    switch (str.charCodeAt(index)) {
      // "
      case 34:
        escape = """;
        break;
      // &

      case 38:
        escape = "&";
        break;
      // '

      case 39:
        escape = "'";
        break;
      // <

      case 60:
        escape = "<";
        break;
      // >

      case 62:
        escape = ">";
        break;

      default:
        // eslint-disable-next-line no-continue
        continue;
    }

    if (lastIndex !== index) {
      html += str.substring(lastIndex, index);
    }

    lastIndex = index + 1;
    html += escape;
  }

  return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}
/** @type {Record<number, string>} */

const statuses = {
  400: "Bad Request",
  403: "Forbidden",
  404: "Not Found",
  416: "Range Not Satisfiable",
  500: "Internal Server Error"
};
/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {Request} req response
 * @param {Response} res response
 * @param {number} status status
 * @returns {void}
 */

function sendError(req, res, status) {
  const content = statuses[status] || String(status);
  const document = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>${escapeHtml(content)}</pre>
</body>
</html>`; // Clear existing headers

  clearHeadersForResponse(res); // Send basic response

  setStatusCode(res, status);
  setHeaderForResponse(res, "Content-Type", "text/html; charset=utf-8");
  setHeaderForResponse(res, "Content-Security-Policy", "default-src 'none'");
  setHeaderForResponse(res, "X-Content-Type-Options", "nosniff");
  const byteLength = Buffer.byteLength(document);
  setHeaderForResponse(res, "Content-Length", byteLength);
  res.end(document);
}

module.exports = {
  getHeaderNames,
  getHeaderFromRequest,
  getHeaderFromResponse,
  setHeaderForResponse,
  setStatusCode,
  send,
  sendError
};


code:
"use strict";

const path = require("path");

const {
  parse
} = require("url");

const querystring = require("querystring");

const getPaths = require("./getPaths");
/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */

/** @typedef {import("../index.js").ServerResponse} ServerResponse */

const cacheStore = new WeakMap();
/**
 * @template T
 * @param {Function} fn
 * @param {{ cache?: Map<string, { data: T }> } | undefined} cache
 * @param {(value: T) => T} callback
 * @returns {any}
 */
// @ts-ignore

const mem = (fn, {
  cache = new Map()
} = {}, callback) => {
  /**
   * @param {any} arguments_
   * @return {any}
   */
  const memoized = (...arguments_) => {
    const [key] = arguments_;
    const cacheItem = cache.get(key);

    if (cacheItem) {
      return cacheItem.data;
    }

    let result = fn.apply(void 0, arguments_);
    result = callback(result);
    cache.set(key, {
      data: result
    });
    return result;
  };

  cacheStore.set(memoized, cache);
  return memoized;
}; // eslint-disable-next-line no-undefined

const memoizedParse = mem(parse, undefined, value => {
  if (value.pathname) {
    // eslint-disable-next-line no-param-reassign
    value.pathname = decode(value.pathname);
  }

  return value;
});
const UP_PATH_REGEXP = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
/**
 * @typedef {Object} Extra
 * @property {import("fs").Stats=} stats
 * @property {number=} errorCode
 */

/**
 * decodeURIComponent.
 *
 * Allows V8 to only deoptimize this fn instead of all of send().
 *
 * @param {string} input
 * @returns {string}
 */

function decode(input) {
  return querystring.unescape(input);
}
/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {import("../index.js").Context<Request, Response>} context
 * @param {string} url
 * @param {Extra=} extra
 * @returns {string | undefined}
 */

function getFilenameFromUrl(context, url, extra = {}) {
  const {
    options
  } = context;
  const paths = getPaths(context);
  /** @type {string | undefined} */

  let foundFilename;
  /** @type {URL} */

  let urlObject;

  try {
    // The `url` property of the `request` is contains only  `pathname`, `search` and `hash`
    urlObject = memoizedParse(url, false, true);
  } catch (_ignoreError) {
    return;
  }

  for (const {
    publicPath,
    outputPath
  } of paths) {
    /** @type {string | undefined} */
    let filename;
    /** @type {URL} */

    let publicPathObject;

    try {
      publicPathObject = memoizedParse(publicPath !== "auto" && publicPath ? publicPath : "/", false, true);
    } catch (_ignoreError) {
      // eslint-disable-next-line no-continue
      continue;
    }

    const {
      pathname
    } = urlObject;
    const {
      pathname: publicPathPathname
    } = publicPathObject;

    if (pathname && pathname.startsWith(publicPathPathname)) {
      // Null byte(s)
      if (pathname.includes("\0")) {
        // eslint-disable-next-line no-param-reassign
        extra.errorCode = 400;
        return;
      } // ".." is malicious

      if (UP_PATH_REGEXP.test(path.normalize(`./${pathname}`))) {
        // eslint-disable-next-line no-param-reassign
        extra.errorCode = 403;
        return;
      } // Strip the `pathname` property from the `publicPath` option from the start of requested url
      // `/complex/foo.js` => `foo.js`
      // and add outputPath
      // `foo.js` => `/home/user/my-project/dist/foo.js`

      filename = path.join(outputPath, pathname.slice(publicPathPathname.length));

      try {
        // eslint-disable-next-line no-param-reassign
        extra.stats =
        /** @type {import("fs").statSync} */
        context.outputFileSystem.statSync(filename);
      } catch (_ignoreError) {
        // eslint-disable-next-line no-continue
        continue;
      }

      if (extra.stats.isFile()) {
        foundFilename = filename;
        break;
      } else if (extra.stats.isDirectory() && (typeof options.index === "undefined" || options.index)) {
        const indexValue = typeof options.index === "undefined" || typeof options.index === "boolean" ? "index.html" : options.index;
        filename = path.join(filename, indexValue);

        try {
          // eslint-disable-next-line no-param-reassign
          extra.stats =
          /** @type {import("fs").statSync} */
          context.outputFileSystem.statSync(filename);
        } catch (__ignoreError) {
          // eslint-disable-next-line no-continue
          continue;
        }

        if (extra.stats.isFile()) {
          foundFilename = filename;
          break;
        }
      }
    }
  } // eslint-disable-next-line consistent-return

  return foundFilename;
}

module.exports = getFilenameFromUrl;


code:
"use strict";

/** @typedef {import("webpack").Compiler} Compiler */

/** @typedef {import("webpack").Stats} Stats */

/** @typedef {import("webpack").MultiStats} MultiStats */

/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */

/** @typedef {import("../index.js").ServerResponse} ServerResponse */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {import("../index.js").Context<Request, Response>} context
 */
function getPaths(context) {
  const {
    stats,
    options
  } = context;
  /** @type {Stats[]} */

  const childStats =
  /** @type {MultiStats} */
  stats.stats ?
  /** @type {MultiStats} */
  stats.stats : [
  /** @type {Stats} */
  stats];
  const publicPaths = [];

  for (const {
    compilation
  } of childStats) {
    // The `output.path` is always present and always absolute
    const outputPath = compilation.getPath(compilation.outputOptions.path || "");
    const publicPath = options.publicPath ? compilation.getPath(options.publicPath) : compilation.outputOptions.publicPath ? compilation.getPath(compilation.outputOptions.publicPath) : "";
    publicPaths.push({
      outputPath,
      publicPath
    });
  }

  return publicPaths;
}

module.exports = getPaths;


code:
"use strict";

/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */

/** @typedef {import("../index.js").ServerResponse} ServerResponse */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {import("../index.js").Context<Request, Response>} context
 * @param {(...args: any[]) => any} callback
 * @param {Request} [req]
 * @returns {void}
 */
function ready(context, callback, req) {
  if (context.state) {
    callback(context.stats);
    return;
  }

  const name = req && req.url || callback.name;
  context.logger.info(`wait until bundle finished${name ? `: ${name}` : ""}`);
  context.callbacks.push(callback);
}

module.exports = ready;


code:
"use strict";

const webpack = require("webpack");

const {
  isColorSupported
} = require("colorette");
/** @typedef {import("webpack").Configuration} Configuration */

/** @typedef {import("webpack").Compiler} Compiler */

/** @typedef {import("webpack").MultiCompiler} MultiCompiler */

/** @typedef {import("webpack").Stats} Stats */

/** @typedef {import("webpack").MultiStats} MultiStats */

/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */

/** @typedef {import("../index.js").ServerResponse} ServerResponse */

/** @typedef {Configuration["stats"]} StatsOptions */

/** @typedef {{ children: Configuration["stats"][] }} MultiStatsOptions */

/** @typedef {Exclude<Configuration["stats"], boolean | string | undefined>} NormalizedStatsOptions */
// TODO remove `color` after dropping webpack v4

/** @typedef {{ children: StatsOptions[], colors?: any }} MultiNormalizedStatsOptions */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {import("../index.js").Context<Request, Response>} context
 */

function setupHooks(context) {
  function invalid() {
    if (context.state) {
      context.logger.log("Compilation starting...");
    } // We are now in invalid state
    // eslint-disable-next-line no-param-reassign

    context.state = false; // eslint-disable-next-line no-param-reassign, no-undefined

    context.stats = undefined;
  } // @ts-ignore

  const statsForWebpack4 = webpack.Stats && webpack.Stats.presetToOptions;
  /**
   * @param {Configuration["stats"]} statsOptions
   * @returns {NormalizedStatsOptions}
   */

  function normalizeStatsOptions(statsOptions) {
    if (statsForWebpack4) {
      if (typeof statsOptions === "undefined") {
        // eslint-disable-next-line no-param-reassign
        statsOptions = {};
      } else if (typeof statsOptions === "boolean" || typeof statsOptions === "string") {
        // @ts-ignore
        // eslint-disable-next-line no-param-reassign
        statsOptions = webpack.Stats.presetToOptions(statsOptions);
      } // @ts-ignore

      return statsOptions;
    }

    if (typeof statsOptions === "undefined") {
      // eslint-disable-next-line no-param-reassign
      statsOptions = {
        preset: "normal"
      };
    } else if (typeof statsOptions === "boolean") {
      // eslint-disable-next-line no-param-reassign
      statsOptions = statsOptions ? {
        preset: "normal"
      } : {
        preset: "none"
      };
    } else if (typeof statsOptions === "string") {
      // eslint-disable-next-line no-param-reassign
      statsOptions = {
        preset: statsOptions
      };
    }

    return statsOptions;
  }
  /**
   * @param {Stats | MultiStats} stats
   */

  function done(stats) {
    // We are now on valid state
    // eslint-disable-next-line no-param-reassign
    context.state = true; // eslint-disable-next-line no-param-reassign

    context.stats = stats; // Do the stuff in nextTick, because bundle may be invalidated if a change happened while compiling

    process.nextTick(() => {
      const {
        compiler,
        logger,
        options,
        state,
        callbacks
      } = context; // Check if still in valid state

      if (!state) {
        return;
      }

      logger.log("Compilation finished");
      const isMultiCompilerMode = Boolean(
      /** @type {MultiCompiler} */
      compiler.compilers);
      /**
       * @type {StatsOptions | MultiStatsOptions | NormalizedStatsOptions | MultiNormalizedStatsOptions}
       */

      let statsOptions;

      if (typeof options.stats !== "undefined") {
        statsOptions = isMultiCompilerMode ? {
          children:
          /** @type {MultiCompiler} */
          compiler.compilers.map(() => options.stats)
        } : options.stats;
      } else {
        statsOptions = isMultiCompilerMode ? {
          children:
          /** @type {MultiCompiler} */
          compiler.compilers.map(child => child.options.stats)
        } :
        /** @type {Compiler} */
        compiler.options.stats;
      }

      if (isMultiCompilerMode) {
        /** @type {MultiNormalizedStatsOptions} */
        statsOptions.children =
        /** @type {MultiStatsOptions} */
        statsOptions.children.map(
        /**
         * @param {StatsOptions} childStatsOptions
         * @return {NormalizedStatsOptions}
         */
        childStatsOptions => {
          // eslint-disable-next-line no-param-reassign
          childStatsOptions = normalizeStatsOptions(childStatsOptions);

          if (typeof childStatsOptions.colors === "undefined") {
            // eslint-disable-next-line no-param-reassign
            childStatsOptions.colors = isColorSupported;
          }

          return childStatsOptions;
        });
      } else {
        /** @type {NormalizedStatsOptions} */
        statsOptions = normalizeStatsOptions(
        /** @type {StatsOptions} */
        statsOptions);

        if (typeof statsOptions.colors === "undefined") {
          statsOptions.colors = isColorSupported;
        }
      } // TODO webpack@4 doesn't support `{ children: [{ colors: true }, { colors: true }] }` for stats

      if (
      /** @type {MultiCompiler} */
      compiler.compilers && statsForWebpack4) {
        /** @type {MultiNormalizedStatsOptions} */
        statsOptions.colors =
        /** @type {MultiNormalizedStatsOptions} */
        statsOptions.children.some(
        /**
         * @param {StatsOptions} child
         */
        // @ts-ignore
        child => child.colors);
      }

      const printedStats = stats.toString(statsOptions); // Avoid extra empty line when `stats: 'none'`

      if (printedStats) {
        // eslint-disable-next-line no-console
        console.log(printedStats);
      } // eslint-disable-next-line no-param-reassign

      context.callbacks = []; // Execute callback that are delayed

      callbacks.forEach(
      /**
       * @param {(...args: any[]) => Stats | MultiStats} callback
       */
      callback => {
        callback(stats);
      });
    });
  }

  context.compiler.hooks.watchRun.tap("webpack-dev-middleware", invalid);
  context.compiler.hooks.invalid.tap("webpack-dev-middleware", invalid);
  context.compiler.hooks.done.tap("webpack-dev-middleware", done);
}

module.exports = setupHooks;


code:
"use strict";

const path = require("path");

const memfs = require("memfs");
/** @typedef {import("webpack").MultiCompiler} MultiCompiler */

/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */

/** @typedef {import("../index.js").ServerResponse} ServerResponse */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {import("../index.js").Context<Request, Response>} context
 */

function setupOutputFileSystem(context) {
  let outputFileSystem;

  if (context.options.outputFileSystem) {
    const {
      outputFileSystem: outputFileSystemFromOptions
    } = context.options; // Todo remove when we drop webpack@4 support

    if (typeof outputFileSystemFromOptions.join !== "function") {
      throw new Error("Invalid options: options.outputFileSystem.join() method is expected");
    } // Todo remove when we drop webpack@4 support
    // @ts-ignore

    if (typeof outputFileSystemFromOptions.mkdirp !== "function") {
      throw new Error("Invalid options: options.outputFileSystem.mkdirp() method is expected");
    }

    outputFileSystem = outputFileSystemFromOptions;
  } else {
    outputFileSystem = memfs.createFsFromVolume(new memfs.Volume()); // TODO: remove when we drop webpack@4 support
    // @ts-ignore

    outputFileSystem.join = path.join.bind(path);
  }

  const compilers =
  /** @type {MultiCompiler} */
  context.compiler.compilers || [context.compiler];

  for (const compiler of compilers) {
    compiler.outputFileSystem = outputFileSystem;
  } // @ts-ignore
  // eslint-disable-next-line no-param-reassign

  context.outputFileSystem = outputFileSystem;
}

module.exports = setupOutputFileSystem;


code:
"use strict";

const fs = require("fs");

const path = require("path");
/** @typedef {import("webpack").Compiler} Compiler */

/** @typedef {import("webpack").MultiCompiler} MultiCompiler */

/** @typedef {import("webpack").Compilation} Compilation */

/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */

/** @typedef {import("../index.js").ServerResponse} ServerResponse */

/**
 * @template {IncomingMessage} Request
 * @template {ServerResponse} Response
 * @param {import("../index.js").Context<Request, Response>} context
 */

function setupWriteToDisk(context) {
  /**
   * @type {Compiler[]}
   */
  const compilers =
  /** @type {MultiCompiler} */
  context.compiler.compilers || [context.compiler];

  for (const compiler of compilers) {
    compiler.hooks.emit.tap("DevMiddleware",
    /**
     * @param {Compilation} compilation
     */
    compilation => {
      // @ts-ignore
      if (compiler.hasWebpackDevMiddlewareAssetEmittedCallback) {
        return;
      }

      compiler.hooks.assetEmitted.tapAsync("DevMiddleware", (file, info, callback) => {
        /**
         * @type {string}
         */
        let targetPath;
        /**
         * @type {Buffer}
         */

        let content; // webpack@5

        if (info.compilation) {
          ({
            targetPath,
            content
          } = info);
        } else {
          let targetFile = file;
          const queryStringIdx = targetFile.indexOf("?");

          if (queryStringIdx >= 0) {
            targetFile = targetFile.slice(0, queryStringIdx);
          }

          let {
            outputPath
          } = compiler;
          outputPath = compilation.getPath(outputPath, {}); // @ts-ignore

          content = info;
          targetPath = path.join(outputPath, targetFile);
        }

        const {
          writeToDisk: filter
        } = context.options;
        const allowWrite = filter && typeof filter === "function" ? filter(targetPath) : true;

        if (!allowWrite) {
          return callback();
        }

        const dir = path.dirname(targetPath);
        const name = compiler.options.name ? `Child "${compiler.options.name}": ` : "";
        return fs.mkdir(dir, {
          recursive: true
        }, mkdirError => {
          if (mkdirError) {
            context.logger.error(`${name}Unable to write "${dir}" directory to disk:\n${mkdirError}`);
            return callback(mkdirError);
          }

          return fs.writeFile(targetPath, content, writeFileError => {
            if (writeFileError) {
              context.logger.error(`${name}Unable to write "${targetPath}" asset to disk:\n${writeFileError}`);
              return callback(writeFileError);
            }

            context.logger.log(`${name}Asset written to disk: "${targetPath}"`);
            return callback();
          });
        });
      }); // @ts-ignore

      compiler.hasWebpackDevMiddlewareAssetEmittedCallback = true;
    });
  }
}

module.exports = setupWriteToDisk;


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;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const sequences = {};
const DEFAULTS = {
    timestamp: () => () => Date.now(),
    datetime: () => () => new Date().toISOString(),
    date: () => () => new Date().toISOString().slice(0, 10),
    time: () => () => new Date().toISOString().slice(11),
    random: () => () => Math.random(),
    randomint: (args) => {
        var _a;
        const max = (_a = args === null || args === void 0 ? void 0 : args.max) !== null && _a !== void 0 ? _a : 2;
        return () => Math.floor(Math.random() * max);
    },
    seq: (args) => {
        var _a;
        const name = (_a = args === null || args === void 0 ? void 0 : args.name) !== null && _a !== void 0 ? _a : "";
        sequences[name] || (sequences[name] = 0);
        return () => sequences[name]++;
    },
};
const getDef = Object.assign(_getDef, { DEFAULTS });
function _getDef() {
    return {
        keyword: "dynamicDefaults",
        type: "object",
        schemaType: ["string", "object"],
        modifying: true,
        valid: true,
        compile(schema, _parentSchema, it) {
            if (!it.opts.useDefaults || it.compositeRule)
                return () => true;
            const fs = {};
            for (const key in schema)
                fs[key] = getDefault(schema[key]);
            const empty = it.opts.useDefaults === "empty";
            return (data) => {
                for (const prop in schema) {
                    if (data[prop] === undefined || (empty && (data[prop] === null || data[prop] === ""))) {
                        data[prop] = fs[prop]();
                    }
                }
                return true;
            };
        },
        metaSchema: {
            type: "object",
            additionalProperties: {
                anyOf: [
                    { type: "string" },
                    {
                        type: "object",
                        additionalProperties: false,
                        required: ["func", "args"],
                        properties: {
                            func: { type: "string" },
                            args: { type: "object" },
                        },
                    },
                ],
            },
        },
    };
}
function getDefault(d) {
    return typeof d == "object" ? getObjDefault(d) : getStrDefault(d);
}
function getObjDefault({ func, args }) {
    const def = DEFAULTS[func];
    assertDefined(func, def);
    return def(args);
}
function getStrDefault(d = "") {
    const def = DEFAULTS[d];
    assertDefined(d, def);
    return def();
}
function assertDefined(name, def) {
    if (!def)
        throw new Error(`invalid "dynamicDefaults" keyword property value: ${name}`);
}
exports.default = getDef;
module.exports = getDef;
//# sourceMappingURL=dynamicDefaults.js.map


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


code:
import type { Vocabulary, ErrorNoParams } from "ajv";
import type { DefinitionOptions } from "./_types";
import { PatternRequiredError } from "./patternRequired";
import { SelectError } from "./select";
export default function ajvKeywords(opts?: DefinitionOptions): Vocabulary;
export declare type AjvKeywordsError = PatternRequiredError | SelectError | ErrorNoParams<"range" | "exclusiveRange" | "anyRequired" | "oneRequired" | "allRequired" | "deepProperties" | "deepRequired" | "dynamicDefaults" | "instanceof" | "prohibited" | "regexp" | "transform" | "uniqueItemProperties">;


code:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const typeof_1 = __importDefault(require("./typeof"));
const instanceof_1 = __importDefault(require("./instanceof"));
const range_1 = __importDefault(require("./range"));
const exclusiveRange_1 = __importDefault(require("./exclusiveRange"));
const regexp_1 = __importDefault(require("./regexp"));
const transform_1 = __importDefault(require("./transform"));
const uniqueItemProperties_1 = __importDefault(require("./uniqueItemProperties"));
const allRequired_1 = __importDefault(require("./allRequired"));
const anyRequired_1 = __importDefault(require("./anyRequired"));
const oneRequired_1 = __importDefault(require("./oneRequired"));
const patternRequired_1 = __importDefault(require("./patternRequired"));
const prohibited_1 = __importDefault(require("./prohibited"));
const deepProperties_1 = __importDefault(require("./deepProperties"));
const deepRequired_1 = __importDefault(require("./deepRequired"));
const dynamicDefaults_1 = __importDefault(require("./dynamicDefaults"));
const select_1 = __importDefault(require("./select"));
const definitions = [
    typeof_1.default,
    instanceof_1.default,
    range_1.default,
    exclusiveRange_1.default,
    regexp_1.default,
    transform_1.default,
    uniqueItemProperties_1.default,
    allRequired_1.default,
    anyRequired_1.default,
    oneRequired_1.default,
    patternRequired_1.default,
    prohibited_1.default,
    deepProperties_1.default,
    deepRequired_1.default,
    dynamicDefaults_1.default,
];
function ajvKeywords(opts) {
    return definitions.map((d) => d(opts)).concat((0, select_1.default)(opts));
}
exports.default = ajvKeywords;
module.exports = ajvKeywords;
//# sourceMappingURL=index.js.map


code:
import type { FuncKeywordDefinition } from "ajv";
declare type Constructor = new (...args: any[]) => any;
declare const CONSTRUCTORS: Record<string, Constructor | undefined>;
declare const getDef: (() => FuncKeywordDefinition) & {
    CONSTRUCTORS: typeof CONSTRUCTORS;
};
export default getDef;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const CONSTRUCTORS = {
    Object,
    Array,
    Function,
    Number,
    String,
    Date,
    RegExp,
};
/* istanbul ignore else */
if (typeof Buffer != "undefined")
    CONSTRUCTORS.Buffer = Buffer;
/* istanbul ignore else */
if (typeof Promise != "undefined")
    CONSTRUCTORS.Promise = Promise;
const getDef = Object.assign(_getDef, { CONSTRUCTORS });
function _getDef() {
    return {
        keyword: "instanceof",
        schemaType: ["string", "array"],
        compile(schema) {
            if (typeof schema == "string") {
                const C = getConstructor(schema);
                return (data) => data instanceof C;
            }
            if (Array.isArray(schema)) {
                const constructors = schema.map(getConstructor);
                return (data) => {
                    for (const C of constructors) {
                        if (data instanceof C)
                            return true;
                    }
                    return false;
                };
            }
            /* istanbul ignore next */
            throw new Error("ajv implementation error");
        },
        metaSchema: {
            anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
        },
    };
}
function getConstructor(c) {
    const C = CONSTRUCTORS[c];
    if (C)
        return C;
    throw new Error(`invalid "instanceof" keyword value ${c}`);
}
exports.default = getDef;
module.exports = getDef;
//# sourceMappingURL=instanceof.js.map


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)("oneRequired");
exports.default = getDef;
module.exports = getDef;
//# sourceMappingURL=oneRequired.js.map


code:
import type { CodeKeywordDefinition, ErrorObject } from "ajv";
export declare type PatternRequiredError = ErrorObject<"patternRequired", {
    missingPattern: string;
}>;
export default function getDef(): CodeKeywordDefinition;