code:
'use strict';

var traverse = require('../index');
var assert = require('assert');

describe('json-schema-traverse', function() {
  var calls;

  beforeEach(function() {
    calls = [];
  });

  it('should traverse all keywords containing schemas recursively', function() {
    var schema = require('./fixtures/schema').schema;
    var expectedCalls = require('./fixtures/schema').expectedCalls;

    traverse(schema, {cb: callback});
    assert.deepStrictEqual(calls, expectedCalls);
  });

  describe('Legacy v0.3.1 API', function() {
    it('should traverse all keywords containing schemas recursively', function() {
      var schema = require('./fixtures/schema').schema;
      var expectedCalls = require('./fixtures/schema').expectedCalls;

      traverse(schema, callback);
      assert.deepStrictEqual(calls, expectedCalls);
    });

    it('should work when an options object is provided', function() {
      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
      var schema = require('./fixtures/schema').schema;
      var expectedCalls = require('./fixtures/schema').expectedCalls;

      traverse(schema, {}, callback);
      assert.deepStrictEqual(calls, expectedCalls);
    });
  });

  describe('allKeys option', function() {
    var schema = {
      someObject: {
        minimum: 1,
        maximum: 2
      }
    };

    it('should traverse objects with allKeys: true option', function() {
      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
      var expectedCalls = [
        [schema, '', schema, undefined, undefined, undefined, undefined],
        [schema.someObject, '/someObject', schema, '', 'someObject', schema, undefined]
      ];

      traverse(schema, {allKeys: true, cb: callback});
      assert.deepStrictEqual(calls, expectedCalls);
    });

    it('should NOT traverse objects with allKeys: false option', function() {
      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
      var expectedCalls = [
        [schema, '', schema, undefined, undefined, undefined, undefined]
      ];

      traverse(schema, {allKeys: false, cb: callback});
      assert.deepStrictEqual(calls, expectedCalls);
    });

    it('should NOT traverse objects without allKeys option', function() {
      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
      var expectedCalls = [
        [schema, '', schema, undefined, undefined, undefined, undefined]
      ];

      traverse(schema, {cb: callback});
      assert.deepStrictEqual(calls, expectedCalls);
    });

    it('should NOT travers objects in standard keywords which value is not a schema', function() {
      var schema2 = {
        const: {foo: 'bar'},
        enum: ['a', 'b'],
        required: ['foo'],
        another: {

        },
        patternProperties: {}, // will not traverse - no properties
        dependencies: true, // will not traverse - invalid
        properties: {
          smaller: {
            type: 'number'
          },
          larger: {
            type: 'number',
            minimum: {$data: '1/smaller'}
          }
        }
      };

      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
      var expectedCalls = [
        [schema2, '', schema2, undefined, undefined, undefined, undefined],
        [schema2.another, '/another', schema2, '', 'another', schema2, undefined],
        [schema2.properties.smaller, '/properties/smaller', schema2, '', 'properties', schema2, 'smaller'],
        [schema2.properties.larger, '/properties/larger', schema2, '', 'properties', schema2, 'larger'],
      ];

      traverse(schema2, {allKeys: true, cb: callback});
      assert.deepStrictEqual(calls, expectedCalls);
    });
  });

  describe('pre and post', function() {
    var schema = {
      type: 'object',
      properties: {
        name: {type: 'string'},
        age: {type: 'number'}
      }
    };

    it('should traverse schema in pre-order', function() {
      traverse(schema, {cb: {pre}});
      var expectedCalls = [
        ['pre', schema, '', schema, undefined, undefined, undefined, undefined],
        ['pre', schema.properties.name, '/properties/name', schema, '', 'properties', schema, 'name'],
        ['pre', schema.properties.age, '/properties/age', schema, '', 'properties', schema, 'age'],
      ];
      assert.deepStrictEqual(calls, expectedCalls);
    });

    it('should traverse schema in post-order', function() {
      traverse(schema, {cb: {post}});
      var expectedCalls = [
        ['post', schema.properties.name, '/properties/name', schema, '', 'properties', schema, 'name'],
        ['post', schema.properties.age, '/properties/age', schema, '', 'properties', schema, 'age'],
        ['post', schema, '', schema, undefined, undefined, undefined, undefined],
      ];
      assert.deepStrictEqual(calls, expectedCalls);
    });

    it('should traverse schema in pre- and post-order at the same time', function() {
      traverse(schema, {cb: {pre, post}});
      var expectedCalls = [
        ['pre', schema, '', schema, undefined, undefined, undefined, undefined],
        ['pre', schema.properties.name, '/properties/name', schema, '', 'properties', schema, 'name'],
        ['post', schema.properties.name, '/properties/name', schema, '', 'properties', schema, 'name'],
        ['pre', schema.properties.age, '/properties/age', schema, '', 'properties', schema, 'age'],
        ['post', schema.properties.age, '/properties/age', schema, '', 'properties', schema, 'age'],
        ['post', schema, '', schema, undefined, undefined, undefined, undefined],
      ];
      assert.deepStrictEqual(calls, expectedCalls);
    });
  });

  function callback() {
    calls.push(Array.prototype.slice.call(arguments));
  }

  function pre() {
    calls.push(['pre'].concat(Array.prototype.slice.call(arguments)));
  }

  function post() {
    calls.push(['post'].concat(Array.prototype.slice.call(arguments)));
  }
});


code:
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![tests][tests]][tests-url]
[![coverage][cover]][cover-url]
[![GitHub Discussions][discussion]][discussion-url]
[![size][size]][size-url]

# schema-utils

Package for validate options in loaders and plugins.

## Getting Started

To begin, you'll need to install `schema-utils`:

```console
npm install schema-utils
```

## API

**schema.json**

```json
{
  "type": "object",
  "properties": {
    "option": {
      "type": "boolean"
    }
  },
  "additionalProperties": false
}
```

```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";

const options = { option: true };
const configuration = { name: "Loader Name/Plugin Name/Name" };

validate(schema, options, configuration);
```

### `schema`

Type: `String`

JSON schema.

Simple example of schema:

```json
{
  "type": "object",
  "properties": {
    "name": {
      "description": "This is description of option.",
      "type": "string"
    }
  },
  "additionalProperties": false
}
```

### `options`

Type: `Object`

Object with options.

```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";

const options = { foo: "bar" };

validate(schema, { name: 123 }, { name: "MyPlugin" });
```

### `configuration`

Allow to configure validator.

There is an alternative method to configure the `name` and`baseDataPath` options via the `title` property in the schema.
For example:

```json
{
  "title": "My Loader options",
  "type": "object",
  "properties": {
    "name": {
      "description": "This is description of option.",
      "type": "string"
    }
  },
  "additionalProperties": false
}
```

The last word used for the `baseDataPath` option, other words used for the `name` option.
Based on the example above the `name` option equals `My Loader`, the `baseDataPath` option equals `options`.

#### `name`

Type: `Object`
Default: `"Object"`

Allow to setup name in validation errors.

```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";

const options = { foo: "bar" };

validate(schema, options, { name: "MyPlugin" });
```

```shell
Invalid configuration object. MyPlugin has been initialised using a configuration object that does not match the API schema.
 - configuration.optionName should be a integer.
```

#### `baseDataPath`

Type: `String`
Default: `"configuration"`

Allow to setup base data path in validation errors.

```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";

const options = { foo: "bar" };

validate(schema, options, { name: "MyPlugin", baseDataPath: "options" });
```

```shell
Invalid options object. MyPlugin has been initialised using an options object that does not match the API schema.
 - options.optionName should be a integer.
```

#### `postFormatter`

Type: `Function`
Default: `undefined`

Allow to reformat errors.

```js
import schema from "./path/to/schema.json";
import { validate } from "schema-utils";

const options = { foo: "bar" };

validate(schema, options, {
  name: "MyPlugin",
  postFormatter: (formattedError, error) => {
    if (error.keyword === "type") {
      return `${formattedError}\nAdditional Information.`;
    }

    return formattedError;
  },
});
```

```shell
Invalid options object. MyPlugin has been initialized using an options object that does not match the API schema.
 - options.optionName should be a integer.
   Additional Information.
```

## Examples

**schema.json**

```json
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "test": {
      "anyOf": [
        { "type": "array" },
        { "type": "string" },
        { "instanceof": "RegExp" }
      ]
    },
    "transform": {
      "instanceof": "Function"
    },
    "sourceMap": {
      "type": "boolean"
    }
  },
  "additionalProperties": false
}
```

### `Loader`

```js
import { getOptions } from "loader-utils";
import { validate } from "schema-utils";

import schema from "path/to/schema.json";

function loader(src, map) {
  const options = getOptions(this);

  validate(schema, options, {
    name: "Loader Name",
    baseDataPath: "options",
  });

  // Code...
}

export default loader;
```

### `Plugin`

```js
import { validate } from "schema-utils";

import schema from "path/to/schema.json";

class Plugin {
  constructor(options) {
    validate(schema, options, {
      name: "Plugin Name",
      baseDataPath: "options",
    });

    this.options = options;
  }

  apply(compiler) {
    // Code...
  }
}

export default Plugin;
```

### Allow to disable and enable validation (the `validate` function do nothing)

This can be useful when you don't want to do validation for `production` builds.

```js
import { disableValidation, enableValidation, validate } from "schema-utils";

// Disable validation
disableValidation();
// Do nothing
validate(schema, options);

// Enable validation
enableValidation();
// Will throw an error if schema is not valid
validate(schema, options);

// Allow to undestand do you need validation or not
const need = needValidate();

console.log(need);
```

Also you can enable/disable validation using the `process.env.SKIP_VALIDATION` env variable.

Supported values (case insensitive):

- `yes`/`y`/`true`/`1`/`on`
- `no`/`n`/`false`/`0`/`off`

## Contributing

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

[CONTRIBUTING](./.github/CONTRIBUTING.md)

## License

[MIT](./LICENSE)

[npm]: https://img.shields.io/npm/v/schema-utils.svg
[npm-url]: https://npmjs.com/package/schema-utils
[node]: https://img.shields.io/node/v/schema-utils.svg
[node-url]: https://nodejs.org
[tests]: https://github.com/webpack/schema-utils/workflows/schema-utils/badge.svg
[tests-url]: https://github.com/webpack/schema-utils/actions
[cover]: https://codecov.io/gh/webpack/schema-utils/branch/master/graph/badge.svg
[cover-url]: https://codecov.io/gh/webpack/schema-utils
[discussion]: https://img.shields.io/github/discussions/webpack/webpack
[discussion-url]: https://github.com/webpack/webpack/discussions
[size]: https://packagephobia.com/badge?p=schema-utils
[size-url]: https://packagephobia.com/result?p=schema-utils

license: mit


code:
export default ValidationError;
export type JSONSchema6 = import("json-schema").JSONSchema6;
export type JSONSchema7 = import("json-schema").JSONSchema7;
export type Schema = import("./validate").Schema;
export type ValidationErrorConfiguration =
  import("./validate").ValidationErrorConfiguration;
