code:
/**
 * A utility type for choosing between synchronous and asynchronous return
 * values.
 *
 * This is used as the return value for plugins like {@link CustomFunction},
 * {@link Importer}, and {@link FileImporter} so that TypeScript enforces that
 * asynchronous plugins are only passed to {@link compileAsync} and {@link
 * compileStringAsync}, not {@link compile} or {@link compileString}.
 *
 * @typeParam sync - If this is `'sync'`, this can only be a `T`. If it's
 * `'async'`, this can be either a `T` or a `Promise<T>`.
 *
 * @category Other
 */
export type PromiseOr<T, sync extends 'sync' | 'async'> = sync extends 'async'
  ? T | Promise<T>
  : T;


code:
import {List, OrderedMap} from 'immutable';

import {Value} from './index';
import {SassList, ListSeparator} from './list';

/**
 * Sass's [argument list
 * type](https://sass-lang.com/documentation/values/lists#argument-lists).
 *
 * An argument list comes from a rest argument. It's distinct from a normal
 * {@link SassList} in that it may contain a keyword map as well as the
 * positional arguments.
 *
 * @category Custom Function
 */
export class SassArgumentList extends SassList {
  /**
   * Creates a new argument list.
   *
   * @param contents - The positional arguments that make up the primary
   * contents of the list. This may be either a plain JavaScript array or an
   * immutable {@link List} from the [`immutable`
   * package](https://immutable-js.com/).
   *
   * @param keywords - The keyword arguments attached to this argument list,
   * whose names should exclude `$`. This can be either a plain JavaScript
   * object with argument names as fields, or an immutable {@link OrderedMap}
   * from the [`immutable` package](https://immutable-js.com/)
   *
   * @param separator - The separator for this list. Defaults to `','`.
   */
  constructor(
    contents: Value[] | List<Value>,
    keywords: Record<string, Value> | OrderedMap<string, Value>,
    separator?: ListSeparator
  );

  /**
   * The keyword arguments attached to this argument list.
   *
   * The argument names don't include `$`.
   *
   * @returns An immutable {@link OrderedMap} from the [`immutable`
   * package](https://immutable-js.com/).
   */
  get keywords(): OrderedMap<string, Value>;
}

license: mit


code:
import {Value} from './index';

/**
 * Sass's [`true` value](https://sass-lang.com/documentation/values/booleans).
 *
 * @category Custom Function
 */
export const sassTrue: SassBoolean;

/**
 * Sass's [`false` value](https://sass-lang.com/documentation/values/booleans).
 *
 * @category Custom Function
 */
export const sassFalse: SassBoolean;

/**
 * Sass's [boolean type](https://sass-lang.com/documentation/values/booleans).
 *
 * @category Custom Function
 */
export class SassBoolean extends Value {
  private constructor();

  /**
   * Whether this value is `true` or `false`.
   */
  get value(): boolean;
}


code:
import {List, ValueObject} from 'immutable';
import {Value, SassNumber, SassString} from './index';

/**
 * The type of values that can be arguments to a {@link SassCalculation}.
 * @category Custom Function
 * */
export type CalculationValue =
  | SassNumber
  | SassCalculation
  | SassString
  | CalculationOperation
  | CalculationInterpolation;

/**
 * Sass's [calculation
 * type](https://sass-lang.com/documentation/values/calculations).
 *
 * Note: in the JS API calculations are not simplified eagerly. This also means
 * that unsimplified calculations are not equal to the numbers they would be
 * simplified to.
 *
 * @category Custom Function
 */
export class SassCalculation extends Value {
  /**
   * Creates a value that represents `calc(argument)`.
   *
   * @throws `Error` if `argument` is a quoted {@link SassString}
   * @returns A calculation with the name `calc` and `argument` as its single
   * argument.
   */
  static calc(argument: CalculationValue): SassCalculation;

  /**
   * Creates a value that represents `min(arguments...)`.
   *
   * @throws `Error` if `arguments` contains a quoted {@link SassString}
   * @returns A calculation with the name `min` and `arguments` as its
   * arguments.
   */
  static min(
    arguments: CalculationValue[] | List<CalculationValue>
  ): SassCalculation;

  /**
   * Creates a value that represents `max(arguments...)`.
   *
   * @throws `Error` if `arguments` contains a quoted {@link SassString}
   * @returns A calculation with the name `max` and `arguments` as its
   * arguments.
   */
  static max(
    arguments: CalculationValue[] | List<CalculationValue>
  ): SassCalculation;

  /**
   * Creates a value that represents `clamp(value, min, max)`.
   *
   * @throws `Error` if any of `value`, `min`, or `max` are  a quoted
   * {@link SassString}.
   * @throws `Error` if `value` is undefined and `max` is not undefined.
   * @throws `Error` if either `value` or `max` is undefined and neither `min`
     nor `value` is a {@link SassString} or {@link CalculationInterpolation}.
     @returns A calculation with the name `clamp` and `min`, `value`, and `max`
     as it's arguments, excluding any arguments that are undefined.
   */
  static clamp(
    min: CalculationValue,
    value?: CalculationValue,
    max?: CalculationValue
  ): SassCalculation;

  /** Returns the calculation's `name` field. */
  get name(): string;

  /** Returns a list of the calculation's `arguments` */
  get arguments(): List<CalculationValue>;
}

/**
 * The set of possible operators in a Sass calculation.
 * @category Custom Function
 */
export type CalculationOperator = '+' | '-' | '*' | '/';

/**
 * A binary operation that can appear in a {@link SassCalculation}.
 * @category Custom Function
 */
export class CalculationOperation implements ValueObject {
  /**
   * Creates a Sass CalculationOperation with the given `operator`, `left`, and
   * `right` values.
   * @throws `Error` if `left` or `right` are quoted {@link SassString}s.
   */
  constructor(
    operator: CalculationOperator,
    left: CalculationValue,
    right: CalculationValue
  );

  /** Returns the operation's `operator` field. */
  get operator(): CalculationOperator;

  /** Returns the operation's `left` field. */
  get left(): CalculationValue;

  /** Returns the operation's `right` field. */
  get right(): CalculationValue;

  equals(other: unknown): boolean;

  hashCode(): number;
}

/**
 * A string injected into a {@link SassCalculation} using interpolation. Unlike
 * unquoted strings, interpolations are always surrounded in parentheses when
 * they appear in {@link CalculationOperation}s.
 * @category Custom Function
 */
export class CalculationInterpolation implements ValueObject {
  /**
   * Creates a Sass CalculationInterpolation with the given `value`.
   */
  constructor(value: string);

  /**
   * Returns the interpolation's `value` field.
   */
  get value(): string;

  equals(other: unknown): boolean;

  hashCode(): number;
}


code:
import {Value} from './index';

/**
 * Sass's [color type](https://sass-lang.com/documentation/values/colors).
 *
 * No matter what representation was originally used to create this color, all
 * of its channels are accessible.
 *
 * @category Custom Function
 */
