Skip to content

Commit bab6a8c

Browse files
committed
refactor(ast-utils, rules): enhance utility extraction and test coverage
- Rename extractDeclarations to extractTopLevelDeclarations for clarity and scope consistency - Add extractReturnIdentifiers for improved return property identifier detection - Extend unit tests for AST utilities to cover edge cases and TS/JS wrappers - Update rules to ensure consistent usage of extractTopLevelDeclarations and identifiers handling - Improve test coverage and robustness of rule logic
1 parent 1852849 commit bab6a8c

10 files changed

+331
-70
lines changed

packages/eslint-plugin/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,20 @@
4545
"scripts": {
4646
"build": "tsup",
4747
"test": "vitest run",
48+
"test:coverage": "vitest --coverage",
4849
"test:watch": "vitest",
4950
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l @pinia/eslint-plugin -r 1"
5051
},
5152
"devDependencies": {
53+
"@eslint/js": "^9.35.0",
5254
"@typescript-eslint/parser": "^8.35.1",
5355
"@typescript-eslint/rule-tester": "^8.35.1",
5456
"@typescript-eslint/utils": "^8.35.1",
55-
"eslint": "^9.0.0",
57+
"eslint": "^9.35.0",
5658
"pinia": "workspace:*",
5759
"tsup": "^8.5.0",
5860
"typescript": "~5.8.3",
61+
"typescript-eslint": "^8.44.0",
5962
"vitest": "^3.2.4"
6063
},
6164
"engines": {

packages/eslint-plugin/src/rules/__tests__/no-circular-store-dependencies.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
import { RuleTester } from '@typescript-eslint/rule-tester'
66
import { noCircularStoreDependencies } from '../no-circular-store-dependencies'
7+
import * as parser from '@typescript-eslint/parser'
78

89
const ruleTester = new RuleTester({
910
languageOptions: {
10-
parser: require('@typescript-eslint/parser'),
11+
parser,
1112
parserOptions: {
1213
ecmaVersion: 2020,
1314
sourceType: 'module',

packages/eslint-plugin/src/rules/__tests__/no-store-in-computed.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
import { RuleTester } from '@typescript-eslint/rule-tester'
66
import { noStoreInComputed } from '../no-store-in-computed'
7+
import * as parser from '@typescript-eslint/parser'
78

89
const ruleTester = new RuleTester({
910
languageOptions: {
10-
parser: require('@typescript-eslint/parser'),
11+
parser,
1112
parserOptions: {
1213
ecmaVersion: 2020,
1314
sourceType: 'module',

packages/eslint-plugin/src/rules/no-circular-store-dependencies.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export const noCircularStoreDependencies = createRule({
3131
type: 'problem',
3232
docs: {
3333
description: 'disallow circular dependencies between stores',
34-
recommended: 'warn',
3534
},
3635
schema: [],
3736
messages: {

packages/eslint-plugin/src/rules/no-store-in-computed.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export const noStoreInComputed = createRule({
2626
type: 'problem',
2727
docs: {
2828
description: 'disallow store instantiation in computed properties',
29-
recommended: 'error',
3029
},
3130
schema: [],
3231
messages: {

packages/eslint-plugin/src/rules/prefer-use-store-naming.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export const preferUseStoreNaming = createRule({
2222
type: 'suggestion',
2323
docs: {
2424
description: 'enforce consistent store naming conventions',
25-
recommended: 'warn',
2625
},
2726
hasSuggestions: true,
2827
schema: [
@@ -44,6 +43,7 @@ export const preferUseStoreNaming = createRule({
4443
messages: {
4544
invalidNaming:
4645
'Store function should follow the naming pattern "{{expected}}"',
46+
renameTo: 'Rename to "{{name}}"',
4747
},
4848
},
4949
defaultOptions: [{ prefix: 'use', suffix: 'Store' }],
@@ -95,7 +95,8 @@ export const preferUseStoreNaming = createRule({
9595
},
9696
suggest: [
9797
{
98-
desc: `Rename to "${suggestedName}"`,
98+
messageId: 'renameTo',
99+
data: { name: suggestedName },
99100
fix(fixer) {
100101
return fixer.replaceText(node.id, suggestedName)
101102
},

packages/eslint-plugin/src/rules/require-setup-store-properties-export.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
isDefineStoreCall,
99
isSetupStore,
1010
getSetupFunction,
11-
extractDeclarations,
11+
extractTopLevelDeclarations,
1212
extractReturnIdentifiers,
1313
findReturnStatement,
1414
hasSpreadInReturn,
@@ -30,7 +30,6 @@ export const requireSetupStorePropertiesExport = createRule({
3030
type: 'problem',
3131
docs: {
3232
description: 'require all setup store properties to be exported',
33-
recommended: 'error',
3433
},
3534
fixable: 'code',
3635
schema: [],
@@ -55,8 +54,10 @@ export const requireSetupStorePropertiesExport = createRule({
5554
return
5655
}
5756

58-
// Extract all declared variables and functions
59-
const { variables, functions } = extractDeclarations(setupFunction.body)
57+
// Extract all declared variables and functions (top-level only)
58+
const { variables, functions } = extractTopLevelDeclarations(
59+
setupFunction.body
60+
)
6061
const allDeclared = [...variables, ...functions]
6162

6263
// Find the return statement

packages/eslint-plugin/src/utils/__tests__/ast-utils.test.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import type { TSESTree } from '@typescript-eslint/utils'
88
import {
99
isDefineStoreCall,
1010
getStoreId,
11-
extractDeclarations,
11+
extractTopLevelDeclarations,
1212
extractReturnProperties,
13+
extractReturnIdentifiers,
1314
} from '../ast-utils'
1415

1516
function parseCode(code: string): TSESTree.Program {
@@ -110,8 +111,8 @@ describe('getStoreId', () => {
110111
})
111112
})
112113

113-
describe('extractDeclarations', () => {
114-
it('should extract variable declarations and deduplicate', () => {
114+
describe('extractTopLevelDeclarations', () => {
115+
it('should extract variable declarations and deduplicate (top-level only)', () => {
115116
const code = `
116117
function setup() {
117118
var name = ref('test')
@@ -122,12 +123,12 @@ describe('extractDeclarations', () => {
122123
`
123124
const ast = parseCode(code)
124125
const func = ast.body[0] as TSESTree.FunctionDeclaration
125-
const result = extractDeclarations(func.body!)
126+
const result = extractTopLevelDeclarations(func.body!)
126127

127128
expect(result.variables).toEqual(['name', 'count'])
128129
})
129130

130-
it('should extract loop initializer declarations', () => {
131+
it('should not include loop initializer declarations (top-level only)', () => {
131132
const code = `
132133
function setup() {
133134
for (let i = 0; i < 10; i++) {
@@ -141,13 +142,14 @@ describe('extractDeclarations', () => {
141142
`
142143
const ast = parseCode(code)
143144
const func = ast.body[0] as TSESTree.FunctionDeclaration
144-
const result = extractDeclarations(func.body!)
145+
const result = extractTopLevelDeclarations(func.body!)
145146

146-
expect(result.variables).toContain('i')
147-
expect(result.variables).toContain('item')
147+
expect(result.variables).not.toContain('i')
148+
expect(result.variables).not.toContain('item')
149+
expect(result.variables).toEqual([])
148150
})
149151

150-
it('should extract catch clause parameters', () => {
152+
it('should not include catch clause parameters (top-level only)', () => {
151153
const code = `
152154
function setup() {
153155
try {
@@ -160,9 +162,10 @@ describe('extractDeclarations', () => {
160162
`
161163
const ast = parseCode(code)
162164
const func = ast.body[0] as TSESTree.FunctionDeclaration
163-
const result = extractDeclarations(func.body!)
165+
const result = extractTopLevelDeclarations(func.body!)
164166

165-
expect(result.variables).toContain('error')
167+
expect(result.variables).not.toContain('error')
168+
expect(result.variables).toEqual([])
166169
})
167170
})
168171

@@ -191,7 +194,19 @@ describe('extractReturnProperties', () => {
191194
const func = ast.body[0] as TSESTree.FunctionDeclaration
192195
const returnStmt = func.body!.body[0] as TSESTree.ReturnStatement
193196
const result = extractReturnProperties(returnStmt)
197+
expect(result).toEqual(['name', 'count'])
198+
})
194199

200+
it('should extract computed string-literal property keys', () => {
201+
const code = `
202+
function setup() {
203+
return { ["name"]: value, ['count']: value2 }
204+
}
205+
`
206+
const ast = parseCode(code)
207+
const func = ast.body[0] as TSESTree.FunctionDeclaration
208+
const returnStmt = func.body!.body[0] as TSESTree.ReturnStatement
209+
const result = extractReturnProperties(returnStmt)
195210
expect(result).toEqual(['name', 'count'])
196211
})
197212

@@ -222,4 +237,19 @@ describe('extractReturnProperties', () => {
222237

223238
expect(result).toEqual([])
224239
})
240+
241+
it('extractReturnIdentifiers should unwrap TS/JS wrappers to find identifiers', () => {
242+
const code = `
243+
function setup() {
244+
const count = 1
245+
const total = 2
246+
return { a: (count as number), b: ((total)), c: (count!) }
247+
}
248+
`
249+
const ast = parseCode(code)
250+
const func = ast.body[0] as TSESTree.FunctionDeclaration
251+
const returnStmt = func.body!.body[2] as TSESTree.ReturnStatement
252+
const result = extractReturnIdentifiers(returnStmt)
253+
expect(result).toEqual(['count', 'total', 'count'])
254+
})
225255
})

0 commit comments

Comments
 (0)