@xstate/codemod 0.1.0-beta.1 → 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,3 +9,5 @@ The transforms rename imports, wrap string targets, convert inline `types` decla
9
9
 
10
10
  <!-- helper binding and output validation from src/util.ts, src/transforms/types-to-schemas.ts and src/runner.ts -->
11
11
  The codemod supports named, namespace and type-only imports. It checks transformed syntax before saving. Run your application's typecheck and tests after migration, and review any reported manual migration steps.
12
+
13
+ The `string-targets` transform also wraps inline `xstate/fsm` targets in `on` and `always`, including `setup(...).createFSM` and aliased `createFSM` imports. It preserves string `initial` values. Review separately stored configurations and string-returning transition functions manually.
@@ -1,7 +1,7 @@
1
1
  import type { Transform } from "../types.js";
2
2
  /**
3
3
  * Converts bare string transition values inside `createMachine`/`setup().
4
- * createMachine`/`createStateConfig` config objects into `{ target: '...' }`.
4
+ * createMachine`/`createStateConfig`/`createFSM` config objects into `{ target: '...' }`.
5
5
  *
6
6
  * Does NOT touch `initial:` (still a string in v6) or existing `target:`
7
7
  * values.
@@ -165,7 +165,7 @@ const DIRECT_TRANSITION_KEYS = new Set(['onDone', 'onError', 'always']);
165
165
  * Returns true if a change was made.
166
166
  */
167
167
  function wrapValue(node) {
168
- if (Node.isStringLiteral(node)) {
168
+ if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node)) {
169
169
  const raw = node.getText(); // includes quotes
170
170
  node.replaceWithText(`{ target: ${raw} }`);
171
171
  return true;
@@ -175,7 +175,7 @@ function wrapValue(node) {
175
175
  // Iterate over a snapshot; replacing elements is index-stable here since
176
176
  // we replace text in place without adding/removing elements.
177
177
  for (const el of node.getElements()) {
178
- if (Node.isStringLiteral(el)) {
178
+ if (Node.isStringLiteral(el) || Node.isNoSubstitutionTemplateLiteral(el)) {
179
179
  el.replaceWithText(`{ target: ${el.getText()} }`);
180
180
  changed = true;
181
181
  }
@@ -267,18 +267,29 @@ function getKey$1(prop) {
267
267
 
268
268
  /**
269
269
  * Converts bare string transition values inside `createMachine`/`setup().
270
- * createMachine`/`createStateConfig` config objects into `{ target: '...' }`.
270
+ * createMachine`/`createStateConfig`/`createFSM` config objects into `{ target: '...' }`.
271
271
  *
272
272
  * Does NOT touch `initial:` (still a string in v6) or existing `target:`
273
273
  * values.
274
274
  */
275
275
  const stringTargets = {
276
276
  name: 'string-targets',
277
- description: "Wrap bare string transition values (on/after/onDone/onError/always) into { target: '...' } inside machine config objects. Best-effort: matches createMachine/createStateConfig by call name.",
277
+ description: "Wrap bare string transition values (on/after/onDone/onError/always) into { target: '...' } inside machine config objects. Best-effort: matches createMachine/createStateConfig/createFSM by call name.",
278
278
  apply(sourceFile) {
279
279
  const notes = [];
280
280
  let total = 0;
281
- for (const configObj of findMachineConfigObjects(sourceFile)) {
281
+ const callNames = ['createMachine', 'createStateConfig', 'createFSM'];
282
+ for (const declaration of sourceFile.getImportDeclarations()) {
283
+ if (declaration.getModuleSpecifierValue() !== 'xstate/fsm') {
284
+ continue;
285
+ }
286
+ for (const specifier of declaration.getNamedImports()) {
287
+ if (specifier.getName() === 'createFSM') {
288
+ callNames.push(specifier.getAliasNode()?.getText() ?? 'createFSM');
289
+ }
290
+ }
291
+ }
292
+ for (const configObj of findMachineConfigObjects(sourceFile, callNames)) {
282
293
  total += walkStateNode(configObj, notes);
283
294
  }
284
295
  return {
@@ -3567,7 +3578,7 @@ function toJson(value, stats, seen = new Set(), maxDepth = Infinity) {
3567
3578
  if (value === null) return null;
3568
3579
  const obj = value;
3569
3580
  if (isActorRef(obj)) return {
3570
- $actor: normalizeId(obj.id)
3581
+ '@actor': normalizeId(obj.id)
3571
3582
  };
3572
3583
  if (obj instanceof Error) return String(obj);
3573
3584
  if (typeof obj.toJSON === 'function') return toJson(obj.toJSON(), stats, seen, maxDepth);
@@ -3667,7 +3678,7 @@ function mapDeep(value, fn) {
3667
3678
  return value;
3668
3679
  }
3669
3680
  const stripAnonSuffix = value => mapDeep(value, (key, item) => (key === 'id' || key === 'to') && typeof item === 'string' ? item.replace(/(:\d+|\[\d+\])$/, '').replace(/^(x:\d+|\d+\.)/, '') : item);
3670
- const isActorMarker = item => !!item && typeof item === 'object' && '$actor' in item;
3681
+ const isActorMarker = item => !!item && typeof item === 'object' && '@actor' in item;
3671
3682
  const stripErrorPrefix = message => message.replace(/^(\w*Error:\s*)+/, '').trim();
3672
3683
  const INTENTIONAL_DELTAS = [{
3673
3684
  rule: 'anonymous-child-id',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xstate/codemod",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.3",
4
4
  "description": "Codemods for migrating XState source between versions (v5 → v6)",
5
5
  "keywords": [
6
6
  "xstate",
@@ -36,7 +36,7 @@
36
36
  "ts-morph": "^24.0.0",
37
37
  "tsx": "^4.23.13",
38
38
  "xstate5": "npm:xstate@^5.33.0",
39
- "xstate": "^6.0.0-beta.1"
39
+ "xstate": "^6.0.0-beta.3"
40
40
  },
41
41
  "devDependencies": {
42
42
  "vitest": "^3.2.2"
@@ -50,3 +50,5 @@ Rules:
50
50
  - Never widen `--compare` to make a diff disappear.
51
51
  - Report every `unstubbed` entry to the user. Inline actor logic runs for real and is not stubbed.
52
52
  - Stop and ask the user after 5 repair rounds without exit 0.
53
+
54
+ The `string-targets` transform also wraps inline `xstate/fsm` targets in `on` and `always`, including `setup(...).createFSM` and aliased `createFSM` imports. It preserves string `initial` values. Review separately stored configurations and string-returning transition functions manually.