Skip to content

Commit

Permalink
feat(prefer-predictable-action-arguments): add a deprecation error fo…
Browse files Browse the repository at this point in the history
…r this rule with xstate v5
  • Loading branch information
rlaffers committed Aug 16, 2023
1 parent 2b0346d commit 15f778c
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 19 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# eslint-plugin-xstate

TODO add prefer-predictable, no-auto-forward + their docs

ESLint plugin to check for common mistakes and enforce good practices when using [XState library](https://xstate.js.org/).

[![npm version](https://img.shields.io/npm/v/eslint-plugin-xstate)](https://npmjs.com/package/eslint-plugin-xstate)
Expand Down Expand Up @@ -42,6 +44,7 @@ Then configure the rules you want to use under the rules section.
"xstate/invoke-usage": "error",
"xstate/entry-exit-action": "error",
"xstate/prefer-always": "error",
"xstate/prefer-predictable-action-arguments": "error",
"xstate/no-misplaced-on-transition": "error",
"xstate/no-invalid-transition-props": "error",
"xstate/no-invalid-state-props": "error",
Expand Down Expand Up @@ -72,6 +75,21 @@ There is also an `all` configuration which includes every available rule. It enf
}
```

### XState v4
The default shareable configurations are for XState v5. If you use the older XState v4, append `_v4` to the name of the configuration you want to use.

```json
{
"extends": ["plugin:xstate/recommended_v4"]
}
```

```json
{
"extends": ["plugin:xstate/all_v4"]
}
```

## Supported Rules

### Possible Errors
Expand All @@ -95,6 +113,7 @@ There is also an `all` configuration which includes every available rule. It enf
| ------------------------------------------------------------------ | ----------------------------------------------------------------------- | ------------------ |
| [no-inline-implementation](docs/rules/no-inline-implementation.md) | Suggest refactoring guards, actions and services into machine options | |
| [prefer-always](docs/rules/prefer-always.md) | Suggest using the `always` syntax for transient (eventless) transitions | :heavy_check_mark: |
| [prefer-predictable-action-arguments](docs/rules/prefer-predictable-action-arguments.md) | Suggest turning on the `predictableActionArguments` option | :heavy_check_mark: |
| [no-auto-forward](docs/rules/no-auto-forward.md) | Forbid auto-forwarding events to invoked services or spawned actors | |

### Stylistic Issues
Expand Down
16 changes: 12 additions & 4 deletions docs/rules/prefer-predictable-action-arguments.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Use `predictableActionArguments: true` at the top-level of your machine config

Suggest using setting `predictableActionArguments` to `true` at the top-level of your machine config, like this:
Suggest using setting `predictableActionArguments` to `true` at the top-level of your machine config (with XState v4), like this:

```javascript
createMachine({
Expand All @@ -15,6 +15,10 @@ With this flag XState will always call an action with the event directly respons

Source from docs: https://xstate.js.org/docs/guides/actions.html#api

### XState v5

In XState v5 the `predictableActionArguments` was removed. This rule will report an error if the option is used in your machine config.

Examples of **incorrect** code for this rule:

```javascript
Expand All @@ -24,8 +28,7 @@ createMachine({
// ...
})

// ❌ predictableActionArguments is omitted

// ❌ predictableActionArguments is omitted (XState v4)
createMachine({
states: {
// ...
Expand All @@ -36,9 +39,14 @@ createMachine({
Examples of **correct** code for this rule:

```javascript
// ✅ predictableActionArguments is enabled
// ✅ predictableActionArguments is enabled (XState v4)
createMachine({
predictableActionArguments: true,
// ...
})

// ✅ predictableActionArguments is not used (XState v5)
createMachine({
// ...
})
```
4 changes: 3 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module.exports = {
'xstate/invoke-usage': 'error',
'xstate/entry-exit-action': 'error',
'xstate/prefer-always': 'error',
'xstate/prefer-predictable-action-arguments': 'warn',
'xstate/prefer-predictable-action-arguments': 'error',
'xstate/no-misplaced-on-transition': 'error',
'xstate/no-invalid-transition-props': 'error',
'xstate/no-invalid-state-props': 'error',
Expand Down Expand Up @@ -73,6 +73,7 @@ module.exports = {
'xstate/no-inline-implementation': 'warn',
'xstate/no-auto-forward': 'error',
'xstate/prefer-always': 'error',
'xstate/prefer-predictable-action-arguments': 'error',
'xstate/no-misplaced-on-transition': 'error',
'xstate/no-invalid-transition-props': 'error',
'xstate/no-invalid-state-props': 'error',
Expand Down Expand Up @@ -122,6 +123,7 @@ module.exports = {
'xstate/no-inline-implementation': 'warn',
'xstate/no-auto-forward': 'warn',
'xstate/prefer-always': 'error',
'xstate/prefer-predictable-action-arguments': 'warn',
'xstate/no-misplaced-on-transition': 'error',
'xstate/no-invalid-transition-props': 'error',
'xstate/no-invalid-state-props': 'error',
Expand Down
53 changes: 48 additions & 5 deletions lib/rules/prefer-predictable-action-arguments.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const getDocsUrl = require('../utils/getDocsUrl')
const getSettings = require('../utils/getSettings')

module.exports = {
meta: {
Expand All @@ -12,14 +13,18 @@ module.exports = {
url: getDocsUrl('prefer-predictable-action-arguments'),
recommended: 'warn',
},
fixable: 'code',
schema: [],
messages: {
preferPredictableActionArguments:
'It is advised to configure predictableActionArguments: true at the top-level of your machine config',
deprecatedPredictableActionArguments:
'The predictableActionArguments prop was removed in XState v5 so it has no effect. Please remove it.',
},
},

create: function (context) {
const { version } = getSettings(context)
return {
'CallExpression[callee.name=/^createMachine$|^Machine$/] > ObjectExpression:first-child':
function (node) {
Expand All @@ -28,22 +33,60 @@ module.exports = {
return prop.key.name === 'predictableActionArguments'
}
)

if (!predictableActionArgumentsProperty) {
if (version > 4) {
if (!predictableActionArgumentsProperty) {
return
}
context.report({
node,
messageId: 'preferPredictableActionArguments',
node: predictableActionArgumentsProperty,
messageId: 'deprecatedPredictableActionArguments',
fix(fixer) {
return fixer.remove(predictableActionArgumentsProperty)
},
})
return
}

if (!predictableActionArgumentsProperty) {
if (node.properties.length === 0) {
context.report({
node,
messageId: 'preferPredictableActionArguments',
fix(fixer) {
return fixer.replaceText(
node,
'{ predictableActionArguments: true }'
)
},
})
} else {
context.report({
node,
messageId: 'preferPredictableActionArguments',
fix(fixer) {
return fixer.insertTextBefore(
node.properties[0],
'predictableActionArguments: true,\n'
)
},
})
}
return
}

if (
!predictableActionArgumentsProperty.value ||
predictableActionArgumentsProperty.value.value !== true
) {
context.report({
node,
node: predictableActionArgumentsProperty,
messageId: 'preferPredictableActionArguments',
fix(fixer) {
return fixer.replaceText(
predictableActionArgumentsProperty.value,
'true'
)
},
})
}
},
Expand Down
67 changes: 58 additions & 9 deletions tests/lib/rules/prefer-predictable-action-arguments.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,89 @@
const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/prefer-predictable-action-arguments')
const { withVersion } = require('../utils/settings')

const tests = {
valid: [
`
withVersion(
4,
`
createMachine({
predictableActionArguments: true
})
`,
`
),
withVersion(
5,
`
createMachine({})
`
),
],
invalid: [
{
withVersion(4, {
code: `
createMachine({
predictableActionArguments: false
})
`,
errors: [{ messageId: 'preferPredictableActionArguments' }],
},
{
output: `
createMachine({
predictableActionArguments: true
})
`,
}),
withVersion(5, {
code: `
createMachine({
states: {},
predictableActionArguments: false
})
`,
errors: [{ messageId: 'deprecatedPredictableActionArguments' }],
output: `
createMachine({
})
`,
}),
withVersion(4, {
code: `
createMachine({})
`,
errors: [{ messageId: 'preferPredictableActionArguments' }],
output: `
createMachine({ predictableActionArguments: true })
`,
}),
withVersion(4, {
code: `
createMachine({
initial: 'ready'
})
`,
errors: [{ messageId: 'preferPredictableActionArguments' }],
},
{
output: `
createMachine({
predictableActionArguments: true,
initial: 'ready'
})
`,
}),
withVersion(4, {
code: `
createMachine({
states: {},
predictableActionArguments: 42
})
`,
errors: [{ messageId: 'preferPredictableActionArguments' }],
},
output: `
createMachine({
states: {},
predictableActionArguments: true
})
`,
}),
],
}

Expand Down

0 comments on commit 15f778c

Please sign in to comment.