code:
/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";

/* eslint-disable no-underscore-dangle */
/* eslint-disable no-undefined */

const Syntax = require("estraverse").Syntax;

const Reference = require("./reference");
const Variable = require("./variable");
const Definition = require("./definition").Definition;
const assert = require("assert");

/**
 * Test if scope is struct
 * @param {Scope} scope - scope
 * @param {Block} block - block
 * @param {boolean} isMethodDefinition - is method definition
 * @param {boolean} useDirective - use directive
 * @returns {boolean} is strict scope
 */
function isStrictScope(scope, block, isMethodDefinition, useDirective) {
    let body;

    // When upper scope is exists and strict, inner scope is also strict.
    if (scope.upper && scope.upper.isStrict) {
        return true;
    }

    if (isMethodDefinition) {
        return true;
    }

    if (scope.type === "class" || scope.type === "module") {
        return true;
    }

    if (scope.type === "block" || scope.type === "switch") {
        return false;
    }

    if (scope.type === "function") {
        if (block.type === Syntax.ArrowFunctionExpression && block.body.type !== Syntax.BlockStatement) {
            return false;
        }

        if (block.type === Syntax.Program) {
            body = block;
        } else {
            body = block.body;
        }

        if (!body) {
            return false;
        }
    } else if (scope.type === "global") {
        body = block;
    } else {
        return false;
    }

    // Search 'use strict' directive.
    if (useDirective) {
        for (let i = 0, iz = body.body.length; i < iz; ++i) {
            const stmt = body.body[i];

            if (stmt.type !== Syntax.DirectiveStatement) {
                break;
            }
            if (stmt.raw === "\"use strict\"" || stmt.raw === "'use strict'") {
                return true;
            }
        }
    } else {
        for (let i = 0, iz = body.body.length; i < iz; ++i) {
            const stmt = body.body[i];

            if (stmt.type !== Syntax.ExpressionStatement) {
                break;
            }
            const expr = stmt.expression;

            if (expr.type !== Syntax.Literal || typeof expr.value !== "string") {
                break;
            }
            if (expr.raw !== null && expr.raw !== undefined) {
                if (expr.raw === "\"use strict\"" || expr.raw === "'use strict'") {
                    return true;
                }
            } else {
                if (expr.value === "use strict") {
                    return true;
                }
            }
        }
    }
    return false;
}

/**
 * Register scope
 * @param {ScopeManager} scopeManager - scope manager
 * @param {Scope} scope - scope
 * @returns {void}
 */
function registerScope(scopeManager, scope) {
    scopeManager.scopes.push(scope);

    const scopes = scopeManager.__nodeToScope.get(scope.block);

    if (scopes) {
        scopes.push(scope);
    } else {
        scopeManager.__nodeToScope.set(scope.block, [scope]);
    }
}

/**
 * Should be statically
 * @param {Object} def - def
 * @returns {boolean} should be statically
 */
function shouldBeStatically(def) {
    return (
        (def.type === Variable.ClassName) ||
        (def.type === Variable.Variable && def.parent.kind !== "var")
    );
}

/**
 * @class Scope
 */
class Scope {
    constructor(scopeManager, type, upperScope, block, isMethodDefinition) {

        /**
         * One of 'module', 'block', 'switch', 'function', 'catch', 'with', 'function', 'class', 'global'.
         * @member {String} Scope#type
         */
        this.type = type;

        /**
         * The scoped {@link Variable}s of this scope, as { Variable.name
         * : Variable }.
         * @member {Map} Scope#set
         */
        this.set = new Map();

        /**
         * The tainted variables of this scope, as { Variable.name :
         * boolean }.
         * @member {Map} Scope#taints */
        this.taints = new Map();

        /**
         * Generally, through the lexical scoping of JS you can always know
         * which variable an identifier in the source code refers to. There are
         * a few exceptions to this rule. With 'global' and 'with' scopes you
         * can only decide at runtime which variable a reference refers to.
         * Moreover, if 'eval()' is used in a scope, it might introduce new
         * bindings in this or its parent scopes.
         * All those scopes are considered 'dynamic'.
         * @member {boolean} Scope#dynamic
         */
        this.dynamic = this.type === "global" || this.type === "with";

        /**
         * A reference to the scope-defining syntax node.
         * @member {espree.Node} Scope#block
         */
        this.block = block;

        /**
         * The {@link Reference|references} that are not resolved with this scope.
         * @member {Reference[]} Scope#through
         */
        this.through = [];

        /**
         * The scoped {@link Variable}s of this scope. In the case of a
         * 'function' scope this includes the automatic argument arguments as
         * its first element, as well as all further formal arguments.
         * @member {Variable[]} Scope#variables
         */
        this.variables = [];

        /**
         * Any variable {@link Reference|reference} found in this scope. This
         * includes occurrences of local variables as well as variables from
         * parent scopes (including the global scope). For local variables
         * this also includes defining occurrences (like in a 'var' statement).
         * In a 'function' scope this does not include the occurrences of the
         * formal parameter in the parameter list.
         * @member {Reference[]} Scope#references
         */
        this.references = [];

        /**
         * For 'global' and 'function' scopes, this is a self-reference. For
         * other scope types this is the variableScope value of the
         * parent scope.
         * @member {Scope} Scope#variableScope
         */
        this.variableScope =
            (this.type === "global" || this.type === "function" || this.type === "module") ? this : upperScope.variableScope;

        /**
         * Whether this scope is created by a FunctionExpression.
         * @member {boolean} Scope#functionExpressionScope
         */
        this.functionExpressionScope = false;

        /**
         * Whether this is a scope that contains an 'eval()' invocation.
         * @member {boolean} Scope#directCallToEvalScope
         */
        this.directCallToEvalScope = false;

        /**
         * @member {boolean} Scope#thisFound
         */
        this.thisFound = false;

        this.__left = [];

        /**
         * Reference to the parent {@link Scope|scope}.
         * @member {Scope} Scope#upper
         */
        this.upper = upperScope;

        /**
         * Whether 'use strict' is in effect in this scope.
         * @member {boolean} Scope#isStrict
         */
        this.isStrict = isStrictScope(this, block, isMethodDefinition, scopeManager.__useDirective());

        /**
         * List of nested {@link Scope}s.
         * @member {Scope[]} Scope#childScopes
         */
        this.childScopes = [];
        if (this.upper) {
            this.upper.childScopes.push(this);
        }

        this.__declaredVariables = scopeManager.__declaredVariables;

        registerScope(scopeManager, this);
    }

    __shouldStaticallyClose(scopeManager) {
        return (!this.dynamic || scopeManager.__isOptimistic());
    }

    __shouldStaticallyCloseForGlobal(ref) {

        // On global scope, let/const/class declarations should be resolved statically.
        const name = ref.identifier.name;

        if (!this.set.has(name)) {
            return false;
        }

        const variable = this.set.get(name);
        const defs = variable.defs;

        return defs.length > 0 && defs.every(shouldBeStatically);
    }

    __staticCloseRef(ref) {
        if (!this.__resolve(ref)) {
            this.__delegateToUpperScope(ref);
        }
    }

    __dynamicCloseRef(ref) {

        // notify all names are through to global
        let current = this;

        do {
            current.through.push(ref);
            current = current.upper;
        } while (current);
    }

    __globalCloseRef(ref) {

        // let/const/class declarations should be resolved statically.
        // others should be resolved dynamically.
        if (this.__shouldStaticallyCloseForGlobal(ref)) {
            this.__staticCloseRef(ref);
        } else {
            this.__dynamicCloseRef(ref);
        }
    }

