Skip to content

Commit 4aecdff

Browse files
committed
feat(no-invalid-state-props): modify the rule to accept new xstate@5 state props
Newly accepted props: types, output. No longer valid props: activities, tsTypes, schema, preserveActionOrder, predictableActionArguments, strict, data.
1 parent 0637a6f commit 4aecdff

File tree

4 files changed

+290
-59
lines changed

4 files changed

+290
-59
lines changed

lib/index.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ module.exports = {
2929
'no-async-guard': require('./rules/no-async-guard'),
3030
},
3131
configs: {
32+
// Requires: xstate@5
3233
recommended: {
34+
settings: {
35+
xstate: {
36+
version: 5,
37+
},
38+
},
3339
plugins: ['xstate'],
3440
rules: {
3541
'xstate/spawn-usage': 'error',
@@ -46,7 +52,62 @@ module.exports = {
4652
'xstate/no-async-guard': 'error',
4753
},
4854
},
55+
// Requires: xstate@5
4956
all: {
57+
settings: {
58+
xstate: {
59+
version: 5,
60+
},
61+
},
62+
plugins: ['xstate'],
63+
rules: {
64+
'xstate/spawn-usage': 'error',
65+
'xstate/no-infinite-loop': 'error',
66+
'xstate/no-imperative-action': 'error',
67+
'xstate/no-ondone-outside-compound-state': 'error',
68+
'xstate/invoke-usage': 'error',
69+
'xstate/entry-exit-action': 'error',
70+
'xstate/event-names': ['warn', 'macroCase'],
71+
'xstate/state-names': ['warn', 'camelCase'],
72+
'xstate/no-inline-implementation': 'warn',
73+
'xstate/no-auto-forward': 'warn',
74+
'xstate/prefer-always': 'error',
75+
'xstate/no-misplaced-on-transition': 'error',
76+
'xstate/no-invalid-transition-props': 'error',
77+
'xstate/no-invalid-state-props': 'error',
78+
'xstate/no-async-guard': 'error',
79+
},
80+
},
81+
// Requires: xstate@4
82+
recommended_v4: {
83+
settings: {
84+
xstate: {
85+
version: 4,
86+
},
87+
},
88+
plugins: ['xstate'],
89+
rules: {
90+
'xstate/spawn-usage': 'error',
91+
'xstate/no-infinite-loop': 'error',
92+
'xstate/no-imperative-action': 'error',
93+
'xstate/no-ondone-outside-compound-state': 'error',
94+
'xstate/invoke-usage': 'error',
95+
'xstate/entry-exit-action': 'error',
96+
'xstate/prefer-always': 'error',
97+
'xstate/prefer-predictable-action-arguments': 'warn',
98+
'xstate/no-misplaced-on-transition': 'error',
99+
'xstate/no-invalid-transition-props': 'error',
100+
'xstate/no-invalid-state-props': 'error',
101+
'xstate/no-async-guard': 'error',
102+
},
103+
},
104+
// Requires: xstate@4
105+
all_v4: {
106+
settings: {
107+
xstate: {
108+
version: 4,
109+
},
110+
},
50111
plugins: ['xstate'],
51112
rules: {
52113
'xstate/spawn-usage': 'error',

lib/rules/no-invalid-state-props.js

Lines changed: 98 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,104 @@ const {
88
hasProperty,
99
} = require('../utils/predicates')
1010
const { allPass } = require('../utils/combinators')
11+
const getSettings = require('../utils/getSettings')
1112

12-
const validProperties = [
13-
'after',
14-
'always',
15-
'entry',
16-
'exit',
17-
'history', // only when type=history
18-
'id',
19-
'initial',
20-
'invoke',
21-
'meta',
22-
'on',
23-
'onDone',
24-
'states',
25-
'tags',
26-
'target', // only when type=history
27-
'type',
28-
'data',
29-
'description',
30-
'activities',
31-
]
32-
function isValidStateProperty(property) {
33-
return validProperties.includes(property.key.name)
13+
const validProperties = {
14+
4: [
15+
'after',
16+
'always',
17+
'entry',
18+
'exit',
19+
'history', // only when type=history
20+
'id',
21+
'initial',
22+
'invoke',
23+
'meta',
24+
'on',
25+
'onDone',
26+
'states',
27+
'tags',
28+
'target', // only when type=history
29+
'type',
30+
'data',
31+
'description',
32+
'activities',
33+
],
34+
5: [
35+
'after',
36+
'always',
37+
'entry',
38+
'exit',
39+
'history', // only when type=history
40+
'id',
41+
'initial',
42+
'invoke',
43+
'meta',
44+
'on',
45+
'onDone',
46+
'states',
47+
'tags',
48+
'target', // only when type=history
49+
'type',
50+
'description',
51+
'output',
52+
],
3453
}
3554

36-
const validRootProperties = [
37-
'after',
38-
'context',
39-
'description',
40-
'entry',
41-
'exit',
42-
'history', // only when type=history
43-
'id',
44-
'initial',
45-
'invoke',
46-
'meta',
47-
'on',
48-
'predictableActionArguments',
49-
'preserveActionOrder',
50-
'schema',
51-
'states',
52-
'strict',
53-
'tags',
54-
'target', // only when type=history
55-
'tsTypes',
56-
'type',
57-
]
58-
function isValidRootStateProperty(property) {
59-
return validRootProperties.includes(property.key.name)
55+
function isValidStateProperty(property, version) {
56+
return (
57+
validRootProperties[version] &&
58+
validProperties[version].includes(property.key.name)
59+
)
60+
}
61+
62+
const validRootProperties = {
63+
4: [
64+
'after',
65+
'context',
66+
'description',
67+
'entry',
68+
'exit',
69+
'history', // only when type=history
70+
'id',
71+
'initial',
72+
'invoke',
73+
'meta',
74+
'on',
75+
'predictableActionArguments',
76+
'preserveActionOrder',
77+
'schema',
78+
'states',
79+
'strict',
80+
'tags',
81+
'target', // only when type=history
82+
'tsTypes',
83+
'type',
84+
],
85+
5: [
86+
'after',
87+
'context',
88+
'description',
89+
'entry',
90+
'exit',
91+
'history', // only when type=history
92+
'id',
93+
'initial',
94+
'invoke',
95+
'meta',
96+
'on',
97+
'states',
98+
'tags',
99+
'target', // only when type=history
100+
'types',
101+
'type',
102+
],
103+
}
104+
function isValidRootStateProperty(property, version) {
105+
return (
106+
validRootProperties[version] &&
107+
validRootProperties[version].includes(property.key.name)
108+
)
60109
}
61110

62111
function hasHistoryTypeProperty(node) {
@@ -156,6 +205,7 @@ module.exports = {
156205
},
157206

158207
create: function (context) {
208+
const { version } = getSettings(context)
159209
return {
160210
[stateDeclaration]: function (node) {
161211
const isHistoryNode = hasHistoryTypeProperty(node)
@@ -189,7 +239,7 @@ module.exports = {
189239
return
190240
}
191241

192-
if (!isValidStateProperty(prop)) {
242+
if (!isValidStateProperty(prop, version)) {
193243
context.report({
194244
node: prop,
195245
messageId: 'invalidStateProperty',
@@ -230,7 +280,7 @@ module.exports = {
230280
return
231281
}
232282

233-
if (!isValidRootStateProperty(prop)) {
283+
if (!isValidRootStateProperty(prop, version)) {
234284
context.report({
235285
node: prop,
236286
messageId: 'invalidRootStateProperty',

lib/utils/getSettings.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict'
2+
3+
const defaults = {
4+
version: 5
5+
}
6+
7+
const supportedVersions = [4, 5]
8+
9+
module.exports = function getSettings(context) {
10+
const settings = {
11+
...defaults,
12+
...(context.settings ? context.settings.xstate : undefined)
13+
}
14+
15+
if (!supportedVersions.includes(settings.version)) {
16+
throw new Error(`XState version "${settings.version}" is not supported. Check "settings.xstate.version" in your ESLint config.`)
17+
}
18+
return settings
19+
}

0 commit comments

Comments
 (0)