export type PostFormatter = import("./validate").PostFormatter;
export type SchemaUtilErrorObject = import("./validate").SchemaUtilErrorObject;
declare class ValidationError extends Error {
  /**
   * @param {Array<SchemaUtilErrorObject>} errors
   * @param {Schema} schema
   * @param {ValidationErrorConfiguration} configuration
   */
  constructor(
    errors: Array<SchemaUtilErrorObject>,
    schema: Schema,
    configuration?: ValidationErrorConfiguration
  );
  /** @type {Array<SchemaUtilErrorObject>} */
  errors: Array<SchemaUtilErrorObject>;
  /** @type {Schema} */
  schema: Schema;
  /** @type {string} */
  headerName: string;
  /** @type {string} */
  baseDataPath: string;
  /** @type {PostFormatter | null} */
  postFormatter: PostFormatter | null;
  /**
   * @param {string} path
   * @returns {Schema}
   */
  getSchemaPart(path: string): Schema;
  /**
   * @param {Schema} schema
   * @param {boolean} logic
   * @param {Array<Object>} prevSchemas
   * @returns {string}
   */
  formatSchema(
    schema: Schema,
    logic?: boolean,
    prevSchemas?: Array<Object>
  ): string;
  /**
   * @param {Schema=} schemaPart
   * @param {(boolean | Array<string>)=} additionalPath
   * @param {boolean=} needDot
   * @param {boolean=} logic
   * @returns {string}
   */
  getSchemaPartText(
    schemaPart?: Schema | undefined,
    additionalPath?: (boolean | Array<string>) | undefined,
    needDot?: boolean | undefined,
    logic?: boolean | undefined
  ): string;
  /**
   * @param {Schema=} schemaPart
   * @returns {string}
   */
  getSchemaPartDescription(schemaPart?: Schema | undefined): string;
  /**
   * @param {SchemaUtilErrorObject} error
   * @returns {string}
   */
  formatValidationError(error: SchemaUtilErrorObject): string;
  /**
   * @param {Array<SchemaUtilErrorObject>} errors
   * @returns {string}
   */
  formatValidationErrors(errors: Array<SchemaUtilErrorObject>): string;
}


code:
import { validate } from "./validate";
import { ValidationError } from "./validate";
import { enableValidation } from "./validate";
import { disableValidation } from "./validate";
import { needValidate } from "./validate";
export {
  validate,
  ValidationError,
  enableValidation,
  disableValidation,
  needValidate,
};


code:
export default addAbsolutePathKeyword;
export type Ajv = import("ajv").default;
export type SchemaValidateFunction = import("ajv").SchemaValidateFunction;
export type AnySchemaObject = import("ajv").AnySchemaObject;
export type SchemaUtilErrorObject = import("../validate").SchemaUtilErrorObject;
/**
 *
 * @param {Ajv} ajv
 * @returns {Ajv}
 */
declare function addAbsolutePathKeyword(ajv: Ajv): Ajv;


code:
export default addUndefinedAsNullKeyword;
export type Ajv = import("ajv").default;
export type SchemaValidateFunction = import("ajv").SchemaValidateFunction;
export type AnySchemaObject = import("ajv").AnySchemaObject;
export type ValidateFunction = import("ajv").ValidateFunction;
/** @typedef {import("ajv").default} Ajv */
/** @typedef {import("ajv").SchemaValidateFunction} SchemaValidateFunction */
/** @typedef {import("ajv").AnySchemaObject} AnySchemaObject */
/** @typedef {import("ajv").ValidateFunction} ValidateFunction */
/**
 *
 * @param {Ajv} ajv
 * @returns {Ajv}
 */
declare function addUndefinedAsNullKeyword(ajv: Ajv): Ajv;


code:
export = Range;
/**
 * @typedef {[number, boolean]} RangeValue
 */
/**
 * @callback RangeValueCallback
 * @param {RangeValue} rangeValue
 * @returns {boolean}
 */
declare class Range {
  /**
   * @param {"left" | "right"} side
   * @param {boolean} exclusive
   * @returns {">" | ">=" | "<" | "<="}
   */
  static getOperator(
    side: "left" | "right",
    exclusive: boolean
  ): ">" | ">=" | "<" | "<=";
  /**
   * @param {number} value
   * @param {boolean} logic is not logic applied
   * @param {boolean} exclusive is range exclusive
   * @returns {string}
   */
  static formatRight(value: number, logic: boolean, exclusive: boolean): string;
  /**
   * @param {number} value
   * @param {boolean} logic is not logic applied
   * @param {boolean} exclusive is range exclusive
   * @returns {string}
   */
  static formatLeft(value: number, logic: boolean, exclusive: boolean): string;
  /**
   * @param {number} start left side value
   * @param {number} end right side value
   * @param {boolean} startExclusive is range exclusive from left side
   * @param {boolean} endExclusive is range exclusive from right side
   * @param {boolean} logic is not logic applied
   * @returns {string}
   */
  static formatRange(
    start: number,
    end: number,
    startExclusive: boolean,
    endExclusive: boolean,
    logic: boolean
  ): string;
  /**
   * @param {Array<RangeValue>} values
   * @param {boolean} logic is not logic applied
   * @return {RangeValue} computed value and it's exclusive flag
   */
  static getRangeValue(values: Array<RangeValue>, logic: boolean): RangeValue;
  /** @type {Array<RangeValue>} */
  _left: Array<RangeValue>;
  /** @type {Array<RangeValue>} */
  _right: Array<RangeValue>;
  /**
   * @param {number} value
   * @param {boolean=} exclusive
   */
  left(value: number, exclusive?: boolean | undefined): void;
  /**
   * @param {number} value
   * @param {boolean=} exclusive
   */
  right(value: number, exclusive?: boolean | undefined): void;
  /**
   * @param {boolean} logic is not logic applied
   * @return {string} "smart" range string representation
   */
  format(logic?: boolean): string;
}
declare namespace Range {
  export { RangeValue, RangeValueCallback };
}
type RangeValue = [number, boolean];
type RangeValueCallback = (rangeValue: RangeValue) => boolean;


code:
export function stringHints(schema: Schema, logic: boolean): string[];
export function numberHints(schema: Schema, logic: boolean): string[];
export type Schema = import("../validate").Schema;


code:
export default memoize;
/**
 * @template T
 * @param fn {(function(): any) | undefined}
 * @returns {function(): T}
 */
declare function memoize<T>(fn: (() => any) | undefined): () => T;


code:
export type JSONSchema4 = import("json-schema").JSONSchema4;
export type JSONSchema6 = import("json-schema").JSONSchema6;
export type JSONSchema7 = import("json-schema").JSONSchema7;
export type ErrorObject = import("ajv").ErrorObject;
export type Extend = {
  formatMinimum?: string | undefined;
  formatMaximum?: string | undefined;
  formatExclusiveMinimum?: string | undefined;
  formatExclusiveMaximum?: string | undefined;
  link?: string | undefined;
  undefinedAsNull?: boolean | undefined;
};
export type Schema = (JSONSchema4 | JSONSchema6 | JSONSchema7) & Extend;
export type SchemaUtilErrorObject = ErrorObject & {
  children?: Array<ErrorObject>;
};
export type PostFormatter = (
  formattedError: string,
  error: SchemaUtilErrorObject
) => string;
export type ValidationErrorConfiguration = {
  name?: string | undefined;
  baseDataPath?: string | undefined;
  postFormatter?: PostFormatter | undefined;
};
/**
 * @param {Schema} schema
 * @param {Array<object> | object} options
 * @param {ValidationErrorConfiguration=} configuration
 * @returns {void}
 */