    __close(scopeManager) {
        let closeRef;

        if (this.__shouldStaticallyClose(scopeManager)) {
            closeRef = this.__staticCloseRef;
        } else if (this.type !== "global") {
            closeRef = this.__dynamicCloseRef;
        } else {
            closeRef = this.__globalCloseRef;
        }

        // Try Resolving all references in this scope.
        for (let i = 0, iz = this.__left.length; i < iz; ++i) {
            const ref = this.__left[i];

            closeRef.call(this, ref);
        }
        this.__left = null;

        return this.upper;
    }

    // To override by function scopes.
    // References in default parameters isn't resolved to variables which are in their function body.
    __isValidResolution(ref, variable) { // eslint-disable-line class-methods-use-this, no-unused-vars
        return true;
    }

    __resolve(ref) {
        const name = ref.identifier.name;

        if (!this.set.has(name)) {
            return false;
        }
        const variable = this.set.get(name);

        if (!this.__isValidResolution(ref, variable)) {
            return false;
        }
        variable.references.push(ref);
        variable.stack = variable.stack && ref.from.variableScope === this.variableScope;
        if (ref.tainted) {
            variable.tainted = true;
            this.taints.set(variable.name, true);
        }
        ref.resolved = variable;

        return true;
    }

    __delegateToUpperScope(ref) {
        if (this.upper) {
            this.upper.__left.push(ref);
        }
        this.through.push(ref);
    }

    __addDeclaredVariablesOfNode(variable, node) {
        if (node === null || node === undefined) {
            return;
        }

        let variables = this.__declaredVariables.get(node);

        if (variables === null || variables === undefined) {
            variables = [];
            this.__declaredVariables.set(node, variables);
        }
        if (variables.indexOf(variable) === -1) {
            variables.push(variable);
        }
    }

    __defineGeneric(name, set, variables, node, def) {
        let variable;

        variable = set.get(name);
        if (!variable) {
            variable = new Variable(name, this);
            set.set(name, variable);
            variables.push(variable);
        }

        if (def) {
            variable.defs.push(def);
            this.__addDeclaredVariablesOfNode(variable, def.node);
            this.__addDeclaredVariablesOfNode(variable, def.parent);
        }
        if (node) {
            variable.identifiers.push(node);
        }
    }

    __define(node, def) {
        if (node && node.type === Syntax.Identifier) {
            this.__defineGeneric(
                node.name,
                this.set,
                this.variables,
                node,
                def
            );
        }
    }

    __referencing(node, assign, writeExpr, maybeImplicitGlobal, partial, init) {

        // because Array element may be null
        if (!node || node.type !== Syntax.Identifier) {
            return;
        }

        // Specially handle like `this`.
        if (node.name === "super") {
            return;
        }

        const ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal, !!partial, !!init);

        this.references.push(ref);
        this.__left.push(ref);
    }

    __detectEval() {
        let current = this;

        this.directCallToEvalScope = true;
        do {
            current.dynamic = true;
            current = current.upper;
        } while (current);
    }

    __detectThis() {
        this.thisFound = true;
    }

    __isClosed() {
        return this.__left === null;
    }

    /**
     * returns resolved {Reference}
     * @method Scope#resolve
     * @param {Espree.Identifier} ident - identifier to be resolved.
     * @returns {Reference} reference
     */
    resolve(ident) {
        let ref, i, iz;

        assert(this.__isClosed(), "Scope should be closed.");
        assert(ident.type === Syntax.Identifier, "Target should be identifier.");
        for (i = 0, iz = this.references.length; i < iz; ++i) {
            ref = this.references[i];
            if (ref.identifier === ident) {
                return ref;
            }
        }
        return null;
    }

    /**
     * returns this scope is static
     * @method Scope#isStatic
     * @returns {boolean} static
     */
    isStatic() {
        return !this.dynamic;
    }

    /**
     * returns this scope has materialized arguments
     * @method Scope#isArgumentsMaterialized
     * @returns {boolean} arguemnts materialized
     */
    isArgumentsMaterialized() { // eslint-disable-line class-methods-use-this
        return true;
    }

    /**
     * returns this scope has materialized `this` reference
     * @method Scope#isThisMaterialized
     * @returns {boolean} this materialized
     */
    isThisMaterialized() { // eslint-disable-line class-methods-use-this
        return true;
    }

    isUsedName(name) {
        if (this.set.has(name)) {
            return true;
        }
        for (let i = 0, iz = this.through.length; i < iz; ++i) {
            if (this.through[i].identifier.name === name) {
                return true;
            }
        }
        return false;
    }
}

class GlobalScope extends Scope {
    constructor(scopeManager, block) {
        super(scopeManager, "global", null, block, false);
        this.implicit = {
            set: new Map(),
            variables: [],

            /**
            * List of {@link Reference}s that are left to be resolved (i.e. which
            * need to be linked to the variable they refer to).
            * @member {Reference[]} Scope#implicit#left
            */
            left: []
        };
    }

    __close(scopeManager) {
        const implicit = [];

        for (let i = 0, iz = this.__left.length; i < iz; ++i) {
            const ref = this.__left[i];

            if (ref.__maybeImplicitGlobal && !this.set.has(ref.identifier.name)) {
                implicit.push(ref.__maybeImplicitGlobal);
            }
        }

        // create an implicit global variable from assignment expression
        for (let i = 0, iz = implicit.length; i < iz; ++i) {
            const info = implicit[i];

            this.__defineImplicit(info.pattern,
                new Definition(
                    Variable.ImplicitGlobalVariable,
                    info.pattern,
                    info.node,
                    null,
                    null,
                    null
                ));

        }

        this.implicit.left = this.__left;

        return super.__close(scopeManager);
    }

    __defineImplicit(node, def) {
        if (node && node.type === Syntax.Identifier) {
            this.__defineGeneric(
                node.name,
                this.implicit.set,
                this.implicit.variables,
                node,
                def
            );
        }
    }
}

class ModuleScope extends Scope {
    constructor(scopeManager, upperScope, block) {
        super(scopeManager, "module", upperScope, block, false);
    }
}

class FunctionExpressionNameScope extends Scope {
    constructor(scopeManager, upperScope, block) {
        super(scopeManager, "function-expression-name", upperScope, block, false);
        this.__define(block.id,
            new Definition(
                Variable.FunctionName,
                block.id,
                block,
                null,
                null,
                null
            ));
        this.functionExpressionScope = true;
    }
}

class CatchScope extends Scope {
    constructor(scopeManager, upperScope, block) {
        super(scopeManager, "catch", upperScope, block, false);
    }
}

class WithScope extends Scope {
    constructor(scopeManager, upperScope, block) {
        super(scopeManager, "with", upperScope, block, false);
    }

    __close(scopeManager) {
        if (this.__shouldStaticallyClose(scopeManager)) {
            return super.__close(scopeManager);
        }

        for (let i = 0, iz = this.__left.length; i < iz; ++i) {
            const ref = this.__left[i];

            ref.tainted = true;
            this.__delegateToUpperScope(ref);
        }
        this.__left = null;

        return this.upper;
    }
}

class BlockScope extends Scope {
    constructor(scopeManager, upperScope, block) {
        super(scopeManager, "block", upperScope, block, false);
    }
}

class SwitchScope extends Scope {
    constructor(scopeManager, upperScope, block) {
        super(scopeManager, "switch", upperScope, block, false);
    }
}

