Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create eslint config and plugin for v5 #216

Open
with-heart opened this issue Sep 16, 2022 · 6 comments · May be fixed by #221
Open

create eslint config and plugin for v5 #216

with-heart opened this issue Sep 16, 2022 · 6 comments · May be fixed by #221

Comments

@with-heart
Copy link
Contributor

with-heart commented Sep 16, 2022

I've been thinking about how cool it would be to provide tooling to assist with (eventual) user migration to v5. One of the things that could be quite helpful would be an eslint plugin and config defining rules that help the user work through all of the breaking changes or utilize new functionality.

Here's what I think those rules might look like, using the [email protected] release notes as a starting point.

rule description
avoid-context-spread Since machine.withContext now permits partial context, no need to spread machine.context
no-atomic-internal internal property no longer has an effect on atomic state nodes
no-cond cond has been renamed to guard
no-deprecated-config-properties onEntry, onExit, parallel, and forward have been removed
no-eventless-on-transition Eventless transitions must now be specified using always instead of on[''].
no-emitted-from EmittedFrom has been renamed to SnapshotFrom
no-factory-context-arg The 3rd arg (context) has been removed from and createMachine()
no-generic-state-schema StateSchema has been removed from all generics
no-guard-in The in property for transitions has been removed and replaced with guards. Prefer stateIn() or not(StateIn())
no-machine-factory Machine factory has been removed; use createMachine instead
no-machine-transition-context-arg 3rd arg (context) to machine.transition has been removed. Can use State.from('state', {}) as 1st argument instead.
no-service-batch service.batch(events) has been removed
no-service-children children can only be accessed from state.children
no-service-execute service.execute has been removed
no-service-onChange service.onChange has been removed in favor of service.onTransition or service.subscribe
no-service-onEvent service.onEvent has been removed in favor of service.onTransition or service.subscribe
no-service-onSend service.onSend has been removed in favor of service.onTransition or service.subscribe
no-service-send-type-payload service.send(type, payload) is no longer supported. use service.send({ type, …payload }) instead
no-spawn-import spawn is now available in 3rd arg to assign
no-state-activities state.activites has been removed
no-state-children-direct-reference state.children is now a mapping of invoked actor IDs to their ActorRef and should never be referenced directly. Not really sure about the name or what this rule would even do, but wanted to make a note of it
no-state-events state.events has been removed
no-state-history state.history has been removed
no-state-historyValue state.historyValue is considered internal
no-state-node-isTransient stateNode.isTransient has been removed
no-state-node-version stateNode.version has been removed. version is only available on root machine node
prefer-spawn-implementation-name Prefer referencing actors defined in config.services by name (spawn('promiseActor') instead of spawn(promiseActor))
prefer-wildcard-event-descriptors Detects multiple namespaced actions with the same config object that could be combined (?)
rename-machine-withConfig-to-provide machine.withConfig(…) -> machine.provide(…)
require-compound-state-initial-key Compound states now require an initial key (did they not already?)
require-object-context machine.context is now required to be an object
require-parameterized-actions-params Parameterized actions now require a params property ({ message: 'Hello' } -> { params: { message: 'Hello' }})
require-parameterized-guards-params Guard parameters should now be placed in object at params key ({ minQueryLength: 3 } -> { params: { minQueryLength: 3 }})
@with-heart with-heart changed the title create eslint config for v5 create eslint config and plugin for v5 Sep 16, 2022
@davidkpiano
Copy link
Member

This is an excellent list, thanks for making it! I think it's a good idea, and an eslint plugin for XState is generally a good idea as well.

@with-heart
Copy link
Contributor Author

Thanks for the encouragement @davidkpiano! Already have a few rules working 😁

@with-heart with-heart linked a pull request Sep 21, 2022 that will close this issue
@with-heart
Copy link
Contributor Author

with-heart commented Sep 22, 2022

I need to think about how this would behave but after talking to @Andarist earlier, we likely need a rule to encourage static configs. Bidirectional editing between code and Studio requires semi-static configs, so the more we can encourage static configs, the better things will be.

Basically we want users to avoid evaluating/computing things in their machine definitions:

const IDLE = 'idle'
const a = 'action'
const b = 1

const machine = createMachine(
  {
    // no vars as values
    initial: IDLE,
    // no `TemplateLiteral`s that require evaluation
    entry: `${a}${b}`,
    states: {
      // no computed properties
      [IDLE]: {}
    }
  },
  {
    actions: {
      action1: () => {},
    }
  }
)

@davidkpiano
Copy link
Member

I need to think about how this would behave but after talking to @Andarist earlier, we likely need a rule to encourage static configs. Bidirectional editing between code and Studio requires semi-static configs, so the more we can encourage static configs, the better things will be.

Basically we want users to avoid evaluating/computing things in their machine definitions:

const IDLE = 'idle'
const a = 'action'
const b = 1

const machine = createMachine(
  {
    // no vars as values
    initial: IDLE,
    // no `TemplateLiteral`s that require evaluation
    entry: `${a}${b}`,
    states: {
      // no computed properties
      [IDLE]: {}
    }
  },
  {
    actions: {
      action1: () => {},
    }
  }
)

In the future, I feel like it should be possible to even have some of these work with bidirectional editing. If we treat the AST like a directed graph, then it's a matter of taking one extra "step" to find the source of the value, and modifying it there (as long as it's not shared anywhere non-machine-related).

Computed properties are not uncommon, so I'm wondering if we can't at least support that.

@with-heart
Copy link
Contributor Author

as long as it's not shared anywhere non-machine-related

Can you clarify what that means?

@Andarist
Copy link
Member

If we treat the AST like a directed graph, then it's a matter of taking one extra "step" to find the source of the value,

I agree that it's possible to resolve (and even modify) a lot of pattern.

(as long as it's not shared anywhere non-machine-related).

This is a major blocker though - and how do you make this intuitive so people would know where the line between supported patterns and unsupported ones is?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants