Open with-heart opened 2 years ago
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.
Thanks for the encouragement @davidkpiano! Already have a few rules working 😁
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: () => {},
}
}
)
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.
as long as it's not shared anywhere non-machine-related
Can you clarify what that means?
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?
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
xstate@5.0.0-alpha.0
release notes as a starting point.avoid-context-spread
machine.withContext
now permits partial context, no need to spreadmachine.context
no-atomic-internal
internal
property no longer has an effect on atomic state nodesno-cond
cond
has been renamed toguard
no-deprecated-config-properties
onEntry
,onExit
,parallel
, andforward
have been removedno-eventless-on-transition
always
instead ofon['']
.no-emitted-from
EmittedFrom
has been renamed toSnapshotFrom
no-factory-context-arg
context
) has been removed from andcreateMachine()
no-generic-state-schema
StateSchema
has been removed from all genericsno-guard-in
in
property for transitions has been removed and replaced with guards. PreferstateIn()
ornot(StateIn())
no-machine-factory
Machine
factory has been removed; usecreateMachine
insteadno-machine-transition-context-arg
context
) tomachine.transition
has been removed. Can useState.from('state', {})
as 1st argument instead.no-service-batch
service.batch(events)
has been removedno-service-children
state.children
no-service-execute
service.execute
has been removedno-service-onChange
service.onChange
has been removed in favor ofservice.onTransition
orservice.subscribe
no-service-onEvent
service.onEvent
has been removed in favor ofservice.onTransition
orservice.subscribe
no-service-onSend
service.onSend
has been removed in favor ofservice.onTransition
orservice.subscribe
no-service-send-type-payload
service.send(type, payload)
is no longer supported. useservice.send({ type, …payload })
insteadno-spawn-import
spawn
is now available in 3rd arg toassign
no-state-activities
state.activites
has been removedno-state-children-direct-reference
state.children
is now a mapping of invoked actor IDs to theirActorRef
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 itno-state-events
state.events
has been removedno-state-history
state.history
has been removedno-state-historyValue
state.historyValue
is considered internalno-state-node-isTransient
stateNode.isTransient
has been removedno-state-node-version
stateNode.version
has been removed.version
is only available on rootmachine
nodeprefer-spawn-implementation-name
config.services
by name (spawn('promiseActor')
instead ofspawn(promiseActor)
)prefer-wildcard-event-descriptors
rename-machine-withConfig-to-provide
machine.withConfig(…)
->machine.provide(…)
require-compound-state-initial-key
initial
key (did they not already?)require-object-context
machine.context
is now required to be anobject
require-parameterized-actions-params
params
property ({ message: 'Hello' }
->{ params: { message: 'Hello' }}
)require-parameterized-guards-params
params
key ({ minQueryLength: 3 }
->{ params: { minQueryLength: 3 }}
)