Skip to content

Commit c9a2b9c

Browse files
committed
feat(no-invalid-transition-props): support new transition props from xstate v5
Newly accepted props in transitions: guard, reenter. No longer valid props: cond, in, internal.
1 parent 4aecdff commit c9a2b9c

File tree

2 files changed

+203
-20
lines changed

2 files changed

+203
-20
lines changed

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

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22

33
const getDocsUrl = require('../utils/getDocsUrl')
44
const { isObjectExpression, isArrayExpression } = require('../utils/predicates')
5+
const getSettings = require('../utils/getSettings')
56

6-
const validProperties = [
7-
'target',
8-
'cond',
9-
'actions',
10-
'in',
11-
'internal',
12-
'description',
13-
]
14-
function isValidTransitionProperty(property) {
15-
return validProperties.includes(property.key.name)
7+
const validProperties = {
8+
4: ['target', 'cond', 'actions', 'in', 'internal', 'description'],
9+
5: ['target', 'guard', 'actions', 'reenter', 'description'],
10+
}
11+
12+
function isValidTransitionProperty(property, version) {
13+
return (
14+
validProperties[version] &&
15+
validProperties[version].includes(property.key.name)
16+
)
1617
}
1718

1819
// e.g.
@@ -52,11 +53,12 @@ module.exports = {
5253
},
5354

5455
create: function (context) {
56+
const { version } = getSettings(context)
5557
function checkTransitionDeclaration(node) {
5658
const transitionValue = node.value
5759
if (isObjectExpression(transitionValue)) {
5860
transitionValue.properties.forEach((prop) => {
59-
if (!isValidTransitionProperty(prop)) {
61+
if (!isValidTransitionProperty(prop, version)) {
6062
context.report({
6163
node: prop,
6264
messageId: 'invalidTransitionProperty',
@@ -73,7 +75,7 @@ module.exports = {
7375
return
7476
}
7577
transitionObject.properties.forEach((prop) => {
76-
if (!isValidTransitionProperty(prop)) {
78+
if (!isValidTransitionProperty(prop, version)) {
7779
context.report({
7880
node: prop,
7981
messageId: 'invalidTransitionProperty',
@@ -91,7 +93,10 @@ module.exports = {
9193
return
9294
}
9395
transitionObject.properties.forEach((prop) => {
94-
if (prop.key.name !== 'event' && !isValidTransitionProperty(prop)) {
96+
if (
97+
prop.key.name !== 'event' &&
98+
!isValidTransitionProperty(prop, version)
99+
) {
95100
context.report({
96101
node: prop,
97102
messageId: 'invalidTransitionProperty',

tests/lib/rules/no-invalid-transition-props.js

Lines changed: 185 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
const RuleTester = require('eslint').RuleTester
22
const rule = require('../../../lib/rules/no-invalid-transition-props')
3+
const { withVersion } = require('../utils/settings')
34

45
const tests = {
56
valid: [
6-
`
7+
withVersion(
8+
4,
9+
`
710
createMachine({
811
states: {
912
idle: {
@@ -18,15 +21,57 @@ const tests = {
1821
},
1922
},
2023
},
24+
ready: {
25+
on: [
26+
{ event: "*", target: "elsewhere", internal: false },
27+
{ event: "SOME_EVENT", target: "here", cond: () => true },
28+
],
29+
},
2130
},
2231
on: {
2332
EVENT: [{
33+
cond: () => true,
2434
target: 'active',
2535
}],
2636
},
2737
})
28-
`,
2938
`
39+
),
40+
withVersion(
41+
5,
42+
`
43+
createMachine({
44+
states: {
45+
idle: {
46+
on: {
47+
EVENT: {
48+
guard: () => true,
49+
target: 'active',
50+
actions: [],
51+
reenter: true,
52+
description: 'some text',
53+
},
54+
},
55+
},
56+
ready: {
57+
on: [
58+
{ event: "*", target: "elsewhere", reenter: true },
59+
{ event: "SOME_EVENT", target: "here", guard: () => true },
60+
],
61+
},
62+
},
63+
on: {
64+
EVENT: [{
65+
guard: () => true,
66+
target: 'active',
67+
}],
68+
},
69+
})
70+
`
71+
),
72+
withVersion(
73+
4,
74+
`
3075
createMachine({
3176
states: {
3277
idle: {
@@ -55,10 +100,43 @@ const tests = {
55100
},
56101
},
57102
})
58-
`,
103+
`
104+
),
105+
withVersion(
106+
5,
107+
`
108+
createMachine({
109+
states: {
110+
idle: {
111+
invoke: {
112+
src: 'someService',
113+
onDone: {
114+
guard: () => true,
115+
target: 'active',
116+
actions: [],
117+
reenter: true,
118+
description: 'some text',
119+
},
120+
onError: [
121+
{
122+
guard: () => false,
123+
target: 'failed',
124+
description: 'some text',
125+
},
126+
{
127+
target: 'failed',
128+
description: 'some text',
129+
},
130+
],
131+
},
132+
},
133+
},
134+
})
135+
`
136+
),
59137
],
60138
invalid: [
61-
{
139+
withVersion(4, {
62140
code: `
63141
createMachine({
64142
states: {
@@ -70,6 +148,12 @@ const tests = {
70148
},
71149
},
72150
},
151+
ready: {
152+
on: [
153+
{ event: "*", target: "elsewhere", beeep: '???' },
154+
{ event: "SOME_EVENT", target: "here", guard: () => true },
155+
],
156+
},
73157
},
74158
on: {
75159
EVENT: [{
@@ -80,13 +164,99 @@ const tests = {
80164
`,
81165
errors: [
82166
{ messageId: 'invalidTransitionProperty', data: { propName: 'foo' } },
167+
{ messageId: 'invalidTransitionProperty', data: { propName: 'beeep' } },
168+
{ messageId: 'invalidTransitionProperty', data: { propName: 'guard' } },
83169
{
84170
messageId: 'invalidTransitionProperty',
85171
data: { propName: 'invoke' },
86172
},
87173
],
88-
},
89-
{
174+
}),
175+
withVersion(5, {
176+
code: `
177+
createMachine({
178+
states: {
179+
idle: {
180+
on: {
181+
EVENT: {
182+
target: 'active',
183+
foo: '???',
184+
cond: () => true,
185+
in: 'otherState.ready',
186+
internal: false,
187+
},
188+
},
189+
},
190+
ready: {
191+
on: [
192+
{ event: "*", target: "elsewhere", internal: '???' },
193+
{ event: "SOME_EVENT", target: "here", cond: () => true },
194+
],
195+
},
196+
},
197+
on: {
198+
EVENT: [{
199+
invoke: '???',
200+
}],
201+
},
202+
})
203+
`,
204+
errors: [
205+
{ messageId: 'invalidTransitionProperty', data: { propName: 'foo' } },
206+
{ messageId: 'invalidTransitionProperty', data: { propName: 'cond' } },
207+
{ messageId: 'invalidTransitionProperty', data: { propName: 'in' } },
208+
{
209+
messageId: 'invalidTransitionProperty',
210+
data: { propName: 'internal' },
211+
},
212+
{
213+
messageId: 'invalidTransitionProperty',
214+
data: { propName: 'internal' },
215+
},
216+
{ messageId: 'invalidTransitionProperty', data: { propName: 'cond' } },
217+
{
218+
messageId: 'invalidTransitionProperty',
219+
data: { propName: 'invoke' },
220+
},
221+
],
222+
}),
223+
withVersion(4, {
224+
code: `
225+
createMachine({
226+
states: {
227+
idle: {
228+
invoke: {
229+
src: 'someService',
230+
onDone: {
231+
target: 'active',
232+
always: '???',
233+
},
234+
onError: [
235+
{
236+
cond: () => false,
237+
target: 'failed',
238+
after: 1000,
239+
},
240+
{
241+
target: 'failed',
242+
entry: '???',
243+
},
244+
],
245+
},
246+
},
247+
},
248+
})
249+
`,
250+
errors: [
251+
{
252+
messageId: 'invalidTransitionProperty',
253+
data: { propName: 'always' },
254+
},
255+
{ messageId: 'invalidTransitionProperty', data: { propName: 'after' } },
256+
{ messageId: 'invalidTransitionProperty', data: { propName: 'entry' } },
257+
],
258+
}),
259+
withVersion(5, {
90260
code: `
91261
createMachine({
92262
states: {
@@ -96,12 +266,14 @@ const tests = {
96266
onDone: {
97267
target: 'active',
98268
always: '???',
269+
in: 'otherState.ready',
99270
},
100271
onError: [
101272
{
102273
cond: () => false,
103274
target: 'failed',
104275
after: 1000,
276+
internal: false,
105277
},
106278
{
107279
target: 'failed',
@@ -118,10 +290,16 @@ const tests = {
118290
messageId: 'invalidTransitionProperty',
119291
data: { propName: 'always' },
120292
},
293+
{ messageId: 'invalidTransitionProperty', data: { propName: 'in' } },
294+
{ messageId: 'invalidTransitionProperty', data: { propName: 'cond' } },
121295
{ messageId: 'invalidTransitionProperty', data: { propName: 'after' } },
296+
{
297+
messageId: 'invalidTransitionProperty',
298+
data: { propName: 'internal' },
299+
},
122300
{ messageId: 'invalidTransitionProperty', data: { propName: 'entry' } },
123301
],
124-
},
302+
}),
125303
],
126304
}
127305

0 commit comments

Comments
 (0)