class FunctionScope extends Scope {
    constructor(scopeManager, upperScope, block, isMethodDefinition) {
        super(scopeManager, "function", upperScope, block, isMethodDefinition);

        // section 9.2.13, FunctionDeclarationInstantiation.
        // NOTE Arrow functions never have an arguments objects.
        if (this.block.type !== Syntax.ArrowFunctionExpression) {
            this.__defineArguments();
        }
    }

    isArgumentsMaterialized() {

        // TODO(Constellation)
        // We can more aggressive on this condition like this.
        //
        // function t() {
        //     // arguments of t is always hidden.
        //     function arguments() {
        //     }
        // }
        if (this.block.type === Syntax.ArrowFunctionExpression) {
            return false;
        }

        if (!this.isStatic()) {
            return true;
        }

        const variable = this.set.get("arguments");

        assert(variable, "Always have arguments variable.");
        return variable.tainted || variable.references.length !== 0;
    }

    isThisMaterialized() {
        if (!this.isStatic()) {
            return true;
        }
        return this.thisFound;
    }

    __defineArguments() {
        this.__defineGeneric(
            "arguments",
            this.set,
            this.variables,
            null,
            null
        );
        this.taints.set("arguments", true);
    }

    // References in default parameters isn't resolved to variables which are in their function body.
    //     const x = 1
    //     function f(a = x) { // This `x` is resolved to the `x` in the outer scope.
    //         const x = 2
    //         console.log(a)
    //     }
    __isValidResolution(ref, variable) {

        // If `options.nodejsScope` is true, `this.block` becomes a Program node.
        if (this.block.type === "Program") {
            return true;
        }

        const bodyStart = this.block.body.range[0];

        // It's invalid resolution in the following case:
        return !(
            variable.scope === this &&
            ref.identifier.range[0] < bodyStart && // the reference is in the parameter part.
            variable.defs.every(d => d.name.range[0] >= bodyStart) // the variable is in the body.
        );
    }
}

class ForScope extends Scope {
    constructor(scopeManager, upperScope, block) {
        super(scopeManager, "for", upperScope, block, false);
    }
}

class ClassScope extends Scope {
    constructor(scopeManager, upperScope, block) {
        super(scopeManager, "class", upperScope, block, false);
    }
}

module.exports = {
    Scope,
    GlobalScope,
    ModuleScope,
    FunctionExpressionNameScope,
    CatchScope,
    WithScope,
    BlockScope,
    SwitchScope,
    FunctionScope,
    ForScope,
    ClassScope
};

/* vim: set sw=4 ts=4 et tw=80 : */


code:
/*
  Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";

/**
 * A Variable represents a locally scoped identifier. These include arguments to
 * functions.
 * @class Variable
 */
class Variable {
    constructor(name, scope) {

        /**
         * The variable name, as given in the source code.
         * @member {String} Variable#name
         */
        this.name = name;

        /**
         * List of defining occurrences of this variable (like in 'var ...'
         * statements or as parameter), as AST nodes.
         * @member {espree.Identifier[]} Variable#identifiers
         */
        this.identifiers = [];

        /**
         * List of {@link Reference|references} of this variable (excluding parameter entries)
         * in its defining scope and all nested scopes. For defining
         * occurrences only see {@link Variable#defs}.
         * @member {Reference[]} Variable#references
         */
        this.references = [];

        /**
         * List of defining occurrences of this variable (like in 'var ...'
         * statements or as parameter), as custom objects.
         * @member {Definition[]} Variable#defs
         */
        this.defs = [];

        this.tainted = false;

        /**
         * Whether this is a stack variable.
         * @member {boolean} Variable#stack
         */
        this.stack = true;

        /**
         * Reference to the enclosing Scope.
         * @member {Scope} Variable#scope
         */
        this.scope = scope;
    }
}

Variable.CatchClause = "CatchClause";
Variable.Parameter = "Parameter";
Variable.FunctionName = "FunctionName";
Variable.ClassName = "ClassName";
Variable.Variable = "Variable";
Variable.ImportBinding = "ImportBinding";
Variable.ImplicitGlobalVariable = "ImplicitGlobalVariable";

module.exports = Variable;

/* vim: set sw=4 ts=4 et tw=80 : */

license: mit


eslint-scope

description: ECMAScript scope analyzer for ESLint

repository: eslint/eslint-scope

test: node Makefile.js test

lint: node Makefile.js lint

generate-release: eslint-generate-release

generate-alpharelease: eslint-generate-prerelease alpha

generate-betarelease: eslint-generate-prerelease beta

generate-rcrelease: eslint-generate-prerelease rc

publish-release: eslint-publish-release

files: LICENSE README.md lib


code:
v2.1.0 - May 3, 2021