export class SassColor extends Value {
  /**
   * Creates an RGB color.
   *
   * **Only** `undefined` should be passed to indicate a missing `alpha`. If
   * `null` is passed instead, it will be treated as a [missing component] in
   * future versions of Dart Sass. See [breaking changes] for details.
   *
   * [missing component]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#missing_color_components
   * [breaking changes]: /documentation/breaking-changes/null-alpha
   *
   * @throws `Error` if `red`, `green`, and `blue` aren't between `0` and
   * `255`, or if `alpha` isn't between `0` and `1`.
   */
  constructor(options: {
    red: number;
    green: number;
    blue: number;
    alpha?: number;
  });

  /**
   * Creates an HSL color.
   *
   * **Only** `undefined` should be passed to indicate a missing `alpha`. If
   * `null` is passed instead, it will be treated as a [missing component] in
   * future versions of Dart Sass. See [breaking changes] for details.
   *
   * [missing component]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#missing_color_components
   * [breaking changes]: /documentation/breaking-changes/null-alpha
   *
   * @throws `Error` if `saturation` or `lightness` aren't between `0` and
   * `100`, or if `alpha` isn't between `0` and `1`.
   */
  constructor(options: {
    hue: number;
    saturation: number;
    lightness: number;
    alpha?: number;
  });

  /**
   * Creates an HWB color.
   *
   * **Only** `undefined` should be passed to indicate a missing `alpha`. If
   * `null` is passed instead, it will be treated as a [missing component] in
   * future versions of Dart Sass. See [breaking changes] for details.
   *
   * [missing component]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#missing_color_components
   * [breaking changes]: /documentation/breaking-changes/null-alpha
   *
   * @throws `Error` if `whiteness` or `blackness` aren't between `0` and `100`,
   * or if `alpha` isn't between `0` and `1`.
   */
  constructor(options: {
    hue: number;
    whiteness: number;
    blackness: number;
    alpha?: number;
  });

  /** This color's red channel, between `0` and `255`. */
  get red(): number;

  /** This color's green channel, between `0` and `255`. */
  get green(): number;

  /** This color's blue channel, between `0` and `255`. */
  get blue(): number;

  /** This color's hue, between `0` and `360`. */
  get hue(): number;

  /** This color's saturation, between `0` and `100`. */
  get saturation(): number;

  /** This color's lightness, between `0` and `100`. */
  get lightness(): number;

  /** This color's whiteness, between `0` and `100`. */
  get whiteness(): number;

  /** This color's blackness, between `0` and `100`. */
  get blackness(): number;

  /** This color's alpha channel, between `0` and `1`. */
  get alpha(): number;

  /**
   * Changes one or more of this color's RGB channels and returns the result.
   */
  change(options: {
    red?: number;
    green?: number;
    blue?: number;
    alpha?: number;
  }): SassColor;

  /**
   * Changes one or more of this color's HSL channels and returns the result.
   */
  change(options: {
    hue?: number;
    saturation?: number;
    lightness?: number;
    alpha?: number;
  }): SassColor;

  /**
   * Changes one or more of this color's HWB channels and returns the result.
   */
  change(options: {
    hue?: number;
    whiteness?: number;
    blackness?: number;
    alpha?: number;
  }): SassColor;
}


code:
import {Value} from './index';

/**
 * Sass's [function type](https://sass-lang.com/documentation/values/functions).
 *
 * **Heads up!** Although first-class Sass functions can be processed by custom
 * functions, there's no way to invoke them outside of a Sass stylesheet.
 *
 * @category Custom Function
 */
export class SassFunction extends Value {
  /**
   * Creates a new first-class function that can be invoked using
   * [`meta.call()`](https://sass-lang.com/documentation/modules/meta#call).
   *
   * @param signature - The function signature, like you'd write for the
   * [`@function rule`](https://sass-lang.com/documentation/at-rules/function).
   * @param callback - The callback that's invoked when this function is called,
   * just like for a {@link CustomFunction}.
   */
  constructor(signature: string, callback: (args: Value[]) => Value);
}


code:
import {List, ValueObject} from 'immutable';

import {SassBoolean} from './boolean';
import {SassCalculation} from './calculation';
import {SassColor} from './color';
import {SassFunction} from './function';
import {ListSeparator} from './list';
import {SassMap} from './map';
import {SassMixin} from './mixin';
import {SassNumber} from './number';
import {SassString} from './string';

export {SassArgumentList} from './argument_list';
export {SassBoolean, sassTrue, sassFalse} from './boolean';
export {
  SassCalculation,
  CalculationValue,
  CalculationOperator,
  CalculationOperation,
  CalculationInterpolation,
} from './calculation';
export {SassColor} from './color';
export {SassFunction} from './function';
export {SassList, ListSeparator} from './list';
export {SassMap} from './map';
export {SassMixin} from './mixin';
export {SassNumber} from './number';
export {SassString} from './string';

/**
 * Sass's [`null` value](https://sass-lang.com/documentation/values/null).
 *
 * @category Custom Function
 */
export const sassNull: Value;

/**
 * The abstract base class of Sass's value types.
 *
 * This is passed to and returned by {@link CustomFunction}s, which are passed
 * into the Sass implementation using {@link Options.functions}.
 *
 * @category Custom Function
 */
export abstract class Value implements ValueObject {
  protected constructor();

  /**
   * This value as a list.
   *
   * All SassScript values can be used as lists. Maps count as lists of pairs,
   * and all other values count as single-value lists.
   *
   * @returns An immutable {@link List} from the [`immutable`
   * package](https://immutable-js.com/).
   */
  get asList(): List<Value>;

  /**
   * Whether this value as a list has brackets.
   *
   * All SassScript values can be used as lists. Maps count as lists of pairs,
   * and all other values count as single-value lists.
   */
  get hasBrackets(): boolean;

  /**
   * Whether the value counts as `true` in an `@if` statement and other
   * contexts.
   */
  get isTruthy(): boolean;

  /**
   * Returns JavaScript's `null` value if this is {@link sassNull}, and returns
   * `this` otherwise.
   */
  get realNull(): null | Value;

  /**
   * The separator for this value as a list.
   *
   * All SassScript values can be used as lists. Maps count as lists of pairs,
   * and all other values count as single-value lists.
   */
  get separator(): ListSeparator;

  /**
   * Converts `sassIndex` into a JavaScript-style index into the list returned
   * by {@link asList}.
   *
   * Sass indexes are one-based, while JavaScript indexes are zero-based. Sass
   * indexes may also be negative in order to index from the end of the list.
   *
   * @param sassIndex - The Sass-style index into this as a list.
   * @param name - The name of the function argument `sassIndex` came from
   * (without the `$`) if it came from an argument. Used for error reporting.
   * @throws `Error` If `sassIndex` isn't a number, if that number isn't an
   * integer, or if that integer isn't a valid index for {@link asList}.
   */
  sassIndexToListIndex(sassIndex: Value, name?: string): number;

