Skip to content

Commit b95101d

Browse files
committed
feat(no-inline-implementation): detect inline implementations inside spawn call with xstate v5
1 parent 9654443 commit b95101d

File tree

4 files changed

+79
-6
lines changed

4 files changed

+79
-6
lines changed

lib/rules/no-inline-implementation.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const { getTypeProperty } = require('../utils/selectors')
1313
const { anyPass } = require('../utils/combinators')
1414
const getSettings = require('../utils/getSettings')
1515
const XStateDetector = require('../utils/XStateDetector')
16+
const isSpawnFromParametersCallExpresion = require('../utils/isSpawnFromParametersCallExpression')
1617

1718
function isArrayWithFunctionExpressionOrIdentifier(node) {
1819
return (
@@ -247,7 +248,7 @@ module.exports = {
247248
}
248249

249250
return {
250-
...xstateDetector.visitors,
251+
...(version === 4 ? xstateDetector.visitors : {}),
251252
[propertyOfEventTransition]: checkTransitionProperty,
252253
[propertyOfEventTransitionInArray]: checkTransitionProperty,
253254
[propertyOfAltEventTransitionInArray]: checkTransitionProperty,
@@ -309,7 +310,22 @@ module.exports = {
309310

310311
'CallExpression[callee.name=/^createMachine$|^Machine$/] > ObjectExpression:first-child CallExpression':
311312
function (node) {
312-
if (!xstateDetector.isSpawnCallExpression(node)) {
313+
if (version === 4) {
314+
if (
315+
xstateDetector.isSpawnCallExpression(node) &&
316+
node.arguments[0] &&
317+
!isStringLiteral(node.arguments[0])
318+
) {
319+
context.report({
320+
node: node.arguments[0],
321+
messageId: 'moveActorToOptions',
322+
})
323+
}
324+
return
325+
}
326+
327+
// In XState v5, spawn comes from arguments passed to the callback within assign()
328+
if (!isSpawnFromParametersCallExpresion(node)) {
313329
return
314330
}
315331

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const { getParentFunctionExpression } = require('./selectors')
2+
3+
module.exports = function isSpawnFromParametersCallExpresion(node) {
4+
const functionNode = getParentFunctionExpression(node)
5+
if (!functionNode) {
6+
return false
7+
}
8+
9+
if (!functionNode.params[0]) {
10+
return false
11+
}
12+
13+
// populated when there is a destructured object for params
14+
let spawnIdentifier = null
15+
// populated when the whole first arg object is grabbed
16+
let firstArgIdentifier = null
17+
18+
if (functionNode.params[0].type === 'Identifier') {
19+
firstArgIdentifier = functionNode.params[0].name
20+
} else if (functionNode.params[0].type === 'ObjectPattern') {
21+
const spawnProperty = functionNode.params[0].properties.find(
22+
(property) => property.key.name === 'spawn'
23+
)
24+
if (!spawnProperty) {
25+
return false
26+
}
27+
spawnIdentifier = spawnProperty.value.name
28+
} else {
29+
return false
30+
}
31+
32+
return (
33+
(node.callee.type === 'Identifier' &&
34+
node.callee.name === spawnIdentifier) ||
35+
(node.callee.type === 'MemberExpression' &&
36+
node.callee.object.name === firstArgIdentifier &&
37+
(node.callee.property.name === 'spawn' ||
38+
node.callee.property.value === 'spawn'))
39+
)
40+
}

lib/utils/selectors.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@ function getTypeProperty(node) {
77
return node.properties.find(propertyHasName('type'))
88
}
99

10+
function getParentFunctionExpression(node) {
11+
let current = node.parent
12+
while (true) {
13+
if (!current) {
14+
return null
15+
}
16+
if (
17+
current.type === 'FunctionExpression' ||
18+
current.type === 'ArrowFunctionExpression'
19+
) {
20+
return current
21+
}
22+
current = current.parent
23+
}
24+
}
25+
1026
module.exports = {
1127
getTypeProperty,
28+
getParentFunctionExpression,
1229
}

tests/lib/rules/no-inline-implementation.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ const tests = {
133133
states: {
134134
active: {
135135
entry: assign({
136-
childActor: () => spawn('childActor'),
136+
childActor: ({ spawn }) => spawn('childActor'),
137137
}),
138138
on: {
139139
OFF: {
@@ -555,7 +555,7 @@ const tests = {
555555
withVersion(5, {
556556
code: `
557557
/* eslint no-inline-implementation: [ "warn", { "allowKnownActionCreators": true } ] */
558-
const { createMachine, spawn } = require('xstate')
558+
const { createMachine } = require('xstate')
559559
createMachine({
560560
states: {
561561
active: {
@@ -575,8 +575,8 @@ const tests = {
575575
},
576576
},
577577
exit: assign({
578-
childActor1: () => spawn(() => {}),
579-
childActor2: () => spawn(childActor),
578+
childActor1: ({ spawn }) => spawn(() => {}),
579+
childActor2: ({ spawn }) => spawn(childActor),
580580
}),
581581
},
582582
},

0 commit comments

Comments
 (0)