Skip to content

Commit

Permalink
fix(no-ondone-outside-compound-state): allow the use of onDone with…
Browse files Browse the repository at this point in the history
…in an array of invoke blocks

Fix a problem where `onDone` was incorrectly reported as invalid when used within an array of
`invoke` blocks

fix #18
  • Loading branch information
rlaffers committed Aug 4, 2023
1 parent a3adedf commit 10e1e0c
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 5 deletions.
30 changes: 30 additions & 0 deletions docs/rules/no-ondone-outside-compound-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,36 @@ createMachine({
},
},
})

// ✅ onDone transition inside an invoke block
createMachine({
states: {
active: {
invoke: {
src: 'fetchSomething',
onDone: 'idle',
},
},
},
})

// ✅ onDone transition inside an array of invoke blocks
createMachine({
states: {
active: {
invoke: [
{
src: 'fetchSomething',
onDone: 'done',
},
{
src: 'listen',
onDone: 'done',
},
],
},
},
})
```

## Further Reading
Expand Down
35 changes: 31 additions & 4 deletions lib/rules/no-ondone-outside-compound-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,27 @@ const getDocsUrl = require('../utils/getDocsUrl')
const { getTypeProperty } = require('../utils/selectors')
const { hasProperty, isWithinInvoke } = require('../utils/predicates')

function isWithinAtomicStateNode(node) {
function isWithinCompoundStateNode(node) {
const stateNode = node.parent
const type = getTypeProperty(stateNode)
return (
!isWithinInvoke(node) &&
((type != null && type.value.value === 'atomic') ||
(type == null && !hasProperty('initial', stateNode)))
hasProperty('initial', stateNode) ||
(type != null && type.value.value === 'compound')
)
}

function isWithinParallelStateNode(node) {
const stateNode = node.parent
const type = getTypeProperty(stateNode)
return type != null && type.value.value === 'parallel'
}

function isWithinAtomicStateNode(node) {
const stateNode = node.parent
const type = getTypeProperty(stateNode)
return type != null && type.value.value === 'atomic'
}

function isWithinHistoryStateNode(node) {
const stateNode = node.parent
const type = getTypeProperty(stateNode)
Expand Down Expand Up @@ -44,13 +55,24 @@ module.exports = {
'History state nodes cannot have an "onDone" transition. The "onDone" transition has effect only in compound/parallel state nodes or in service invocations.',
onDoneOnFinalStateForbidden:
'Final state nodes cannot have an "onDone" transition. The "onDone" transition has effect only in compound/parallel state nodes or in service invocations.',
onDoneUsedIncorrectly:
'The "onDone" transition cannot be used here. The "onDone" transition has effect only in compound/parallel state nodes or in service invocations.',
},
},

create: function (context) {
return {
'CallExpression[callee.name=/^createMachine$|^Machine$/] Property[key.name="onDone"]':
function (node) {
if (isWithinInvoke(node)) {
return
}
if (isWithinCompoundStateNode(node)) {
return
}
if (isWithinParallelStateNode(node)) {
return
}
if (isWithinAtomicStateNode(node)) {
context.report({
node,
Expand All @@ -70,7 +92,12 @@ module.exports = {
node,
messageId: 'onDoneOnFinalStateForbidden',
})
return
}
context.report({
node,
messageId: 'onDoneUsedIncorrectly',
})
},
}
},
Expand Down
14 changes: 13 additions & 1 deletion lib/utils/predicates.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,19 @@ function isKnownActionCreatorCall(node) {

function isWithinInvoke(property) {
const parentProp = property.parent.parent
return parentProp.type === 'Property' && parentProp.key.name === 'invoke'
if (
parentProp &&
parentProp.type === 'Property' &&
parentProp.key.name === 'invoke'
) {
return true
}
return (
parentProp.type === 'ArrayExpression' &&
parentProp.parent &&
parentProp.parent.type === 'Property' &&
parentProp.parent.key.name === 'invoke'
)
}

// list of property names which have special meaning to XState in some contexts (they are
Expand Down
49 changes: 49 additions & 0 deletions tests/lib/rules/no-ondone-outside-compound-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,59 @@ const tests = {
},
})
`,
`
createMachine({
initial: 'loading',
states: {
loading: {
invoke: {
src: 'fetchData',
onDone: 'ready',
},
},
},
})
`,
`
createMachine({
initial: 'loading',
states: {
loading: {
invoke: [{
src: 'fetchData',
onDone: 'ready',
}],
},
},
})
`,
],
invalid: [
{
code: `
createMachine({
states: {
active: {
type: 'atomic',
onDone: 'idle',
},
},
})
`,
errors: [{ messageId: 'onDoneOnAtomicStateForbidden' }],
},
{
code: `
createMachine({
states: {
active: {
onDone: 'idle',
},
},
})
`,
errors: [{ messageId: 'onDoneUsedIncorrectly' }],
},
{
code: `
createMachine({
Expand Down Expand Up @@ -75,6 +114,16 @@ const tests = {
`,
errors: [{ messageId: 'onDoneOnHistoryStateForbidden' }],
},
{
code: `
createMachine({
on: {
onDone: 'idle',
},
})
`,
errors: [{ messageId: 'onDoneUsedIncorrectly' }],
},
],
}

Expand Down

0 comments on commit 10e1e0c

Please sign in to comment.