  /**
   * Returns the value at index `index` in this value as a list, or `undefined`
   * if `index` isn't valid for this list.
   *
   * All SassScript values can be used as lists. Maps count as lists of pairs,
   * and all other values count as single-value lists.
   *
   * This is a shorthand for `this.asList.get(index)`, although it may be more
   * efficient in some cases.
   *
   * **Heads up!** This method uses the same indexing conventions as the
   * `immutable` package: unlike Sass the index of the first element is 0, but
   * like Sass negative numbers index from the end of the list.
   */
  get(index: number): Value | undefined;

  /**
   * Throws if `this` isn't a {@link SassBoolean}.
   *
   * **Heads up!** Functions should generally use {@link isTruthy} rather than
   * requiring a literal boolean.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertBoolean(name?: string): SassBoolean;

  /**
   * Throws if `this` isn't a {@link SassCalculation}.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertCalculation(name?: string): SassCalculation;

  /**
   * Throws if `this` isn't a {@link SassColor}.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertColor(name?: string): SassColor;

  /**
   * Throws if `this` isn't a {@link SassFunction}.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertFunction(name?: string): SassFunction;

  /**
   * Throws if `this` isn't a {@link SassMap}.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertMap(name?: string): SassMap;

  /**
   * Throws if `this` isn't a {@link SassMixin}.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertMixin(name?: string): SassMixin;

  /**
   * Throws if `this` isn't a {@link SassNumber}.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertNumber(name?: string): SassNumber;

  /**
   * Throws if `this` isn't a {@link SassString}.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertString(name?: string): SassString;

  /**
   * Returns `this` as a map if it counts as one (empty lists count as empty
   * maps) or `null` if it doesn't.
   */
  tryMap(): SassMap | null;

  /** Returns whether `this` represents the same value as `other`. */
  equals(other: Value): boolean;

  /** Returns a hash code that can be used to store `this` in a hash map. */
  hashCode(): number;

  /** @hidden */
  toString(): string;
}


code:
import {List} from 'immutable';

import {Value} from './index';

/**
 * Possible separators used by Sass lists. The special separator `null` is only
 * used for lists with fewer than two elements, and indicates that the separator
 * has not yet been decided for this list.
 *
 * @category Custom Function
 */
export type ListSeparator = ',' | '/' | ' ' | null;

/**
 * Sass's [list type](https://sass-lang.com/documentation/values/lists).
 *
 * @category Custom Function
 */
export class SassList extends Value {
  /**
   * Creates a new list.
   *
   * @param contents - The contents of the list. This may be either a plain
   * JavaScript array or an immutable {@link List} from the [`immutable`
   * package](https://immutable-js.com/).
   *
   * @param options.separator - The separator to use between elements of this
   * list. Defaults to `','`.
   *
   * @param options.brackets - Whether the list has square brackets. Defaults to
   * `false`.
   */
  constructor(
    contents: Value[] | List<Value>,
    options?: {
      separator?: ListSeparator;
      brackets?: boolean;
    }
  );

  /**
   * Creates an empty list.
   *
   * @param options.separator - The separator to use between elements of this
   * list. Defaults to `','`.
   *
   * @param options.brackets - Whether the list has square brackets. Defaults to
   * `false`.
   */
  constructor(options?: {separator?: ListSeparator; brackets?: boolean});

  /** @hidden */
  get separator(): ListSeparator;
}


code:
import {OrderedMap} from 'immutable';

import {SassList} from './list';
import {Value} from './index';

/**
 * Sass's [map type](https://sass-lang.com/documentation/values/maps).
 *
 * @category Custom Function
 */
export class SassMap extends Value {
  /**
   * Creates a new map.
   *
   * @param contents - The contents of the map. This is an immutable
   * `OrderedMap` from the [`immutable` package](https://immutable-js.com/).
   * Defaults to an empty map.
   */
  constructor(contents?: OrderedMap<Value, Value>);

  /**
   * Returns the contents of this map as an immutable {@link OrderedMap} from the
   * [`immutable` package](https://immutable-js.com/).
   */
  get contents(): OrderedMap<Value, Value>;

  /**
   * Returns the value associated with `key` in this map, or `undefined` if
   * `key` isn't in the map.
   *
   * This is a shorthand for `this.contents.get(key)`, although it may be more
   * efficient in some cases.
   */
  get(key: Value): Value | undefined;

  /** Inherited from {@link Value.get}. */
  get(index: number): SassList | undefined;

  /** @hidden */
  tryMap(): SassMap;
}


code:
import {Value} from './index';

/**
 * Sass's [mixin type](https://sass-lang.com/documentation/values/mixins).
 *
 * @category Custom Function
 */
export class SassMixin extends Value {
  /**
   * It is not possible to construct a Sass mixin outside of Sass. Attempting to
   * construct one will throw an exception.
   */
  constructor();
}


code:
import {List} from 'immutable';

import {Value} from './index';

/**
 * Sass's [number type](https://sass-lang.com/documentation/values/numbers).
 *
 * @category Custom Function
 */
export class SassNumber extends Value {
  /**
   * Creates a new number with more complex units than just a single numerator.
   *
   * Upon construction, any compatible numerator and denominator units are
   * simplified away according to the conversion factor between them.
   *
   * @param value - The number's numeric value.
   *
   * @param unit - If this is a string, it's used as the single numerator unit
   * for the number.
   *
   * @param unit.numeratorUnits - If passed, these are the numerator units to
   * use for the number. This may be either a plain JavaScript array or an
   * immutable {@link List} from the [`immutable`
   * package](https://immutable-js.com/).
   *
   * @param unit.denominatorUnits - If passed, these are the denominator units
   * to use for the number. This may be either a plain JavaScript array or an
   * immutable {@link List} from the [`immutable`
   * package](https://immutable-js.com/).
   */
  constructor(
    value: number,
    unit?:
      | string
      | {
          numeratorUnits?: string[] | List<string>;
          denominatorUnits?: string[] | List<string>;
        }
  );

  /** This number's numeric value. */
  get value(): number;

  /** Whether {@link value} is an integer according to Sass's equality logic. */
  get isInt(): boolean;

  /**
   * If {@link value} is an integer according to {@link isInt}, returns {@link
   * value} rounded to that integer. If it's not an integer, returns `null`.
   */
  get asInt(): number | null;

  /**
   * This number's numerator units as an immutable {@link List} from the
   * [`immutable` package](https://immutable-js.com/).
   */
  get numeratorUnits(): List<string>;

  /**
   * This number's denominator units as an immutable {@link List} from the
   * [`immutable` package](https://immutable-js.com/).
   */
  get denominatorUnits(): List<string>;

  /** Whether this number has any numerator or denominator units. */
  get hasUnits(): boolean;

