@@ -7,6 +7,20 @@ import semver from 'semver';
7
7
// Writable derived were introduced in Svelte 5.25.0
8
8
const shouldRun = semver . satisfies ( SVELTE_VERSION , '>=5.25.0' ) ;
9
9
10
+ type ValidFunctionType = TSESTree . FunctionExpression | TSESTree . ArrowFunctionExpression ;
11
+ type ValidFunction = ValidFunctionType & {
12
+ body : TSESTree . BlockStatement ;
13
+ } ;
14
+
15
+ type ValidAssignmentExpression = TSESTree . AssignmentExpression & {
16
+ operator : '=' ;
17
+ left : TSESTree . Identifier ;
18
+ } ;
19
+
20
+ type ValidExpressionStatement = TSESTree . ExpressionStatement & {
21
+ expression : ValidAssignmentExpression ;
22
+ } ;
23
+
10
24
function isEffectOrEffectPre ( node : TSESTree . CallExpression ) {
11
25
if ( node . callee . type === 'Identifier' ) {
12
26
return node . callee . name === '$effect' ;
@@ -23,6 +37,40 @@ function isEffectOrEffectPre(node: TSESTree.CallExpression) {
23
37
return false ;
24
38
}
25
39
40
+ function isValidFunctionArgument ( argument : TSESTree . Node ) : argument is ValidFunction {
41
+ if (
42
+ ( argument . type !== 'FunctionExpression' && argument . type !== 'ArrowFunctionExpression' ) ||
43
+ argument . params . length !== 0
44
+ ) {
45
+ return false ;
46
+ }
47
+
48
+ if ( argument . body . type !== 'BlockStatement' ) {
49
+ return false ;
50
+ }
51
+
52
+ return argument . body . body . length === 1 ;
53
+ }
54
+
55
+ function isValidAssignment ( statement : TSESTree . Statement ) : statement is ValidExpressionStatement {
56
+ if ( statement . type !== 'ExpressionStatement' ) return false ;
57
+
58
+ const { expression } = statement ;
59
+ return (
60
+ expression . type === 'AssignmentExpression' &&
61
+ expression . operator === '=' &&
62
+ expression . left . type === 'Identifier'
63
+ ) ;
64
+ }
65
+
66
+ function isStateVariable ( init : TSESTree . Expression | null ) : init is TSESTree . CallExpression {
67
+ return (
68
+ init ?. type === 'CallExpression' &&
69
+ init . callee . type === 'Identifier' &&
70
+ init . callee . name === '$state'
71
+ ) ;
72
+ }
73
+
26
74
export default createRule ( 'prefer-writable-derived' , {
27
75
meta : {
28
76
docs : {
@@ -50,69 +98,33 @@ export default createRule('prefer-writable-derived', {
50
98
}
51
99
return {
52
100
CallExpression : ( node : TSESTree . CallExpression ) => {
53
- if ( ! isEffectOrEffectPre ( node ) ) {
54
- return ;
55
- }
56
-
57
- if ( node . arguments . length !== 1 ) {
101
+ if ( ! isEffectOrEffectPre ( node ) || node . arguments . length !== 1 ) {
58
102
return ;
59
103
}
60
104
61
105
const argument = node . arguments [ 0 ] ;
62
- if ( argument . type !== 'FunctionExpression' && argument . type !== 'ArrowFunctionExpression' ) {
63
- return ;
64
- }
65
-
66
- if ( argument . params . length !== 0 ) {
67
- return ;
68
- }
69
-
70
- if ( argument . body . type !== 'BlockStatement' ) {
71
- return ;
72
- }
73
-
74
- const body = argument . body . body ;
75
- if ( body . length !== 1 ) {
76
- return ;
77
- }
78
-
79
- const statement = body [ 0 ] ;
80
- if ( statement . type !== 'ExpressionStatement' ) {
106
+ if ( ! isValidFunctionArgument ( argument ) ) {
81
107
return ;
82
108
}
83
109
84
- const expression = statement . expression ;
85
- if ( expression . type !== 'AssignmentExpression' ) {
86
- return ;
87
- }
88
-
89
- const { left, right, operator } = expression ;
90
- if ( operator !== '=' || left . type !== 'Identifier' ) {
110
+ const statement = argument . body . body [ 0 ] ;
111
+ if ( ! isValidAssignment ( statement ) ) {
91
112
return ;
92
113
}
93
114
115
+ const { left, right } = statement . expression ;
94
116
const scope = getScope ( context , statement ) ;
95
- const reference = scope . references . find ( ( reference ) => {
96
- return (
97
- reference . identifier . type === 'Identifier' && reference . identifier . name === left . name
98
- ) ;
99
- } ) ;
100
- const defs = reference ?. resolved ?. defs ;
101
- if ( defs == null || defs . length !== 1 ) {
102
- return ;
103
- }
104
-
105
- const def = defs [ 0 ] ;
106
- if ( def . type !== 'Variable' || def . node . type !== 'VariableDeclarator' ) {
107
- return ;
108
- }
117
+ const reference = scope . references . find (
118
+ ( ref ) => ref . identifier . type === 'Identifier' && ref . identifier . name === left . name
119
+ ) ;
109
120
110
- const init = def . node . init ;
111
- if ( init == null || init . type !== 'CallExpression ' ) {
121
+ const def = reference ?. resolved ?. defs ?. [ 0 ] ;
122
+ if ( ! def || def . type ! == 'Variable' || def . node . type !== 'VariableDeclarator ' ) {
112
123
return ;
113
124
}
114
125
115
- if ( init . callee . type !== 'Identifier' || init . callee . name !== '$state' ) {
126
+ const { init } = def . node ;
127
+ if ( ! isStateVariable ( init ) ) {
116
128
return ;
117
129
}
118
130
0 commit comments