From 9acbf0ddf5836cff71e7c5c14e2b72c1a9f81a9d Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Thu, 20 Nov 2025 16:39:54 +0100 Subject: [PATCH 01/11] feat: add types to ESLint Scope --- .github/workflows/ci.yml | 13 + eslint.config.js | 25 +- package.json | 7 +- packages/eslint-scope/lib/index.d.cts | 614 ++++++++++++++++++ packages/eslint-scope/lib/index.d.ts | 6 + packages/eslint-scope/package.json | 19 +- .../tests/types/cjs-import.test.cts | 16 + .../eslint-scope/tests/types/tsconfig.json | 17 + .../eslint-scope/tests/types/types.test.ts | 410 ++++++++++++ 9 files changed, 1119 insertions(+), 8 deletions(-) create mode 100644 packages/eslint-scope/lib/index.d.cts create mode 100644 packages/eslint-scope/lib/index.d.ts create mode 100644 packages/eslint-scope/tests/types/cjs-import.test.cts create mode 100644 packages/eslint-scope/tests/types/tsconfig.json create mode 100644 packages/eslint-scope/tests/types/types.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74ab1811..97a91d7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,3 +45,16 @@ jobs: run: npm run build - name: Run tests run: npm run test + are-the-types-wrong: + name: Are the types wrong? + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: 'lts/*' + - name: Install dependencies + run: npm install + - name: Are the types wrong? + run: npm run lint:types diff --git a/eslint.config.js b/eslint.config.js index 433c4f75..9fdba50c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,7 +2,9 @@ import { defineConfig, globalIgnores } from "eslint/config"; import eslintConfigESLint from "eslint-config-eslint"; import eslintConfigESLintFormatting from "eslint-config-eslint/formatting"; import eslintPluginChaiFriendly from "eslint-plugin-chai-friendly"; +import * as expectType from "eslint-plugin-expect-type"; import globals from "globals"; +import tsParser from "@typescript-eslint/parser"; export default defineConfig([ globalIgnores([ @@ -11,8 +13,10 @@ export default defineConfig([ "**/coverage/", "packages/espree/tools/create-test-example.js" ]), - eslintConfigESLint, - eslintConfigESLintFormatting, + { + files: ["**/*.{,c}js"], + extends: [eslintConfigESLint, eslintConfigESLintFormatting] + }, { files: ["packages/*/tests/lib/**"], languageOptions: { @@ -22,7 +26,7 @@ export default defineConfig([ } }, { - files: ["packages/eslint-scope/tests/**"], + files: ["packages/eslint-scope/tests/**/*.{,c}js"], languageOptions: { globals: { ...globals.mocha @@ -68,6 +72,21 @@ export default defineConfig([ } } }, + { + files: ["packages/eslint-scope/tests/types/*.{,c}ts"], + languageOptions: { + parser: tsParser, + parserOptions: { + project: ["packages/eslint-scope/tests/types/tsconfig.json"] + } + }, + plugins: { + "expect-type": expectType + }, + rules: { + "expect-type/expect": "error" + } + }, { files: ["**/tools/**"], rules: { diff --git a/package.json b/package.json index 1a47d83c..fbc9d8bc 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "test": "npm test --workspaces --if-present", "build": "npm run build --workspaces --if-present", "lint": "eslint", - "lint:fix": "eslint --fix" + "lint:fix": "eslint --fix", + "lint:types": "npm run lint:types --workspaces --if-present" }, "workspaces": [ "packages/*" @@ -16,15 +17,17 @@ "pre-commit": "lint-staged" }, "lint-staged": { - "*.{js,cjs}": [ + "*.{js,cjs,ts,cts}": [ "eslint --fix" ] }, "devDependencies": { + "@typescript-eslint/parser": "^8.47.0", "c8": "^10.1.3", "eslint": "^9.35.0", "eslint-config-eslint": "^13.0.0", "eslint-plugin-chai-friendly": "^1.0.0", + "eslint-plugin-expect-type": "^0.6.2", "globals": "^16.0.0", "lint-staged": "^15.2.0", "mocha": "^11.1.0", diff --git a/packages/eslint-scope/lib/index.d.cts b/packages/eslint-scope/lib/index.d.cts new file mode 100644 index 00000000..a136fe56 --- /dev/null +++ b/packages/eslint-scope/lib/index.d.cts @@ -0,0 +1,614 @@ +/** + * @fileoverview This file contains the types for ESLint Scope. + * It was initially extracted from the DefinitelyTyped repository. + */ + +/* + * MIT License + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + */ + +import * as eslint from "eslint"; +import { VisitorKeys } from "eslint-visitor-keys"; +import { Visitor, VisitorOptions } from "esrecurse"; +import * as ESTree from "estree"; + +/** + * Options for scope analysis. + */ +export interface AnalyzeOptions { + /** + * Whether to ignore eval() calls, which normally create scopes. + * @default false + */ + ignoreEval?: boolean; + + /** + * Whether to create a top-level function scope for CommonJS evaluation. + * @default false + */ + nodejsScope?: boolean; + + /** + * Whether to evaluate code in strict mode even outside modules or without "use strict". + * @default false + */ + impliedStrict?: boolean; + + /** + * The ECMAScript version to use for evaluation (e.g., 5, 2015, 2022). + * @default 5 + */ + ecmaVersion?: number; + + /** + * The type of JavaScript file to evaluate. + * @default "script" + */ + sourceType?: "script" | "module" | "commonjs"; + + /** + * Visitor key information for performance enhancement. + * @default null + */ + childVisitorKeys?: VisitorKeys | null; + + /** + * Strategy to use when childVisitorKeys is not specified. + * @default "iteration" + */ + fallback?: "iteration" | ((node: ESTree.Node) => string[]); + + /** + * Whether to enable optimistic scope analysis. + * @default false + */ + optimistic?: boolean; + + /** + * Enables the tracking of JSX components as variable references. + * @default false + */ + jsx?: boolean; +} + +export type PatternVisitorCallback = ( + pattern: ESTree.Identifier, + misc: { + topLevel: boolean; + rest: boolean; + assignments: ESTree.AssignmentPattern[]; + }, +) => void; + +/** + * Manages the scope hierarchy of an AST. + */ +export class ScopeManager implements eslint.Scope.ScopeManager { + /** + * Creates a new ScopeManager instance. + * @param options Options for scope analysis. + */ + constructor(options: AnalyzeOptions); + + /** + * The global scope. + */ + globalScope: GlobalScope; + + /** + * All scopes in the analyzed program. + */ + scopes: Scope[]; + + /** + * Adds variables to the global scope and resolves references to them. + * @param names An array of strings, the names of variables to add to the global scope. + * @returns void + */ + addGlobals(names: ReadonlyArray): void; + + /** + * Acquires the scope for a given node. + * @param node The AST node to get the scope for. + * @param inner Whether to get the innermost scope. + * @returns The scope or null if not found. + */ + acquire(node: ESTree.Node, inner?: boolean): Scope | null; + + /** + * acquire all scopes from node. + * @param node node for the acquired scope. + * @returns Scope array + */ + acquireAll(node: ESTree.Node): Scope[] | null; + + /** + * Releases a scope, moving to its parent. + * @param node The AST node to release the scope for. + * @param inner Whether to release the innermost scope. + * @returns The parent scope or null if not found. + */ + release(node: ESTree.Node, inner?: boolean): Scope | null; + + /** + * Gets all scopes for a given node, including parents. + * @param node The AST node to get scopes for. + * @param inner Whether to start from the innermost scope. + * @returns Array of scopes or empty array if none found. + */ + getDeclaredVariables(node: ESTree.Node): Variable[]; + + isGlobalReturn(): boolean; + + isModule(): boolean; + + isImpliedStrict(): boolean; + + isStrictModeSupported(): boolean; +} + +/** + * Base export class for all scopes. + */ +export class Scope + implements eslint.Scope.Scope +{ + /** + * Creates a new Scope instance. + * @param scopeManager The scope manager this scope belongs to. + * @param type The type of the scope. + * @param upperScope The parent scope, or null for the global scope. + * @param block The AST node that created this scope. + * @param isMethodDefinition Whether this scope is for a method definition. + */ + constructor( + scopeManager: ScopeManager, + type: string, + upperScope: Scope | null, + block: ESTree.Node, + isMethodDefinition: boolean, + ); + + /** + * The type of the scope (e.g., 'global', 'function'). + */ + type: eslint.Scope.Scope["type"]; + + /** + * Whether the scope is in strict mode. + */ + isStrict: boolean; + + /** + * The parent scope, or null for the global scope. + */ + upper: Scope | null; + + /** + * The scope where variables are declared (same as this for most scopes). + */ + variableScope: this; + + /** + * Variables defined in this scope. + */ + variables: TVariable[]; + + /** + * References to variables in this scope. + */ + references: TReference[]; + + /** + * Child scopes. + */ + childScopes: Scope[]; + + /** + * The AST node that created this scope. + */ + block: ESTree.Node; + + /** + * Whether this is a function expression scope. + */ + functionExpressionScope: boolean; + + /** + * Implicit references (e.g., 'arguments' in functions). + */ + implicit: { left: TReference[]; set: Map; variables: Variable[] }; + + /** + * Map of variable names to variables. + */ + set: eslint.Scope.Scope["set"]; + + /** + * The tainted variables of this scope. + */ + taints: Map; + + /** + * References that pass through this scope to outer scopes. + */ + through: eslint.Scope.Scope["through"]; + + /** + * Dynamic flag for certain scope types. + */ + dynamic: boolean; + + /** + * Direct call to eval() flag. + */ + directCallToEvalScope: boolean; + + /** + * This scope flag. + */ + thisFound: boolean; + + /** + * Resolves a reference in this scope. + * @param ref The reference to resolve. + * @param noChain Whether to avoid chaining to parent scopes. + */ + resolve(ref: Reference, noChain?: boolean): void; + + /** + * Whether the reference is static. + */ + isStatic(): boolean; + + /** + * returns this scope has materialized arguments. + */ + isArgumentsMaterialized(): boolean; + + /** + * returns this scope has materialized `this` reference + */ + isThisMaterialized(): boolean; + + isUsedName(name: string): boolean; +} + +/** + * Global scope. + */ +export class GlobalScope extends Scope { + /** + * Creates a new GlobalScope instance. + * @param scopeManager The scope manager this scope belongs to. + * @param block The AST node that created this scope. + */ + constructor(scopeManager: ScopeManager, block: ESTree.Node); + + type: "global"; +} + +/** + * Module scope. + */ +export class ModuleScope extends Scope { + /** + * Creates a new ModuleScope instance. + * @param scopeManager The scope manager this scope belongs to. + * @param upper The parent scope. + * @param block The AST node that created this scope. + */ + constructor(scopeManager: ScopeManager, upper: Scope, block: ESTree.Node); + + type: "module"; +} + +/** + * Function scope. + */ +export class FunctionScope extends Scope { + /** + * Creates a new FunctionScope instance. + * @param scopeManager The scope manager this scope belongs to. + * @param upper The parent scope. + * @param block The AST node that created this scope. + * @param isMethodDefinition Whether this scope is for a method definition. + */ + constructor( + scopeManager: ScopeManager, + upper: Scope, + block: ESTree.Node, + isMethodDefinition: boolean, + ); + + type: "function"; + + isArgumentsMaterialized(): boolean; + + isThisMaterialized(): boolean; +} + +/** + * Block scope. + */ +export class BlockScope extends Scope { + /** + * Creates a new BlockScope instance. + * @param scopeManager The scope manager this scope belongs to. + * @param upper The parent scope. + * @param block The AST node that created this scope. + */ + constructor(scopeManager: ScopeManager, upper: Scope, block: ESTree.Node); + + type: "block"; +} + +/** + * Represents a variable in a scope. + */ +export class Variable implements eslint.Scope.Variable { + /** + * Creates a new Variable instance. + * @param name The name of the variable. + * @param scope The scope where the variable is defined. + */ + constructor(name: string, scope: Scope); + + /** + * The name of the variable. + */ + name: string; + + /** + * The scope where the variable is defined. + */ + scope: Scope; + + /** + * Identifiers that declare this variable. + */ + identifiers: ESTree.Identifier[]; + + /** + * References to this variable. + */ + references: TReference[]; + + /** + * Definitions of this variable. + */ + defs: eslint.Scope.Definition[]; + + /** + * Whether the variable is tainted (e.g., potentially modified externally). + */ + tainted: boolean; + + /** + * Stack flag for certain variable types. + */ + stack: boolean; +} + +/** + * Represents a reference to a variable. + */ +export class Reference implements eslint.Scope.Reference { + /** + * Creates a new Reference instance. + * @param identifier The identifier node of the reference. + * @param scope The scope where the reference occurs. + * @param flag The reference flag (read, write, or read-write). + * @param writeExpr The expression being written, if applicable. + * @param maybeImplicitGlobal Whether this is a potential implicit global. + * @param partial Whether this is a partial reference. + * @param init Whether this is an initialization reference. + */ + constructor( + identifier: ESTree.Identifier, + scope: Scope, + flag: number, + writeExpr: ESTree.Expression | null, + maybeImplicitGlobal: boolean, + partial: boolean, + init: boolean, + ); + + /** + * The identifier node of the reference. + */ + identifier: ESTree.Identifier; + + /** + * The variable being referenced, or null if unresolved. + */ + resolved: Variable | null; + + /** + * Whether the reference is static. + */ + isStatic(): boolean; + + /** + * Whether this is a write operation. + */ + isWrite: eslint.Scope.Reference["isWrite"]; + + /** + * Whether this is a read operation. + */ + isRead: eslint.Scope.Reference["isRead"]; + + /** + * The scope where the reference occurs. + */ + from: Scope; + + /** + * Whether the reference comes from a dynamic scope (such as 'eval', + * 'with', etc.), and may be trapped by dynamic scopes. + */ + tainted: boolean; + + /** + * The expression being written, if applicable. + */ + writeExpr: ESTree.Expression | null; + + /** + * Whether this is a partial reference. + */ + partial: boolean; + + /** + * Whether this is an initialization reference. + */ + init: boolean; + + /** + * Whether this reference is only read. + * @returns True if the reference is read-only. + */ + isReadOnly(): boolean; + + /** + * Whether this reference is only written. + * @returns True if the reference is write-only. + */ + isWriteOnly(): boolean; + + /** + * Whether this reference is read-write. + * @returns True if the reference is read-write. + */ + isReadWrite(): boolean; +} + +/** + * Represents a variable definition. + * @todo extends eslint.Scope.Definition for this class + */ +export class Definition { + /** + * Creates a new Definition instance. + * @param type The type of definition (e.g., 'Variable', 'Parameter'). + * @param name The identifier node of the definition. + * @param node The AST node where the definition occurs. + * @param parent The parent node, if applicable. + * @param index The index of the definition in a pattern, if applicable. + * @param kind The kind of variable (e.g., 'var', 'let', 'const'), if applicable. + */ + constructor( + type: eslint.Scope.Definition["type"], + name: eslint.Scope.Definition["name"], + node: eslint.Scope.Definition["node"], + parent: eslint.Scope.Definition["parent"], + index: number | null, + kind: string | null, + ); + + /** + * The type of definition (e.g., 'Variable', 'Parameter'). + */ + type: eslint.Scope.Definition["type"]; + + /** + * The identifier node of the definition. + */ + name: eslint.Scope.Definition["name"]; + + /** + * The AST node where the definition occurs. + */ + node: eslint.Scope.Definition["node"]; + + /** + * The parent node, if applicable. + */ + parent: eslint.Scope.Definition["parent"]; + + /** + * The index of the definition in a pattern, if applicable. + */ + index: number | null; + + /** + * The kind of variable (e.g., 'var', 'let', 'const'), if applicable. + */ + kind: string | null; +} + +/** + * Visitor for destructuring patterns. + */ +export class PatternVisitor extends Visitor { + static isPattern(node: ESTree.Node): node is + | ESTree.Identifier + | ESTree.ObjectPattern + | ESTree.ArrayPattern + | ESTree.SpreadElement + | ESTree.RestElement + | ESTree.AssignmentPattern; + + constructor( + options: VisitorOptions | null | undefined, + rootPattern: ESTree.Pattern, + callback: PatternVisitorCallback, + ); + + rootPattern: ESTree.Pattern; + + callback: PatternVisitorCallback; + + assignments: ESTree.AssignmentPattern[]; + + rightHandNodes: ESTree.Expression[]; + + restElements: ESTree.RestElement[]; + + Identifier(pattern: ESTree.Identifier): void; + + Property(pattern: ESTree.Property): void; + + ArrayPattern(pattern: ESTree.ArrayPattern): void; + + AssignmentPattern(pattern: ESTree.AssignmentPattern): void; + + RestElement(pattern: ESTree.RestElement): void; + + MemberExpression(pattern: ESTree.MemberExpression): void; + + SpreadElement(pattern: ESTree.SpreadElement): void; + + ArrayExpression(pattern: ESTree.SpreadElement): void; + + AssignmentExpression(pattern: ESTree.AssignmentExpression): void; + + CallExpression(pattern: ESTree.CallExpression): void; +} + +/** + * Analyzes the scope of an AST. + * @param ast The ESTree-compliant AST to analyze. + * @param options Options for scope analysis. + * @returns The scope manager for the analyzed AST. + */ +export function analyze(ast: ESTree.Program, options?: AnalyzeOptions): ScopeManager; diff --git a/packages/eslint-scope/lib/index.d.ts b/packages/eslint-scope/lib/index.d.ts new file mode 100644 index 00000000..def35507 --- /dev/null +++ b/packages/eslint-scope/lib/index.d.ts @@ -0,0 +1,6 @@ +/** + * @fileoverview ESLint Scope types in ESM format. + * @author Francesco Trotta + */ + +export * from "./index.cjs"; diff --git a/packages/eslint-scope/package.json b/packages/eslint-scope/package.json index 764de6f1..bec42451 100644 --- a/packages/eslint-scope/package.json +++ b/packages/eslint-scope/package.json @@ -3,11 +3,18 @@ "description": "ECMAScript scope analyzer for ESLint", "homepage": "https://github.com/eslint/js/blob/main/packages/eslint-scope/README.md", "main": "./dist/eslint-scope.cjs", + "types": "./lib/index.d.ts", "type": "module", "exports": { ".": { - "import": "./lib/index.js", - "require": "./dist/eslint-scope.cjs" + "import": { + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "require": { + "types": "./lib/index.d.cts", + "default": "./dist/eslint-scope.cjs" + } }, "./package.json": "./package.json" }, @@ -31,9 +38,11 @@ "scripts": { "build": "rollup -c", "build:update-version": "node tools/update-version.js", + "lint:types": "attw --pack", "prepublishOnly": "npm run build:update-version && npm run build", "pretest": "npm run build", - "test": "node Makefile.js test" + "test": "node Makefile.js test && npm run test:types", + "test:types": "tsc -p tests/types/tsconfig.json" }, "files": [ "LICENSE", @@ -42,12 +51,16 @@ "dist/eslint-scope.cjs" ], "dependencies": { + "@types/espree": "*", + "@types/esrecurse": "*", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "devDependencies": { + "@arethetypeswrong/cli": "^0.18.2", "@typescript-eslint/parser": "^8.7.0", "chai": "^6.0.0", + "eslint": ">=10.0.0-alpha.0 <10.0.0 || ^10.0.0", "eslint-visitor-keys": "^5.0.0", "espree": "^11.0.0", "npm-license": "^0.3.3", diff --git a/packages/eslint-scope/tests/types/cjs-import.test.cts b/packages/eslint-scope/tests/types/cjs-import.test.cts new file mode 100644 index 00000000..ffde5079 --- /dev/null +++ b/packages/eslint-scope/tests/types/cjs-import.test.cts @@ -0,0 +1,16 @@ +/** + * @fileoverview CommonJS type import test for ESLint Scope package. + * @author Francesco Trotta + */ + +//----------------------------------------------------------------------------- +// Imports +//----------------------------------------------------------------------------- + +import { + Definition, + Reference, + ScopeManager, + Scope, + Variable +} from "eslint-scope"; diff --git a/packages/eslint-scope/tests/types/tsconfig.json b/packages/eslint-scope/tests/types/tsconfig.json new file mode 100644 index 00000000..ddfb4d95 --- /dev/null +++ b/packages/eslint-scope/tests/types/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "nodenext", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "exactOptionalPropertyTypes": true, + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "include": [".", "../../lib"] +} diff --git a/packages/eslint-scope/tests/types/types.test.ts b/packages/eslint-scope/tests/types/types.test.ts new file mode 100644 index 00000000..2b29acb4 --- /dev/null +++ b/packages/eslint-scope/tests/types/types.test.ts @@ -0,0 +1,410 @@ +/** + * @fileoverview This file contains code to test the ESLint Scope types. + * It was initially extracted from the DefinitelyTyped repository. + */ + +/* + * MIT License + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + */ + +import * as eslintScope from "eslint-scope"; +import type { AnalyzeOptions } from "eslint-scope"; +import * as espree from "espree"; +import * as estree from "estree"; + +const code = ` +function example() { + let x = 1; + console.log(x); +} +`; + +const ast = espree.parse(code, { ecmaVersion: 2022, sourceType: "module" }) as estree.Program; + +// $ExpectType ScopeManager +const scopeManager = eslintScope.analyze( + ast, + { + ecmaVersion: 2022, + sourceType: "module", + ignoreEval: true, + nodejsScope: false, + impliedStrict: false, + childVisitorKeys: null, + fallback: "iteration", + } satisfies AnalyzeOptions, +); + +// $ExpectType GlobalScope +scopeManager.globalScope; +// $ExpectType Scope, Reference>[] +scopeManager.scopes; + +// $ExpectType Scope, Reference> | null +const scope = scopeManager.acquire(ast); + +// $ExpectType Scope, Reference> | null +scopeManager.release(ast); + +if (scope) { + (( + type: + | "function" + | "module" + | "block" + | "catch" + | "class" + | "class-field-initializer" + | "class-static-block" + | "for" + | "function-expression-name" + | "global" + | "switch" + | "with", + ) => type satisfies typeof scope.type); + // $ExpectType boolean + scope.isStrict; + // $ExpectType Scope, Reference> | null + scope.upper; + // $ExpectType Scope, Reference> + scope.variableScope; + // $ExpectType Variable[] + scope.variables; + // $ExpectType Reference[] + scope.references; + // $ExpectType Scope, Reference>[] + scope.childScopes; + // $ExpectType Node + scope.block; + // $ExpectType boolean + scope.functionExpressionScope; + // $ExpectType Reference[] + scope.implicit.left; + // $ExpectType Map> + scope.implicit.set; + // $ExpectType Variable[] + scope.implicit.variables; + // $ExpectType Map + scope.set; + // $ExpectType Reference[] + scope.through; +} + +const variable = scope?.variables[0]; +if (variable) { + // $ExpectType string + variable.name; + // $ExpectType Scope, Reference> + variable.scope; + // $ExpectType Identifier[] + variable.identifiers; + // $ExpectType Reference[] + variable.references; + // $ExpectType Definition[] + variable.defs; +} + +const reference = scope?.references[0]; +if (reference) { + // $ExpectType Identifier + reference.identifier; + // $ExpectType Variable | null + reference.resolved; + // $ExpectType () => boolean + reference.isWrite; + // $ExpectType () => boolean + reference.isRead; + // $ExpectType Scope, Reference> + reference.from; +} + +const definition = variable?.defs[0]; +if (definition) { + (( + type: + | "CatchClause" + | "ClassName" + | "FunctionName" + | "ImplicitGlobalVariable" + | "ImportBinding" + | "Parameter" + | "Variable", + ) => type satisfies typeof definition.type); + // $ExpectType Identifier + definition.name; + // $ExpectType ImportDeclaration | VariableDeclaration | null + definition.parent; +} + +// $ExpectType GlobalScope +const globalScope = scopeManager.globalScope; +// $ExpectType 'global' +globalScope.type; + +// $ExpectType ScopeManager +eslintScope.analyze(ast); + +const blockScope = new eslintScope.BlockScope(scopeManager, scopeManager.globalScope, ast); +// $ExpectType "block" +blockScope.type; + +const identifier: estree.Identifier = { + type: "Identifier", + name: "foo", +}; +const definition2 = new eslintScope.Definition( + "Variable", + identifier, + ast, + null, + null, + "let", +); +(( + type: + | "CatchClause" + | "ClassName" + | "FunctionName" + | "ImplicitGlobalVariable" + | "ImportBinding" + | "Parameter" + | "Variable", +) => type satisfies typeof definition2.type); +// $ExpectType Identifier +definition2.name; + +const functionScope = new eslintScope.FunctionScope(scopeManager, scopeManager.globalScope, ast, false); +// $ExpectType "function" +functionScope.type; +// $ExpectType boolean +functionScope.isArgumentsMaterialized(); +// $ExpectType boolean +functionScope.isThisMaterialized(); + +const globalScopeInstance = new eslintScope.GlobalScope(scopeManager, ast); +// $ExpectType "global" +globalScopeInstance.type; + +const moduleScope = new eslintScope.ModuleScope(scopeManager, scopeManager.globalScope, ast); +// $ExpectType "module" +moduleScope.type; + +const ref = new eslintScope.Reference( + identifier, + scopeManager.globalScope, + 0, + null, + false, + false, + false, +); +// $ExpectType Identifier +ref.identifier; +// $ExpectType Scope, Reference> +ref.from; +// $ExpectType boolean +ref.isRead(); +// $ExpectType boolean +ref.isWrite(); +// $ExpectType boolean +ref.isReadOnly(); +// $ExpectType boolean +ref.isWriteOnly(); +// $ExpectType boolean +ref.isReadWrite(); + +const scopeInstance = new eslintScope.Scope( + scopeManager, + "block", + null, + ast, + false, +); +(( + type: + | "function" + | "module" + | "block" + | "catch" + | "class" + | "class-field-initializer" + | "class-static-block" + | "for" + | "function-expression-name" + | "global" + | "switch" + | "with", +) => type satisfies typeof scopeInstance.type); +// $ExpectType boolean +scopeInstance.isStrict; +// $ExpectType Scope, Reference> | null +scopeInstance.upper; +// $ExpectType Scope, Reference> +scopeInstance.variableScope; +// $ExpectType Variable[] +scopeInstance.variables; +// $ExpectType Reference[] +scopeInstance.references; +// $ExpectType Scope, Reference>[] +scopeInstance.childScopes; +// $ExpectType Node +scopeInstance.block; +// $ExpectType boolean +scopeInstance.functionExpressionScope; +// $ExpectType { left: Reference[]; set: Map>; variables: Variable[]; } +scopeInstance.implicit; +// $ExpectType Map +scopeInstance.set; +// $ExpectType Map> +scopeInstance.taints; +// $ExpectType Reference[] +scopeInstance.through; +// $ExpectType boolean +scopeInstance.dynamic; +// $ExpectType boolean +scopeInstance.directCallToEvalScope; +// $ExpectType boolean +scopeInstance.thisFound; +// $ExpectType void +scopeInstance.resolve(ref); +// $ExpectType boolean +scopeInstance.isStatic(); +// $ExpectType boolean +scopeInstance.isArgumentsMaterialized(); +// $ExpectType boolean +scopeInstance.isThisMaterialized(); +// $ExpectType boolean +scopeInstance.isUsedName("foo"); + +const scopeManagerInstance = new eslintScope.ScopeManager({ + ecmaVersion: 2022, + sourceType: "module", +}); +// $ExpectType GlobalScope +scopeManagerInstance.globalScope; +// $ExpectType Scope, Reference>[] +scopeManagerInstance.scopes; +// $ExpectType void +scopeManagerInstance.addGlobals(["window", "self"]); +// $ExpectType Scope, Reference> | null +scopeManagerInstance.acquire(ast); +// $ExpectType Scope, Reference>[] | null +scopeManagerInstance.acquireAll(ast); +// $ExpectType Scope, Reference> | null +scopeManagerInstance.release(ast); +// $ExpectType Variable[] +scopeManagerInstance.getDeclaredVariables(ast); +// $ExpectType boolean +scopeManagerInstance.isGlobalReturn(); +// $ExpectType boolean +scopeManagerInstance.isModule(); +// $ExpectType boolean +scopeManagerInstance.isImpliedStrict(); +// $ExpectType boolean +scopeManagerInstance.isStrictModeSupported(); + +const variableInstance = new eslintScope.Variable("foo", scopeInstance); +// $ExpectType string +variableInstance.name; +// $ExpectType Scope, Reference> +variableInstance.scope; +// $ExpectType Identifier[] +variableInstance.identifiers; +// $ExpectType Reference[] +variableInstance.references; +// $ExpectType Definition[] +variableInstance.defs; +// $ExpectType boolean +variableInstance.tainted; +// $ExpectType boolean +variableInstance.stack; + +let node: any; +if (eslintScope.PatternVisitor.isPattern(node)) { + // $ExpectType Identifier | ObjectPattern | ArrayPattern | SpreadElement | RestElement | AssignmentPattern + node; +} + +declare let rootPattern: estree.Pattern; + +// // $ExpectType PatternVisitor +const patternVisitor = new eslintScope.PatternVisitor( + { + fallback: (node: any) => Object.keys(node).filter((key) => key !== "parent"), + childVisitorKeys: { TestExpression: ["argument"] }, + }, + rootPattern, + (pattern, misc) => { + // $ExpectType Identifier + pattern; + // $ExpectType AssignmentPattern[] + misc.assignments; + // $ExpectType boolean + misc.rest; + // $ExpectType boolean + misc.topLevel; + }, +); + +// $ExpectType Pattern +patternVisitor.rootPattern; + +// $ExpectType PatternVisitorCallback +patternVisitor.callback; + +// $ExpectType AssignmentPattern[] +patternVisitor.assignments; + +// $ExpectType Expression[] +patternVisitor.rightHandNodes; + +// $ExpectType RestElement[] +patternVisitor.restElements; + +// $ExpectType (pattern: Identifier) => void +patternVisitor.Identifier; + +// $ExpectType (pattern: Property) => void +patternVisitor.Property; + +// $ExpectType (pattern: ArrayPattern) => void +patternVisitor.ArrayPattern; + +// $ExpectType (pattern: AssignmentPattern) => void +patternVisitor.AssignmentPattern; + +// $ExpectType (pattern: RestElement) => void +patternVisitor.RestElement; + +// $ExpectType (pattern: MemberExpression) => void +patternVisitor.MemberExpression; + +// $ExpectType (pattern: SpreadElement) => void +patternVisitor.SpreadElement; + +// $ExpectType (pattern: SpreadElement) => void +patternVisitor.ArrayExpression; + +// $ExpectType (pattern: AssignmentExpression) => void +patternVisitor.AssignmentExpression; + +// $ExpectType (pattern: CallExpression) => void +patternVisitor.CallExpression; From ec36239a04e86a04030ced30e472d6916e1f69f1 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 21 Nov 2025 08:26:58 +0100 Subject: [PATCH 02/11] fallback to `.d.cts` types --- packages/eslint-scope/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-scope/package.json b/packages/eslint-scope/package.json index bec42451..ca141284 100644 --- a/packages/eslint-scope/package.json +++ b/packages/eslint-scope/package.json @@ -3,7 +3,7 @@ "description": "ECMAScript scope analyzer for ESLint", "homepage": "https://github.com/eslint/js/blob/main/packages/eslint-scope/README.md", "main": "./dist/eslint-scope.cjs", - "types": "./lib/index.d.ts", + "types": "./lib/index.d.cts", "type": "module", "exports": { ".": { From 2d40e1dc0d3a22bd4a47120f1f6d0716b04b37ba Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Fri, 21 Nov 2025 08:27:49 +0100 Subject: [PATCH 03/11] add missing scopes --- packages/eslint-scope/lib/index.d.cts | 166 ++++++++++++++++-- .../eslint-scope/tests/types/types.test.ts | 80 ++++++++- 2 files changed, 225 insertions(+), 21 deletions(-) diff --git a/packages/eslint-scope/lib/index.d.cts b/packages/eslint-scope/lib/index.d.cts index a136fe56..3ed29eb2 100644 --- a/packages/eslint-scope/lib/index.d.cts +++ b/packages/eslint-scope/lib/index.d.cts @@ -205,7 +205,7 @@ export class Scope Date: Fri, 21 Nov 2025 15:48:33 +0100 Subject: [PATCH 04/11] add deprecations --- packages/eslint-scope/lib/index.d.cts | 31 +++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/eslint-scope/lib/index.d.cts b/packages/eslint-scope/lib/index.d.cts index 3ed29eb2..a32c53b4 100644 --- a/packages/eslint-scope/lib/index.d.cts +++ b/packages/eslint-scope/lib/index.d.cts @@ -137,6 +137,7 @@ export class ScopeManager implements eslint.Scope.ScopeManager { * acquire all scopes from node. * @param node node for the acquired scope. * @returns Scope array + * @deprecated */ acquireAll(node: ESTree.Node): Scope[] | null; @@ -158,10 +159,13 @@ export class ScopeManager implements eslint.Scope.ScopeManager { isGlobalReturn(): boolean; + /** @deprecated */ isModule(): boolean; + /** @deprecated */ isImpliedStrict(): boolean; + /** @deprecated */ isStrictModeSupported(): boolean; } @@ -244,6 +248,7 @@ export class Scope; @@ -254,16 +259,19 @@ export class Scope implements eslin /** * Whether the variable is tainted (e.g., potentially modified externally). + * @deprecated */ tainted: boolean; /** * Stack flag for certain variable types. + * @deprecated */ stack: boolean; } @@ -554,7 +569,7 @@ export class Variable implements eslin export class Reference implements eslint.Scope.Reference { /** * Creates a new Reference instance. - * @param identifier The identifier node of the reference. + * @param ident The identifier node of the reference. * @param scope The scope where the reference occurs. * @param flag The reference flag (read, write, or read-write). * @param writeExpr The expression being written, if applicable. @@ -563,7 +578,7 @@ export class Reference implements eslint.Scope.Reference { * @param init Whether this is an initialization reference. */ constructor( - identifier: ESTree.Identifier, + ident: ESTree.Identifier, scope: Scope, flag: number, writeExpr: ESTree.Expression | null, @@ -584,6 +599,7 @@ export class Reference implements eslint.Scope.Reference { /** * Whether the reference is static. + * @deprecated */ isStatic(): boolean; @@ -605,6 +621,7 @@ export class Reference implements eslint.Scope.Reference { /** * Whether the reference comes from a dynamic scope (such as 'eval', * 'with', etc.), and may be trapped by dynamic scopes. + * @deprecated */ tainted: boolean; @@ -615,6 +632,7 @@ export class Reference implements eslint.Scope.Reference { /** * Whether this is a partial reference. + * @deprecated */ partial: boolean; @@ -640,6 +658,9 @@ export class Reference implements eslint.Scope.Reference { * @returns True if the reference is read-write. */ isReadWrite(): boolean; + + /** @deprecated */ + flag: 1 | 2 | 3; } /** @@ -687,11 +708,13 @@ export class Definition { /** * The index of the definition in a pattern, if applicable. + * @deprecated */ index: number | null; /** * The kind of variable (e.g., 'var', 'let', 'const'), if applicable. + * @deprecated */ kind: string | null; } From b23b18faf78a5af8e77a885b99efc53454a048bf Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Sat, 22 Nov 2025 20:26:04 +0100 Subject: [PATCH 05/11] fixes --- packages/eslint-scope/lib/index.d.cts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/eslint-scope/lib/index.d.cts b/packages/eslint-scope/lib/index.d.cts index a32c53b4..bcd8c305 100644 --- a/packages/eslint-scope/lib/index.d.cts +++ b/packages/eslint-scope/lib/index.d.cts @@ -277,11 +277,10 @@ export class Scope Date: Sat, 22 Nov 2025 20:33:32 +0100 Subject: [PATCH 06/11] fix tests --- packages/eslint-scope/tests/types/types.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-scope/tests/types/types.test.ts b/packages/eslint-scope/tests/types/types.test.ts index 640cbd77..b7269771 100644 --- a/packages/eslint-scope/tests/types/types.test.ts +++ b/packages/eslint-scope/tests/types/types.test.ts @@ -276,7 +276,7 @@ const ref = new eslintScope.Reference( scopeManager.globalScope, 0, null, - false, + null, false, false, ); @@ -347,8 +347,8 @@ scopeInstance.dynamic; scopeInstance.directCallToEvalScope; // $ExpectType boolean scopeInstance.thisFound; -// $ExpectType void -scopeInstance.resolve(ref); +// $ExpectType Reference | null +scopeInstance.resolve(identifier); // $ExpectType boolean scopeInstance.isStatic(); // $ExpectType boolean From 951db737ba611059f7e3d40a13bf2ed65b63c795 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Sun, 23 Nov 2025 11:39:30 +0100 Subject: [PATCH 07/11] more fixes --- packages/eslint-scope/lib/index.d.cts | 20 +++---- .../eslint-scope/tests/types/types.test.ts | 52 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/eslint-scope/lib/index.d.cts b/packages/eslint-scope/lib/index.d.cts index bcd8c305..4dfe674d 100644 --- a/packages/eslint-scope/lib/index.d.cts +++ b/packages/eslint-scope/lib/index.d.cts @@ -109,9 +109,9 @@ export class ScopeManager implements eslint.Scope.ScopeManager { constructor(options: AnalyzeOptions); /** - * The global scope. + * The global scope, initially set to `null`. */ - globalScope: GlobalScope; + globalScope: GlobalScope | null; /** * All scopes in the analyzed program. @@ -236,11 +236,6 @@ export class Scope; variables: Variable[] }; - /** * Map of variable names to variables. */ @@ -318,6 +313,11 @@ export class GlobalScope extends Scope { type: "global"; functionExpressionScope: false; + + /** + * Implicit references (e.g., 'arguments' in functions). + */ + implicit: { left: Reference[]; set: Map; variables: Variable[] }; } /** @@ -740,9 +740,9 @@ export class PatternVisitor extends Visitor { callback: PatternVisitorCallback; - assignments: ESTree.AssignmentPattern[]; + assignments: Array; - rightHandNodes: ESTree.Expression[]; + rightHandNodes: ESTree.Node[]; restElements: ESTree.RestElement[]; @@ -760,7 +760,7 @@ export class PatternVisitor extends Visitor { SpreadElement(pattern: ESTree.SpreadElement): void; - ArrayExpression(pattern: ESTree.SpreadElement): void; + ArrayExpression(pattern: ESTree.ArrayExpression): void; AssignmentExpression(pattern: ESTree.AssignmentExpression): void; diff --git a/packages/eslint-scope/tests/types/types.test.ts b/packages/eslint-scope/tests/types/types.test.ts index b7269771..eb20fbd1 100644 --- a/packages/eslint-scope/tests/types/types.test.ts +++ b/packages/eslint-scope/tests/types/types.test.ts @@ -53,7 +53,7 @@ const scopeManager = eslintScope.analyze( } satisfies AnalyzeOptions, ); -// $ExpectType GlobalScope +// $ExpectType GlobalScope | null scopeManager.globalScope; // $ExpectType Scope, Reference>[] scopeManager.scopes; @@ -96,12 +96,6 @@ if (scope) { scope.block; // $ExpectType boolean scope.functionExpressionScope; - // $ExpectType Reference[] - scope.implicit.left; - // $ExpectType Map> - scope.implicit.set; - // $ExpectType Variable[] - scope.implicit.variables; // $ExpectType Map scope.set; // $ExpectType Reference[] @@ -155,9 +149,15 @@ if (definition) { } // $ExpectType GlobalScope -const globalScope = scopeManager.globalScope; +const globalScope = scopeManager.globalScope!; // $ExpectType 'global' globalScope.type; +// $ExpectType Reference[] +globalScope.implicit.left; +// $ExpectType Map> +globalScope.implicit.set; +// $ExpectType Variable[] +globalScope.implicit.variables; // $ExpectType ScopeManager eslintScope.analyze(ast); @@ -187,13 +187,13 @@ const definition2 = new eslintScope.Definition( // $ExpectType Identifier definition2.name; -const blockScope = new eslintScope.BlockScope(scopeManager, scopeManager.globalScope, ast); +const blockScope = new eslintScope.BlockScope(scopeManager, scopeManager.globalScope!, ast); // $ExpectType "block" blockScope.type; // $ExpectType false blockScope.functionExpressionScope; -const catchScope = new eslintScope.CatchScope(scopeManager, scopeManager.globalScope, ast); +const catchScope = new eslintScope.CatchScope(scopeManager, scopeManager.globalScope!, ast); // $ExpectType "catch" catchScope.type; // $ExpectType false @@ -201,7 +201,7 @@ catchScope.functionExpressionScope; const classFieldInitializerScope = new eslintScope.ClassFieldInitializerScope( scopeManager, - scopeManager.globalScope, + scopeManager.globalScope!, ast, ); // $ExpectType "class-field-initializer" @@ -209,7 +209,7 @@ classFieldInitializerScope.type; // $ExpectType false classFieldInitializerScope.functionExpressionScope; -const classScope = new eslintScope.ClassScope(scopeManager, scopeManager.globalScope, ast); +const classScope = new eslintScope.ClassScope(scopeManager, scopeManager.globalScope!, ast); // $ExpectType "class" classScope.type; // $ExpectType false @@ -217,7 +217,7 @@ classScope.functionExpressionScope; const classStaticBlockScope = new eslintScope.ClassStaticBlockScope( scopeManager, - scopeManager.globalScope, + scopeManager.globalScope!, ast, ); // $ExpectType "class-static-block" @@ -225,7 +225,7 @@ classStaticBlockScope.type; // $ExpectType false classStaticBlockScope.functionExpressionScope; -const forScope = new eslintScope.ForScope(scopeManager, scopeManager.globalScope, ast); +const forScope = new eslintScope.ForScope(scopeManager, scopeManager.globalScope!, ast); // $ExpectType "for" forScope.type; // $ExpectType false @@ -233,7 +233,7 @@ forScope.functionExpressionScope; const functionExpressionNameScope = new eslintScope.FunctionExpressionNameScope( scopeManager, - scopeManager.globalScope, + scopeManager.globalScope!, ast, ); // $ExpectType "function-expression-name" @@ -241,7 +241,7 @@ functionExpressionNameScope.type; // $ExpectType true functionExpressionNameScope.functionExpressionScope; -const functionScope = new eslintScope.FunctionScope(scopeManager, scopeManager.globalScope, ast, false); +const functionScope = new eslintScope.FunctionScope(scopeManager, scopeManager.globalScope!, ast, false); // $ExpectType "function" functionScope.type; // $ExpectType false @@ -252,20 +252,22 @@ const globalScopeInstance = new eslintScope.GlobalScope(scopeManager, ast); globalScopeInstance.type; // $ExpectType false globalScopeInstance.functionExpressionScope; +// $ExpectType { left: Reference[]; set: Map>; variables: Variable[]; } +globalScopeInstance.implicit; -const moduleScope = new eslintScope.ModuleScope(scopeManager, scopeManager.globalScope, ast); +const moduleScope = new eslintScope.ModuleScope(scopeManager, scopeManager.globalScope!, ast); // $ExpectType "module" moduleScope.type; // $ExpectType false moduleScope.functionExpressionScope; -const switchScope = new eslintScope.SwitchScope(scopeManager, scopeManager.globalScope, ast); +const switchScope = new eslintScope.SwitchScope(scopeManager, scopeManager.globalScope!, ast); // $ExpectType "switch" switchScope.type; // $ExpectType false switchScope.functionExpressionScope; -const withScope = new eslintScope.WithScope(scopeManager, scopeManager.globalScope, ast); +const withScope = new eslintScope.WithScope(scopeManager, scopeManager.globalScope!, ast); // $ExpectType "with" withScope.type; // $ExpectType false @@ -273,7 +275,7 @@ withScope.functionExpressionScope; const ref = new eslintScope.Reference( identifier, - scopeManager.globalScope, + scopeManager.globalScope!, 0, null, null, @@ -333,8 +335,6 @@ scopeInstance.childScopes; scopeInstance.block; // $ExpectType boolean scopeInstance.functionExpressionScope; -// $ExpectType { left: Reference[]; set: Map>; variables: Variable[]; } -scopeInstance.implicit; // $ExpectType Map scopeInstance.set; // $ExpectType Map> @@ -362,7 +362,7 @@ const scopeManagerInstance = new eslintScope.ScopeManager({ ecmaVersion: 2022, sourceType: "module", }); -// $ExpectType GlobalScope +// $ExpectType GlobalScope | null scopeManagerInstance.globalScope; // $ExpectType Scope, Reference>[] scopeManagerInstance.scopes; @@ -434,10 +434,10 @@ patternVisitor.rootPattern; // $ExpectType PatternVisitorCallback patternVisitor.callback; -// $ExpectType AssignmentPattern[] +// $ExpectType (AssignmentExpression | AssignmentPattern)[] patternVisitor.assignments; -// $ExpectType Expression[] +// $ExpectType Node[] patternVisitor.rightHandNodes; // $ExpectType RestElement[] @@ -464,7 +464,7 @@ patternVisitor.MemberExpression; // $ExpectType (pattern: SpreadElement) => void patternVisitor.SpreadElement; -// $ExpectType (pattern: SpreadElement) => void +// $ExpectType (pattern: ArrayExpression) => void patternVisitor.ArrayExpression; // $ExpectType (pattern: AssignmentExpression) => void From 095c33a1ac2262f2eade147ba05d7aee49bae2ec Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Tue, 25 Nov 2025 10:40:53 +0100 Subject: [PATCH 08/11] type check source files --- packages/eslint-scope/lib/definition.js | 3 ++ packages/eslint-scope/lib/index.js | 10 ++-- packages/eslint-scope/lib/pattern-visitor.js | 3 ++ packages/eslint-scope/lib/reference.js | 9 ++-- packages/eslint-scope/lib/referencer.js | 11 +++-- packages/eslint-scope/lib/scope-manager.js | 24 +++++++--- packages/eslint-scope/lib/scope.js | 47 +++++++++++++++++-- packages/eslint-scope/lib/variable.js | 4 ++ packages/eslint-scope/package.json | 2 +- .../eslint-scope/tests/types/tsconfig.json | 13 +---- packages/eslint-scope/tsconfig.json | 17 +++++++ 11 files changed, 108 insertions(+), 35 deletions(-) create mode 100644 packages/eslint-scope/tsconfig.json diff --git a/packages/eslint-scope/lib/definition.js b/packages/eslint-scope/lib/definition.js index 9744ef48..e493a0ed 100644 --- a/packages/eslint-scope/lib/definition.js +++ b/packages/eslint-scope/lib/definition.js @@ -24,8 +24,11 @@ import Variable from "./variable.js"; +/** @import * as types from "eslint-scope" */ + /** * @constructor Definition + * @implements {types.Definition} */ class Definition { constructor(type, name, node, parent, index, kind) { diff --git a/packages/eslint-scope/lib/index.js b/packages/eslint-scope/lib/index.js index 85f8b510..268ae650 100644 --- a/packages/eslint-scope/lib/index.js +++ b/packages/eslint-scope/lib/index.js @@ -55,6 +55,8 @@ import Variable from "./variable.js"; import eslintScopeVersion from "./version.js"; +/** @import ESTree from "estree" */ + /** * Set the default options * @returns {Object} options @@ -73,9 +75,9 @@ function defaultOptions() { /** * Preform deep update on option object - * @param {Object} target Options - * @param {Object} override Updates - * @returns {Object} Updated options + * @param {Record} target Options + * @param {Record} override Updates + * @returns {Record} Updated options */ function updateDeeply(target, override) { @@ -110,7 +112,7 @@ function updateDeeply(target, override) { * Main interface function. Takes an Espree syntax tree and returns the * analyzed scopes. * @function analyze - * @param {espree.Tree} tree Abstract Syntax Tree + * @param {ESTree.Program} tree Abstract Syntax Tree * @param {Object} providedOptions Options that tailor the scope analysis * @param {boolean} [providedOptions.optimistic=false] the optimistic flag * @param {boolean} [providedOptions.ignoreEval=false] whether to check 'eval()' calls diff --git a/packages/eslint-scope/lib/pattern-visitor.js b/packages/eslint-scope/lib/pattern-visitor.js index 367a3773..f870f6c9 100644 --- a/packages/eslint-scope/lib/pattern-visitor.js +++ b/packages/eslint-scope/lib/pattern-visitor.js @@ -25,6 +25,8 @@ import estraverse from "estraverse"; import esrecurse from "esrecurse"; +/** @import * as types from "eslint-scope" */ + const { Syntax } = estraverse; /** @@ -38,6 +40,7 @@ function getLast(xs) { /** * Visitor for destructuring patterns. + * @implements {types.PatternVisitor} */ class PatternVisitor extends esrecurse.Visitor { static isPattern(node) { diff --git a/packages/eslint-scope/lib/reference.js b/packages/eslint-scope/lib/reference.js index e657d628..3f85b164 100644 --- a/packages/eslint-scope/lib/reference.js +++ b/packages/eslint-scope/lib/reference.js @@ -22,6 +22,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** @import * as types from "eslint-scope" */ + const READ = 0x1; const WRITE = 0x2; const RW = READ | WRITE; @@ -29,6 +31,7 @@ const RW = READ | WRITE; /** * A Reference represents a single occurrence of an identifier in code. * @constructor Reference + * @implements {types.Reference} */ class Reference { constructor(ident, scope, flag, writeExpr, maybeImplicitGlobal, partial, init) { @@ -62,7 +65,6 @@ class Reference { * The read-write mode of the reference. (Value is one of {@link * Reference.READ}, {@link Reference.RW}, {@link Reference.WRITE}). * @member {number} Reference#flag - * @private */ this.flag = flag; if (this.isWrite()) { @@ -94,7 +96,7 @@ class Reference { * @returns {boolean} static */ isStatic() { - return !this.tainted && this.resolved && this.resolved.scope.isStatic(); + return !this.tainted && !!this.resolved && this.resolved.scope.isStatic(); } /** @@ -145,19 +147,16 @@ class Reference { /** * @constant Reference.READ - * @private */ Reference.READ = READ; /** * @constant Reference.WRITE - * @private */ Reference.WRITE = WRITE; /** * @constant Reference.RW - * @private */ Reference.RW = RW; diff --git a/packages/eslint-scope/lib/referencer.js b/packages/eslint-scope/lib/referencer.js index ad88ea20..971fa0e9 100644 --- a/packages/eslint-scope/lib/referencer.js +++ b/packages/eslint-scope/lib/referencer.js @@ -30,14 +30,17 @@ import PatternVisitor from "./pattern-visitor.js"; import { Definition, ParameterDefinition } from "./definition.js"; import { assert } from "./assert.js"; +/** @import * as types from "eslint-scope" */ +/** @import ESTree from "estree" */ + const { Syntax } = estraverse; /** * Traverse identifier in pattern * @param {Object} options options - * @param {pattern} rootPattern root pattern - * @param {Refencer} referencer referencer - * @param {callback} callback callback + * @param {ESTree.Pattern} rootPattern root pattern + * @param {?Referencer} referencer referencer + * @param {types.PatternVisitorCallback} callback callback * @returns {void} */ function traverseIdentifierInPattern(options, rootPattern, referencer, callback) { @@ -209,7 +212,7 @@ class Referencer extends esrecurse.Visitor { /** * Visit pattern callback - * @param {pattern} pattern pattern + * @param {ESTree.Pattern} pattern pattern * @param {Object} info info * @returns {void} */ diff --git a/packages/eslint-scope/lib/scope-manager.js b/packages/eslint-scope/lib/scope-manager.js index a9f5c943..ca10206c 100644 --- a/packages/eslint-scope/lib/scope-manager.js +++ b/packages/eslint-scope/lib/scope-manager.js @@ -38,8 +38,14 @@ import { } from "./scope.js"; import { assert } from "./assert.js"; +/** @import * as types from "eslint-scope" */ +/** @import ESTree from "estree" */ +/** @import { Scope } from "./scope.js" */ +/** @import Variable from "./variable.js" */ + /** * @constructor ScopeManager + * @implements {types.ScopeManager} */ class ScopeManager { constructor(options) { @@ -72,10 +78,12 @@ class ScopeManager { } isImpliedStrict() { - return this.__options.impliedStrict; + return !!this.__options.impliedStrict; } isStrictModeSupported() { + + // @ts-ignore -- if ecmaVersion is undefined, the comparison returns false. return this.__options.ecmaVersion >= 5; } @@ -90,7 +98,7 @@ class ScopeManager { * "are declared by the node" means the node is same as `Variable.defs[].node` or `Variable.defs[].parent`. * If the node declares nothing, this method returns an empty array. * CAUTION: This API is experimental. See https://github.com/estools/escope/pull/69 for more details. - * @param {Espree.Node} node a node to get. + * @param {ESTree.Node} node a node to get. * @returns {Variable[]} variables that declared by the node. */ getDeclaredVariables(node) { @@ -100,7 +108,7 @@ class ScopeManager { /** * acquire scope from node. * @function ScopeManager#acquire - * @param {Espree.Node} node node for the acquired scope. + * @param {ESTree.Node} node node for the acquired scope. * @param {?boolean} [inner=false] look up the most inner scope, default value is false. * @returns {Scope?} Scope from node */ @@ -154,8 +162,8 @@ class ScopeManager { /** * acquire all scopes from node. * @function ScopeManager#acquireAll - * @param {Espree.Node} node node for the acquired scope. - * @returns {Scopes?} Scope array + * @param {ESTree.Node} node node for the acquired scope. + * @returns {Scope[]?} Scope array */ acquireAll(node) { return this.__get(node); @@ -164,7 +172,7 @@ class ScopeManager { /** * release the node. * @function ScopeManager#release - * @param {Espree.Node} node releasing node. + * @param {ESTree.Node} node releasing node. * @param {?boolean} [inner=false] look up the most inner scope, default value is false. * @returns {Scope?} upper scope for the node. */ @@ -189,6 +197,8 @@ class ScopeManager { * @returns {void} */ addGlobals(names) { + + // @ts-ignore -- globalScope must be set before this method is called. this.globalScope.__addVariables(names); } @@ -254,6 +264,8 @@ class ScopeManager { } __isES6() { + + // @ts-ignore -- if ecmaVersion is undefined, the comparison returns false. return this.__options.ecmaVersion >= 6; } } diff --git a/packages/eslint-scope/lib/scope.js b/packages/eslint-scope/lib/scope.js index 0b7f5890..d1612ea9 100644 --- a/packages/eslint-scope/lib/scope.js +++ b/packages/eslint-scope/lib/scope.js @@ -29,6 +29,12 @@ import Variable from "./variable.js"; import { Definition } from "./definition.js"; import { assert } from "./assert.js"; +/** @import * as types from "eslint-scope" */ +/** @import ESTree from "estree" */ +/** @import ScopeManager from "./scope-manager.js" */ +/** @typedef {ESTree.Function | ESTree.Program | ESTree.StaticBlock} Block */ +/** @typedef {{pattern: any, node: any}} MaybeImplicitGlobal */ + const { Syntax } = estraverse; /** @@ -59,6 +65,8 @@ function isStrictScope(scope, block, isMethodDefinition) { } if (scope.type === "function") { + + // @ts-ignore -- when block is ArrowFunctionExpression if (block.type === Syntax.ArrowFunctionExpression && block.body.type !== Syntax.BlockStatement) { return false; } @@ -79,7 +87,10 @@ function isStrictScope(scope, block, isMethodDefinition) { } // Search for a 'use strict' directive. + // @ts-ignore -- body is a function body for (let i = 0, iz = body.body.length; i < iz; ++i) { + + // @ts-ignore -- body is a function body const stmt = body.body[i]; /* @@ -124,6 +135,7 @@ function registerScope(scopeManager, scope) { /** * @constructor Scope + * @implements {types.Scope} */ class Scope { constructor(scopeManager, type, upperScope, block, isMethodDefinition) { @@ -211,7 +223,7 @@ class Scope { * Whether this scope is created by a FunctionExpression. * @member {boolean} Scope#functionExpressionScope */ - this.functionExpressionScope = false; + this.functionExpressionScope = /** @type {any} */ (false); /** * Whether this is a scope that contains an 'eval()' invocation. @@ -224,6 +236,7 @@ class Scope { */ this.thisFound = false; + /** @type {?Reference[]} */ this.__left = []; /** @@ -285,7 +298,10 @@ class Scope { } // Try Resolving all references in this scope. + // @ts-ignore -- __left should be an array here for (let i = 0, iz = this.__left.length; i < iz; ++i) { + + // @ts-ignore -- __left should be an array here const ref = this.__left[i]; closeRef.call(this, ref); @@ -393,6 +409,8 @@ class Scope { const ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal, !!partial, !!init); this.references.push(ref); + + // @ts-ignore -- __left should be an array here this.__left.push(ref); } @@ -417,8 +435,8 @@ class Scope { /** * returns resolved {Reference} * @function Scope#resolve - * @param {Espree.Identifier} ident identifier to be resolved. - * @returns {Reference} reference + * @param {ESTree.Identifier} ident identifier to be resolved. + * @returns {?Reference} reference */ resolve(ident) { let ref, i, iz; @@ -476,18 +494,22 @@ class Scope { /** * Global scope. + * @implements {types.GlobalScope} */ class GlobalScope extends Scope { constructor(scopeManager, block) { super(scopeManager, "global", null, block, false); this.implicit = { set: new Map(), + + /** @type {Variable[]} */ 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 + * @type {Reference[]} */ left: [] }; @@ -496,7 +518,10 @@ class GlobalScope extends Scope { __close(scopeManager) { const implicit = []; + // @ts-ignore -- __left should be an array here for (let i = 0, iz = this.__left.length; i < iz; ++i) { + + // @ts-ignore -- __left should be an array here const ref = this.__left[i]; if (ref.__maybeImplicitGlobal && !this.set.has(ref.identifier.name)) { @@ -587,6 +612,7 @@ class GlobalScope extends Scope { /** * Module scope. + * @implements {types.ModuleScope} */ class ModuleScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -596,6 +622,7 @@ class ModuleScope extends Scope { /** * Function expression name scope. + * @implements {types.FunctionExpressionNameScope} */ class FunctionExpressionNameScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -609,12 +636,13 @@ class FunctionExpressionNameScope extends Scope { null, null )); - this.functionExpressionScope = true; + this.functionExpressionScope = /** @type {const} */ (true); } } /** * Catch scope. + * @implements {types.CatchScope} */ class CatchScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -624,6 +652,7 @@ class CatchScope extends Scope { /** * With statement scope. + * @implements {types.WithScope} */ class WithScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -635,7 +664,10 @@ class WithScope extends Scope { return super.__close(scopeManager); } + // @ts-ignore -- __left should be an array here for (let i = 0, iz = this.__left.length; i < iz; ++i) { + + // @ts-ignore -- __left should be an array here const ref = this.__left[i]; ref.tainted = true; @@ -649,6 +681,7 @@ class WithScope extends Scope { /** * Block scope. + * @implements {types.BlockScope} */ class BlockScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -658,6 +691,7 @@ class BlockScope extends Scope { /** * Switch scope. + * @implements {types.SwitchScope} */ class SwitchScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -667,6 +701,7 @@ class SwitchScope extends Scope { /** * Function scope. + * @implements {types.FunctionScope} */ class FunctionScope extends Scope { constructor(scopeManager, upperScope, block, isMethodDefinition) { @@ -747,6 +782,7 @@ class FunctionScope extends Scope { /** * Scope of for, for-in, and for-of statements. + * @implements {types.ForScope} */ class ForScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -756,6 +792,7 @@ class ForScope extends Scope { /** * Class scope. + * @implements {types.ClassScope} */ class ClassScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -765,6 +802,7 @@ class ClassScope extends Scope { /** * Class field initializer scope. + * @implements {types.ClassFieldInitializerScope} */ class ClassFieldInitializerScope extends Scope { constructor(scopeManager, upperScope, block) { @@ -774,6 +812,7 @@ class ClassFieldInitializerScope extends Scope { /** * Class static block scope. + * @implements {types.ClassStaticBlockScope} */ class ClassStaticBlockScope extends Scope { constructor(scopeManager, upperScope, block) { diff --git a/packages/eslint-scope/lib/variable.js b/packages/eslint-scope/lib/variable.js index 286202f7..93787b25 100644 --- a/packages/eslint-scope/lib/variable.js +++ b/packages/eslint-scope/lib/variable.js @@ -22,10 +22,14 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** @import * as types from "eslint-scope" */ +/** @import Reference from "./reference.js" */ + /** * A Variable represents a locally scoped identifier. These include arguments to * functions. * @constructor Variable + * @implements {types.Variable} */ class Variable { constructor(name, scope) { diff --git a/packages/eslint-scope/package.json b/packages/eslint-scope/package.json index ca141284..6c2fedb0 100644 --- a/packages/eslint-scope/package.json +++ b/packages/eslint-scope/package.json @@ -42,7 +42,7 @@ "prepublishOnly": "npm run build:update-version && npm run build", "pretest": "npm run build", "test": "node Makefile.js test && npm run test:types", - "test:types": "tsc -p tests/types/tsconfig.json" + "test:types": "tsc -p tsconfig.json && tsc -p tests/types/tsconfig.json" }, "files": [ "LICENSE", diff --git a/packages/eslint-scope/tests/types/tsconfig.json b/packages/eslint-scope/tests/types/tsconfig.json index ddfb4d95..89a4a918 100644 --- a/packages/eslint-scope/tests/types/tsconfig.json +++ b/packages/eslint-scope/tests/types/tsconfig.json @@ -1,17 +1,8 @@ { + "extends": "../../tsconfig.json", "compilerOptions": { - "module": "nodenext", - "lib": [ - "es6" - ], + "checkJs": false, "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "exactOptionalPropertyTypes": true, - "types": [], - "noEmit": true, - "forceConsistentCasingInFileNames": true }, "include": [".", "../../lib"] } diff --git a/packages/eslint-scope/tsconfig.json b/packages/eslint-scope/tsconfig.json new file mode 100644 index 00000000..fd3c19ea --- /dev/null +++ b/packages/eslint-scope/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "checkJs": true, + "exactOptionalPropertyTypes": true, + "forceConsistentCasingInFileNames": true, + "lib": [ + "es2022" + ], + "module": "nodenext", + "noEmit": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "types": [], + }, + "include": ["lib"] +} From 8a7992900bdb0da2006339c023228a3fd98b94fd Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Tue, 25 Nov 2025 11:59:26 +0100 Subject: [PATCH 09/11] make sure to use the latest versions of type dependencies --- packages/eslint-scope/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-scope/package.json b/packages/eslint-scope/package.json index 6c2fedb0..e42d1b08 100644 --- a/packages/eslint-scope/package.json +++ b/packages/eslint-scope/package.json @@ -51,8 +51,8 @@ "dist/eslint-scope.cjs" ], "dependencies": { - "@types/espree": "*", - "@types/esrecurse": "*", + "@types/espree": "^10.1.0", + "@types/esrecurse": "^4.3.1", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, From c6d38b2ea0ae310d33741d605393c6c2b16f6608 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Tue, 25 Nov 2025 12:04:44 +0100 Subject: [PATCH 10/11] explain unused imports --- packages/eslint-scope/tests/types/cjs-import.test.cts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/eslint-scope/tests/types/cjs-import.test.cts b/packages/eslint-scope/tests/types/cjs-import.test.cts index ffde5079..05b38cc1 100644 --- a/packages/eslint-scope/tests/types/cjs-import.test.cts +++ b/packages/eslint-scope/tests/types/cjs-import.test.cts @@ -7,6 +7,7 @@ // Imports //----------------------------------------------------------------------------- +// Make sure the named exports exist. import { Definition, Reference, From 3658660c0bbc4878498ae3f25c3c8f6cc3f029e4 Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Thu, 27 Nov 2025 19:50:43 +0100 Subject: [PATCH 11/11] remove dependency on `eslint` types --- packages/eslint-scope/lib/definition.js | 3 +- packages/eslint-scope/lib/index.d.cts | 113 +++++++++++------- .../eslint-scope/tests/types/types.test.ts | 20 +++- 3 files changed, 89 insertions(+), 47 deletions(-) diff --git a/packages/eslint-scope/lib/definition.js b/packages/eslint-scope/lib/definition.js index e493a0ed..12c227aa 100644 --- a/packages/eslint-scope/lib/definition.js +++ b/packages/eslint-scope/lib/definition.js @@ -26,9 +26,10 @@ import Variable from "./variable.js"; /** @import * as types from "eslint-scope" */ +// Cannot implement `types.Definition` directly because it contains a union. /** * @constructor Definition - * @implements {types.Definition} + * @implements {Omit} */ class Definition { constructor(type, name, node, parent, index, kind) { diff --git a/packages/eslint-scope/lib/index.d.cts b/packages/eslint-scope/lib/index.d.cts index 4dfe674d..f873b729 100644 --- a/packages/eslint-scope/lib/index.d.cts +++ b/packages/eslint-scope/lib/index.d.cts @@ -25,7 +25,6 @@ * SOFTWARE */ -import * as eslint from "eslint"; import { VisitorKeys } from "eslint-visitor-keys"; import { Visitor, VisitorOptions } from "esrecurse"; import * as ESTree from "estree"; @@ -101,7 +100,7 @@ export type PatternVisitorCallback = ( /** * Manages the scope hierarchy of an AST. */ -export class ScopeManager implements eslint.Scope.ScopeManager { +export class ScopeManager { /** * Creates a new ScopeManager instance. * @param options Options for scope analysis. @@ -172,9 +171,7 @@ export class ScopeManager implements eslint.Scope.ScopeManager { /** * Base export class for all scopes. */ -export class Scope - implements eslint.Scope.Scope -{ +export class Scope { /** * Creates a new Scope instance. * @param scopeManager The scope manager this scope belongs to. @@ -194,7 +191,19 @@ export class Scope; /** * The tainted variables of this scope. * @deprecated */ - taints: Map; + taints: Map; /** * References that pass through this scope to outer scopes. */ - through: eslint.Scope.Scope["through"]; + through: TReference[]; /** * Dynamic flag for certain scope types. @@ -275,7 +284,7 @@ export class Scope implements eslint.Scope.Variable { +export class Variable { /** * Creates a new Variable instance. * @param name The name of the variable. @@ -547,7 +556,7 @@ export class Variable implements eslin /** * Definitions of this variable. */ - defs: eslint.Scope.Definition[]; + defs: Definition[]; /** * Whether the variable is tainted (e.g., potentially modified externally). @@ -565,7 +574,7 @@ export class Variable implements eslin /** * Represents a reference to a variable. */ -export class Reference implements eslint.Scope.Reference { +export class Reference { /** * Creates a new Reference instance. * @param ident The identifier node of the reference. @@ -605,12 +614,12 @@ export class Reference implements eslint.Scope.Reference { /** * Whether this is a write operation. */ - isWrite: eslint.Scope.Reference["isWrite"]; + isWrite(): boolean; /** * Whether this is a read operation. */ - isRead: eslint.Scope.Reference["isRead"]; + isRead(): boolean; /** * The scope where the reference occurs. @@ -664,9 +673,8 @@ export class Reference implements eslint.Scope.Reference { /** * Represents a variable definition. - * @todo extends eslint.Scope.Definition for this class */ -export class Definition { +export const Definition: { /** * Creates a new Definition instance. * @param type The type of definition (e.g., 'Variable', 'Parameter'). @@ -676,34 +684,57 @@ export class Definition { * @param index The index of the definition in a pattern, if applicable. * @param kind The kind of variable (e.g., 'var', 'let', 'const'), if applicable. */ - constructor( - type: eslint.Scope.Definition["type"], - name: eslint.Scope.Definition["name"], - node: eslint.Scope.Definition["node"], - parent: eslint.Scope.Definition["parent"], + new ( + type: Definition["type"], + name: ESTree.Identifier, + node: Definition["node"], + parent: Definition["parent"], index: number | null, kind: string | null, - ); - - /** - * The type of definition (e.g., 'Variable', 'Parameter'). - */ - type: eslint.Scope.Definition["type"]; + ): Definition; +}; + +export type Definition = ( + | { type: "CatchClause"; node: ESTree.CatchClause; parent: null } + | { + type: "ClassName"; + node: ESTree.ClassDeclaration | ESTree.ClassExpression; + parent: null; + } | { + type: "FunctionName"; + node: ESTree.FunctionDeclaration | ESTree.FunctionExpression; + parent: null; + } | { + type: "ImplicitGlobalVariable"; + node: + | ESTree.AssignmentExpression + | ESTree.ForInStatement + | ESTree.ForOfStatement; + parent: null; + } | { + type: "ImportBinding"; + node: + | ESTree.ImportSpecifier + | ESTree.ImportDefaultSpecifier + | ESTree.ImportNamespaceSpecifier; + parent: ESTree.ImportDeclaration; + } | { + type: "Parameter"; + node: + | ESTree.FunctionDeclaration + | ESTree.FunctionExpression + | ESTree.ArrowFunctionExpression; + parent: null; + } | { + type: "Variable"; + node: ESTree.VariableDeclarator; + parent: ESTree.VariableDeclaration; + }) & { /** * The identifier node of the definition. */ - name: eslint.Scope.Definition["name"]; - - /** - * The AST node where the definition occurs. - */ - node: eslint.Scope.Definition["node"]; - - /** - * The parent node, if applicable. - */ - parent: eslint.Scope.Definition["parent"]; + name: ESTree.Identifier; /** * The index of the definition in a pattern, if applicable. @@ -716,7 +747,7 @@ export class Definition { * @deprecated */ kind: string | null; -} +}; /** * Visitor for destructuring patterns. diff --git a/packages/eslint-scope/tests/types/types.test.ts b/packages/eslint-scope/tests/types/types.test.ts index eb20fbd1..3cb3bb49 100644 --- a/packages/eslint-scope/tests/types/types.test.ts +++ b/packages/eslint-scope/tests/types/types.test.ts @@ -25,8 +25,8 @@ * SOFTWARE */ +import * as eslint from "eslint"; import * as eslintScope from "eslint-scope"; -import type { AnalyzeOptions } from "eslint-scope"; import * as espree from "espree"; import * as estree from "estree"; @@ -50,7 +50,7 @@ const scopeManager = eslintScope.analyze( impliedStrict: false, childVisitorKeys: null, fallback: "iteration", - } satisfies AnalyzeOptions, + } satisfies eslintScope.AnalyzeOptions, ); // $ExpectType GlobalScope | null @@ -96,7 +96,7 @@ if (scope) { scope.block; // $ExpectType boolean scope.functionExpressionScope; - // $ExpectType Map + // $ExpectType Map> scope.set; // $ExpectType Reference[] scope.through; @@ -169,7 +169,7 @@ const identifier: estree.Identifier = { const definition2 = new eslintScope.Definition( "Variable", identifier, - ast, + { type: "VariableDeclarator", id: identifier }, null, null, "let", @@ -335,7 +335,7 @@ scopeInstance.childScopes; scopeInstance.block; // $ExpectType boolean scopeInstance.functionExpressionScope; -// $ExpectType Map +// $ExpectType Map> scopeInstance.set; // $ExpectType Map> scopeInstance.taints; @@ -472,3 +472,13 @@ patternVisitor.AssignmentExpression; // $ExpectType (pattern: CallExpression) => void patternVisitor.CallExpression; + +(definition: eslintScope.Definition) => definition satisfies eslint.Scope.Definition; + +(reference: eslintScope.Reference) => reference satisfies eslint.Scope.Reference; + +(scope: eslintScope.Scope) => scope satisfies eslint.Scope.Scope; + +(scopeManager: eslintScope.ScopeManager) => scopeManager satisfies eslint.Scope.ScopeManager; + +(variable: eslintScope.Variable) => variable satisfies eslint.Scope.Variable;