  /**
   * If {@link value} is an integer according to {@link isInt}, returns it
   * rounded to that integer. Otherwise, throws an error.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertInt(name?: string): number;

  /**
   * Returns {@link value} if it's within `min` and `max`. If {@link value} is
   * equal to `min` or `max` according to Sass's equality, returns `min` or
   * `max` respectively. Otherwise, throws an error.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertInRange(min: number, max: number, name?: string): number;

  /**
   * If this number has no units, returns it. Otherwise, throws an error.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertNoUnits(name?: string): SassNumber;

  /**
   * If this number has `unit` as its only unit (and as a numerator), returns
   * this number. Otherwise, throws an error.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  assertUnit(unit: string, name?: string): SassNumber;

  /** Whether this number has `unit` as its only unit (and as a numerator). */
  hasUnit(unit: string): boolean;

  /**
   * Whether this has exactly one numerator unit, and that unit is compatible
   * with `unit`.
   */
  compatibleWithUnit(unit: string): boolean;

  /**
   * Returns a copy of this number, converted to the units represented by
   * `newNumerators` and `newDenominators`.
   *
   * @throws `Error` if this number's units are incompatible with
   * `newNumerators` and `newDenominators`; or if this number is unitless and
   * either `newNumerators` or `newDenominators` are not empty, or vice-versa.
   *
   * @param newNumerators - The numerator units to convert this number to. This
   * may be either a plain JavaScript array or an immutable {@link List} from
   * the [`immutable` package](https://immutable-js.com/).
   *
   * @param newDenominators - The denominator units to convert this number to.
   * This may be either a plain JavaScript array or an immutable {@link List}
   * from the [`immutable` package](https://immutable-js.com/).
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  convert(
    newNumerators: string[] | List<string>,
    newDenominators: string[] | List<string>,
    name?: string
  ): SassNumber;

  /**
   * Returns a copy of this number, converted to the same units as `other`.
   *
   * @throws `Error` if this number's units are incompatible with `other`'s
   * units, or if either number is unitless but the other is not.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   *
   * @param otherName - The name of the function argument `other` came from
   * (without the `$`) if it came from an argument. Used for error reporting.
   */
  convertToMatch(
    other: SassNumber,
    name?: string,
    otherName?: string
  ): SassNumber;

  /**
   * Returns {@link value}, converted to the units represented by
   * `newNumerators` and `newDenominators`.
   *
   * @throws `Error` if this number's units are incompatible with
   * `newNumerators` and `newDenominators`; or if this number is unitless and
   * either `newNumerators` or `newDenominators` are not empty, or vice-versa.
   *
   * @param newNumerators - The numerator units to convert {@link value} to.
   * This may be either a plain JavaScript array or an immutable {@link List}
   * from the [`immutable` package](https://immutable-js.com/).
   *
   * @param newDenominators - The denominator units to convert {@link value} to.
   * This may be either a plain JavaScript array or an immutable {@link List}
   * from the [`immutable` package](https://immutable-js.com/).
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  convertValue(
    newNumerators: string[] | List<string>,
    newDenominators: string[] | List<string>,
    name?: string
  ): number;

  /**
   * Returns {@link value}, converted to the same units as `other`.
   *
   * @throws `Error` if this number's units are incompatible with `other`'s
   * units, or if either number is unitless but the other is not.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   *
   * @param otherName - The name of the function argument `other` came from
   * (without the `$`) if it came from an argument. Used for error reporting.
   */
  convertValueToMatch(
    other: SassNumber,
    name?: string,
    otherName?: string
  ): number;

  /**
   * Returns a copy of this number, converted to the units represented by
   * `newNumerators` and `newDenominators`.
   *
   * Unlike {@link convert} this does *not* throw an error if this number is
   * unitless and either `newNumerators` or `newDenominators` are not empty, or
   * vice-versa. Instead, it treats all unitless numbers as convertible to and
   * from all units without changing the value.
   *
   * @throws `Error` if this number's units are incompatible with
   * `newNumerators` and `newDenominators`.
   *
   * @param newNumerators - The numerator units to convert this number to. This
   * may be either a plain JavaScript array or an immutable {@link List} from
   * the [`immutable` package](https://immutable-js.com/).
   *
   * @param newDenominators - The denominator units to convert this number to.
   * This may be either a plain JavaScript array or an immutable {@link List}
   * from the [`immutable` package](https://immutable-js.com/).
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  coerce(
    newNumerators: string[] | List<string>,
    newDenominators: string[] | List<string>,
    name?: string
  ): SassNumber;

  /**
   * Returns a copy of this number, converted to the units represented by
   * `newNumerators` and `newDenominators`.
   *
   * Unlike {@link convertToMatch} this does *not* throw an error if this number
   * is unitless and either `newNumerators` or `newDenominators` are not empty,
   * or vice-versa. Instead, it treats all unitless numbers as convertible to
   * and from all units without changing the value.
   *
   * @throws `Error` if this number's units are incompatible with `other`'s
   * units.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   *
   * @param otherName - The name of the function argument `other` came from
   * (without the `$`) if it came from an argument. Used for error reporting.
   */
  coerceToMatch(
    other: SassNumber,
    name?: string,
    otherName?: string
  ): SassNumber;

  /**
   * Returns {@link value}, converted to the units represented by
   * `newNumerators` and `newDenominators`.
   *
   * Unlike {@link convertValue} this does *not* throw an error if this number
   * is unitless and either `newNumerators` or `newDenominators` are not empty,
   * or vice-versa. Instead, it treats all unitless numbers as convertible to
   * and from all units without changing the value.
   *
   * @throws `Error` if this number's units are incompatible with
   * `newNumerators` and `newDenominators`.
   *
   * @param newNumerators - The numerator units to convert {@link value} to.
   * This may be either a plain JavaScript array or an immutable {@link List}
   * from the [`immutable` package](https://immutable-js.com/).
   *
   * @param newDenominators - The denominator units to convert {@link value} to.
   * This may be either a plain JavaScript array or an immutable {@link List}
   * from the [`immutable` package](https://immutable-js.com/).
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   */
  coerceValue(
    newNumerators: string[] | List<string>,
    newDenominators: string[] | List<string>,
    name?: string
  ): number;

  /**
   * Returns {@link value}, converted to the units represented by
   * `newNumerators` and `newDenominators`.
   *
   * Unlike {@link convertValueToMatch} this does *not* throw an error if this
   * number is unitless and either `newNumerators` or `newDenominators` are not
   * empty, or vice-versa. Instead, it treats all unitless numbers as
   * convertible to and from all units without changing the value.
   *
   * @throws `Error` if this number's units are incompatible with `other`'s
   * units.
   *
   * @param name - The name of the function argument `this` came from (without
   * the `$`) if it came from an argument. Used for error reporting.
   *
   * @param otherName - The name of the function argument `other` came from
   * (without the `$`) if it came from an argument. Used for error reporting.
   */
  coerceValueToMatch(
    other: SassNumber,
    name?: string,
    otherName?: string
  ): number;
}


code:
import {Value} from './index';

/**
 * Sass's [string type](https://sass-lang.com/documentation/values/strings).
 *
 * @category Custom Function
 */
