From c7ae8f92d0f4f36a454b5e1c95bdb898bb4f10d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 8 Mar 2024 10:56:45 +0100 Subject: [PATCH] Handle guard edits --- .../__tests__/source-edits/edit-guard.test.ts | 96 +++++++++++++++++++ new-packages/ts-project/__tests__/utils.ts | 28 +++++- new-packages/ts-project/src/index.ts | 31 +++++- 3 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 new-packages/ts-project/__tests__/source-edits/edit-guard.test.ts diff --git a/new-packages/ts-project/__tests__/source-edits/edit-guard.test.ts b/new-packages/ts-project/__tests__/source-edits/edit-guard.test.ts new file mode 100644 index 00000000..e6c80a3d --- /dev/null +++ b/new-packages/ts-project/__tests__/source-edits/edit-guard.test.ts @@ -0,0 +1,96 @@ +import { expect, test } from 'vitest'; +import { createTestProject, testdir, ts } from '../utils'; + +test('should be possible to update a guard name', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': ts` + import { createMachine } from "xstate"; + + createMachine({ + on: { + CALL_HIM_MAYBE: { + guard: "isItTooLate", + actions: "callDavid", + }, + }, + }); + `, + }); + + const project = await createTestProject(tmpPath); + + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'edit_guard', + path: [], + transitionPath: ['on', 'CALL_HIM_MAYBE', 0], + name: 'isHeAwake', + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + on: { + CALL_HIM_MAYBE: { + guard: "isHeAwake", + actions: "callDavid", + }, + }, + });", + } + `); +}); + +test('should be possible to update a parametrized guard name', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': ts` + import { createMachine } from "xstate"; + + createMachine({ + on: { + CALL_HIM_MAYBE: { + guard: { type: "isItTooLate", time: "9am" }, + actions: "callDavid", + }, + }, + }); + `, + }); + + const project = await createTestProject(tmpPath); + + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'edit_guard', + path: [], + transitionPath: ['on', 'CALL_HIM_MAYBE', 0], + name: 'isHeAwake', + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + on: { + CALL_HIM_MAYBE: { + guard: { type: "isHeAwake", time: "9am" }, + actions: "callDavid", + }, + }, + });", + } + `); +}); diff --git a/new-packages/ts-project/__tests__/utils.ts b/new-packages/ts-project/__tests__/utils.ts index 957a0193..a1e861ea 100644 --- a/new-packages/ts-project/__tests__/utils.ts +++ b/new-packages/ts-project/__tests__/utils.ts @@ -407,7 +407,7 @@ function produceNewDigraphUsingEdit( : undefined; break; } - case 'add_transition': + case 'add_transition': { const sourceNode = findNodeByStatePath(digraphDraft, edit.sourcePath); const targetNode = edit.targetPath && findNodeByStatePath(digraphDraft, edit.targetPath); @@ -455,6 +455,7 @@ function produceNewDigraphUsingEdit( } break; + } case 'remove_transition': case 'reanchor_transition': case 'change_transition_path': @@ -553,8 +554,28 @@ function produceNewDigraphUsingEdit( break; } case 'remove_guard': - case 'edit_guard': throw new Error(`Not implemented: ${edit.type}`); + case 'edit_guard': { + const eventTypeData = getEventTypeData(digraphDraft, { + sourcePath: edit.path, + transitionPath: edit.transitionPath, + }); + const edge = + digraphDraft.edges[ + getEdgeGroup(digraphDraft, eventTypeData)[ + last(edit.transitionPath) as number + ] + ]; + const blockId = edge.data.guard; + assert(typeof blockId === 'string'); + digraphDraft.blocks[blockId].sourceId = edit.name; + digraphDraft.implementations.guards[edit.name] ??= { + type: 'guard', + id: edit.name, + name: edit.name, + }; + break; + } case 'add_invoke': { const node = findNodeByStatePath(digraphDraft, edit.path); const block = createActorBlock({ @@ -573,13 +594,14 @@ function produceNewDigraphUsingEdit( case 'remove_invoke': case 'edit_invoke': throw new Error(`Not implemented: ${edit.type}`); - case 'set_description': + case 'set_description': { const node = findNodeByStatePath(digraphDraft, edit.statePath); if (edit.transitionPath) { throw new Error(`Not implemented`); } node.data.description = edit.description; break; + } } } diff --git a/new-packages/ts-project/src/index.ts b/new-packages/ts-project/src/index.ts index 83539322..e46fc2c5 100644 --- a/new-packages/ts-project/src/index.ts +++ b/new-packages/ts-project/src/index.ts @@ -926,8 +926,35 @@ function createProjectMachine({ ); break; } - case 'actor': - case 'guard': + case 'actor': { + break; + } + case 'guard': { + const edge = currentState.digraph!.edges[block.parentId]; + const transitionNode = findNodeByAstPath( + host.ts, + createMachineCall, + currentState.astPaths.edges[edge.uniqueId], + ); + assert(host.ts.isObjectLiteralExpression(transitionNode)); + + const guardProperty = findProperty( + undefined, + host.ts, + transitionNode, + 'guard', + ); + + assert(!!guardProperty); + + updateParameterizedObjectLocation( + host.ts, + codeChanges, + guardProperty.initializer, + block.sourceId, + ); + break; + } } } }