Skip to content

Migrate XState to v5 from v4 Has Fix

Description

XState is a state management/orchestration library based on state machines, statecharts, and the actor model. It allows you to model complex logic in event-driven ways, and orchestrate the behavior of many actors communicating with each other.

XState's v5 version introduced some breaking changes and new features compared to v4. While the migration should be a straightforward process, it is a tedious process and requires knowledge of the differences between v4 and v5.

ast-grep provides a way to automate the process and a way to encode valuable knowledge to executable rules.

The following example picks up some migration items and demonstrates the power of ast-grep's rule system.

YAML

The rules below correspond to XState v5's createMachine, createActor, and machine.provide.

The example shows how ast-grep can use various features like utility rule, transformation and multiple rule in single file to automate the migration. Each rule has a clear and descriptive id field that explains its purpose.

For more information, you can use @ast-grep-bot to provide more detailed explanation for each rule.

yaml
id: migrate-import-name
utils:
  FROM_XS: {kind: import_statement, has: {kind: string, regex: xstate}}
  XS_EXPORT:
    kind: identifier
    inside: { has: { matches: FROM_XS }, stopBy: end }
rule: { regex: ^Machine|interpret$, pattern: $IMPT, matches: XS_EXPORT }
transform:
  STEP1:
    replace: {by: create$1, replace: (Machine), source: $IMPT }
  FINAL:
    replace: { by: createActor, replace: interpret, source: $STEP1 }
fix: $FINAL

---

id: migrate-to-provide
rule: { pattern: $MACHINE.withConfig }
fix: $MACHINE.provide

---

id: migrate-to-actors
rule:
  kind: property_identifier
  regex: ^services$
  inside: { pattern:  $M.withConfig($$$ARGS), stopBy: end }
fix: actors

Example

js
import { Machine, interpret } from 'xstate';

const machine = Machine({ /*...*/});

const specificMachine = machine.withConfig({
  actions: { /* ... */ },
  guards: { /* ... */ },
  services: { /* ... */ },
});

const actor = interpret(specificMachine, {
  /* actor options */
});

Diff

js
import { Machine, interpret } from 'xstate'; 
import { createMachine, createActor } from 'xstate'; 

const machine = Machine({ /*...*/}); 
const machine = createMachine({ /*...*/}); 

const specificMachine = machine.withConfig({ 
const specificMachine = machine.provide({ 
  actions: { /* ... */ },
  guards: { /* ... */ },
  services: { /* ... */ }, 
  actors: { /* ... */ }, 
});

const actor = interpret(specificMachine, { 
const actor = createActor(specificMachine, { 
  /* actor options */
});

Contributed by

Inspired by XState's blog.

Made with ❤️ with Rust