export class SassString extends Value {
  /**
   * Creates a new string.
   *
   * @param text - The contents of the string. For quoted strings, this is the
   * semantic content—any escape sequences that were been written in the source
   * text are resolved to their Unicode values. For unquoted strings, though,
   * escape sequences are preserved as literal backslashes.
   *
   * @param options.quotes - Whether the string is quoted. Defaults to `true`.
   */
  constructor(
    text: string,
    options?: {
      quotes?: boolean;
    }
  );

  /**
   * Creates an empty string.
   *
   * @param options.quotes - Whether the string is quoted. Defaults to `true`.
   */
  constructor(options?: {quotes?: boolean});

  /**
   * The contents of the string.
   *
   * For quoted strings, this is the semantic content—any escape sequences that
   * were been written in the source text are resolved to their Unicode values.
   * For unquoted strings, though, escape sequences are preserved as literal
   * backslashes.
   *
   * This difference allows us to distinguish between identifiers with escapes,
   * such as `url\u28 http://example.com\u29`, and unquoted strings that contain
   * characters that aren't valid in identifiers, such as
   * `url(http://example.com)`. Unfortunately, it also means that we don't
   * consider `foo` and `f\6F\6F` the same string.
   */
  get text(): string;

  /** Whether this string has quotes. */
  get hasQuotes(): boolean;

  /**
   * Sass's notion of this string's length.
   *
   * Sass treats strings as a series of Unicode code points while JavaScript
   * treats them as a series of UTF-16 code units. For example, the character
   * U+1F60A SMILING FACE WITH SMILING EYES is a single Unicode code point but
   * is represented in UTF-16 as two code units (`0xD83D` and `0xDE0A`). So in
   * JavaScript, `"n😊b".length` returns `4`, whereas in Sass
   * `string.length("n😊b")` returns `3`.
   */
  get sassLength(): number;

  /**
   * Converts `sassIndex` to a JavaScript index into {@link text}.
   *
   * Sass indices are one-based, while JavaScript indices are zero-based. Sass
   * indices may also be negative in order to index from the end of the string.
   *
   * In addition, Sass indices refer to Unicode code points while JavaScript
   * string indices refer to UTF-16 code units. For example, the character
   * U+1F60A SMILING FACE WITH SMILING EYES is a single Unicode code point but
   * is represented in UTF-16 as two code units (`0xD83D` and `0xDE0A`). So in
   * JavaScript, `"n😊b".charCodeAt(1)` returns `0xD83D`, whereas in Sass
   * `string.slice("n😊b", 1, 1)` returns `"😊"`.
   *
   * This function converts Sass's code point indices to JavaScript's code unit
   * indices. This means it's O(n) in the length of `text`.
   *
   * @throws `Error` - If `sassIndex` isn't a number, if that number isn't an
   * integer, or if that integer isn't a valid index for this string.
   */
  sassIndexToStringIndex(sassIndex: Value, name?: string): number;
}


