Skip to content

Commit 98caac0

Browse files
fasttimelumirlumirMichaelDeBoey
authored
feat: add types to ESLint Scope (#709)
* feat: add types to ESLint Scope * fallback to `.d.cts` types * add missing scopes * add deprecations * fixes * fix tests * more fixes * type check source files * make sure to use the latest versions of type dependencies * explain unused imports * remove dependency on `eslint` types * Apply suggestions from code review Co-authored-by: 루밀LuMir <rpfos@naver.com> * Update packages/eslint-scope/lib/index.d.cts Co-authored-by: 루밀LuMir <rpfos@naver.com> * update readme file * update `type` parameter of `Scope` constructor * Update packages/eslint-scope/lib/index.d.cts * apply `mocha` globals to `*.test.*` files only * move `@types/espree` to `devDependencies` * stricter settings in `packages/eslint-scope/tests/types/tsconfig.json` * remove redundant `exactOptionalPropertyTypes` * add `@types/estree` dependency * Apply suggestions from code review Co-authored-by: 루밀LuMir <rpfos@naver.com> * inline `VisitorKeys` type * optional `Reference` fields * add `Variable` static properties * add `Reference` static properties * add `JSXIdentifier` * add `attach()` and `detach()` to `ScopeManager` * add `version` * add JSDoc for `version` * stricter return types for `isArgumentsMaterialized()` and `isThisMaterialized()` * add `Referencer` * Update packages/eslint-scope/tests/types/cjs-import.test.cts Co-authored-by: 루밀LuMir <rpfos@naver.com> * build types before `lint:types` * apply review suggestions * Update packages/eslint-scope/lib/index.d.cts Co-authored-by: 루밀LuMir <rpfos@naver.com> * Update packages/eslint-scope/lib/scope.js Co-authored-by: Michaël De Boey <info@michaeldeboey.be> * use `unknown` in `updateDeeply()` * update ESLint and re-enable tests * remove `@types/espree` dependency --------- Co-authored-by: 루밀LuMir <rpfos@naver.com> Co-authored-by: Michaël De Boey <info@michaeldeboey.be>
1 parent 5603ea9 commit 98caac0

20 files changed

Lines changed: 1794 additions & 50 deletions

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,18 @@ jobs:
4545
run: npm run build
4646
- name: Run tests
4747
run: npm run test
48+
are-the-types-wrong:
49+
name: Are the types wrong?
50+
runs-on: ubuntu-latest
51+
steps:
52+
- uses: actions/checkout@v6
53+
- name: Setup Node.js
54+
uses: actions/setup-node@v6
55+
with:
56+
node-version: 'lts/*'
57+
- name: Install dependencies
58+
run: npm install
59+
- name: Build types
60+
run: npm run build:types
61+
- name: Are the types wrong?
62+
run: npm run lint:types

eslint.config.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { defineConfig, globalIgnores } from "eslint/config";
22
import eslintConfigESLint from "eslint-config-eslint";
33
import eslintConfigESLintFormatting from "eslint-config-eslint/formatting";
44
import eslintPluginChaiFriendly from "eslint-plugin-chai-friendly";
5+
import * as expectType from "eslint-plugin-expect-type";
56
import globals from "globals";
7+
import tsParser from "@typescript-eslint/parser";
68

79
export default defineConfig([
810
globalIgnores([
@@ -11,23 +13,20 @@ export default defineConfig([
1113
"**/coverage/",
1214
"packages/espree/tools/create-test-example.js"
1315
]),
14-
eslintConfigESLint,
15-
eslintConfigESLintFormatting,
1616
{
17-
files: ["packages/*/tests/lib/**"],
17+
files: ["**/*.{,c}js"],
18+
extends: [eslintConfigESLint, eslintConfigESLintFormatting]
19+
},
20+
{
21+
files: ["packages/*/tests/**/*.test.{,c}js"],
1822
languageOptions: {
1923
globals: {
2024
...globals.mocha
2125
}
2226
}
2327
},
2428
{
25-
files: ["packages/eslint-scope/tests/**"],
26-
languageOptions: {
27-
globals: {
28-
...globals.mocha
29-
}
30-
},
29+
files: ["packages/eslint-scope/tests/**/*.{,c}js"],
3130
plugins: {
3231
"chai-friendly": eslintPluginChaiFriendly
3332
},
@@ -68,6 +67,21 @@ export default defineConfig([
6867
}
6968
}
7069
},
70+
{
71+
files: ["packages/eslint-scope/tests/types/*.{,c}ts"],
72+
languageOptions: {
73+
parser: tsParser,
74+
parserOptions: {
75+
project: ["packages/eslint-scope/tests/types/tsconfig.json"]
76+
}
77+
},
78+
plugins: {
79+
"expect-type": expectType
80+
},
81+
rules: {
82+
"expect-type/expect": "error"
83+
}
84+
},
7185
{
7286
files: ["**/tools/**"],
7387
rules: {

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
"scripts": {
66
"test": "npm test --workspaces --if-present",
77
"build": "npm run build --workspaces --if-present",
8+
"build:types": "npm run build:types --workspaces --if-present",
89
"lint": "eslint",
9-
"lint:fix": "eslint --fix"
10+
"lint:fix": "eslint --fix",
11+
"lint:types": "npm run lint:types --workspaces --if-present"
1012
},
1113
"workspaces": [
1214
"packages/*"
@@ -15,15 +17,17 @@
1517
"pre-commit": "lint-staged"
1618
},
1719
"lint-staged": {
18-
"*.{js,cjs}": [
20+
"*.{js,cjs,ts,cts}": [
1921
"eslint --fix"
2022
]
2123
},
2224
"devDependencies": {
25+
"@typescript-eslint/parser": "^8.49.0",
2326
"c8": "^10.1.3",
2427
"eslint": "^9.35.0",
2528
"eslint-config-eslint": "^13.0.0",
2629
"eslint-plugin-chai-friendly": "^1.0.0",
30+
"eslint-plugin-expect-type": "^0.6.2",
2731
"globals": "^17.0.0",
2832
"lint-staged": "^16.0.0",
2933
"mocha": "^11.1.0",

packages/eslint-scope/README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ In order to analyze scope, you'll need to have an [ESTree](https://github.com/es
3737
* `sourceType` (default: `"script"`) - The type of JavaScript file to evaluate. Change to `"module"` for ECMAScript module code.
3838
* `childVisitorKeys` (default: `null`) - An object with visitor key information (like [`eslint-visitor-keys`](https://github.com/eslint/js/tree/main/packages/eslint-visitor-keys)). Without this, `eslint-scope` finds child nodes to visit algorithmically. Providing this option is a performance enhancement.
3939
* `fallback` (default: `"iteration"`) - The strategy to use when `childVisitorKeys` is not specified. May be a function.
40+
* `optimistic` (default: `false`) - Set to `true` to enable optimistic scope analysis.
4041
* `jsx` (default: `false`) - Enables the tracking of JSX components as variable references.
4142

4243
Example:
@@ -100,7 +101,7 @@ The `ScopeManager` class is at the core of eslint-scope and is returned when you
100101
- `inner` - Optional boolean. When `true`, returns the innermost scope, otherwise returns the outermost scope. Default is `false`.
101102
- Returns: The acquired scope or `null` if no scope is found.
102103

103-
- **`acquireAll(node)`**
104+
- **`acquireAll(node)` (Deprecated)**
104105
Acquires all scopes for a given node.
105106
- `node` - The AST node to acquire scopes from.
106107
- Returns: An array of scopes or `undefined` if none are found.
@@ -120,27 +121,29 @@ The `ScopeManager` class is at the core of eslint-scope and is returned when you
120121
Determines if the global return statement should be allowed.
121122
- Returns: `true` if the global return is enabled.
122123

123-
- **`isModule()`**
124+
- **`isModule()` (Deprecated)**
124125
Checks if the code should be handled as an ECMAScript module.
125126
- Returns: `true` if the sourceType is "module".
126127

127-
- **`isImpliedStrict()`**
128+
- **`isImpliedStrict()` (Deprecated)**
128129
Checks if implied strict mode is enabled.
129130
- Returns: `true` if implied strict mode is enabled.
130131

131-
- **`isStrictModeSupported()`**
132+
- **`isStrictModeSupported()` (Deprecated)**
132133
Checks if strict mode is supported based on ECMAScript version.
133134
- Returns: `true` if the ECMAScript version supports strict mode.
134135

135136
### Scope Objects
136137

137138
Scopes returned by the ScopeManager methods have the following properties:
138139

139-
- `type` - The type of scope (e.g., "function", "block", "global").
140+
- `type` - The type of scope (e.g., `"function"`, `"block"`, `"global"`).
141+
- `isStrict` - `true` if this scope is in strict mode.
140142
- `variables` - Array of variables declared in this scope.
141143
- `set` - A Map of variable names to Variable objects for variables declared in this scope.
142144
- `references` - Array of references in this scope.
143145
- `through` - Array of references in this scope and its child scopes that aren't resolved in this scope or its child scopes.
146+
- `functionExpressionScope` - `true` if this is a `"function-expression-name"` scope.
144147
- `variableScope` - Reference to the closest variable scope.
145148
- `upper` - Reference to the parent scope.
146149
- `childScopes` - Array of child scopes.

packages/eslint-scope/lib/definition.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@
2424

2525
import Variable from "./variable.js";
2626

27+
/** @import * as types from "eslint-scope" */
28+
29+
// Cannot implement `types.Definition` directly because it contains a union.
2730
/**
2831
* @constructor Definition
32+
* @implements {Omit<types.Definition, never>}
2933
*/
3034
class Definition {
3135
constructor(type, name, node, parent, index, kind) {

0 commit comments

Comments
 (0)