export function validate(
  schema: Schema,
  options: Array<object> | object,
  configuration?: ValidationErrorConfiguration | undefined
): void;
export function enableValidation(): void;
export function disableValidation(): void;
export function needValidate(): boolean;
import ValidationError from "./ValidationError";
export { ValidationError };


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _memorize = _interopRequireDefault(require("./util/memorize"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
/** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */

/** @typedef {import("./validate").Schema} Schema */
/** @typedef {import("./validate").ValidationErrorConfiguration} ValidationErrorConfiguration */
/** @typedef {import("./validate").PostFormatter} PostFormatter */
/** @typedef {import("./validate").SchemaUtilErrorObject} SchemaUtilErrorObject */
/** @enum {number} */
const SPECIFICITY = {
  type: 1,
  not: 1,
  oneOf: 1,
  anyOf: 1,
  if: 1,
  enum: 1,
  const: 1,
  instanceof: 1,
  required: 2,
  pattern: 2,
  patternRequired: 2,
  format: 2,
  formatMinimum: 2,
  formatMaximum: 2,
  minimum: 2,
  exclusiveMinimum: 2,
  maximum: 2,
  exclusiveMaximum: 2,
  multipleOf: 2,
  uniqueItems: 2,
  contains: 2,
  minLength: 2,
  maxLength: 2,
  minItems: 2,
  maxItems: 2,
  minProperties: 2,
  maxProperties: 2,
  dependencies: 2,
  propertyNames: 2,
  additionalItems: 2,
  additionalProperties: 2,
  absolutePath: 2
};

/**
 * @param {string} value
 * @returns {value is number}
 */
function isNumeric(value) {
  return /^-?\d+$/.test(value);
}

/**
 *
 * @param {Array<SchemaUtilErrorObject>} array
 * @param {(item: SchemaUtilErrorObject) => number} fn
 * @returns {Array<SchemaUtilErrorObject>}
 */
function filterMax(array, fn) {
  const evaluatedMax = array.reduce((max, item) => Math.max(max, fn(item)), 0);
  return array.filter(item => fn(item) === evaluatedMax);
}

/**
 *
 * @param {Array<SchemaUtilErrorObject>} children
 * @returns {Array<SchemaUtilErrorObject>}
 */
function filterChildren(children) {
  let newChildren = children;
  newChildren = filterMax(newChildren,
  /**
   *
   * @param {SchemaUtilErrorObject} error
   * @returns {number}
   */
  error => error.instancePath ? error.instancePath.length : 0);
  newChildren = filterMax(newChildren,
  /**
   * @param {SchemaUtilErrorObject} error
   * @returns {number}
   */
  error => SPECIFICITY[/** @type {keyof typeof SPECIFICITY} */error.keyword] || 2);
  return newChildren;
}

/**
 * Find all children errors
 * @param {Array<SchemaUtilErrorObject>} children
 * @param {Array<string>} schemaPaths
 * @return {number} returns index of first child
 */
function findAllChildren(children, schemaPaths) {
  let i = children.length - 1;
  const predicate =
  /**
   * @param {string} schemaPath
   * @returns {boolean}
   */
  schemaPath => children[i].schemaPath.indexOf(schemaPath) !== 0;
  while (i > -1 && !schemaPaths.every(predicate)) {
    if (children[i].keyword === "anyOf" || children[i].keyword === "oneOf") {
      const refs = extractRefs(children[i]);
      const childrenStart = findAllChildren(children.slice(0, i), refs.concat(children[i].schemaPath));
      i = childrenStart - 1;
    } else {
      i -= 1;
    }
  }
  return i + 1;
}

/**
 * Extracts all refs from schema
 * @param {SchemaUtilErrorObject} error
 * @return {Array<string>}
 */
function extractRefs(error) {
  const {
    schema
  } = error;
  if (!Array.isArray(schema)) {
    return [];
  }
  return schema.map(({
    $ref
  }) => $ref).filter(s => s);
}

/**
 * Groups children by their first level parent (assuming that error is root)
 * @param {Array<SchemaUtilErrorObject>} children
 * @return {Array<SchemaUtilErrorObject>}
 */
function groupChildrenByFirstChild(children) {
  const result = [];
  let i = children.length - 1;
  while (i > 0) {
    const child = children[i];
    if (child.keyword === "anyOf" || child.keyword === "oneOf") {
      const refs = extractRefs(child);
      const childrenStart = findAllChildren(children.slice(0, i), refs.concat(child.schemaPath));
      if (childrenStart !== i) {
        result.push(Object.assign({}, child, {
          children: children.slice(childrenStart, i)
        }));
        i = childrenStart;
      } else {
        result.push(child);
      }
    } else {
      result.push(child);
    }
    i -= 1;
  }
  if (i === 0) {
    result.push(children[i]);
  }
  return result.reverse();
}

/**
 * @param {string} str
 * @param {string} prefix
 * @returns {string}
 */
function indent(str, prefix) {
  return str.replace(/\n(?!$)/g, `\n${prefix}`);
}

/**
 * @param {Schema} schema
 * @returns {schema is (Schema & {not: Schema})}
 */
function hasNotInSchema(schema) {
  return !!schema.not;
}

/**
 * @param {Schema} schema
 * @return {Schema}
 */
function findFirstTypedSchema(schema) {
  if (hasNotInSchema(schema)) {
    return findFirstTypedSchema(schema.not);
  }
  return schema;
}

/**
 * @param {Schema} schema
 * @return {boolean}
 */
function canApplyNot(schema) {
  const typedSchema = findFirstTypedSchema(schema);
  return likeNumber(typedSchema) || likeInteger(typedSchema) || likeString(typedSchema) || likeNull(typedSchema) || likeBoolean(typedSchema);
}

/**
 * @param {any} maybeObj
 * @returns {boolean}
 */
function isObject(maybeObj) {
  return typeof maybeObj === "object" && maybeObj !== null;
}

/**
 * @param {Schema} schema
 * @returns {boolean}
 */
function likeNumber(schema) {
  return schema.type === "number" || typeof schema.minimum !== "undefined" || typeof schema.exclusiveMinimum !== "undefined" || typeof schema.maximum !== "undefined" || typeof schema.exclusiveMaximum !== "undefined" || typeof schema.multipleOf !== "undefined";
}

/**
 * @param {Schema} schema
 * @returns {boolean}
 */
function likeInteger(schema) {
  return schema.type === "integer" || typeof schema.minimum !== "undefined" || typeof schema.exclusiveMinimum !== "undefined" || typeof schema.maximum !== "undefined" || typeof schema.exclusiveMaximum !== "undefined" || typeof schema.multipleOf !== "undefined";
}

/**
 * @param {Schema} schema
 * @returns {boolean}
 */
function likeString(schema) {
  return schema.type === "string" || typeof schema.minLength !== "undefined" || typeof schema.maxLength !== "undefined" || typeof schema.pattern !== "undefined" || typeof schema.format !== "undefined" || typeof schema.formatMinimum !== "undefined" || typeof schema.formatMaximum !== "undefined";
}

/**
 * @param {Schema} schema
 * @returns {boolean}
 */
function likeBoolean(schema) {
  return schema.type === "boolean";
}

/**
 * @param {Schema} schema
 * @returns {boolean}
 */
function likeArray(schema) {
  return schema.type === "array" || typeof schema.minItems === "number" || typeof schema.maxItems === "number" || typeof schema.uniqueItems !== "undefined" || typeof schema.items !== "undefined" || typeof schema.additionalItems !== "undefined" || typeof schema.contains !== "undefined";
}

/**
 * @param {Schema & {patternRequired?: Array<string>}} schema
 * @returns {boolean}
 */
function likeObject(schema) {
  return schema.type === "object" || typeof schema.minProperties !== "undefined" || typeof schema.maxProperties !== "undefined" || typeof schema.required !== "undefined" || typeof schema.properties !== "undefined" || typeof schema.patternProperties !== "undefined" || typeof schema.additionalProperties !== "undefined" || typeof schema.dependencies !== "undefined" || typeof schema.propertyNames !== "undefined" || typeof schema.patternRequired !== "undefined";
}

/**
 * @param {Schema} schema
 * @returns {boolean}
 */
function likeNull(schema) {
  return schema.type === "null";
}

/**
 * @param {string} type
 * @returns {string}
 */
function getArticle(type) {
  if (/^[aeiou]/i.test(type)) {
    return "an";
  }
  return "a";
}

/**
 * @param {Schema=} schema
 * @returns {string}
 */
function getSchemaNonTypes(schema) {
  if (!schema) {
    return "";
  }
  if (!schema.type) {
    if (likeNumber(schema) || likeInteger(schema)) {
      return " | should be any non-number";
    }
    if (likeString(schema)) {
      return " | should be any non-string";
    }
    if (likeArray(schema)) {
      return " | should be any non-array";
    }
    if (likeObject(schema)) {
      return " | should be any non-object";
    }
  }
  return "";
}

/**
 * @param {Array<string>} hints
 * @returns {string}
 */
function formatHints(hints) {
  return hints.length > 0 ? `(${hints.join(", ")})` : "";
}
const getUtilHints = (0, _memorize.default)(() =>
// eslint-disable-next-line global-require
require("./util/hints"));

/**
 * @param {Schema} schema
 * @param {boolean} logic
 * @returns {string[]}
 */
function getHints(schema, logic) {
  if (likeNumber(schema) || likeInteger(schema)) {
    const util = getUtilHints();
    return util.numberHints(schema, logic);
  } else if (likeString(schema)) {
    const util = getUtilHints();
    return util.stringHints(schema, logic);
  }
  return [];
}
class ValidationError extends Error {
  /**
   * @param {Array<SchemaUtilErrorObject>} errors
   * @param {Schema} schema
   * @param {ValidationErrorConfiguration} configuration
   */
  constructor(errors, schema, configuration = {}) {
    super();

    /** @type {string} */
    this.name = "ValidationError";
    /** @type {Array<SchemaUtilErrorObject>} */
    this.errors = errors;
    /** @type {Schema} */
    this.schema = schema;
    let headerNameFromSchema;
    let baseDataPathFromSchema;
    if (schema.title && (!configuration.name || !configuration.baseDataPath)) {
      const splittedTitleFromSchema = schema.title.match(/^(.+) (.+)$/);
      if (splittedTitleFromSchema) {
        if (!configuration.name) {
          [, headerNameFromSchema] = splittedTitleFromSchema;
        }
        if (!configuration.baseDataPath) {
          [,, baseDataPathFromSchema] = splittedTitleFromSchema;
        }
      }
    }

    /** @type {string} */
    this.headerName = configuration.name || headerNameFromSchema || "Object";
    /** @type {string} */
    this.baseDataPath = configuration.baseDataPath || baseDataPathFromSchema || "configuration";

    /** @type {PostFormatter | null} */
    this.postFormatter = configuration.postFormatter || null;
    const header = `Invalid ${this.baseDataPath} object. ${this.headerName} has been initialized using ${getArticle(this.baseDataPath)} ${this.baseDataPath} object that does not match the API schema.\n`;

    /** @type {string} */
    this.message = `${header}${this.formatValidationErrors(errors)}`;
    Error.captureStackTrace(this, this.constructor);
  }

  /**
   * @param {string} path
   * @returns {Schema}
   */
  getSchemaPart(path) {
    const newPath = path.split("/");
    let schemaPart = this.schema;
    for (let i = 1; i < newPath.length; i++) {
      const inner = schemaPart[/** @type {keyof Schema} */newPath[i]];
      if (!inner) {
        break;
      }
      schemaPart = inner;
    }
    return schemaPart;
  }

  /**
   * @param {Schema} schema
   * @param {boolean} logic
   * @param {Array<Object>} prevSchemas
   * @returns {string}
   */
  formatSchema(schema, logic = true, prevSchemas = []) {
    let newLogic = logic;
    const formatInnerSchema =
    /**
     *
     * @param {Object} innerSchema
     * @param {boolean=} addSelf
     * @returns {string}
     */
    (innerSchema, addSelf) => {
      if (!addSelf) {
        return this.formatSchema(innerSchema, newLogic, prevSchemas);
      }
      if (prevSchemas.includes(innerSchema)) {
        return "(recursive)";
      }
      return this.formatSchema(innerSchema, newLogic, prevSchemas.concat(schema));
    };
    if (hasNotInSchema(schema) && !likeObject(schema)) {
      if (canApplyNot(schema.not)) {
        newLogic = !logic;
        return formatInnerSchema(schema.not);
      }
      const needApplyLogicHere = !schema.not.not;
      const prefix = logic ? "" : "non ";
      newLogic = !logic;
      return needApplyLogicHere ? prefix + formatInnerSchema(schema.not) : formatInnerSchema(schema.not);
    }
    if ( /** @type {Schema & {instanceof: string | Array<string>}} */schema.instanceof) {
      const {
        instanceof: value
      } = /** @type {Schema & {instanceof: string | Array<string>}} */schema;
      const values = !Array.isArray(value) ? [value] : value;
      return values.map(
      /**
       * @param {string} item
       * @returns {string}
       */
      item => item === "Function" ? "function" : item).join(" | ");
    }
    if (schema.enum) {
      const enumValues = /** @type {Array<any>} */schema.enum.map(item => {
        if (item === null && schema.undefinedAsNull) {
          return `${JSON.stringify(item)} | undefined`;
        }
        return JSON.stringify(item);
      }).join(" | ");
      return `${enumValues}`;
    }
    if (typeof schema.const !== "undefined") {
      return JSON.stringify(schema.const);
    }
    if (schema.oneOf) {
      return (/** @type {Array<Schema>} */schema.oneOf.map(item => formatInnerSchema(item, true)).join(" | ")
      );
    }
    if (schema.anyOf) {
      return (/** @type {Array<Schema>} */schema.anyOf.map(item => formatInnerSchema(item, true)).join(" | ")
      );
    }
    if (schema.allOf) {
      return (/** @type {Array<Schema>} */schema.allOf.map(item => formatInnerSchema(item, true)).join(" & ")
      );
    }
    if ( /** @type {JSONSchema7} */schema.if) {
      const {
        if: ifValue,
        then: thenValue,
        else: elseValue
      } = /** @type {JSONSchema7} */schema;
      return `${ifValue ? `if ${formatInnerSchema(ifValue)}` : ""}${thenValue ? ` then ${formatInnerSchema(thenValue)}` : ""}${elseValue ? ` else ${formatInnerSchema(elseValue)}` : ""}`;
    }
    if (schema.$ref) {
      return formatInnerSchema(this.getSchemaPart(schema.$ref), true);
    }
    if (likeNumber(schema) || likeInteger(schema)) {
      const [type, ...hints] = getHints(schema, logic);
      const str = `${type}${hints.length > 0 ? ` ${formatHints(hints)}` : ""}`;
      return logic ? str : hints.length > 0 ? `non-${type} | ${str}` : `non-${type}`;
    }
    if (likeString(schema)) {
      const [type, ...hints] = getHints(schema, logic);
      const str = `${type}${hints.length > 0 ? ` ${formatHints(hints)}` : ""}`;
      return logic ? str : str === "string" ? "non-string" : `non-string | ${str}`;
    }
    if (likeBoolean(schema)) {
      return `${logic ? "" : "non-"}boolean`;
    }
    if (likeArray(schema)) {
      // not logic already applied in formatValidationError
      newLogic = true;
      const hints = [];
      if (typeof schema.minItems === "number") {
        hints.push(`should not have fewer than ${schema.minItems} item${schema.minItems > 1 ? "s" : ""}`);
      }
      if (typeof schema.maxItems === "number") {
        hints.push(`should not have more than ${schema.maxItems} item${schema.maxItems > 1 ? "s" : ""}`);
      }
      if (schema.uniqueItems) {
        hints.push("should not have duplicate items");
      }
      const hasAdditionalItems = typeof schema.additionalItems === "undefined" || Boolean(schema.additionalItems);
      let items = "";
      if (schema.items) {
        if (Array.isArray(schema.items) && schema.items.length > 0) {
          items = `${
          /** @type {Array<Schema>} */schema.items.map(item => formatInnerSchema(item)).join(", ")}`;
          if (hasAdditionalItems) {
            if (schema.additionalItems && isObject(schema.additionalItems) && Object.keys(schema.additionalItems).length > 0) {
              hints.push(`additional items should be ${formatInnerSchema(schema.additionalItems)}`);
            }
          }
        } else if (schema.items && Object.keys(schema.items).length > 0) {
          // "additionalItems" is ignored
          items = `${formatInnerSchema(schema.items)}`;
        } else {
          // Fallback for empty `items` value
          items = "any";
        }
      } else {
        // "additionalItems" is ignored
        items = "any";
      }
      if (schema.contains && Object.keys(schema.contains).length > 0) {
        hints.push(`should contains at least one ${this.formatSchema(schema.contains)} item`);
      }
      return `[${items}${hasAdditionalItems ? ", ..." : ""}]${hints.length > 0 ? ` (${hints.join(", ")})` : ""}`;
    }
    if (likeObject(schema)) {
      // not logic already applied in formatValidationError
      newLogic = true;
      const hints = [];
      if (typeof schema.minProperties === "number") {
        hints.push(`should not have fewer than ${schema.minProperties} ${schema.minProperties > 1 ? "properties" : "property"}`);
      }
      if (typeof schema.maxProperties === "number") {
        hints.push(`should not have more than ${schema.maxProperties} ${schema.minProperties && schema.minProperties > 1 ? "properties" : "property"}`);
      }
      if (schema.patternProperties && Object.keys(schema.patternProperties).length > 0) {
        const patternProperties = Object.keys(schema.patternProperties);
        hints.push(`additional property names should match pattern${patternProperties.length > 1 ? "s" : ""} ${patternProperties.map(pattern => JSON.stringify(pattern)).join(" | ")}`);
      }
      const properties = schema.properties ? Object.keys(schema.properties) : [];
      /** @type {Array<string>} */
      // @ts-ignore
      const required = schema.required ? schema.required : [];
      const allProperties = [...new Set( /** @type {Array<string>} */[].concat(required).concat(properties))];
      const objectStructure = allProperties.map(property => {
        const isRequired = required.includes(property);

        // Some properties need quotes, maybe we should add check
        // Maybe we should output type of property (`foo: string`), but it is looks very unreadable
        return `${property}${isRequired ? "" : "?"}`;
      }).concat(typeof schema.additionalProperties === "undefined" || Boolean(schema.additionalProperties) ? schema.additionalProperties && isObject(schema.additionalProperties) ? [`<key>: ${formatInnerSchema(schema.additionalProperties)}`] : ["…"] : []).join(", ");
      const {
        dependencies,
        propertyNames,
        patternRequired
      } = /** @type {Schema & {patternRequired?: Array<string>;}} */schema;
      if (dependencies) {
        Object.keys(dependencies).forEach(dependencyName => {
          const dependency = dependencies[dependencyName];
          if (Array.isArray(dependency)) {
            hints.push(`should have ${dependency.length > 1 ? "properties" : "property"} ${dependency.map(dep => `'${dep}'`).join(", ")} when property '${dependencyName}' is present`);
          } else {
            hints.push(`should be valid according to the schema ${formatInnerSchema(dependency)} when property '${dependencyName}' is present`);
          }
        });
      }
      if (propertyNames && Object.keys(propertyNames).length > 0) {
        hints.push(`each property name should match format ${JSON.stringify(schema.propertyNames.format)}`);
      }
      if (patternRequired && patternRequired.length > 0) {
        hints.push(`should have property matching pattern ${patternRequired.map(
        /**
         * @param {string} item
         * @returns {string}
         */
        item => JSON.stringify(item))}`);
      }
      return `object {${objectStructure ? ` ${objectStructure} ` : ""}}${hints.length > 0 ? ` (${hints.join(", ")})` : ""}`;
    }
    if (likeNull(schema)) {
      return `${logic ? "" : "non-"}null`;
    }
    if (Array.isArray(schema.type)) {
      // not logic already applied in formatValidationError
      return `${schema.type.join(" | ")}`;
    }

    // Fallback for unknown keywords
    // not logic already applied in formatValidationError
    /* istanbul ignore next */
    return JSON.stringify(schema, null, 2);
  }

  /**
   * @param {Schema=} schemaPart
   * @param {(boolean | Array<string>)=} additionalPath
   * @param {boolean=} needDot
   * @param {boolean=} logic
   * @returns {string}
   */
  getSchemaPartText(schemaPart, additionalPath, needDot = false, logic = true) {
    if (!schemaPart) {
      return "";
    }
    if (Array.isArray(additionalPath)) {
      for (let i = 0; i < additionalPath.length; i++) {
        /** @type {Schema | undefined} */
        const inner = schemaPart[/** @type {keyof Schema} */additionalPath[i]];
        if (inner) {
          // eslint-disable-next-line no-param-reassign
          schemaPart = inner;
        } else {
          break;
        }
      }
    }
    while (schemaPart.$ref) {
      // eslint-disable-next-line no-param-reassign
      schemaPart = this.getSchemaPart(schemaPart.$ref);
    }
    let schemaText = `${this.formatSchema(schemaPart, logic)}${needDot ? "." : ""}`;
    if (schemaPart.description) {
      schemaText += `\n-> ${schemaPart.description}`;
    }
    if (schemaPart.link) {
      schemaText += `\n-> Read more at ${schemaPart.link}`;
    }
    return schemaText;
  }

  /**
   * @param {Schema=} schemaPart
   * @returns {string}
   */
  getSchemaPartDescription(schemaPart) {
    if (!schemaPart) {
      return "";
    }
    while (schemaPart.$ref) {
      // eslint-disable-next-line no-param-reassign
      schemaPart = this.getSchemaPart(schemaPart.$ref);
    }
    let schemaText = "";
    if (schemaPart.description) {
      schemaText += `\n-> ${schemaPart.description}`;
    }
    if (schemaPart.link) {
      schemaText += `\n-> Read more at ${schemaPart.link}`;
    }
    return schemaText;
  }

  /**
   * @param {SchemaUtilErrorObject} error
   * @returns {string}
   */
  formatValidationError(error) {
    const {
      keyword,
      instancePath: errorInstancePath
    } = error;
    const splittedInstancePath = errorInstancePath.split("/");
    /**
     * @type {Array<string>}
     */
    const defaultValue = [];
    const prettyInstancePath = splittedInstancePath.reduce((acc, val) => {
      if (val.length > 0) {
        if (isNumeric(val)) {
          acc.push(`[${val}]`);
        } else if (/^\[/.test(val)) {
          acc.push(val);
        } else {
          acc.push(`.${val}`);
        }
      }
      return acc;
    }, defaultValue).join("");
    const instancePath = `${this.baseDataPath}${prettyInstancePath}`;

    // const { keyword, instancePath: errorInstancePath } = error;
    // const instancePath = `${this.baseDataPath}${errorInstancePath.replace(/\//g, '.')}`;

    switch (keyword) {
      case "type":
        {
          const {
            parentSchema,
            params
          } = error;
          switch (params.type) {
            case "number":
              return `${instancePath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;
            case "integer":
              return `${instancePath} should be an ${this.getSchemaPartText(parentSchema, false, true)}`;
            case "string":
              return `${instancePath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;
            case "boolean":
              return `${instancePath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;
            case "array":
              return `${instancePath} should be an array:\n${this.getSchemaPartText(parentSchema)}`;
            case "object":
              return `${instancePath} should be an object:\n${this.getSchemaPartText(parentSchema)}`;
            case "null":
              return `${instancePath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;
            default:
              return `${instancePath} should be:\n${this.getSchemaPartText(parentSchema)}`;
          }
        }
      case "instanceof":
        {
          const {
            parentSchema
          } = error;
          return `${instancePath} should be an instance of ${this.getSchemaPartText(parentSchema, false, true)}`;
        }
      case "pattern":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            pattern
          } = params;
          return `${instancePath} should match pattern ${JSON.stringify(pattern)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "format":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            format
          } = params;
          return `${instancePath} should match format ${JSON.stringify(format)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "formatMinimum":
      case "formatExclusiveMinimum":
      case "formatMaximum":
      case "formatExclusiveMaximum":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            comparison,
            limit
          } = params;
          return `${instancePath} should be ${comparison} ${JSON.stringify(limit)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "minimum":
      case "maximum":
      case "exclusiveMinimum":
      case "exclusiveMaximum":
        {
          const {
            parentSchema,
            params
          } = error;
          const {
            comparison,
            limit
          } = params;
          const [, ...hints] = getHints( /** @type {Schema} */parentSchema, true);
          if (hints.length === 0) {
            hints.push(`should be ${comparison} ${limit}`);
          }
          return `${instancePath} ${hints.join(" ")}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "multipleOf":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            multipleOf
          } = params;
          return `${instancePath} should be multiple of ${multipleOf}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "patternRequired":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            missingPattern
          } = params;
          return `${instancePath} should have property matching pattern ${JSON.stringify(missingPattern)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "minLength":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } = params;
          if (limit === 1) {
            return `${instancePath} should be a non-empty string${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
          }
          const length = limit - 1;
          return `${instancePath} should be longer than ${length} character${length > 1 ? "s" : ""}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "minItems":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } = params;
          if (limit === 1) {
            return `${instancePath} should be a non-empty array${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
          }
          return `${instancePath} should not have fewer than ${limit} items${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "minProperties":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } = params;
          if (limit === 1) {
            return `${instancePath} should be a non-empty object${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
          }
          return `${instancePath} should not have fewer than ${limit} properties${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "maxLength":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } = params;
          const max = limit + 1;
          return `${instancePath} should be shorter than ${max} character${max > 1 ? "s" : ""}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "maxItems":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } = params;
          return `${instancePath} should not have more than ${limit} items${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "maxProperties":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } = params;
          return `${instancePath} should not have more than ${limit} properties${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "uniqueItems":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            i
          } = params;
          return `${instancePath} should not contain the item '${
          /** @type {{ data: Array<any> }} **/error.data[i]}' twice${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "additionalItems":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } = params;
          return `${instancePath} should not have more than ${limit} items${getSchemaNonTypes(parentSchema)}. These items are valid:\n${this.getSchemaPartText(parentSchema)}`;
        }
      case "contains":
        {
          const {
            parentSchema
          } = error;
          return `${instancePath} should contains at least one ${this.getSchemaPartText(parentSchema, ["contains"])} item${getSchemaNonTypes(parentSchema)}.`;
        }
      case "required":
        {
          const {
            parentSchema,
            params
          } = error;
          const missingProperty = params.missingProperty.replace(/^\./, "");
          const hasProperty = parentSchema && Boolean( /** @type {Schema} */
          parentSchema.properties && /** @type {Schema} */
          parentSchema.properties[missingProperty]);
          return `${instancePath} misses the property '${missingProperty}'${getSchemaNonTypes(parentSchema)}.${hasProperty ? ` Should be:\n${this.getSchemaPartText(parentSchema, ["properties", missingProperty])}` : this.getSchemaPartDescription(parentSchema)}`;
        }
      case "additionalProperties":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            additionalProperty
          } = params;
          return `${instancePath} has an unknown property '${additionalProperty}'${getSchemaNonTypes(parentSchema)}. These properties are valid:\n${this.getSchemaPartText(parentSchema)}`;
        }
      case "dependencies":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            property,
            deps
          } = params;
          const dependencies = deps.split(",").map(
          /**
           * @param {string} dep
           * @returns {string}
           */
          dep => `'${dep.trim()}'`).join(", ");
          return `${instancePath} should have properties ${dependencies} when property '${property}' is present${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "propertyNames":
        {
          const {
            params,
            parentSchema,
            schema
          } = error;
          const {
            propertyName
          } = params;
          return `${instancePath} property name '${propertyName}' is invalid${getSchemaNonTypes(parentSchema)}. Property names should be match format ${JSON.stringify(schema.format)}.${this.getSchemaPartDescription(parentSchema)}`;
        }
      case "enum":
        {
          const {
            parentSchema
          } = error;
          if (parentSchema && /** @type {Schema} */
          parentSchema.enum && /** @type {Schema} */
          parentSchema.enum.length === 1) {
            return `${instancePath} should be ${this.getSchemaPartText(parentSchema, false, true)}`;
          }
          return `${instancePath} should be one of these:\n${this.getSchemaPartText(parentSchema)}`;
        }
      case "const":
        {
          const {
            parentSchema
          } = error;
          return `${instancePath} should be equal to constant ${this.getSchemaPartText(parentSchema, false, true)}`;
        }
      case "not":
        {
          const postfix = likeObject( /** @type {Schema} */error.parentSchema) ? `\n${this.getSchemaPartText(error.parentSchema)}` : "";
          const schemaOutput = this.getSchemaPartText(error.schema, false, false, false);
          if (canApplyNot(error.schema)) {
            return `${instancePath} should be any ${schemaOutput}${postfix}.`;
          }
          const {
            schema,
            parentSchema
          } = error;
          return `${instancePath} should not be ${this.getSchemaPartText(schema, false, true)}${parentSchema && likeObject(parentSchema) ? `\n${this.getSchemaPartText(parentSchema)}` : ""}`;
        }
      case "oneOf":
      case "anyOf":
        {
          const {
            parentSchema,
            children
          } = error;
          if (children && children.length > 0) {
            if (error.schema.length === 1) {
              const lastChild = children[children.length - 1];
              const remainingChildren = children.slice(0, children.length - 1);
              return this.formatValidationError(Object.assign({}, lastChild, {
                children: remainingChildren,
                parentSchema: Object.assign({}, parentSchema, lastChild.parentSchema)
              }));
            }
            let filteredChildren = filterChildren(children);
            if (filteredChildren.length === 1) {
              return this.formatValidationError(filteredChildren[0]);
            }
            filteredChildren = groupChildrenByFirstChild(filteredChildren);
            return `${instancePath} should be one of these:\n${this.getSchemaPartText(parentSchema)}\nDetails:\n${filteredChildren.map(
            /**
             * @param {SchemaUtilErrorObject} nestedError
             * @returns {string}
             */
            nestedError => ` * ${indent(this.formatValidationError(nestedError), "   ")}`).join("\n")}`;
          }
          return `${instancePath} should be one of these:\n${this.getSchemaPartText(parentSchema)}`;
        }
      case "if":
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            failingKeyword
          } = params;
          return `${instancePath} should match "${failingKeyword}" schema:\n${this.getSchemaPartText(parentSchema, [failingKeyword])}`;
        }
      case "absolutePath":
        {
          const {
            message,
            parentSchema
          } = error;
          return `${instancePath}: ${message}${this.getSchemaPartDescription(parentSchema)}`;
        }
      /* istanbul ignore next */
      default:
        {
          const {
            message,
            parentSchema
          } = error;
          const ErrorInJSON = JSON.stringify(error, null, 2);

          // For `custom`, `false schema`, `$ref` keywords
          // Fallback for unknown keywords
          return `${instancePath} ${message} (${ErrorInJSON}).\n${this.getSchemaPartText(parentSchema, false)}`;
        }
    }
  }

  /**
   * @param {Array<SchemaUtilErrorObject>} errors
   * @returns {string}
   */
  formatValidationErrors(errors) {
    return errors.map(error => {
      let formattedError = this.formatValidationError(error);
      if (this.postFormatter) {
        formattedError = this.postFormatter(formattedError, error);
      }
      return ` - ${indent(formattedError, "   ")}`;
    }).join("\n");
  }
}
var _default = ValidationError;
exports.default = _default;


code:
"use strict";

const {
  validate,
  ValidationError,
  enableValidation,
  disableValidation,
  needValidate
} = require("./validate");
module.exports = {
  validate,
  ValidationError,
  enableValidation,
  disableValidation,
  needValidate
};


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
/** @typedef {import("ajv").default} Ajv */
/** @typedef {import("ajv").SchemaValidateFunction} SchemaValidateFunction */
/** @typedef {import("ajv").AnySchemaObject} AnySchemaObject */
/** @typedef {import("../validate").SchemaUtilErrorObject} SchemaUtilErrorObject */

/**
 * @param {string} message
 * @param {object} schema
 * @param {string} data
 * @returns {SchemaUtilErrorObject}
 */
function errorMessage(message, schema, data) {
  return {
    // @ts-ignore
    // eslint-disable-next-line no-undefined
    dataPath: undefined,
    // @ts-ignore
    // eslint-disable-next-line no-undefined
    schemaPath: undefined,
    keyword: "absolutePath",
    params: {
      absolutePath: data
    },
    message,
    parentSchema: schema
  };
}

/**
 * @param {boolean} shouldBeAbsolute
 * @param {object} schema
 * @param {string} data
 * @returns {SchemaUtilErrorObject}
 */
function getErrorFor(shouldBeAbsolute, schema, data) {
  const message = shouldBeAbsolute ? `The provided value ${JSON.stringify(data)} is not an absolute path!` : `A relative path is expected. However, the provided value ${JSON.stringify(data)} is an absolute path!`;
  return errorMessage(message, schema, data);
}

/**
 *
 * @param {Ajv} ajv
 * @returns {Ajv}
 */
function addAbsolutePathKeyword(ajv) {
  ajv.addKeyword({
    keyword: "absolutePath",
    type: "string",
    errors: true,
    /**
     * @param {boolean} schema
     * @param {AnySchemaObject} parentSchema
     * @returns {SchemaValidateFunction}
     */
    compile(schema, parentSchema) {
      /** @type {SchemaValidateFunction} */
      const callback = data => {
        let passes = true;
        const isExclamationMarkPresent = data.includes("!");
        if (isExclamationMarkPresent) {
          callback.errors = [errorMessage(`The provided value ${JSON.stringify(data)} contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.`, parentSchema, data)];
          passes = false;
        }

        // ?:[A-Za-z]:\\ - Windows absolute path
        // \\\\ - Windows network absolute path
        // \/ - Unix-like OS absolute path
        const isCorrectAbsolutePath = schema === /^(?:[A-Za-z]:(\\|\/)|\\\\|\/)/.test(data);
        if (!isCorrectAbsolutePath) {
          callback.errors = [getErrorFor(schema, parentSchema, data)];
          passes = false;
        }
        return passes;
      };
      callback.errors = [];
      return callback;
    }
  });
  return ajv;
}
var _default = addAbsolutePathKeyword;
exports.default = _default;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
/** @typedef {import("ajv").default} Ajv */
/** @typedef {import("ajv").SchemaValidateFunction} SchemaValidateFunction */
/** @typedef {import("ajv").AnySchemaObject} AnySchemaObject */
/** @typedef {import("ajv").ValidateFunction} ValidateFunction */

/**
 *
 * @param {Ajv} ajv
 * @returns {Ajv}
 */
function addUndefinedAsNullKeyword(ajv) {
  ajv.addKeyword({
    keyword: "undefinedAsNull",
    before: "enum",
    modifying: true,
    /** @type {SchemaValidateFunction} */
    validate(kwVal, data, metadata, dataCxt) {
      if (kwVal && dataCxt && metadata && typeof metadata.enum !== "undefined") {
        const idx = dataCxt.parentDataProperty;
        if (typeof dataCxt.parentData[idx] === "undefined") {
          // eslint-disable-next-line no-param-reassign
          dataCxt.parentData[dataCxt.parentDataProperty] = null;
        }
      }
      return true;
    }
  });
  return ajv;
}
var _default = addUndefinedAsNullKeyword;
exports.default = _default;


code:
"use strict";

/**
 * @typedef {[number, boolean]} RangeValue
 */

/**
 * @callback RangeValueCallback
 * @param {RangeValue} rangeValue
 * @returns {boolean}
 */

class Range {
  /**
   * @param {"left" | "right"} side
   * @param {boolean} exclusive
   * @returns {">" | ">=" | "<" | "<="}
   */
  static getOperator(side, exclusive) {
    if (side === "left") {
      return exclusive ? ">" : ">=";
    }
    return exclusive ? "<" : "<=";
  }

  /**
   * @param {number} value
   * @param {boolean} logic is not logic applied
   * @param {boolean} exclusive is range exclusive
   * @returns {string}
   */
  static formatRight(value, logic, exclusive) {
    if (logic === false) {
      return Range.formatLeft(value, !logic, !exclusive);
    }
    return `should be ${Range.getOperator("right", exclusive)} ${value}`;
  }

  /**
   * @param {number} value
   * @param {boolean} logic is not logic applied
   * @param {boolean} exclusive is range exclusive
   * @returns {string}
   */
  static formatLeft(value, logic, exclusive) {
    if (logic === false) {
      return Range.formatRight(value, !logic, !exclusive);
    }
    return `should be ${Range.getOperator("left", exclusive)} ${value}`;
  }

  /**
   * @param {number} start left side value
   * @param {number} end right side value
   * @param {boolean} startExclusive is range exclusive from left side
   * @param {boolean} endExclusive is range exclusive from right side
   * @param {boolean} logic is not logic applied
   * @returns {string}
   */
  static formatRange(start, end, startExclusive, endExclusive, logic) {
    let result = "should be";
    result += ` ${Range.getOperator(logic ? "left" : "right", logic ? startExclusive : !startExclusive)} ${start} `;
    result += logic ? "and" : "or";
    result += ` ${Range.getOperator(logic ? "right" : "left", logic ? endExclusive : !endExclusive)} ${end}`;
    return result;
  }

  /**
   * @param {Array<RangeValue>} values
   * @param {boolean} logic is not logic applied
   * @return {RangeValue} computed value and it's exclusive flag
   */
  static getRangeValue(values, logic) {
    let minMax = logic ? Infinity : -Infinity;
    let j = -1;
    const predicate = logic ? /** @type {RangeValueCallback} */
    ([value]) => value <= minMax : /** @type {RangeValueCallback} */
    ([value]) => value >= minMax;
    for (let i = 0; i < values.length; i++) {
      if (predicate(values[i])) {
        [minMax] = values[i];
        j = i;
      }
    }
    if (j > -1) {
      return values[j];
    }
    return [Infinity, true];
  }
  constructor() {
    /** @type {Array<RangeValue>} */
    this._left = [];
    /** @type {Array<RangeValue>} */
    this._right = [];
  }

  /**
   * @param {number} value
   * @param {boolean=} exclusive
   */
  left(value, exclusive = false) {
    this._left.push([value, exclusive]);
  }

  /**
   * @param {number} value
   * @param {boolean=} exclusive
   */
  right(value, exclusive = false) {
    this._right.push([value, exclusive]);
  }

  /**
   * @param {boolean} logic is not logic applied
   * @return {string} "smart" range string representation
   */
  format(logic = true) {
    const [start, leftExclusive] = Range.getRangeValue(this._left, logic);
    const [end, rightExclusive] = Range.getRangeValue(this._right, !logic);
    if (!Number.isFinite(start) && !Number.isFinite(end)) {
      return "";
    }
    const realStart = leftExclusive ? start + 1 : start;
    const realEnd = rightExclusive ? end - 1 : end;

    // e.g. 5 < x < 7, 5 < x <= 6, 6 <= x <= 6
    if (realStart === realEnd) {
      return `should be ${logic ? "" : "!"}= ${realStart}`;
    }

    // e.g. 4 < x < ∞
    if (Number.isFinite(start) && !Number.isFinite(end)) {
      return Range.formatLeft(start, logic, leftExclusive);
    }

    // e.g. ∞ < x < 4
    if (!Number.isFinite(start) && Number.isFinite(end)) {
      return Range.formatRight(end, logic, rightExclusive);
    }
    return Range.formatRange(start, end, leftExclusive, rightExclusive, logic);
  }
}
module.exports = Range;


code:
"use strict";

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

/** @typedef {import("../validate").Schema} Schema */

/**
 * @param {Schema} schema
 * @param {boolean} logic
 * @return {string[]}
 */
module.exports.stringHints = function stringHints(schema, logic) {
  const hints = [];
  let type = "string";
  const currentSchema = {
    ...schema
  };
  if (!logic) {
    const tmpLength = currentSchema.minLength;
    const tmpFormat = currentSchema.formatMinimum;
    currentSchema.minLength = currentSchema.maxLength;
    currentSchema.maxLength = tmpLength;
    currentSchema.formatMinimum = currentSchema.formatMaximum;
    currentSchema.formatMaximum = tmpFormat;
  }
  if (typeof currentSchema.minLength === "number") {
    if (currentSchema.minLength === 1) {
      type = "non-empty string";
    } else {
      const length = Math.max(currentSchema.minLength - 1, 0);
      hints.push(`should be longer than ${length} character${length > 1 ? "s" : ""}`);
    }
  }
  if (typeof currentSchema.maxLength === "number") {
    if (currentSchema.maxLength === 0) {
      type = "empty string";
    } else {
      const length = currentSchema.maxLength + 1;
      hints.push(`should be shorter than ${length} character${length > 1 ? "s" : ""}`);
    }
  }
  if (currentSchema.pattern) {
    hints.push(`should${logic ? "" : " not"} match pattern ${JSON.stringify(currentSchema.pattern)}`);
  }
  if (currentSchema.format) {
    hints.push(`should${logic ? "" : " not"} match format ${JSON.stringify(currentSchema.format)}`);
  }
  if (currentSchema.formatMinimum) {
    hints.push(`should be ${currentSchema.formatExclusiveMinimum ? ">" : ">="} ${JSON.stringify(currentSchema.formatMinimum)}`);
  }
  if (currentSchema.formatMaximum) {
    hints.push(`should be ${currentSchema.formatExclusiveMaximum ? "<" : "<="} ${JSON.stringify(currentSchema.formatMaximum)}`);
  }
  return [type].concat(hints);
};

/**
 * @param {Schema} schema
 * @param {boolean} logic
 * @return {string[]}
 */
module.exports.numberHints = function numberHints(schema, logic) {
  const hints = [schema.type === "integer" ? "integer" : "number"];
  const range = new Range();
  if (typeof schema.minimum === "number") {
    range.left(schema.minimum);
  }
  if (typeof schema.exclusiveMinimum === "number") {
    range.left(schema.exclusiveMinimum, true);
  }
  if (typeof schema.maximum === "number") {
    range.right(schema.maximum);
  }
  if (typeof schema.exclusiveMaximum === "number") {
    range.right(schema.exclusiveMaximum, true);
  }
  const rangeFormat = range.format(logic);
  if (rangeFormat) {
    hints.push(rangeFormat);
  }
  if (typeof schema.multipleOf === "number") {
    hints.push(`should${logic ? "" : " not"} be multiple of ${schema.multipleOf}`);
  }
  return hints;
};


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
/**
 * @template T
 * @param fn {(function(): any) | undefined}
 * @returns {function(): T}
 */
const memoize = fn => {
  let cache = false;
  /** @type {T} */
  let result;
  return () => {
    if (cache) {
      return result;
    }
    result = /** @type {function(): any} */fn();
    cache = true;
    // Allow to clean up memory for fn
    // and all dependent resources
    // eslint-disable-next-line no-undefined, no-param-reassign
    fn = undefined;
    return result;
  };
};
var _default = memoize;
exports.default = _default;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "ValidationError", {
  enumerable: true,
  get: function () {
    return _ValidationError.default;
  }
});
exports.disableValidation = disableValidation;
exports.enableValidation = enableValidation;
exports.needValidate = needValidate;
exports.validate = validate;
var _ValidationError = _interopRequireDefault(require("./ValidationError"));
var _memorize = _interopRequireDefault(require("./util/memorize"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const getAjv = (0, _memorize.default)(() => {
  // Use CommonJS require for ajv libs so TypeScript consumers aren't locked into esModuleInterop (see #110).
  // eslint-disable-next-line global-require
  const Ajv = require("ajv").default;
  // eslint-disable-next-line global-require
  const ajvKeywords = require("ajv-keywords").default;
  // eslint-disable-next-line global-require
  const addFormats = require("ajv-formats").default;

  /**
   * @type {Ajv}
   */
  const ajv = new Ajv({
    strict: false,
    allErrors: true,
    verbose: true,
    $data: true
  });
  ajvKeywords(ajv, ["instanceof", "patternRequired"]);
  addFormats(ajv, {
    keywords: true
  });

  // Custom keywords
  // eslint-disable-next-line global-require
  const addAbsolutePathKeyword = require("./keywords/absolutePath").default;
  addAbsolutePathKeyword(ajv);
  const addUndefinedAsNullKeyword =
  // eslint-disable-next-line global-require
  require("./keywords/undefinedAsNull").default;
  addUndefinedAsNullKeyword(ajv);
  return ajv;
});

/** @typedef {import("json-schema").JSONSchema4} JSONSchema4 */
/** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
/** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */
/** @typedef {import("ajv").ErrorObject} ErrorObject */

/**
 * @typedef {Object} Extend
 * @property {string=} formatMinimum
 * @property {string=} formatMaximum
 * @property {string=} formatExclusiveMinimum
 * @property {string=} formatExclusiveMaximum
 * @property {string=} link
 * @property {boolean=} undefinedAsNull
 */

/** @typedef {(JSONSchema4 | JSONSchema6 | JSONSchema7) & Extend} Schema */

/** @typedef {ErrorObject & { children?: Array<ErrorObject> }} SchemaUtilErrorObject */

/**
 * @callback PostFormatter
 * @param {string} formattedError
 * @param {SchemaUtilErrorObject} error
 * @returns {string}
 */

/**
 * @typedef {Object} ValidationErrorConfiguration
 * @property {string=} name
 * @property {string=} baseDataPath
 * @property {PostFormatter=} postFormatter
 */

/**
 * @param {SchemaUtilErrorObject} error
 * @param {number} idx
 * @returns {SchemaUtilErrorObject}
 */
function applyPrefix(error, idx) {
  // eslint-disable-next-line no-param-reassign
  error.instancePath = `[${idx}]${error.instancePath}`;
  if (error.children) {
    error.children.forEach(err => applyPrefix(err, idx));
  }
  return error;
}
let skipValidation = false;

// We use `process.env.SKIP_VALIDATION` because you can have multiple `schema-utils` with different version,
// so we want to disable it globally, `process.env` doesn't supported by browsers, so we have the local `skipValidation` variables

// Enable validation
function enableValidation() {
  skipValidation = false;

  // Disable validation for any versions
  if (process && process.env) {
    process.env.SKIP_VALIDATION = "n";
  }
}

// Disable validation
function disableValidation() {
  skipValidation = true;
  if (process && process.env) {
    process.env.SKIP_VALIDATION = "y";
  }
}

// Check if we need to confirm
function needValidate() {
  if (skipValidation) {
    return false;
  }
  if (process && process.env && process.env.SKIP_VALIDATION) {
    const value = process.env.SKIP_VALIDATION.trim();
    if (/^(?:y|yes|true|1|on)$/i.test(value)) {
      return false;
    }
    if (/^(?:n|no|false|0|off)$/i.test(value)) {
      return true;
    }
  }
  return true;
}

/**
 * @param {Schema} schema
 * @param {Array<object> | object} options
 * @param {ValidationErrorConfiguration=} configuration
 * @returns {void}
 */
function validate(schema, options, configuration) {
  if (!needValidate()) {
    return;
  }
  let errors = [];
  if (Array.isArray(options)) {
    for (let i = 0; i <= options.length - 1; i++) {
      errors.push(...validateObject(schema, options[i]).map(err => applyPrefix(err, i)));
    }
  } else {
    errors = validateObject(schema, options);
  }
  if (errors.length > 0) {
    throw new _ValidationError.default(errors, schema, configuration);
  }
}

/**
 * @param {Schema} schema
 * @param {Array<object> | object} options
 * @returns {Array<SchemaUtilErrorObject>}
 */
function validateObject(schema, options) {
  // Not need to cache, because `ajv@8` has built-in cache
  const compiledSchema = getAjv().compile(schema);
  const valid = compiledSchema(options);
  if (valid) return [];
  return compiledSchema.errors ? filterErrors(compiledSchema.errors) : [];
}

/**
 * @param {Array<ErrorObject>} errors
 * @returns {Array<SchemaUtilErrorObject>}
 */
function filterErrors(errors) {
  /** @type {Array<SchemaUtilErrorObject>} */
  let newErrors = [];
  for (const error of /** @type {Array<SchemaUtilErrorObject>} */errors) {
    const {
      instancePath
    } = error;
    /** @type {Array<SchemaUtilErrorObject>} */
    let children = [];
    newErrors = newErrors.filter(oldError => {
      if (oldError.instancePath.includes(instancePath)) {
        if (oldError.children) {
          children = children.concat(oldError.children.slice(0));
        }

        // eslint-disable-next-line no-undefined, no-param-reassign
        oldError.children = undefined;
        children.push(oldError);
        return false;
      }
      return true;
    });
    if (children.length) {
      error.children = children;
    }
    newErrors.push(error);
  }
  return newErrors;
}


schema-utils

description: webpack Validation Utils

license: MIT

repository: webpack/schema-utils

type: opencollective

start: npm run build -- -w

clean: del-cli dist declarations

prebuild: npm run clean

build:types: tsc --declaration --emitDeclarationOnly --outDir declarations && prettier "declarations/**/*.ts" --write

build:code: cross-env NODE_ENV=production babel src -d dist --copy-files

build: npm-run-all -p "build:**"

commitlint: commitlint --from=master

security: npm audit --production

lint:js: eslint --cache .

lint:types: tsc --pretty --noEmit

lint: npm-run-all lint:js lint:types fmt:check

fmt: npm run fmt:check -- --write

fix:js: npm run lint:js -- --fix

fix: npm-run-all fix:js fmt

test:only: cross-env NODE_ENV=test jest

test:watch: npm run test:only -- --watch

test:coverage: npm run test:only -- --collectCoverageFrom="src/**/*.js" --coverage

pretest: npm run lint

test: npm run test:coverage

prepare: npm run build && husky install

release: standard-version

files: dist declarations


mini-css-extract-plugin

description: extracts CSS into separate files

repository: webpack-contrib/mini-css-extract-plugin

author: Tobias Koppers @sokra

build:types: tsc --declaration --emitDeclarationOnly --outDir types && prettier "types/**/*.ts" --write

clean: del-cli dist

lint: npm-run-all -l -p "lint:**"

fix:prettier: npm run lint:prettier -- --write

fix: npm-run-all -l fix:js fix:prettier

prepare: husky install && npm run build

test:only:experimental: EXPERIMENTAL_USE_IMPORT_MODULE=true cross-env NODE_ENV=test jest

test:manual: npm run build && webpack serve ./test/manual/src/index.js --open --config ./test/manual/webpack.config.js

test: cross-env NODE_ENV=test npm run test:coverage

files: dist types

keywords: webpack css extract hmr


code:
declare function _exports(moduleId: TODO, options: TODO): TODO;
export = _exports;
export type TODO = any;


code:
declare function _exports(urlString: string): string;
export = _exports;


code:
export function getCompilationHooks(
  compilation: Compilation
): MiniCssExtractPluginCompilationHooks;
export type Compilation = import("webpack").Compilation;
export type VarNames = {
  tag: string;
  chunkId: string;
  href: string;
  resolve: string;
  reject: string;
};
export type MiniCssExtractPluginCompilationHooks = {
  beforeTagInsert: import("tapable").SyncWaterfallHook<
    [string, VarNames],
    string
  >;
};


code:
export = MiniCssExtractPlugin;
declare class MiniCssExtractPlugin {
  /**
   * @param {Compiler["webpack"]} webpack
   * @returns {CssModuleConstructor}
   */
  static getCssModule(webpack: Compiler["webpack"]): CssModuleConstructor;
  /**
   * @param {Compiler["webpack"]} webpack
   * @returns {CssDependencyConstructor}
   */
  static getCssDependency(
    webpack: Compiler["webpack"]
  ): CssDependencyConstructor;
  /**
   * Returns all hooks for the given compilation
   * @param {Compilation} compilation the compilation
   * @returns {MiniCssExtractPluginCompilationHooks} hooks
   */
  static getCompilationHooks(
    compilation: Compilation
  ): MiniCssExtractPluginCompilationHooks;
  /**
   * @param {PluginOptions} [options]
   */
  constructor(options?: PluginOptions | undefined);
  /**
   * @private
   * @type {WeakMap<Chunk, Set<CssModule>>}
   * @private
   */
  private _sortedModulesCache;
  /**
   * @private
   * @type {NormalizedPluginOptions}
   */
  private options;
  /**
   * @private
   * @type {RuntimeOptions}
   */
  private runtimeOptions;
  /**
   * @param {Compiler} compiler
   */
  apply(compiler: Compiler): void;
  /**
   * @private
   * @param {Chunk} chunk
   * @param {ChunkGraph} chunkGraph
   * @returns {Iterable<Module>}
   */
  private getChunkModules;
  /**
   * @private
   * @param {Compilation} compilation
   * @param {Chunk} chunk
   * @param {CssModule[]} modules
   * @param {Compilation["requestShortener"]} requestShortener
   * @returns {Set<CssModule>}
   */
  private sortModules;
  /**
   * @private
   * @param {Compiler} compiler
   * @param {Compilation} compilation
   * @param {Chunk} chunk
   * @param {CssModule[]} modules
   * @param {Compiler["requestShortener"]} requestShortener
   * @param {string} filenameTemplate
   * @param {Parameters<Exclude<Required<Configuration>['output']['filename'], string | undefined>>[0]} pathData
   * @returns {Source}
   */
  private renderContentAsset;
}
declare namespace MiniCssExtractPlugin {
  export {
    pluginName,
    pluginSymbol,
    loader,
    Schema,
    Compiler,
    Compilation,
    ChunkGraph,
    Chunk,
    ChunkGroup,
    Module,
    Dependency,
    Source,
    Configuration,
    WebpackError,
    AssetInfo,
    LoaderDependency,
    LoaderOptions,
    PluginOptions,
    NormalizedPluginOptions,
    RuntimeOptions,
    TODO,
    CssModule,
    CssModuleDependency,
    CssModuleConstructor,
    CssDependency,
    CssDependencyOptions,
    CssDependencyConstructor,
    VarNames,
    MiniCssExtractPluginCompilationHooks,
  };
}
type Compiler = import("webpack").Compiler;
type CssModuleConstructor = new (dependency: CssModuleDependency) => CssModule;
type CssDependencyConstructor = new (
  loaderDependency: CssDependencyOptions,
  context: string | null,
  identifierIndex: number
) => CssDependency;
type Compilation = import("webpack").Compilation;
type MiniCssExtractPluginCompilationHooks = {
  beforeTagInsert: import("tapable").SyncWaterfallHook<
    [string, VarNames],
    string
  >;
  linkPreload: SyncWaterfallHook<[string, Chunk]>;
  linkPrefetch: SyncWaterfallHook<[string, Chunk]>;
};
type PluginOptions = {
  filename?: Required<Configuration>["output"]["filename"];
  chunkFilename?: Required<Configuration>["output"]["chunkFilename"];
  ignoreOrder?: boolean | undefined;
  insert?: string | ((linkTag: HTMLLinkElement) => void) | undefined;
  attributes?: Record<string, string> | undefined;
  linkType?: string | false | undefined;
  runtime?: boolean | undefined;
  experimentalUseImportModule?: boolean | undefined;
};
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
/** @typedef {import("webpack").Compiler} Compiler */
/** @typedef {import("webpack").Compilation} Compilation */
/** @typedef {import("webpack").ChunkGraph} ChunkGraph */
/** @typedef {import("webpack").Chunk} Chunk */
/** @typedef {Parameters<import("webpack").Chunk["isInGroup"]>[0]} ChunkGroup */
/** @typedef {import("webpack").Module} Module */
/** @typedef {import("webpack").Dependency} Dependency */
/** @typedef {import("webpack").sources.Source} Source */
/** @typedef {import("webpack").Configuration} Configuration */
/** @typedef {import("webpack").WebpackError} WebpackError */
/** @typedef {import("webpack").AssetInfo} AssetInfo */
/** @typedef {import("./loader.js").Dependency} LoaderDependency */
/**
 * @typedef {Object} LoaderOptions
 * @property {string | ((resourcePath: string, rootContext: string) => string)} [publicPath]
 * @property {boolean} [emit]
 * @property {boolean} [esModule]
 * @property {string} [layer]
 * @property {boolean} [defaultExport]
 */
/**
 * @typedef {Object} PluginOptions
 * @property {Required<Configuration>['output']['filename']} [filename]
 * @property {Required<Configuration>['output']['chunkFilename']} [chunkFilename]
 * @property {boolean} [ignoreOrder]
 * @property {string | ((linkTag: HTMLLinkElement) => void)} [insert]
 * @property {Record<string, string>} [attributes]
 * @property {string | false | 'text/css'} [linkType]
 * @property {boolean} [runtime]
 * @property {boolean} [experimentalUseImportModule]
 */
/**
 * @typedef {Object} NormalizedPluginOptions
 * @property {Required<Configuration>['output']['filename']} filename
 * @property {Required<Configuration>['output']['chunkFilename']} [chunkFilename]
 * @property {boolean} ignoreOrder
 * @property {string | ((linkTag: HTMLLinkElement) => void)} [insert]
 * @property {Record<string, string>} [attributes]
 * @property {string | false | 'text/css'} [linkType]
 * @property {boolean} runtime
 * @property {boolean} [experimentalUseImportModule]
 */
/**
 * @typedef {Object} RuntimeOptions
 * @property {string | ((linkTag: HTMLLinkElement) => void) | undefined} insert
 * @property {string | false | 'text/css'} linkType
 * @property {Record<string, string> | undefined} attributes
 */
/** @typedef {any} TODO */
declare const pluginName: "mini-css-extract-plugin";
declare const pluginSymbol: unique symbol;
declare var loader: string;
type Schema = import("schema-utils/declarations/validate").Schema;
type ChunkGraph = import("webpack").ChunkGraph;
type Chunk = import("webpack").Chunk;
type ChunkGroup = Parameters<import("webpack").Chunk["isInGroup"]>[0];
type Module = import("webpack").Module;
type Dependency = import("webpack").Dependency;
type Source = import("webpack").sources.Source;
type Configuration = import("webpack").Configuration;
type WebpackError = import("webpack").WebpackError;
type AssetInfo = import("webpack").AssetInfo;
type LoaderDependency = import("./loader.js").Dependency;
type LoaderOptions = {
  publicPath?:
    | string
    | ((resourcePath: string, rootContext: string) => string)
    | undefined;
  emit?: boolean | undefined;
  esModule?: boolean | undefined;
  layer?: string | undefined;
  defaultExport?: boolean | undefined;
};
type NormalizedPluginOptions = {
  filename: Required<Configuration>["output"]["filename"];
  chunkFilename?: Required<Configuration>["output"]["chunkFilename"];
  ignoreOrder: boolean;
  insert?: string | ((linkTag: HTMLLinkElement) => void) | undefined;
  attributes?: Record<string, string> | undefined;
  linkType?: string | false | undefined;
  runtime: boolean;
  experimentalUseImportModule?: boolean | undefined;
};
type RuntimeOptions = {
  insert: string | ((linkTag: HTMLLinkElement) => void) | undefined;
  linkType: string | false | "text/css";
  attributes: Record<string, string> | undefined;
};
type TODO = any;
type CssModule = import("webpack").Module & {
  content: Buffer;
  media?: string | undefined;
  sourceMap?: Buffer | undefined;
  supports?: string | undefined;
  layer?: string | undefined;
  assets?:
    | {
        [key: string]: any;
      }
    | undefined;
  assetsInfo?: Map<string, import("webpack").AssetInfo> | undefined;
};
type CssModuleDependency = {
  context: string | null;
  identifier: string;
  identifierIndex: number;
  content: Buffer;
  sourceMap?: Buffer | undefined;
  media?: string | undefined;
  supports?: string | undefined;
  layer?: TODO;
  assetsInfo?: Map<string, import("webpack").AssetInfo> | undefined;
  assets?:
    | {
        [key: string]: any;
      }
    | undefined;
};
type CssDependency = Dependency & CssModuleDependency;
type CssDependencyOptions = Omit<LoaderDependency, "context">;
type VarNames = {
  tag: string;
  chunkId: string;
  href: string;
  resolve: string;
  reject: string;
};
import { SyncWaterfallHook } from "tapable";


code:
export = loader;
/**
 * @this {import("webpack").LoaderContext<LoaderOptions>}
 * @param {string} content
 */
declare function loader(
  this: import("webpack").LoaderContext<MiniCssExtractPlugin.LoaderOptions>,
  content: string
): string | undefined;
declare namespace loader {
  export {
    pitch,
    hotLoader,
    Schema,
    Compiler,
    Compilation,
    Chunk,
    Module,
    Source,
    AssetInfo,
    NormalModule,
    LoaderOptions,
    Locals,
    TODO,
    Dependency,
  };
}
import MiniCssExtractPlugin = require("./index");
/**
 * @this {import("webpack").LoaderContext<LoaderOptions>}
 * @param {string} request
 */
declare function pitch(
  this: import("webpack").LoaderContext<MiniCssExtractPlugin.LoaderOptions>,
  request: string
): void;
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
/** @typedef {import("webpack").Compiler} Compiler */
/** @typedef {import("webpack").Compilation} Compilation */
/** @typedef {import("webpack").Chunk} Chunk */
/** @typedef {import("webpack").Module} Module */
/** @typedef {import("webpack").sources.Source} Source */
/** @typedef {import("webpack").AssetInfo} AssetInfo */
/** @typedef {import("webpack").NormalModule} NormalModule */
/** @typedef {import("./index.js").LoaderOptions} LoaderOptions */
/** @typedef {{ [key: string]: string | function }} Locals */
/** @typedef {any} TODO */
/**
 * @typedef {Object} Dependency
 * @property {string} identifier
 * @property {string | null} context
 * @property {Buffer} content
 * @property {string} media
 * @property {string} [supports]
 * @property {string} [layer]
 * @property {Buffer} [sourceMap]
 */
/**
 * @param {string} content
 * @param {{ loaderContext: import("webpack").LoaderContext<LoaderOptions>, options: LoaderOptions, locals: Locals | undefined }} context
 * @returns {string}
 */
declare function hotLoader(
  content: string,
  context: {
    loaderContext: import("webpack").LoaderContext<LoaderOptions>;
    options: LoaderOptions;
    locals: Locals | undefined;
  }
): string;
type Schema = import("schema-utils/declarations/validate").Schema;
type Compiler = import("webpack").Compiler;
type Compilation = import("webpack").Compilation;
type Chunk = import("webpack").Chunk;
type Module = import("webpack").Module;
type Source = import("webpack").sources.Source;
type AssetInfo = import("webpack").AssetInfo;
type NormalModule = import("webpack").NormalModule;
type LoaderOptions = import("./index.js").LoaderOptions;
type Locals = {
  [key: string]: string | Function;
};
type TODO = any;
type Dependency = {
  identifier: string;
  context: string | null;
  content: Buffer;
  media: string;
  supports?: string | undefined;
  layer?: string | undefined;
  sourceMap?: Buffer | undefined;
};


code:
export type Compilation = import("webpack").Compilation;
export type Module = import("webpack").Module;
export type LoaderContext = import("webpack").LoaderContext<any>;
/** @typedef {import("webpack").Compilation} Compilation */
/** @typedef {import("webpack").Module} Module */
/** @typedef {import("webpack").LoaderContext<any>} LoaderContext */
/**
 * @returns {boolean}
 */
export function trueFn(): boolean;
/**
 * @param {Compilation} compilation
 * @param {string | number} id
 * @returns {null | Module}
 */
export function findModuleById(
  compilation: Compilation,
  id: string | number
): null | Module;
/**
 * @param {LoaderContext} loaderContext
 * @param {string | Buffer} code
 * @param {string} filename
 * @returns {object}
 */
export function evalModuleCode(
  loaderContext: LoaderContext,
  code: string | Buffer,
  filename: string
): object;
/**
 * @param {Module} a
 * @param {Module} b
 * @returns {0 | 1 | -1}
 */
export function compareModulesByIdentifier(a: Module, b: Module): 0 | 1 | -1;
export const MODULE_TYPE: "css/mini-extract";
export const AUTO_PUBLIC_PATH: "__mini_css_extract_plugin_public_path_auto__";
export const ABSOLUTE_PUBLIC_PATH: "webpack:///mini-css-extract-plugin/";
export const BASE_URI: "webpack://";
export const SINGLE_DOT_PATH_SEGMENT: "__mini_css_extract_plugin_single_dot_path_segment__";
/**
 * @param {LoaderContext} loaderContext
 * @param {string} request
 * @returns {string}
 */
export function stringifyRequest(
  loaderContext: LoaderContext,
  request: string
): string;
/**
 *
 * @param {string | function} value
 * @returns {string}
 */
export function stringifyLocal(value: string | Function): string;
/**
 * @param {string} filename
 * @param {string} outputPath
 * @param {boolean} enforceRelative
 * @returns {string}
 */
export function getUndoPath(
  filename: string,
  outputPath: string,
  enforceRelative: boolean
): string;
/**
 * @param {Record<string|number, boolean>} map value map
 * @returns {boolean|(function(string): string)} true/false, when unconditionally true/false, or a template function to determine the value at runtime
 */
export function compileBooleanMatcher(
  map: Record<string | number, boolean>
): boolean | ((arg0: string) => string);


code:
module.exports = assert;

function assert(val, msg) {
  if (!val)
    throw new Error(msg || 'Assertion failed');
}

assert.equal = function assertEqual(l, r, msg) {
  if (l != r)
    throw new Error(msg || ('Assertion failed: ' + l + ' != ' + r));
};


minimalistic-assert

description: minimalistic-assert ===

test: echo "Error: no test specified" && exit 1

type: git

license: ISC


code:
minimalistic-assert
===

very minimalistic assert module.


code:
# minimatch

A minimal matching utility.

[![Build Status](https://travis-ci.org/isaacs/minimatch.svg?branch=master)](http://travis-ci.org/isaacs/minimatch)

This is the matching library used internally by npm.

It works by converting glob expressions into JavaScript `RegExp`
objects.

## Usage

```javascript
var minimatch = require("minimatch")

minimatch("bar.foo", "*.foo") // true!
minimatch("bar.foo", "*.bar") // false!
minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
```

## Features

Supports these glob features:

* Brace Expansion
* Extended glob matching
* "Globstar" `**` matching

See:

* `man sh`
* `man bash`
* `man 3 fnmatch`
* `man 5 gitignore`

## Minimatch Class

Create a minimatch object by instantiating the `minimatch.Minimatch` class.

```javascript
var Minimatch = require("minimatch").Minimatch
var mm = new Minimatch(pattern, options)
```

### Properties

* `pattern` The original pattern the minimatch object represents.
* `options` The options supplied to the constructor.
* `set` A 2-dimensional array of regexp or string expressions.
  Each row in the
  array corresponds to a brace-expanded pattern.  Each item in the row
  corresponds to a single path-part.  For example, the pattern
  `{a,b/c}/d` would expand to a set of patterns like:

        [ [ a, d ]
        , [ b, c, d ] ]

    If a portion of the pattern doesn't have any "magic" in it
    (that is, it's something like `"foo"` rather than `fo*o?`), then it
    will be left as a string rather than converted to a regular
    expression.

* `regexp` Created by the `makeRe` method.  A single regular expression
  expressing the entire pattern.  This is useful in cases where you wish
  to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
* `negate` True if the pattern is negated.
* `comment` True if the pattern is a comment.
* `empty` True if the pattern is `""`.

### Methods

* `makeRe` Generate the `regexp` member if necessary, and return it.
  Will return `false` if the pattern is invalid.
* `match(fname)` Return true if the filename matches the pattern, or
  false otherwise.
* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
  filename, and match it against a single row in the `regExpSet`.  This
  method is mainly for internal use, but is exposed so that it can be
  used by a glob-walker that needs to avoid excessive filesystem calls.

All other methods are internal, and will be called as necessary.

### minimatch(path, pattern, options)

Main export.  Tests a path against the pattern using the options.

```javascript
var isJS = minimatch(file, "*.js", { matchBase: true })
```

### minimatch.filter(pattern, options)

Returns a function that tests its
supplied argument, suitable for use with `Array.filter`.  Example:

```javascript
var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
```

### minimatch.match(list, pattern, options)

Match against the list of
files, in the style of fnmatch or glob.  If nothing is matched, and
options.nonull is set, then return a list containing the pattern itself.

```javascript
var javascripts = minimatch.match(fileList, "*.js", {matchBase: true}))
```

### minimatch.makeRe(pattern, options)

Make a regular expression object from the pattern.

## Options

All options are `false` by default.

### debug

Dump a ton of stuff to stderr.

### nobrace

Do not expand `{a,b}` and `{1..3}` brace sets.

### noglobstar

Disable `**` matching against multiple folder names.

### dot

Allow patterns to match filenames starting with a period, even if
the pattern does not explicitly have a period in that spot.

Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
is set.

### noext

Disable "extglob" style patterns like `+(a|b)`.

### nocase

Perform a case-insensitive match.

### nonull

When a match is not found by `minimatch.match`, return a list containing
the pattern itself if this option is set.  When not set, an empty list
is returned if there are no matches.

### matchBase

If set, then patterns without slashes will be matched
against the basename of the path if it contains slashes.  For example,
`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.

### nocomment

Suppress the behavior of treating `#` at the start of a pattern as a
comment.

### nonegate

Suppress the behavior of treating a leading `!` character as negation.

### flipNegate

Returns from negate expressions the same as if they were not negated.
(Ie, true on a hit, false on a miss.)

### partial

Compare a partial path to a pattern.  As long as the parts of the path that
are present are not contradicted by the pattern, it will be treated as a
match.  This is useful in applications where you're walking through a
folder structure, and don't yet have the full path, but want to ensure that
you do not walk down paths that can never be a match.

For example,

```js
minimatch('/a/b', '/a/*/c/d', { partial: true })  // true, might be /a/b/c/d
minimatch('/a/b', '/**/d', { partial: true })     // true, might be /a/b/.../d
minimatch('/x/y/z', '/a/**/z', { partial: true }) // false, because x !== a
```

### allowWindowsEscape

Windows path separator `\` is by default converted to `/`, which
prohibits the usage of `\` as a escape character. This flag skips that
behavior and allows using the escape character.

## Comparisons to other fnmatch/glob implementations

While strict compliance with the existing standards is a worthwhile
goal, some discrepancies exist between minimatch and other
implementations, and are intentional.

If the pattern starts with a `!` character, then it is negated.  Set the
`nonegate` flag to suppress this behavior, and treat leading `!`
characters normally.  This is perhaps relevant if you wish to start the
pattern with a negative extglob pattern like `!(a|B)`.  Multiple `!`
characters at the start of a pattern will negate the pattern multiple
times.

If a pattern starts with `#`, then it is treated as a comment, and
will not match anything.  Use `\#` to match a literal `#` at the
start of a line, or set the `nocomment` flag to suppress this behavior.

The double-star character `**` is supported by default, unless the
`noglobstar` flag is set.  This is supported in the manner of bsdglob
and bash 4.1, where `**` only has special significance if it is the only
thing in a path part.  That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.

If an escaped pattern has no matches, and the `nonull` flag is set,
then minimatch.match returns the pattern as-provided, rather than
interpreting the character escapes.  For example,
`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`.  This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.

If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern.  Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity.  Since those two are valid, matching proceeds.