code:
# Changelog

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [2.7.1](https://github.com/webpack/schema-utils/compare/v2.7.0...v2.7.1) (2020-08-31)

### Bug Fixes

* remove esModuleInterop from tsconfig ([#110](https://github.com/webpack/schema-utils/issues/110)) ([#111](https://github.com/webpack/schema-utils/issues/111)) ([2f40154](https://github.com/webpack/schema-utils/commit/2f40154b91e45b393258ae9dd8f10cc3b8590b7d))

## [2.7.0](https://github.com/webpack/schema-utils/compare/v2.6.6...v2.7.0) (2020-05-29)

### Features

* improve hints ([a36e535](https://github.com/webpack/schema-utils/commit/a36e535faca1b01e27c3bfa3c8bee9227c3f836c))
* smart not case ([#101](https://github.com/webpack/schema-utils/issues/101)) ([698d8b0](https://github.com/webpack/schema-utils/commit/698d8b05462d86aadb217e25a45c7b953a79a52e))

### Bug Fixes

* move @types/json-schema from devDependencies to dependencies ([#97](https://github.com/webpack/schema-utils/issues/97)) ([#98](https://github.com/webpack/schema-utils/issues/98)) ([945e67d](https://github.com/webpack/schema-utils/commit/945e67db5e19baf7ec7df72813b0739dd56f950d))

### [2.6.6](https://github.com/webpack/schema-utils/compare/v2.6.5...v2.6.6) (2020-04-17)

### Bug Fixes

* improve perf

### [2.6.5](https://github.com/webpack/schema-utils/compare/v2.6.4...v2.6.5) (2020-03-11)

### Bug Fixes

* correct dots at end of sentence ([7284beb](https://github.com/webpack/schema-utils/commit/7284bebe00cd570f1bef2c15951a07b9794038e6))

### [2.6.4](https://github.com/webpack/schema-utils/compare/v2.6.3...v2.6.4) (2020-01-17)

### Bug Fixes

* change `initialised` to `initialized` ([#87](https://github.com/webpack/schema-utils/issues/87)) ([70f12d3](https://github.com/webpack/schema-utils/commit/70f12d33a8eaa27249bc9c1a27f886724cf91ea7))

### [2.6.3](https://github.com/webpack/schema-utils/compare/v2.6.2...v2.6.3) (2020-01-17)

### Bug Fixes

* prefer the `baseDataPath` option from arguments ([#86](https://github.com/webpack/schema-utils/issues/86)) ([e236859](https://github.com/webpack/schema-utils/commit/e236859e85b28e35e1294f86fc1ff596a5031cea))

### [2.6.2](https://github.com/webpack/schema-utils/compare/v2.6.1...v2.6.2) (2020-01-14)

### Bug Fixes

* better handle Windows absolute paths ([#85](https://github.com/webpack/schema-utils/issues/85)) ([1fa2930](https://github.com/webpack/schema-utils/commit/1fa2930a161e907b9fc53a7233d605910afdb883))

### [2.6.1](https://github.com/webpack/schema-utils/compare/v2.6.0...v2.6.1) (2019-11-28)

### Bug Fixes

* typescript declarations ([#84](https://github.com/webpack/schema-utils/issues/84)) ([89d55a9](https://github.com/webpack/schema-utils/commit/89d55a9a8edfa6a8ac8b112f226bb3154e260319))

## [2.6.0](https://github.com/webpack/schema-utils/compare/v2.5.0...v2.6.0) (2019-11-27)

### Features

* support configuration via title ([#81](https://github.com/webpack/schema-utils/issues/81)) ([afddc10](https://github.com/webpack/schema-utils/commit/afddc109f6891cd37a9f1835d50862d119a072bf))

### Bug Fixes

* typescript definitions ([#70](https://github.com/webpack/schema-utils/issues/70)) ([f38158d](https://github.com/webpack/schema-utils/commit/f38158d6d040e2c701622778ae8122fb26a4f990))

## [2.5.0](https://github.com/webpack/schema-utils/compare/v2.4.1...v2.5.0) (2019-10-15)

### Bug Fixes

* rework format for maxLength, minLength ([#67](https://github.com/webpack/schema-utils/issues/67)) ([0d12259](https://github.com/webpack/schema-utils/commit/0d12259))
* support all cases with one number in range ([#64](https://github.com/webpack/schema-utils/issues/64)) ([7fc8069](https://github.com/webpack/schema-utils/commit/7fc8069))
* typescript definition and export naming ([#69](https://github.com/webpack/schema-utils/issues/69)) ([a435b79](https://github.com/webpack/schema-utils/commit/a435b79))

### Features

* "smart" numbers range ([62fb107](https://github.com/webpack/schema-utils/commit/62fb107))

### [2.4.1](https://github.com/webpack/schema-utils/compare/v2.4.0...v2.4.1) (2019-09-27)

### Bug Fixes

* publish definitions ([#58](https://github.com/webpack/schema-utils/issues/58)) ([1885faa](https://github.com/webpack/schema-utils/commit/1885faa))

## [2.4.0](https://github.com/webpack/schema-utils/compare/v2.3.0...v2.4.0) (2019-09-26)

### Features

* better errors when the `type` keyword doesn't exist ([0988be2](https://github.com/webpack/schema-utils/commit/0988be2))
* support $data reference ([#56](https://github.com/webpack/schema-utils/issues/56)) ([d2f11d6](https://github.com/webpack/schema-utils/commit/d2f11d6))
* types definitions ([#52](https://github.com/webpack/schema-utils/issues/52)) ([facb431](https://github.com/webpack/schema-utils/commit/facb431))

## [2.3.0](https://github.com/webpack/schema-utils/compare/v2.2.0...v2.3.0) (2019-09-26)

### Features

* support `not` keyword ([#53](https://github.com/webpack/schema-utils/issues/53)) ([765f458](https://github.com/webpack/schema-utils/commit/765f458))

## [2.2.0](https://github.com/webpack/schema-utils/compare/v2.1.0...v2.2.0) (2019-09-02)

### Features

* better error output for `oneOf` and `anyOf` ([#48](https://github.com/webpack/schema-utils/issues/48)) ([#50](https://github.com/webpack/schema-utils/issues/50)) ([332242f](https://github.com/webpack/schema-utils/commit/332242f))

## [2.1.0](https://github.com/webpack-contrib/schema-utils/compare/v2.0.1...v2.1.0) (2019-08-07)

### Bug Fixes

* throw error on sparse arrays ([#47](https://github.com/webpack-contrib/schema-utils/issues/47)) ([b85ac38](https://github.com/webpack-contrib/schema-utils/commit/b85ac38))

### Features

* export `ValidateError` ([#46](https://github.com/webpack-contrib/schema-utils/issues/46)) ([ff781d7](https://github.com/webpack-contrib/schema-utils/commit/ff781d7))

### [2.0.1](https://github.com/webpack-contrib/schema-utils/compare/v2.0.0...v2.0.1) (2019-07-18)

### Bug Fixes

* error message for empty object ([#44](https://github.com/webpack-contrib/schema-utils/issues/44)) ([0b4b4a2](https://github.com/webpack-contrib/schema-utils/commit/0b4b4a2))

### [2.0.0](https://github.com/webpack-contrib/schema-utils/compare/v1.0.0...v2.0.0) (2019-07-17)

### BREAKING CHANGES

* drop support for Node.js < 8.9.0
* drop support `errorMessage`, please use `description` for links.
* api was changed, please look documentation.
* error messages was fully rewritten.

# [1.0.0](https://github.com/webpack-contrib/schema-utils/compare/v0.4.7...v1.0.0) (2018-08-07)

### Features

* **src:** add support for custom error messages ([#33](https://github.com/webpack-contrib/schema-utils/issues/33)) ([1cbe4ef](https://github.com/webpack-contrib/schema-utils/commit/1cbe4ef))

## [0.4.7](https://github.com/webpack-contrib/schema-utils/compare/v0.4.6...v0.4.7) (2018-08-07)

### Bug Fixes

* **src:** `node >= v4.0.0` support ([#32](https://github.com/webpack-contrib/schema-utils/issues/32)) ([cb13dd4](https://github.com/webpack-contrib/schema-utils/commit/cb13dd4))

## [0.4.6](https://github.com/webpack-contrib/schema-utils/compare/v0.4.5...v0.4.6) (2018-08-06)

### Bug Fixes

* **package:** remove lockfile ([#28](https://github.com/webpack-contrib/schema-utils/issues/28)) ([69f1a81](https://github.com/webpack-contrib/schema-utils/commit/69f1a81))
* **package:** remove unnecessary `webpack` dependency ([#26](https://github.com/webpack-contrib/schema-utils/issues/26)) ([532eaa5](https://github.com/webpack-contrib/schema-utils/commit/532eaa5))

## [0.4.5](https://github.com/webpack-contrib/schema-utils/compare/v0.4.4...v0.4.5) (2018-02-13)

### Bug Fixes

* **CHANGELOG:** update broken links ([4483b9f](https://github.com/webpack-contrib/schema-utils/commit/4483b9f))
* **package:** update broken links ([f2494ba](https://github.com/webpack-contrib/schema-utils/commit/f2494ba))

## [0.4.4](https://github.com/webpack-contrib/schema-utils/compare/v0.4.3...v0.4.4) (2018-02-13)

### Bug Fixes

* **package:** update `dependencies` ([#22](https://github.com/webpack-contrib/schema-utils/issues/22)) ([3aecac6](https://github.com/webpack-contrib/schema-utils/commit/3aecac6))

## [0.4.3](https://github.com/webpack-contrib/schema-utils/compare/v0.4.2...v0.4.3) (2017-12-14)

### Bug Fixes

* **validateOptions:** throw `err` instead of `process.exit(1)` ([#17](https://github.com/webpack-contrib/schema-utils/issues/17)) ([c595eda](https://github.com/webpack-contrib/schema-utils/commit/c595eda))
* **ValidationError:** never return `this` in the ctor ([#16](https://github.com/webpack-contrib/schema-utils/issues/16)) ([c723791](https://github.com/webpack-contrib/schema-utils/commit/c723791))

## [0.4.2](https://github.com/webpack-contrib/schema-utils/compare/v0.4.1...v0.4.2) (2017-11-09)

### Bug Fixes

* **validateOptions:** catch `ValidationError` and handle it internally ([#15](https://github.com/webpack-contrib/schema-utils/issues/15)) ([9c5ef5e](https://github.com/webpack-contrib/schema-utils/commit/9c5ef5e))

## [0.4.1](https://github.com/webpack-contrib/schema-utils/compare/v0.4.0...v0.4.1) (2017-11-03)

### Bug Fixes

* **ValidationError:** use `Error.captureStackTrace` for `err.stack` handling ([#14](https://github.com/webpack-contrib/schema-utils/issues/14)) ([a6fb974](https://github.com/webpack-contrib/schema-utils/commit/a6fb974))

# [0.4.0](https://github.com/webpack-contrib/schema-utils/compare/v0.3.0...v0.4.0) (2017-10-28)

### Features

* add support for `typeof`, `instanceof` (`{Function\|RegExp}`) ([#10](https://github.com/webpack-contrib/schema-utils/issues/10)) ([9f01816](https://github.com/webpack-contrib/schema-utils/commit/9f01816))

# [0.3.0](https://github.com/webpack-contrib/schema-utils/compare/v0.2.1...v0.3.0) (2017-04-29)

### Features

* add ValidationError ([#8](https://github.com/webpack-contrib/schema-utils/issues/8)) ([d48f0fb](https://github.com/webpack-contrib/schema-utils/commit/d48f0fb))

## [0.2.1](https://github.com/webpack-contrib/schema-utils/compare/v0.2.0...v0.2.1) (2017-03-13)

### Bug Fixes

* Include .babelrc to `files` ([28f0363](https://github.com/webpack-contrib/schema-utils/commit/28f0363))
* Include source to `files` ([43b0f2f](https://github.com/webpack-contrib/schema-utils/commit/43b0f2f))

# [0.2.0](https://github.com/webpack-contrib/schema-utils/compare/v0.1.0...v0.2.0) (2017-03-12)

# 0.1.0 (2017-03-07)

### Features

* **validations:** add validateOptions module ([ae9b47b](https://github.com/webpack-contrib/schema-utils/commit/ae9b47b))

# Change Log

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.


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

# 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
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
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
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
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 validateOptions from 'schema-utils';

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

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

  validateOptions(schema, options, {
    name: 'Loader Name',
    baseDataPath: 'options',
  });

  // Code...
}

export default loader;
```

### `Plugin`

```js
import validateOptions from 'schema-utils';

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

class Plugin {
  constructor(options) {
    validateOptions(schema, options, {
      name: 'Plugin Name',
      baseDataPath: 'options',
    });

    this.options = options;
  }

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

export default Plugin;
```

## Contributing

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

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

## License

[MIT](./LICENSE)

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


code:
export default ValidationError;
export type JSONSchema6 = import('json-schema').JSONSchema6;
export type JSONSchema7 = import('json-schema').JSONSchema7;
export type Schema =
  | (import('json-schema').JSONSchema4 & import('./validate').Extend)
  | (import('json-schema').JSONSchema6 & import('./validate').Extend)
  | (import('json-schema').JSONSchema7 & import('./validate').Extend);
export type ValidationErrorConfiguration = {
  name?: string | undefined;
  baseDataPath?: string | undefined;
  postFormatter?: import('./validate').PostFormatter | undefined;
};
export type PostFormatter = (
  formattedError: string,
  error: import('ajv').ErrorObject & {
    children?: import('ajv').ErrorObject[] | undefined;
  }
) => string;
export type SchemaUtilErrorObject = import('ajv').ErrorObject & {
  children?: import('ajv').ErrorObject[] | undefined;
};
export type SPECIFICITY = number;
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:
declare const _exports: typeof import('./validate').default;
export = _exports;


code:
export default addAbsolutePathKeyword;
export type Ajv = import('ajv').Ajv;
export type ValidateFunction = import('ajv').ValidateFunction;
export type SchemaUtilErrorObject = import('ajv').ErrorObject & {
  children?: import('ajv').ErrorObject[] | undefined;
};
/**
 *
 * @param {Ajv} ajv
 * @returns {Ajv}
 */
declare function addAbsolutePathKeyword(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<[number, boolean]>,
    logic: boolean
  ): [number, boolean];
  /** @type {Array<RangeValue>} */
  _left: Array<[number, boolean]>;
  /** @type {Array<RangeValue>} */
  _right: Array<[number, boolean]>;
  /**
   * @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: [number, boolean]) => boolean;


code:
export function stringHints(schema: Schema, logic: boolean): string[];
export function numberHints(schema: Schema, logic: boolean): string[];
export type Schema =
  | (import('json-schema').JSONSchema4 & import('../validate').Extend)
  | (import('json-schema').JSONSchema6 & import('../validate').Extend)
  | (import('json-schema').JSONSchema7 & import('../validate').Extend);


code:
export default validate;
export type JSONSchema4 = import('json-schema').JSONSchema4;
export type JSONSchema6 = import('json-schema').JSONSchema6;
export type JSONSchema7 = import('json-schema').JSONSchema7;
export type ErrorObject = import('ajv').ErrorObject;
export type Extend = {
  formatMinimum?: number | undefined;
  formatMaximum?: number | undefined;
  formatExclusiveMinimum?: boolean | undefined;
  formatExclusiveMaximum?: boolean | undefined;
};
export type Schema =
  | (import('json-schema').JSONSchema4 & Extend)
  | (import('json-schema').JSONSchema6 & Extend)
  | (import('json-schema').JSONSchema7 & Extend);
export type SchemaUtilErrorObject = import('ajv').ErrorObject & {
  children?: import('ajv').ErrorObject[] | undefined;
};
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}
 */
declare function validate(
  schema: Schema,
  options: Array<object> | object,
  configuration?: ValidationErrorConfiguration | undefined
): void;
declare namespace validate {
  export { ValidationError };
  export { ValidationError as ValidateError };
}
import ValidationError from './ValidationError';


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

const {
  stringHints,
  numberHints
} = require('./util/hints');
/** @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 {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.dataPath ? error.dataPath.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(', ')})` : '';
}
/**
 * @param {Schema} schema
 * @param {boolean} logic
 * @returns {string[]}
 */

function getHints(schema, logic) {
  if (likeNumber(schema) || likeInteger(schema)) {
    return numberHints(schema, logic);
  } else if (likeString(schema)) {
    return 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) {
      return (
        /** @type {Array<any>} */
        schema.enum.map(item => JSON.stringify(item)).join(' | ')
      );
    }

    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) : [];
      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}`;
    }

    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);
    }

    if (schemaPart.description) {
      return `\n-> ${schemaPart.description}`;
    }

    return '';
  }
  /**
   * @param {SchemaUtilErrorObject} error
   * @returns {string}
   */

  formatValidationError(error) {
    const {
      keyword,
      dataPath: errorDataPath
    } = error;
    const dataPath = `${this.baseDataPath}${errorDataPath}`;

    switch (keyword) {
      case 'type':
        {
          const {
            parentSchema,
            params
          } = error; // eslint-disable-next-line default-case

          switch (
          /** @type {import("ajv").TypeParams} */
          params.type) {
            case 'number':
              return `${dataPath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;

            case 'integer':
              return `${dataPath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;

            case 'string':
              return `${dataPath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;

            case 'boolean':
              return `${dataPath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;

            case 'array':
              return `${dataPath} should be an array:\n${this.getSchemaPartText(parentSchema)}`;

            case 'object':
              return `${dataPath} should be an object:\n${this.getSchemaPartText(parentSchema)}`;

            case 'null':
              return `${dataPath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;

            default:
              return `${dataPath} should be:\n${this.getSchemaPartText(parentSchema)}`;
          }
        }

      case 'instanceof':
        {
          const {
            parentSchema
          } = error;
          return `${dataPath} should be an instance of ${this.getSchemaPartText(parentSchema, false, true)}`;
        }

      case 'pattern':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            pattern
          } =
          /** @type {import("ajv").PatternParams} */
          params;
          return `${dataPath} should match pattern ${JSON.stringify(pattern)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'format':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            format
          } =
          /** @type {import("ajv").FormatParams} */
          params;
          return `${dataPath} should match format ${JSON.stringify(format)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'formatMinimum':
      case 'formatMaximum':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            comparison,
            limit
          } =
          /** @type {import("ajv").ComparisonParams} */
          params;
          return `${dataPath} 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
          } =
          /** @type {import("ajv").ComparisonParams} */
          params;
          const [, ...hints] = getHints(
          /** @type {Schema} */
          parentSchema, true);

          if (hints.length === 0) {
            hints.push(`should be ${comparison} ${limit}`);
          }

          return `${dataPath} ${hints.join(' ')}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'multipleOf':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            multipleOf
          } =
          /** @type {import("ajv").MultipleOfParams} */
          params;
          return `${dataPath} should be multiple of ${multipleOf}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'patternRequired':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            missingPattern
          } =
          /** @type {import("ajv").PatternRequiredParams} */
          params;
          return `${dataPath} should have property matching pattern ${JSON.stringify(missingPattern)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'minLength':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } =
          /** @type {import("ajv").LimitParams} */
          params;

          if (limit === 1) {
            return `${dataPath} should be an non-empty string${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
          }

          const length = limit - 1;
          return `${dataPath} should be longer than ${length} character${length > 1 ? 's' : ''}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'minItems':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } =
          /** @type {import("ajv").LimitParams} */
          params;

          if (limit === 1) {
            return `${dataPath} should be an non-empty array${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
          }

          return `${dataPath} should not have fewer than ${limit} items${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'minProperties':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } =
          /** @type {import("ajv").LimitParams} */
          params;

          if (limit === 1) {
            return `${dataPath} should be an non-empty object${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
          }

          return `${dataPath} should not have fewer than ${limit} properties${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'maxLength':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } =
          /** @type {import("ajv").LimitParams} */
          params;
          const max = limit + 1;
          return `${dataPath} should be shorter than ${max} character${max > 1 ? 's' : ''}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'maxItems':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } =
          /** @type {import("ajv").LimitParams} */
          params;
          return `${dataPath} should not have more than ${limit} items${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'maxProperties':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } =
          /** @type {import("ajv").LimitParams} */
          params;
          return `${dataPath} should not have more than ${limit} properties${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'uniqueItems':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            i
          } =
          /** @type {import("ajv").UniqueItemsParams} */
          params;
          return `${dataPath} should not contain the item '${error.data[i]}' twice${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'additionalItems':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            limit
          } =
          /** @type {import("ajv").LimitParams} */
          params;
          return `${dataPath} should not have more than ${limit} items${getSchemaNonTypes(parentSchema)}. These items are valid:\n${this.getSchemaPartText(parentSchema)}`;
        }

      case 'contains':
        {
          const {
            parentSchema
          } = error;
          return `${dataPath} should contains at least one ${this.getSchemaPartText(parentSchema, ['contains'])} item${getSchemaNonTypes(parentSchema)}.`;
        }

      case 'required':
        {
          const {
            parentSchema,
            params
          } = error;
          const missingProperty =
          /** @type {import("ajv").DependenciesParams} */
          params.missingProperty.replace(/^\./, '');
          const hasProperty = parentSchema && Boolean(
          /** @type {Schema} */
          parentSchema.properties &&
          /** @type {Schema} */
          parentSchema.properties[missingProperty]);
          return `${dataPath} 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
          } =
          /** @type {import("ajv").AdditionalPropertiesParams} */
          params;
          return `${dataPath} has an unknown property '${additionalProperty}'${getSchemaNonTypes(parentSchema)}. These properties are valid:\n${this.getSchemaPartText(parentSchema)}`;
        }

      case 'dependencies':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            property,
            deps
          } =
          /** @type {import("ajv").DependenciesParams} */
          params;
          const dependencies = deps.split(',').map(
          /**
           * @param {string} dep
           * @returns {string}
           */
          dep => `'${dep.trim()}'`).join(', ');
          return `${dataPath} should have properties ${dependencies} when property '${property}' is present${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
        }

      case 'propertyNames':
        {
          const {
            params,
            parentSchema,
            schema
          } = error;
          const {
            propertyName
          } =
          /** @type {import("ajv").PropertyNamesParams} */
          params;
          return `${dataPath} 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 `${dataPath} should be ${this.getSchemaPartText(parentSchema, false, true)}`;
          }

          return `${dataPath} should be one of these:\n${this.getSchemaPartText(parentSchema)}`;
        }

      case 'const':
        {
          const {
            parentSchema
          } = error;
          return `${dataPath} 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 `${dataPath} should be any ${schemaOutput}${postfix}.`;
          }

          const {
            schema,
            parentSchema
          } = error;
          return `${dataPath} 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 `${dataPath} 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 `${dataPath} should be one of these:\n${this.getSchemaPartText(parentSchema)}`;
        }

      case 'if':
        {
          const {
            params,
            parentSchema
          } = error;
          const {
            failingKeyword
          } =
          /** @type {import("ajv").IfParams} */
          params;
          return `${dataPath} should match "${failingKeyword}" schema:\n${this.getSchemaPartText(parentSchema, [failingKeyword])}`;
        }

      case 'absolutePath':
        {
          const {
            message,
            parentSchema
          } = error;
          return `${dataPath}: ${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 `${dataPath} ${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 = require('./validate');

module.exports = validate.default;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

/** @typedef {import("ajv").Ajv} Ajv */

/** @typedef {import("ajv").ValidateFunction} ValidateFunction */

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

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

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

function addAbsolutePathKeyword(ajv) {
  ajv.addKeyword('absolutePath', {
    errors: true,
    type: 'string',

    compile(schema, parentSchema) {
      /** @type {ValidateFunction} */
      const callback = data => {
        let passes = true;
        const isExclamationMarkPresent = data.includes('!');

        if (isExclamationMarkPresent) {
          callback.errors = [errorMessage(`The provided value ${JSON.stringify(data)} contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.`, parentSchema, data)];
          passes = false;
        } // ?:[A-Za-z]:\\ - Windows absolute path
        // \\\\ - Windows network absolute path
        // \/ - Unix-like OS absolute path

        const isCorrectAbsolutePath = schema === /^(?:[A-Za-z]:(\\|\/)|\\\\|\/)/.test(data);

        if (!isCorrectAbsolutePath) {
          callback.errors = [getErrorFor(schema, parentSchema, data)];
          passes = false;
        }

        return passes;
      };

      callback.errors = [];
      return callback;
    }

  });
  return ajv;
}

var _default = addAbsolutePathKeyword;
exports.default = _default;