* [`908fdf8`](https://github.com/eslint/eslint-visitor-keys/commit/908fdf8c0d9a352c696c8c1f4901280d1a0795f7) Update: add PrivateIdentifier and PropertyDefinition (#20) (Toru Nagashima)
* [`2d7be11`](https://github.com/eslint/eslint-visitor-keys/commit/2d7be11e4d13ac702c9fe3c529cadbd75b370146) Chore: No longer test in Node.js 13 (#17) (Michaël De Boey)
* [`b41b509`](https://github.com/eslint/eslint-visitor-keys/commit/b41b509b153ecd8d47af46a421122f64e93d4c67) Docs: Update required Node.js version (#15) (Michaël De Boey)

v2.0.0 - August 14, 2020

* [`fb86ca3`](https://github.com/eslint/eslint-visitor-keys/commit/fb86ca315daafc84e23ed9005db40b0892b972a6) Breaking: drop support for Node <10 (#13) (Kai Cataldo)
* [`69383b3`](https://github.com/eslint/eslint-visitor-keys/commit/69383b372915e33ada094880ecc6b6e8f8c7ca4e) Chore: move to GitHub Actions (#14) (Kai Cataldo)

v1.3.0 - June 19, 2020

* [`c92dd7f`](https://github.com/eslint/eslint-visitor-keys/commit/c92dd7ff96f0044dba12d681406a025b92b4c437) Update: add `ChainExpression` node (#12) (Toru Nagashima)

v1.2.0 - June 4, 2020

* [`21f28bf`](https://github.com/eslint/eslint-visitor-keys/commit/21f28bf11be5329d740a8bf6bdbcd0ef13bbf1a2) Update: added exported in exportAllDeclaration key (#10) (Anix)

v1.1.0 - August 13, 2019

* [`9331cc0`](https://github.com/eslint/eslint-visitor-keys/commit/9331cc09e756e65b9044c9186445a474b037fac6) Update: add ImportExpression (#8) (Toru Nagashima)
* [`5967f58`](https://github.com/eslint/eslint-visitor-keys/commit/5967f583b04f17fba9226aaa394e45d476d2b8af) Chore: add supported Node.js versions to CI (#7) (Kai Cataldo)
* [`6f7c60f`](https://github.com/eslint/eslint-visitor-keys/commit/6f7c60fef2ceec9f6323202df718321cec45cab0) Upgrade: eslint-release@1.0.0 (#5) (Teddy Katz)

v1.0.0 - December 18, 2017

* 1f6bd38 Breaking: update keys (#4) (Toru Nagashima)

v0.1.0 - November 17, 2017

* 17b4a88 Chore: update `repository` field in package.json (#3) (Toru Nagashima)
* a5a026b New: eslint-visitor-keys (#1) (Toru Nagashima)
* a1a48b8 Update: Change license to Apache 2 (#2) (Ilya Volodin)
* 2204715 Initial commit (Toru Nagashima)


code:
# eslint-visitor-keys

[![npm version](https://img.shields.io/npm/v/eslint-visitor-keys.svg)](https://www.npmjs.com/package/eslint-visitor-keys)
[![Downloads/month](https://img.shields.io/npm/dm/eslint-visitor-keys.svg)](http://www.npmtrends.com/eslint-visitor-keys)
[![Build Status](https://travis-ci.org/eslint/eslint-visitor-keys.svg?branch=master)](https://travis-ci.org/eslint/eslint-visitor-keys)
[![Dependency Status](https://david-dm.org/eslint/eslint-visitor-keys.svg)](https://david-dm.org/eslint/eslint-visitor-keys)

Constants and utilities about visitor keys to traverse AST.

## 💿 Installation

Use [npm] to install.

```bash
$ npm install eslint-visitor-keys
```

### Requirements

- [Node.js] 10.0.0 or later.

## 📖 Usage

```js
const evk = require("eslint-visitor-keys")
```

### evk.KEYS

> type: `{ [type: string]: string[] | undefined }`

Visitor keys. This keys are frozen.

This is an object. Keys are the type of [ESTree] nodes. Their values are an array of property names which have child nodes.

For example:

```
console.log(evk.KEYS.AssignmentExpression) // → ["left", "right"]
```

### evk.getKeys(node)

> type: `(node: object) => string[]`

Get the visitor keys of a given AST node.

This is similar to `Object.keys(node)` of ES Standard, but some keys are excluded: `parent`, `leadingComments`, `trailingComments`, and names which start with `_`.

This will be used to traverse unknown nodes.

For example:

```
const node = {
    type: "AssignmentExpression",
    left: { type: "Identifier", name: "foo" },
    right: { type: "Literal", value: 0 }
}
console.log(evk.getKeys(node)) // → ["type", "left", "right"]
```

### evk.unionWith(additionalKeys)

> type: `(additionalKeys: object) => { [type: string]: string[] | undefined }`

Make the union set with `evk.KEYS` and the given keys.

- The order of keys is, `additionalKeys` is at first, then `evk.KEYS` is concatenated after that.
- It removes duplicated keys as keeping the first one.

For example:

```
console.log(evk.unionWith({
    MethodDefinition: ["decorators"]
})) // → { ..., MethodDefinition: ["decorators", "key", "value"], ... }
```

## 📰 Change log

See [GitHub releases](https://github.com/eslint/eslint-visitor-keys/releases).

## 🍻 Contributing

Welcome. See [ESLint contribution guidelines](https://eslint.org/docs/developer-guide/contributing/).

### Development commands

- `npm test` runs tests and measures code coverage.
- `npm run lint` checks source codes with ESLint.
- `npm run coverage` opens the code coverage report of the previous test with your default browser.
- `npm run release` publishes this package to [npm] registory.

[npm]: https://www.npmjs.com/
[Node.js]: https://nodejs.org/en/
[ESTree]: https://github.com/estree/estree


code:
/**
 * @author Toru Nagashima <https://github.com/mysticatea>
 * See LICENSE file in root directory for full license.
 */
"use strict";

const KEYS = require("./visitor-keys.json");

// Types.
const NODE_TYPES = Object.freeze(Object.keys(KEYS));

// Freeze the keys.
for (const type of NODE_TYPES) {
    Object.freeze(KEYS[type]);
}
Object.freeze(KEYS);

// List to ignore keys.
const KEY_BLACKLIST = new Set([
    "parent",
    "leadingComments",
    "trailingComments"
]);

/**
 * Check whether a given key should be used or not.
 * @param {string} key The key to check.
 * @returns {boolean} `true` if the key should be used.
 */
function filterKey(key) {
    return !KEY_BLACKLIST.has(key) && key[0] !== "_";
}

//------------------------------------------------------------------------------
// Public interfaces
//------------------------------------------------------------------------------

module.exports = Object.freeze({

    /**
     * Visitor keys.
     * @type {{ [type: string]: string[] | undefined }}
     */
    KEYS,

    /**
     * Get visitor keys of a given node.
     * @param {Object} node The AST node to get keys.
     * @returns {string[]} Visitor keys of the node.
     */
    getKeys(node) {
        return Object.keys(node).filter(filterKey);
    },

    // Disable valid-jsdoc rule because it reports syntax error on the type of @returns.
    // eslint-disable-next-line valid-jsdoc
    /**
     * Make the union set with `KEYS` and given keys.
     * @param {Object} additionalKeys The additional keys.
     * @returns {{ [type: string]: string[] | undefined }} The union set.
     */
    unionWith(additionalKeys) {
        const retv = Object.assign({}, KEYS);

        for (const type of Object.keys(additionalKeys)) {
            if (retv.hasOwnProperty(type)) {
                const keys = new Set(additionalKeys[type]);

                for (const key of retv[type]) {
                    keys.add(key);
                }

                retv[type] = Object.freeze(Array.from(keys));
            } else {
                retv[type] = Object.freeze(Array.from(additionalKeys[type]));
            }
        }

        return Object.freeze(retv);
    }
});


AssignmentExpression: left right

AssignmentPattern: left right

ArrowFunctionExpression: params body

BinaryExpression: left right

CallExpression: callee arguments

CatchClause: param body

ClassDeclaration: id superClass body

ClassExpression: id superClass body

ConditionalExpression: test consequent alternate

DoWhileStatement: body test

ExportAllDeclaration: exported source

ExportNamedDeclaration: declaration specifiers source

ExportSpecifier: exported local

ForStatement: init test update body

ForInStatement: left right body

ForOfStatement: left right body

FunctionDeclaration: id params body

FunctionExpression: id params body

IfStatement: test consequent alternate

ImportDeclaration: specifiers source

ImportSpecifier: imported local

JSXAttribute: name value

JSXElement: openingElement children closingElement

JSXMemberExpression: object property

JSXNamespacedName: namespace name

JSXOpeningElement: name attributes

LabeledStatement: label body

LogicalExpression: left right

MemberExpression: object property

MetaProperty: meta property

MethodDefinition: key value

NewExpression: callee arguments

Property: key value

PropertyDefinition: key value

SwitchStatement: discriminant cases

SwitchCase: test consequent

TaggedTemplateExpression: tag quasi

TemplateLiteral: quasis expressions

TryStatement: block handler finalizer

VariableDeclarator: id init

WhileStatement: test body

WithStatement: object body


eslint-visitor-keys

description: Constants and utilities about visitor keys to traverse AST.

lint: eslint lib tests/lib

test: nyc mocha tests/lib

coverage: nyc report --reporter lcov && opener coverage/lcov-report/index.html

repository: eslint/eslint-visitor-keys


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

# eslint-webpack-plugin

> This is eslint-webpack-plugin 3.0 which works only with webpack 5. For the webpack 4, see the [2.x branch](https://github.com/webpack-contrib/eslint-webpack-plugin/tree/2.x).

This plugin uses [`eslint`](https://eslint.org/) to find and fix problems in your JavaScript code

## Getting Started

To begin, you'll need to install `eslint-webpack-plugin`:

```console
npm install eslint-webpack-plugin --save-dev
```

or

```console
yarn add -D eslint-webpack-plugin
```

or

```console
pnpm add -D eslint-webpack-plugin
```

> **Note**
>
> You also need to install `eslint >= 7` from npm, if you haven't already:

```console
npm install eslint --save-dev
```

or

```console
yarn add -D eslint
```

or

```console
pnpm add -D eslint
```

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

```js
const ESLintPlugin = require('eslint-webpack-plugin');

module.exports = {
  // ...
  plugins: [new ESLintPlugin(options)],
  // ...
};
```

## Options

You can pass [eslint options](https://eslint.org/docs/developer-guide/nodejs-api#-new-eslintoptions).

> **Note**
>
> The config option you provide will be passed to the `ESLint` class.
> This is a different set of options than what you'd specify in `package.json` or `.eslintrc`.
> See the [eslint docs](https://eslint.org/docs/developer-guide/nodejs-api#-new-eslintoptions) for more details.

> **Warning**:
>
> In eslint-webpack-plugin version 1 the options were passed to the now deprecated [CLIEngine](https://eslint.org/docs/developer-guide/nodejs-api#cliengine).

### `context`

- Type:

```ts
type context = string;
```

- Default: `compiler.context`

A string indicating the root of your files.

### `eslintPath`

- Type:

```ts
type eslintPath = string;
```

- Default: `eslint`

Path to `eslint` instance that will be used for linting. If the `eslintPath` is a folder like a official eslint, or specify a `formatter` option. now you don't have to install `eslint`.

### `extensions`

- Type:

```ts
type extensions = string | Array<string>;
```

- Default: `'js'`

Specify extensions that should be checked.

### `exclude`

- Type:

```ts
type exclude = string | Array<string>;
```

- Default: `'node_modules'`

Specify the files and/or directories to exclude. Must be relative to `options.context`.

### `resourceQueryExclude`

- Type:

```ts
type resourceQueryExclude = RegExp | Array<RegExp>;
```

- Default: `[]`

Specify the resource query to exclude.

### `files`

- Type:

```ts
type files = string | Array<string>;
```

- Default: `null`

Specify directories, files, or globs. Must be relative to `options.context`.
Directories are traversed recursively looking for files matching `options.extensions`.
File and glob patterns ignore `options.extensions`.

### `fix`

- Type:

```ts
type fix = boolean;
```

- Default: `false`

Will enable [ESLint autofix feature](https://eslint.org/docs/developer-guide/nodejs-api#-eslintoutputfixesresults).

**Be careful: this option will change source files.**

### `formatter`

- Type:

```ts
type formatter = string| (
  results:  Array<import('eslint').ESLint.LintResult>,
  data?: import('eslint').ESLint.LintResultData | undefined
) => string
```

- Default: `'stylish'`

Accepts a function that will have one argument: an array of eslint messages (object). The function must return the output as a string. You can use official [eslint formatters](https://eslint.org/docs/user-guide/formatters/).

### `lintDirtyModulesOnly`

- Type:

```ts
type lintDirtyModulesOnly = boolean;
```

- Default: `false`

Lint only changed files, skip lint on start.

### `threads`

- Type:

```ts
type threads = boolean | number;
```

- Default: `false`

Will run lint tasks across a thread pool. The pool size is automatic unless you specify a number.

### Errors and Warning

**By default the plugin will auto adjust error reporting depending on eslint errors/warnings counts.**
You can still force this behavior by using `emitError` **or** `emitWarning` options:

#### `emitError`

- Type:

```ts
type emitError = boolean;
```

- Default: `true`

The errors found will always be emitted, to disable set to `false`.

#### `emitWarning`

- Type:

```ts
type emitWarning = boolean;
```

- Default: `true`

The warnings found will always be emitted, to disable set to `false`.

#### `failOnError`

- Type:

```ts
type failOnError = boolean;
```

- Default: `true`

Will cause the module build to fail if there are any errors, to disable set to `false`.

#### `failOnWarning`

- Type:

```ts
type failOnWarning = boolean;
```

- Default: `false`

Will cause the module build to fail if there are any warnings, if set to `true`.

#### `quiet`

- Type:

```ts
type quiet = boolean;
```

- Default: `false`

Will process and report errors only and ignore warnings, if set to `true`.

#### `outputReport`

- Type:

```ts
type outputReport =
  | boolean
  | {
      filePath?: string | undefined;
      formatter?:
        | (
            | string
            | ((
                results: Array<import('eslint').ESLint.LintResult>,
                data?: import('eslint').ESLint.LintResultData | undefined
              ) => string)
          )
        | undefined;
    };
```

- Default: `false`

Write the output of the errors to a file, for example a checkstyle xml file for use for reporting on Jenkins CI.

The `filePath` is an absolute path or relative to the webpack config: `output.path`.
You can pass in a different `formatter` for the output file,
if none is passed in the default/configured formatter will be used.

## Changelog

[Changelog](CHANGELOG.md)

## License

[MIT](./LICENSE)

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


code:
"use strict";

class ESLintError extends Error {
  /**
   * @param {string=} messages
   */
  constructor(messages) {
    super(`[eslint] ${messages}`);
    this.name = 'ESLintError';
    this.stack = '';
  }

}

module.exports = ESLintError;


code:
"use strict";

const {
  cpus
} = require('os');

const {
  Worker: JestWorker
} = require('jest-worker');

const {
  getESLintOptions
} = require('./options');

const {
  jsonStringifyReplacerSortKeys
} = require('./utils');
/** @type {{[key: string]: any}} */

const cache = {};
/** @typedef {import('eslint').ESLint} ESLint */

/** @typedef {import('eslint').ESLint.LintResult} LintResult */

/** @typedef {import('./options').Options} Options */

/** @typedef {() => Promise<void>} AsyncTask */

/** @typedef {(files: string|string[]) => Promise<LintResult[]>} LintTask */

/** @typedef {{threads: number, ESLint: ESLint, eslint: ESLint, lintFiles: LintTask, cleanup: AsyncTask}} Linter */

/** @typedef {JestWorker & {lintFiles: LintTask}} Worker */

/**
 * @param {Options} options
 * @returns {Linter}
 */

function loadESLint(options) {
  const {
    eslintPath
  } = options;

  const {
    ESLint
  } = require(eslintPath || 'eslint'); // Filter out loader options before passing the options to ESLint.

  const eslint = new ESLint(getESLintOptions(options));
  return {
    threads: 1,
    ESLint,
    eslint,
    lintFiles: async files => {
      const results = await eslint.lintFiles(files); // istanbul ignore else

      if (options.fix) {
        await ESLint.outputFixes(results);
      }

      return results;
    },
    // no-op for non-threaded
    cleanup: async () => {}
  };
}
/**
 * @param {string|undefined} key
 * @param {number} poolSize
 * @param {Options} options
 * @returns {Linter}
 */

function loadESLintThreaded(key, poolSize, options) {
  const cacheKey = getCacheKey(key, options);
  const {
    eslintPath = 'eslint'
  } = options;

  const source = require.resolve('./worker');

  const workerOptions = {
    enableWorkerThreads: true,
    numWorkers: poolSize,
    setupArgs: [{
      eslintPath,
      eslintOptions: getESLintOptions(options)
    }]
  };
  const local = loadESLint(options);
  let worker =
  /** @type {Worker?} */
  new JestWorker(source, workerOptions);
  /** @type {Linter} */

  const context = { ...local,
    threads: poolSize,
    lintFiles: async files => worker && (await worker.lintFiles(files)) ||
    /* istanbul ignore next */
    [],
    cleanup: async () => {
      cache[cacheKey] = local;

      context.lintFiles = files => local.lintFiles(files);

      if (worker) {
        worker.end();
        worker = null;
      }
    }
  };
  return context;
}
/**
 * @param {string|undefined} key
 * @param {Options} options
 * @returns {Linter}
 */

function getESLint(key, {
  threads,
  ...options
}) {
  const max = typeof threads !== 'number' ? threads ? cpus().length - 1 : 1 :
  /* istanbul ignore next */
  threads;
  const cacheKey = getCacheKey(key, {
    threads,
    ...options
  });

  if (!cache[cacheKey]) {
    cache[cacheKey] = max > 1 ? loadESLintThreaded(key, max, options) : loadESLint(options);
  }

  return cache[cacheKey];
}
/**
 * @param {string|undefined} key
 * @param {Options} options
 * @returns {string}
 */

function getCacheKey(key, options) {
  return JSON.stringify({
    key,
    options
  }, jsonStringifyReplacerSortKeys);
}

module.exports = {
  loadESLint,
  loadESLintThreaded,
  getESLint
};


code:
"use strict";

const {
  isAbsolute,
  join
} = require('path');

const {
  isMatch
} = require('micromatch');

const {
  getOptions
} = require('./options');

const linter = require('./linter');

const {
  arrify,
  parseFiles,
  parseFoldersToGlobs
} = require('./utils');
/** @typedef {import('webpack').Compiler} Compiler */

/** @typedef {import('./options').Options} Options */

const ESLINT_PLUGIN = 'ESLintWebpackPlugin';
let counter = 0;

class ESLintWebpackPlugin {
  /**
   * @param {Options} options
   */
  constructor(options = {}) {
    this.key = ESLINT_PLUGIN;
    this.options = getOptions(options);
    this.run = this.run.bind(this);
  }
  /**
   * @param {Compiler} compiler
   * @returns {void}
   */

  apply(compiler) {
    // Generate key for each compilation,
    // this differentiates one from the other when being cached.
    this.key = compiler.name || `${this.key}_${counter += 1}`;
    const options = { ...this.options,
      exclude: parseFiles(this.options.exclude || [], this.getContext(compiler)),
      extensions: arrify(this.options.extensions),
      resourceQueryExclude: arrify(this.options.resourceQueryExclude || []).map(item => item instanceof RegExp ? item : new RegExp(item)),
      files: parseFiles(this.options.files || '', this.getContext(compiler))
    };
    const wanted = parseFoldersToGlobs(options.files, options.extensions);
    const exclude = parseFoldersToGlobs(this.options.exclude ? options.exclude : '**/node_modules/**', []); // If `lintDirtyModulesOnly` is disabled,
    // execute the linter on the build

    if (!this.options.lintDirtyModulesOnly) {
      compiler.hooks.run.tapPromise(this.key, c => this.run(c, options, wanted, exclude));
    }

    let isFirstRun = this.options.lintDirtyModulesOnly;
    compiler.hooks.watchRun.tapPromise(this.key, c => {
      if (isFirstRun) {
        isFirstRun = false;
        return Promise.resolve();
      }

      return this.run(c, options, wanted, exclude);
    });
  }
  /**
   * @param {Compiler} compiler
   * @param {Omit<Options, 'resourceQueryExclude'> & {resourceQueryExclude: RegExp[]}} options
   * @param {string[]} wanted
   * @param {string[]} exclude
   */

  async run(compiler, options, wanted, exclude) {
    // Do not re-hook
    if ( // @ts-ignore
    compiler.hooks.compilation.taps.find(({
      name
    }) => name === this.key)) {
      return;
    }

    compiler.hooks.compilation.tap(this.key, compilation => {
      /** @type {import('./linter').Linter} */
      let lint;
      /** @type {import('./linter').Reporter} */

      let report;
      /** @type number */

      let threads;

      try {
        ({
          lint,
          report,
          threads
        } = linter(this.key, options, compilation));
      } catch (e) {
        compilation.errors.push(e);
        return;
      }
      /** @type {string[]} */

      const files = []; // @ts-ignore
      // Add the file to be linted

      compilation.hooks.succeedModule.tap(this.key, ({
        resource
      }) => {
        if (resource) {
          const [file, query] = resource.split('?');

          if (file && !files.includes(file) && isMatch(file, wanted, {
            dot: true
          }) && !isMatch(file, exclude, {
            dot: true
          }) && options.resourceQueryExclude.every(reg => !reg.test(query))) {
            files.push(file);

            if (threads > 1) {
              lint(file);
            }
          }
        }
      }); // Lint all files added

      compilation.hooks.finishModules.tap(this.key, () => {
        if (files.length > 0 && threads <= 1) {
          lint(files);
        }
      }); // await and interpret results

      compilation.hooks.additionalAssets.tapPromise(this.key, processResults);

      async function processResults() {
        const {
          errors,
          warnings,
          generateReportAsset
        } = await report();

        if (warnings && !options.failOnWarning) {
          // @ts-ignore
          compilation.warnings.push(warnings);
        } else if (warnings && options.failOnWarning) {
          // @ts-ignore
          compilation.errors.push(warnings);
        }

        if (errors && options.failOnError) {
          // @ts-ignore
          compilation.errors.push(errors);
        } else if (errors && !options.failOnError) {
          // @ts-ignore
          compilation.warnings.push(errors);
        }

        if (generateReportAsset) {
          await generateReportAsset(compilation);
        }
      }
    });
  }
  /**
   *
   * @param {Compiler} compiler
   * @returns {string}
   */

  getContext(compiler) {
    if (!this.options.context) {
      return String(compiler.options.context);
    }

    if (!isAbsolute(this.options.context)) {
      return join(String(compiler.options.context), this.options.context);
    }

    return this.options.context;
  }

}

module.exports = ESLintWebpackPlugin;


code:
"use strict";

const {
  dirname,
  isAbsolute,
  join
} = require('path');

const ESLintError = require('./ESLintError');

const {
  getESLint
} = require('./getESLint');
/** @typedef {import('eslint').ESLint} ESLint */

/** @typedef {import('eslint').ESLint.Formatter} Formatter */

/** @typedef {import('eslint').ESLint.LintResult} LintResult */

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

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

/** @typedef {import('./options').Options} Options */

/** @typedef {import('./options').FormatterFunction} FormatterFunction */

/** @typedef {(compilation: Compilation) => Promise<void>} GenerateReport */

/** @typedef {{errors?: ESLintError, warnings?: ESLintError, generateReportAsset?: GenerateReport}} Report */

/** @typedef {() => Promise<Report>} Reporter */

/** @typedef {(files: string|string[]) => void} Linter */

/** @typedef {{[files: string]: LintResult}} LintResultMap */

/** @type {WeakMap<Compiler, LintResultMap>} */

const resultStorage = new WeakMap();
/**
 * @param {string|undefined} key
 * @param {Options} options
 * @param {Compilation} compilation
 * @returns {{lint: Linter, report: Reporter, threads: number}}
 */

function linter(key, options, compilation) {
  /** @type {ESLint} */
  let eslint;
  /** @type {(files: string|string[]) => Promise<LintResult[]>} */

  let lintFiles;
  /** @type {() => Promise<void>} */

  let cleanup;
  /** @type number */

  let threads;
  /** @type {Promise<LintResult[]>[]} */

  const rawResults = [];
  const crossRunResultStorage = getResultStorage(compilation);

  try {
    ({
      eslint,
      lintFiles,
      cleanup,
      threads
    } = getESLint(key, options));
  } catch (e) {
    throw new ESLintError(e.message);
  }

  return {
    lint,
    report,
    threads
  };
  /**
   * @param {string | string[]} files
   */

  function lint(files) {
    for (const file of asList(files)) {
      delete crossRunResultStorage[file];
    }

    rawResults.push(lintFiles(files).catch(e => {
      // @ts-ignore
      compilation.errors.push(new ESLintError(e.message));
      return [];
    }));
  }

  async function report() {
    // Filter out ignored files.
    let results = await removeIgnoredWarnings(eslint, // Get the current results, resetting the rawResults to empty
    await flatten(rawResults.splice(0, rawResults.length)));
    await cleanup();

    for (const result of results) {
      crossRunResultStorage[result.filePath] = result;
    }

    results = Object.values(crossRunResultStorage); // do not analyze if there are no results or eslint config

    if (!results || results.length < 1) {
      return {};
    }

    const formatter = await loadFormatter(eslint, options.formatter);
    const {
      errors,
      warnings
    } = await formatResults(formatter, parseResults(options, results));
    return {
      errors,
      warnings,
      generateReportAsset
    };
    /**
     * @param {Compilation} compilation
     * @returns {Promise<void>}
     */

    async function generateReportAsset({
      compiler
    }) {
      const {
        outputReport
      } = options;
      /**
       * @param {string} name
       * @param {string | Buffer} content
       */

      const save = (name, content) =>
      /** @type {Promise<void>} */
      new Promise((finish, bail) => {
        const {
          mkdir,
          writeFile
        } = compiler.outputFileSystem; // ensure directory exists
        // @ts-ignore - the types for `outputFileSystem` are missing the 3 arg overload

        mkdir(dirname(name), {
          recursive: true
        }, err => {
          /* istanbul ignore if */
          if (err) bail(err);else writeFile(name, content, err2 => {
            /* istanbul ignore if */
            if (err2) bail(err2);else finish();
          });
        });
      });

      if (!outputReport || !outputReport.filePath) {
        return;
      }

      const content = await (outputReport.formatter ? (await loadFormatter(eslint, outputReport.formatter)).format(results) : formatter.format(results));
      let {
        filePath
      } = outputReport;

      if (!isAbsolute(filePath)) {
        filePath = join(compiler.outputPath, filePath);
      }

      await save(filePath, content);
    }
  }
}
/**
 * @param {Formatter} formatter
 * @param {{ errors: LintResult[]; warnings: LintResult[]; }} results
 * @returns {Promise<{errors?: ESLintError, warnings?: ESLintError}>}
 */

async function formatResults(formatter, results) {
  let errors;
  let warnings;

  if (results.warnings.length > 0) {
    warnings = new ESLintError(await formatter.format(results.warnings));
  }

  if (results.errors.length > 0) {
    errors = new ESLintError(await formatter.format(results.errors));
  }

  return {
    errors,
    warnings
  };
}
/**
 * @param {Options} options
 * @param {LintResult[]} results
 * @returns {{errors: LintResult[], warnings: LintResult[]}}
 */

function parseResults(options, results) {
  /** @type {LintResult[]} */
  const errors = [];
  /** @type {LintResult[]} */

  const warnings = [];
  results.forEach(file => {
    if (fileHasErrors(file)) {
      const messages = file.messages.filter(message => options.emitError && message.severity === 2);

      if (messages.length > 0) {
        errors.push({ ...file,
          messages
        });
      }
    }

    if (fileHasWarnings(file)) {
      const messages = file.messages.filter(message => options.emitWarning && message.severity === 1);

      if (messages.length > 0) {
        warnings.push({ ...file,
          messages
        });
      }
    }
  });
  return {
    errors,
    warnings
  };
}
/**
 * @param {LintResult} file
 * @returns {boolean}
 */

function fileHasErrors(file) {
  return file.errorCount > 0;
}
/**
 * @param {LintResult} file
 * @returns {boolean}
 */

function fileHasWarnings(file) {
  return file.warningCount > 0;
}
/**
 * @param {ESLint} eslint
 * @param {string|FormatterFunction=} formatter
 * @returns {Promise<Formatter>}
 */

async function loadFormatter(eslint, formatter) {
  if (typeof formatter === 'function') {
    return {
      format: formatter
    };
  }

  if (typeof formatter === 'string') {
    try {
      return eslint.loadFormatter(formatter);
    } catch (_) {// Load the default formatter.
    }
  }

  return eslint.loadFormatter();
}
/**
 * @param {ESLint} eslint
 * @param {LintResult[]} results
 * @returns {Promise<LintResult[]>}
 */

async function removeIgnoredWarnings(eslint, results) {
  const filterPromises = results.map(async result => {
    // Short circuit the call to isPathIgnored.
    //   fatal is false for ignored file warnings.
    //   ruleId is unset for internal ESLint errors.
    //   line is unset for warnings not involving file contents.
    const ignored = result.messages.length === 0 || result.warningCount === 1 && result.errorCount === 0 && !result.messages[0].fatal && !result.messages[0].ruleId && !result.messages[0].line && (await eslint.isPathIgnored(result.filePath));
    return ignored ? false : result;
  }); // @ts-ignore

  return (await Promise.all(filterPromises)).filter(result => !!result);
}
/**
 * @param {Promise<LintResult[]>[]} results
 * @returns {Promise<LintResult[]>}
 */

async function flatten(results) {
  /**
   * @param {LintResult[]} acc
   * @param {LintResult[]} list
   */
  const flat = (acc, list) => [...acc, ...list];

  return (await Promise.all(results)).reduce(flat, []);
}
/**
 * @param {Compilation} compilation
 * @returns {LintResultMap}
 */

function getResultStorage({
  compiler
}) {
  let storage = resultStorage.get(compiler);

  if (!storage) {
    resultStorage.set(compiler, storage = {});
  }

  return storage;
}
/**
 * @param {string | string[]} x
 */

function asList(x) {
  /* istanbul ignore next */
  return Array.isArray(x) ? x : [x];
}

module.exports = linter;


code:
"use strict";

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

const schema = require('./options.json');
/** @typedef {import("eslint").ESLint.Options} ESLintOptions */

/** @typedef {import('eslint').ESLint.LintResult} LintResult */

/** @typedef {import('eslint').ESLint.LintResultData} LintResultData */

/**
 * @callback FormatterFunction
 * @param {LintResult[]} results
 * @param {LintResultData=} data
 * @returns {string}
 */

/**
 * @typedef {Object} OutputReport
 * @property {string=} filePath
 * @property {string|FormatterFunction=} formatter
 */

/**
 * @typedef {Object} PluginOptions
 * @property {string=} context
 * @property {boolean=} emitError
 * @property {boolean=} emitWarning
 * @property {string=} eslintPath
 * @property {string|string[]=} exclude
 * @property {string|string[]=} extensions
 * @property {boolean=} failOnError
 * @property {boolean=} failOnWarning
 * @property {string|string[]=} files
 * @property {boolean=} fix
 * @property {string|FormatterFunction=} formatter
 * @property {boolean=} lintDirtyModulesOnly
 * @property {boolean=} quiet
 * @property {OutputReport=} outputReport
 * @property {number|boolean=} threads
 * @property {RegExp|RegExp[]=} resourceQueryExclude
 */

/** @typedef {PluginOptions & ESLintOptions} Options */

/**
 * @param {Options} pluginOptions
 * @returns {PluginOptions}
 */

function getOptions(pluginOptions) {
  const options = {
    extensions: 'js',
    emitError: true,
    emitWarning: true,
    failOnError: true,
    resourceQueryExclude: [],
    ...pluginOptions,
    ...(pluginOptions.quiet ? {
      emitError: true,
      emitWarning: false
    } : {})
  }; // @ts-ignore

  validate(schema, options, {
    name: 'ESLint Webpack Plugin',
    baseDataPath: 'options'
  });
  return options;
}
/**
 * @param {Options} loaderOptions
 * @returns {ESLintOptions}
 */

function getESLintOptions(loaderOptions) {
  const eslintOptions = { ...loaderOptions
  }; // Keep the fix option because it is common to both the loader and ESLint.

  const {
    fix,
    extensions,
    ...eslintOnlyOptions
  } = schema.properties; // No need to guard the for-in because schema.properties has hardcoded keys.
  // eslint-disable-next-line guard-for-in

  for (const option in eslintOnlyOptions) {
    // @ts-ignore
    delete eslintOptions[option];
  }

  return eslintOptions;
}

module.exports = {
  getOptions,
  getESLintOptions
};


type: object

description: A string indicating the root of your files.

type: string

description: The errors found will always be emitted, to disable set to `false`.

type: boolean

description: The warnings found will always be emitted, to disable set to `false`.

description: Path to `eslint` instance that will be used for linting. If the `eslintPath` is a folder like a official eslint, or specify a `formatter` option. now you dont have to install `eslint`.

description: Specify the files and/or directories to exclude. Must be relative to `options.context`.

type: array

description: Specify the resource query to exclude.

instanceof: RegExp

description: Will cause the module build to fail if there are any errors, to disable set to `false`.

description: Will cause the module build to fail if there are any warnings, if set to `true`.

description: Specify the files and/or directories to traverse. Must be relative to `options.context`.

description: Specify extensions that should be checked.

description: Will enable ESLint autofix feature

description: Accepts a function that will have one argument: an array of eslint messages (object). The function must return the output as a string.

instanceof: Function

description: Lint only changed files, skip lint on start.

description: Will process and report errors only and ignore warnings, if set to `true`.

description: Write the output of the errors to a file, for example a checkstyle xml file for use for reporting on Jenkins CI

description: The `filePath` is relative to the webpack config: output.path

description: You can pass in a different formatter for the output file, if none is passed in the default/configured formatter will be used

description: Default is false. Set to true for an auto-selected pool size based on number of cpus. Set to a number greater than 1 to set an explicit pool size. Set to false, 1, or less to disable and only run in main process.

type: number


code:
"use strict";

const {
  resolve
} = require('path');

const {
  statSync
} = require('fs');

const normalizePath = require('normalize-path');
/**
 * @template T
 * @param {T} value
 * @return {
   T extends (null | undefined)
     ? []
     : T extends string
       ? [string]
       : T extends readonly unknown[]
         ? T
         : T extends Iterable<infer T>
           ? T[]
           : [T]
 }
 */

/* istanbul ignore next */

function arrify(value) {
  // eslint-disable-next-line no-undefined
  if (value === null || value === undefined) {
    // @ts-ignore
    return [];
  }

  if (Array.isArray(value)) {
    // @ts-ignore
    return value;
  }

  if (typeof value === 'string') {
    // @ts-ignore
    return [value];
  } // @ts-ignore

  if (typeof value[Symbol.iterator] === 'function') {
    // @ts-ignore
    return [...value];
  } // @ts-ignore

  return [value];
}
/**
 * @param {string|string[]} files
 * @param {string} context
 * @returns {string[]}
 */

function parseFiles(files, context) {
  return arrify(files).map((
  /** @type {string} */
  file) => normalizePath(resolve(context, file)));
}
/**
 * @param {string|string[]} patterns
 * @param {string|string[]} extensions
 * @returns {string[]}
 */

function parseFoldersToGlobs(patterns, extensions = []) {
  const extensionsList = arrify(extensions);
  const [prefix, postfix] = extensionsList.length > 1 ? ['{', '}'] : ['', ''];
  const extensionsGlob = extensionsList.map((
  /** @type {string} */
  extension) => extension.replace(/^\./u, '')).join(',');
  return arrify(patterns).map((
  /** @type {string} */
  pattern) => {
    try {
      // The patterns are absolute because they are prepended with the context.
      const stats = statSync(pattern);
      /* istanbul ignore else */

      if (stats.isDirectory()) {
        return pattern.replace(/[/\\]*?$/u, `/**${extensionsGlob ? `/*.${prefix + extensionsGlob + postfix}` : ''}`);
      }
    } catch (_) {// Return the pattern as is on error.
    }

    return pattern;
  });
}
/**
 * @param {string} _ key, but unused
 * @param {any} value
 */

const jsonStringifyReplacerSortKeys = (_, value) => {
  /**
   * @param {{ [x: string]: any; }} sorted
   * @param {string | number} key
   */
  const insert = (sorted, key) => {
    // eslint-disable-next-line no-param-reassign
    sorted[key] = value[key];
    return sorted;
  };

  return value instanceof Object && !(value instanceof Array) ? Object.keys(value).sort().reduce(insert, {}) : value;
};

module.exports = {
  arrify,
  parseFiles,
  parseFoldersToGlobs,
  jsonStringifyReplacerSortKeys
};


code:
"use strict";

/** @typedef {import('eslint').ESLint} ESLint */

/** @typedef {import('eslint').ESLint.Options} ESLintOptions */
Object.assign(module.exports, {
  lintFiles,
  setup
});
/** @type {{ new (arg0: import("eslint").ESLint.Options): import("eslint").ESLint; outputFixes: (arg0: import("eslint").ESLint.LintResult[]) => any; }} */

let ESLint;
/** @type {ESLint} */

let eslint;
/** @type {boolean} */

let fix;
/**
 * @typedef {object} setupOptions
 * @property {string=} eslintPath - import path of eslint
 * @property {ESLintOptions=} eslintOptions - linter options
 *
 * @param {setupOptions} arg0 - setup worker
 */

function setup({
  eslintPath,
  eslintOptions = {}
}) {
  fix = !!(eslintOptions && eslintOptions.fix);
  ({
    ESLint
  } = require(eslintPath || 'eslint'));
  eslint = new ESLint(eslintOptions);
}
/**
 * @param {string | string[]} files
 */

async function lintFiles(files) {
  const result = await eslint.lintFiles(files); // if enabled, use eslint autofixing where possible

  if (fix) {
    await ESLint.outputFixes(result);
  }

  return result;
}


code:
# ajv-keywords

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

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

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

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

## Contents

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

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

## Install

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

```
npm install ajv-keywords
```

## Usage

To add all available keywords:

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

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

To add a single keyword:

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

To add multiple keywords:

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

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

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

To add all keywords via Ajv options:

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

To add one or several keywords via options:

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

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

## Keywords

### Types

#### `typeof`

Based on JavaScript `typeof` operation.

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

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

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

#### `instanceof`

Based on JavaScript `instanceof` operation.

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

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

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

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

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

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

### Keywords for numbers

#### `range` and `exclusiveRange`

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

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

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

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

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

### Keywords for strings

#### `regexp`

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

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

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

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

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

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

#### `transform`

This keyword allows a string to be modified during validation.

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

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

**Supported transformations:**

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

Transformations are applied in the order they are listed.

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

**Example: multiple transformations**

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

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

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

**Example: `enumcase`**

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

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

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

### Keywords for arrays

#### `uniqueItemProperties`

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

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

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

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

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

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

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

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

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

### Keywords for objects

#### `allRequired`

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

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

The value of this keyword must be boolean.

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

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

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

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

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

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

#### `anyRequired`

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

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

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

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

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

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

#### `oneRequired`

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

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

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

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

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

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

#### `patternRequired`

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

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

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

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

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

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

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

#### `prohibited`

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

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

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

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

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

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

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

#### `deepProperties`

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

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

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

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

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

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

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

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

#### `deepRequired`

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

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

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

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

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

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

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

### Keywords for all types

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

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

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

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

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

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

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

The validation succeeds in one of the following cases:

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

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

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

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

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

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

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

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

#### `dynamicDefaults`

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

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

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

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

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

There are several predefined dynamic default functions:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## Security contact

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

Please do NOT report security vulnerabilities via GitHub issues.

## Open-source software support

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

## License

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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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