@statelyai/sdk 0.34.2 → 0.36.0
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 +20 -34
- package/dist/assetStorage.d.mts +1 -1
- package/dist/auth.d.mts +14 -1
- package/dist/bin.mjs +1 -3
- package/dist/editor-sync-types-U5uryWOk.d.mts +55 -0
- package/dist/editorHostProtocol.d.mts +203 -0
- package/dist/{editorHostProtocol-B2_t3Le7.mjs → editorHostProtocol.mjs} +1 -1
- package/dist/embed.d.mts +3 -3
- package/dist/embed.mjs +2 -2
- package/dist/{graph-DVfx7FwN.d.mts → graph-B6xUxqR5.d.mts} +1 -1
- package/dist/graph.d.mts +1 -1
- package/dist/{index-A5CMACoO.d.mts → index-CoxF4Saw.d.mts} +8 -255
- package/dist/index.d.mts +8 -31
- package/dist/index.mjs +5 -9
- package/dist/{inspect-ZVDq9tK2.mjs → inspect-BoMln78S.mjs} +1 -1
- package/dist/{inspect-DBH9oOAd.d.mts → inspect-Douvt5k_.d.mts} +1 -1
- package/dist/inspect.d.mts +2 -2
- package/dist/inspect.mjs +2 -2
- package/dist/patchTypes.d.mts +1 -1
- package/dist/{protocol-CCf9j_SZ.d.mts → protocol-CP3DSl5u.d.mts} +27 -5
- package/dist/protocol.d.mts +1 -1
- package/dist/relay.d.mts +1 -1
- package/dist/{openapi-BTDjYWXd.mjs → studio-DhzM6zFx.mjs} +639 -1
- package/dist/{studio-CFN3-0-2.d.mts → studio-DvEgrtk1.d.mts} +4 -2
- package/dist/studio.d.mts +1 -1
- package/dist/studio.mjs +2 -2
- package/dist/{version-CUFh-PYe.mjs → version-DS2izNeF.mjs} +1 -1
- package/package.json +20 -16
- package/dist/auth-CVFc85uF.d.mts +0 -15
- package/dist/dist-DsOTTfYv.mjs +0 -8361
- package/dist/editor-sync-types-D7YYz6fb.d.mts +0 -98
- package/dist/editorHostSession-DCTHhBqC.mjs +0 -912
- package/dist/editorHostSession-yGUKdLjq.d.mts +0 -325
- package/dist/editorHostSession.d.mts +0 -2
- package/dist/editorHostSession.mjs +0 -6
- package/dist/statelyPragma-YtVaBuIe.mjs +0 -102
- package/dist/studio-BZP6DaIO.mjs +0 -638
- package/dist/sync.d.mts +0 -86
- package/dist/sync.mjs +0 -4646
package/dist/sync.mjs
DELETED
|
@@ -1,4646 +0,0 @@
|
|
|
1
|
-
import "./openapi-BTDjYWXd.mjs";
|
|
2
|
-
import { r as createStatelyClient } from "./studio-BZP6DaIO.mjs";
|
|
3
|
-
import { C as toXStateV6MachineConfig, S as serializeJS, a as alignCompareGraphIds, b as normalizeMachineProfile, c as diffGraphsSemantically, d as getDelayKeyFromEventType, g as graphToXStateTS, i as XSTATE_V6_FN_TRANSITION_META, m as graphToMachineConfig, p as graphChangeToPatchesResult, r as XSTATE_V6_BRANCH_DESCRIPTION_META, s as detectXStateSourceProfile, t as INLINE_EXPRESSION_DIRECTIVE_FIELDS, u as extractXStateMachineConfigEntries, v as isCodeExpression } from "./dist-DsOTTfYv.mjs";
|
|
4
|
-
import { n as getStatelyPragma, r as upsertStatelyPragma, t as findStatelyPragmaAttachments } from "./statelyPragma-YtVaBuIe.mjs";
|
|
5
|
-
import { fromStudioMachine, toStudioMachine } from "./graph.mjs";
|
|
6
|
-
import * as ts$1 from "typescript";
|
|
7
|
-
import ts from "typescript";
|
|
8
|
-
import { isEmptyDiff } from "@statelyai/graph";
|
|
9
|
-
import fs from "node:fs/promises";
|
|
10
|
-
import path from "node:path";
|
|
11
|
-
import { execFile } from "node:child_process";
|
|
12
|
-
import { promisify } from "node:util";
|
|
13
|
-
import * as fs$1 from "fs";
|
|
14
|
-
import * as path$1 from "path";
|
|
15
|
-
import { fileURLToPath, pathToFileURL } from "url";
|
|
16
|
-
|
|
17
|
-
//#region ../editor-sync/src/fs.ts
|
|
18
|
-
function createTextDocument(fileName, text, uri = path$1.isAbsolute(fileName) ? pathToFileURL(fileName).toString() : fileName) {
|
|
19
|
-
const lineStarts = getLineStarts(text);
|
|
20
|
-
return {
|
|
21
|
-
fileName,
|
|
22
|
-
uri,
|
|
23
|
-
getText() {
|
|
24
|
-
return text;
|
|
25
|
-
},
|
|
26
|
-
positionAt(offset) {
|
|
27
|
-
const clampedOffset = Math.max(0, Math.min(offset, text.length));
|
|
28
|
-
let low = 0;
|
|
29
|
-
let high = lineStarts.length - 1;
|
|
30
|
-
while (low <= high) {
|
|
31
|
-
const mid = Math.floor((low + high) / 2);
|
|
32
|
-
const start = lineStarts[mid];
|
|
33
|
-
const nextStart = mid + 1 < lineStarts.length ? lineStarts[mid + 1] : text.length + 1;
|
|
34
|
-
if (clampedOffset < start) {
|
|
35
|
-
high = mid - 1;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (clampedOffset >= nextStart) {
|
|
39
|
-
low = mid + 1;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
line: mid,
|
|
44
|
-
character: clampedOffset - start
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
line: lineStarts.length - 1,
|
|
49
|
-
character: clampedOffset - lineStarts[lineStarts.length - 1]
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function applyReplacementsToText(text, replacements) {
|
|
55
|
-
let updated = text;
|
|
56
|
-
const sorted = [...replacements].sort(compareReplacementRangesDescending$1);
|
|
57
|
-
for (const replacement of sorted) {
|
|
58
|
-
const lineStarts = getLineStarts(updated);
|
|
59
|
-
const start = offsetAt(lineStarts, replacement.range.startLine, replacement.range.startChar);
|
|
60
|
-
const end = offsetAt(lineStarts, replacement.range.endLine, replacement.range.endChar);
|
|
61
|
-
updated = `${updated.slice(0, start)}${replacement.newText}${updated.slice(end)}`;
|
|
62
|
-
}
|
|
63
|
-
return updated;
|
|
64
|
-
}
|
|
65
|
-
function sourceUriToFileName(uriOrPath) {
|
|
66
|
-
if (uriOrPath.startsWith("file://")) return fileURLToPath(uriOrPath);
|
|
67
|
-
if (/^[a-z][a-z\d+.-]*:/i.test(uriOrPath)) throw new Error(`Unsupported non-file URI: ${uriOrPath}`);
|
|
68
|
-
return path$1.resolve(uriOrPath);
|
|
69
|
-
}
|
|
70
|
-
function fileNameToUri(fileName) {
|
|
71
|
-
return pathToFileURL(path$1.resolve(fileName)).toString();
|
|
72
|
-
}
|
|
73
|
-
function createFileSourceDocumentResolver() {
|
|
74
|
-
return {
|
|
75
|
-
read(uri) {
|
|
76
|
-
return readFileSnapshot(sourceUriToFileName(uri));
|
|
77
|
-
},
|
|
78
|
-
resolveImport(fromDocument, specifier) {
|
|
79
|
-
const importedFileName = resolveImportPath(fromDocument.fileName, specifier);
|
|
80
|
-
return importedFileName ? readFileSnapshot(importedFileName) : null;
|
|
81
|
-
},
|
|
82
|
-
resolvePackageJson(fromDocument, packageName) {
|
|
83
|
-
let directory = path$1.dirname(path$1.resolve(fromDocument.fileName));
|
|
84
|
-
while (true) {
|
|
85
|
-
const packageJson = readFileSnapshot(path$1.join(directory, "package.json"));
|
|
86
|
-
if (packageJson && packageJsonDeclares(packageJson.text, packageName)) return packageJson;
|
|
87
|
-
const parent = path$1.dirname(directory);
|
|
88
|
-
if (parent === directory) return null;
|
|
89
|
-
directory = parent;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function packageJsonDeclares(text, packageName) {
|
|
95
|
-
try {
|
|
96
|
-
const manifest = JSON.parse(text);
|
|
97
|
-
return [
|
|
98
|
-
"dependencies",
|
|
99
|
-
"devDependencies",
|
|
100
|
-
"peerDependencies",
|
|
101
|
-
"optionalDependencies"
|
|
102
|
-
].some((field) => {
|
|
103
|
-
const dependencies = manifest[field];
|
|
104
|
-
return Boolean(dependencies && typeof dependencies === "object" && Object.prototype.hasOwnProperty.call(dependencies, packageName));
|
|
105
|
-
});
|
|
106
|
-
} catch {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function resolveImportPath(fromFileName, specifier) {
|
|
111
|
-
if (!specifier.startsWith(".")) return null;
|
|
112
|
-
const basePath = path$1.resolve(path$1.dirname(fromFileName), specifier);
|
|
113
|
-
return [
|
|
114
|
-
basePath,
|
|
115
|
-
`${basePath}.ts`,
|
|
116
|
-
`${basePath}.tsx`,
|
|
117
|
-
`${basePath}.js`,
|
|
118
|
-
`${basePath}.jsx`,
|
|
119
|
-
path$1.join(basePath, "index.ts"),
|
|
120
|
-
path$1.join(basePath, "index.tsx"),
|
|
121
|
-
path$1.join(basePath, "index.js"),
|
|
122
|
-
path$1.join(basePath, "index.jsx")
|
|
123
|
-
].find((candidate) => isReadableFile(candidate)) ?? null;
|
|
124
|
-
}
|
|
125
|
-
function isReadableFile(fileName) {
|
|
126
|
-
try {
|
|
127
|
-
return fs$1.statSync(fileName).isFile();
|
|
128
|
-
} catch {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
function readFileSnapshot(fileName) {
|
|
133
|
-
try {
|
|
134
|
-
return {
|
|
135
|
-
fileName,
|
|
136
|
-
uri: fileNameToUri(fileName),
|
|
137
|
-
text: fs$1.readFileSync(fileName, "utf8")
|
|
138
|
-
};
|
|
139
|
-
} catch {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function getLineStarts(text) {
|
|
144
|
-
const lineStarts = [0];
|
|
145
|
-
for (let index = 0; index < text.length; index += 1) if (text.charCodeAt(index) === 10) lineStarts.push(index + 1);
|
|
146
|
-
return lineStarts;
|
|
147
|
-
}
|
|
148
|
-
function offsetAt(lineStarts, line, character) {
|
|
149
|
-
const lineStart = lineStarts[line];
|
|
150
|
-
if (lineStart === void 0) return lineStarts[lineStarts.length - 1] + character;
|
|
151
|
-
return lineStart + character;
|
|
152
|
-
}
|
|
153
|
-
function compareReplacementRangesDescending$1(left, right) {
|
|
154
|
-
if (left.range.startLine !== right.range.startLine) return right.range.startLine - left.range.startLine;
|
|
155
|
-
return right.range.startChar - left.range.startChar;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
//#endregion
|
|
159
|
-
//#region ../editor-sync/src/internal/sourceLocations.ts
|
|
160
|
-
const sourceFileUris = /* @__PURE__ */ new WeakMap();
|
|
161
|
-
function collectXStateSourceLocations(sourceText, document, options = {}) {
|
|
162
|
-
const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$3(document.fileName));
|
|
163
|
-
const uri = document.uri?.toString() ?? document.fileName;
|
|
164
|
-
sourceFileUris.set(sourceFile, uri);
|
|
165
|
-
const bindings = collectBindings(sourceFile, uri, document.fileName, options);
|
|
166
|
-
const staticValues = collectStaticValueBindings(sourceFile);
|
|
167
|
-
const root = findMachineConfigObjectLiteral$1(sourceFile, bindings, options.machineIndex);
|
|
168
|
-
if (!root) return;
|
|
169
|
-
const states = [];
|
|
170
|
-
const statesObjectLocations = [];
|
|
171
|
-
collectStateLocations(root, [], states, statesObjectLocations, bindings, staticValues, root);
|
|
172
|
-
const packageProfile = detectXStatePackageProfile(options.resolver?.resolvePackageJson?.({
|
|
173
|
-
fileName: document.fileName,
|
|
174
|
-
uri
|
|
175
|
-
}, "xstate"));
|
|
176
|
-
const rootProfile = detectXStateSourceProfile(sourceText);
|
|
177
|
-
const referencedStateSymbols = new Set([...states, ...statesObjectLocations].map((state) => state.symbol).filter((symbol) => typeof symbol === "string"));
|
|
178
|
-
const resolvedTransitionFunctions = collectResolvedTransitionFunctions(root, bindings);
|
|
179
|
-
const inferredProfile = rootProfile === "xstate-v6-alpha" || resolvedTransitionFunctions.size > 0 || [...referencedStateSymbols].some((symbol) => {
|
|
180
|
-
const binding = bindings.get(symbol);
|
|
181
|
-
return binding?.kind === "createStateConfig" || detectXStateSourceProfile(binding?.sourceFile.text ?? "") === "xstate-v6-alpha";
|
|
182
|
-
}) ? "xstate-v6-alpha" : rootProfile;
|
|
183
|
-
return {
|
|
184
|
-
version: 1,
|
|
185
|
-
profile: packageProfile?.profile ?? inferredProfile,
|
|
186
|
-
profileRef: packageProfile?.profileRef ?? normalizeMachineProfile(inferredProfile),
|
|
187
|
-
root: {
|
|
188
|
-
uri,
|
|
189
|
-
range: toSourceRange$1(sourceFile, root)
|
|
190
|
-
},
|
|
191
|
-
statesObject: statesObjectLocations[0],
|
|
192
|
-
states,
|
|
193
|
-
staticValues: collectStaticValueReferences(sourceFile, uri, root, staticValues),
|
|
194
|
-
transitionFunctions: [...resolvedTransitionFunctions].flatMap(([symbol, resolved]) => resolved.binding.uri === uri ? [] : [{
|
|
195
|
-
symbol,
|
|
196
|
-
uri: resolved.binding.uri,
|
|
197
|
-
range: toSourceRange$1(resolved.binding.sourceFile, resolved.binding.node),
|
|
198
|
-
sourcePaths: resolved.sourcePaths
|
|
199
|
-
}])
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
function detectXStatePackageProfile(packageJson) {
|
|
203
|
-
if (!packageJson) return void 0;
|
|
204
|
-
let manifest;
|
|
205
|
-
try {
|
|
206
|
-
manifest = JSON.parse(packageJson.text);
|
|
207
|
-
} catch {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
const dependencyFields = [
|
|
211
|
-
"dependencies",
|
|
212
|
-
"devDependencies",
|
|
213
|
-
"peerDependencies",
|
|
214
|
-
"optionalDependencies"
|
|
215
|
-
];
|
|
216
|
-
let range;
|
|
217
|
-
for (const field of dependencyFields) {
|
|
218
|
-
const dependencies = manifest[field];
|
|
219
|
-
if (!dependencies || typeof dependencies !== "object") continue;
|
|
220
|
-
const value = dependencies.xstate;
|
|
221
|
-
if (typeof value === "string") {
|
|
222
|
-
range = value;
|
|
223
|
-
break;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (!range) return void 0;
|
|
227
|
-
const normalizedRange = range.replace(/^(?:workspace:|npm:xstate@)/, "");
|
|
228
|
-
const clauseProfiles = normalizedRange.split("||").map((clause) => detectXStateRangeClauseProfile(clause.trim()));
|
|
229
|
-
const profiles = new Set(clauseProfiles);
|
|
230
|
-
if (profiles.size !== 1 || profiles.has(void 0)) return void 0;
|
|
231
|
-
const profile = clauseProfiles[0];
|
|
232
|
-
if (!profile) return void 0;
|
|
233
|
-
return {
|
|
234
|
-
profile,
|
|
235
|
-
profileRef: /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(normalizedRange) ? `@statelyai/xstate@${normalizedRange}` : normalizeMachineProfile(profile)
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
function detectXStateRangeClauseProfile(clause) {
|
|
239
|
-
const normalizedClause = clause.replace(/([<>]=?)\s+/g, "$1");
|
|
240
|
-
let lowerRequiresV6 = false;
|
|
241
|
-
let upperRequiresV5 = false;
|
|
242
|
-
let directProfile;
|
|
243
|
-
for (const match of normalizedClause.matchAll(/(?:^|\s)([<>]=?|[~^])?v?(\d+)(?:\.(\d+|x|\*))?(?:\.(\d+|x|\*))?(?:-[0-9A-Za-z.-]+)?(?=\s|$)/g)) {
|
|
244
|
-
const operator = match[1] ?? "";
|
|
245
|
-
const major = Number(match[2]);
|
|
246
|
-
const minor = parseRangePart(match[3]);
|
|
247
|
-
const patch = parseRangePart(match[4]);
|
|
248
|
-
if (operator === ">" || operator === ">=") {
|
|
249
|
-
if (major >= 6) lowerRequiresV6 = true;
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
if (operator === "<") {
|
|
253
|
-
if (major < 6 || major === 6 && (minor ?? 0) === 0 && (patch ?? 0) === 0) upperRequiresV5 = true;
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
if (operator === "<=") {
|
|
257
|
-
if (major < 6) upperRequiresV5 = true;
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
const profile = major >= 6 ? "xstate-v6-alpha" : "xstate-v5";
|
|
261
|
-
if (directProfile && directProfile !== profile) return void 0;
|
|
262
|
-
directProfile = profile;
|
|
263
|
-
}
|
|
264
|
-
if (lowerRequiresV6 && upperRequiresV5) return void 0;
|
|
265
|
-
if (lowerRequiresV6) return "xstate-v6-alpha";
|
|
266
|
-
if (upperRequiresV5) return "xstate-v5";
|
|
267
|
-
return directProfile;
|
|
268
|
-
}
|
|
269
|
-
function parseRangePart(value) {
|
|
270
|
-
return value && /^\d+$/.test(value) ? Number(value) : void 0;
|
|
271
|
-
}
|
|
272
|
-
function collectImportedXStateTransitionSources(sourceText, document, options = {}) {
|
|
273
|
-
if (!options.resolver) return [];
|
|
274
|
-
const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$3(document.fileName));
|
|
275
|
-
const uri = document.uri?.toString() ?? document.fileName;
|
|
276
|
-
sourceFileUris.set(sourceFile, uri);
|
|
277
|
-
const bindings = collectBindings(sourceFile, uri, document.fileName, options);
|
|
278
|
-
const root = findMachineConfigObjectLiteral$1(sourceFile, bindings, options.machineIndex);
|
|
279
|
-
if (!root) return [];
|
|
280
|
-
return [...collectResolvedTransitionFunctions(root, bindings)].flatMap(([localName, resolved]) => {
|
|
281
|
-
const { binding } = resolved;
|
|
282
|
-
if (binding.uri === uri) return [];
|
|
283
|
-
if (ts$1.isFunctionDeclaration(binding.node)) {
|
|
284
|
-
const name = binding.node.name;
|
|
285
|
-
if (!name) return [];
|
|
286
|
-
const suffix = binding.node.getText(binding.sourceFile).slice(name.getEnd() - binding.node.getStart(binding.sourceFile));
|
|
287
|
-
return [`${binding.node.modifiers?.some((modifier) => modifier.kind === ts$1.SyntaxKind.AsyncKeyword) ? "async " : ""}function ${localName}${suffix}`];
|
|
288
|
-
}
|
|
289
|
-
return [`const ${localName} = ${binding.node.getText(binding.sourceFile)};`];
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
function collectStaticValueBindings(sourceFile) {
|
|
293
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
294
|
-
for (const statement of sourceFile.statements) {
|
|
295
|
-
if (ts$1.isVariableStatement(statement)) {
|
|
296
|
-
if (!((statement.declarationList.flags & ts$1.NodeFlags.Const) === ts$1.NodeFlags.Const)) continue;
|
|
297
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
298
|
-
if (!ts$1.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
299
|
-
const value = getStaticStringValue(declaration.initializer);
|
|
300
|
-
if (value === null) continue;
|
|
301
|
-
bindings.set(declaration.name.text, {
|
|
302
|
-
kind: "const",
|
|
303
|
-
symbol: declaration.name.text,
|
|
304
|
-
value,
|
|
305
|
-
valueRange: toSourceRange$1(sourceFile, declaration.initializer)
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
if (ts$1.isEnumDeclaration(statement)) for (const member of statement.members) {
|
|
311
|
-
const memberName = getEnumMemberName(member.name);
|
|
312
|
-
if (!memberName || !member.initializer) continue;
|
|
313
|
-
const value = getStaticStringValue(member.initializer);
|
|
314
|
-
if (value === null) continue;
|
|
315
|
-
const symbol = `${statement.name.text}.${memberName}`;
|
|
316
|
-
bindings.set(symbol, {
|
|
317
|
-
kind: "enumMember",
|
|
318
|
-
symbol,
|
|
319
|
-
value,
|
|
320
|
-
valueRange: toSourceRange$1(sourceFile, member.initializer)
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
return bindings;
|
|
325
|
-
}
|
|
326
|
-
function collectBindings(sourceFile, uri, fileName, options, cache = /* @__PURE__ */ new Map()) {
|
|
327
|
-
const cached = cache.get(fileName);
|
|
328
|
-
if (cached) return cached;
|
|
329
|
-
const bindings = collectTopLevelBindings(sourceFile, uri);
|
|
330
|
-
cache.set(fileName, bindings);
|
|
331
|
-
addImportedBindings(bindings, sourceFile, uri, fileName, options, cache);
|
|
332
|
-
return bindings;
|
|
333
|
-
}
|
|
334
|
-
function collectTopLevelBindings(sourceFile, uri) {
|
|
335
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
336
|
-
for (const statement of sourceFile.statements) {
|
|
337
|
-
if (ts$1.isFunctionDeclaration(statement) && statement.name) {
|
|
338
|
-
bindings.set(statement.name.text, {
|
|
339
|
-
kind: "transitionFunction",
|
|
340
|
-
node: statement,
|
|
341
|
-
sourceFile,
|
|
342
|
-
uri,
|
|
343
|
-
bindings
|
|
344
|
-
});
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
if (!ts$1.isVariableStatement(statement)) continue;
|
|
348
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
349
|
-
if (!ts$1.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
350
|
-
const initializer = unwrapExpression$2(declaration.initializer);
|
|
351
|
-
if (ts$1.isArrowFunction(initializer) || ts$1.isFunctionExpression(initializer)) {
|
|
352
|
-
bindings.set(declaration.name.text, {
|
|
353
|
-
kind: "transitionFunction",
|
|
354
|
-
node: initializer,
|
|
355
|
-
sourceFile,
|
|
356
|
-
uri,
|
|
357
|
-
bindings
|
|
358
|
-
});
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
361
|
-
const objectLiteral = unwrapObjectLiteralExpression$1(declaration.initializer);
|
|
362
|
-
if (objectLiteral) {
|
|
363
|
-
bindings.set(declaration.name.text, {
|
|
364
|
-
kind: "objectReference",
|
|
365
|
-
node: objectLiteral,
|
|
366
|
-
sourceFile,
|
|
367
|
-
uri,
|
|
368
|
-
bindings
|
|
369
|
-
});
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
372
|
-
const stateConfig = unwrapCreateConfigArgument(declaration.initializer);
|
|
373
|
-
if (stateConfig) bindings.set(declaration.name.text, {
|
|
374
|
-
kind: "createStateConfig",
|
|
375
|
-
node: stateConfig,
|
|
376
|
-
sourceFile,
|
|
377
|
-
uri,
|
|
378
|
-
bindings
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
return bindings;
|
|
383
|
-
}
|
|
384
|
-
function addImportedBindings(bindings, sourceFile, uri, fileName, options, cache) {
|
|
385
|
-
const resolver = options.resolver;
|
|
386
|
-
if (!resolver) return;
|
|
387
|
-
for (const statement of sourceFile.statements) {
|
|
388
|
-
if (!ts$1.isImportDeclaration(statement) || !ts$1.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
389
|
-
const namedBindings = statement.importClause?.namedBindings;
|
|
390
|
-
if (!namedBindings || !ts$1.isNamedImports(namedBindings)) continue;
|
|
391
|
-
const importedDocument = resolver.resolveImport({
|
|
392
|
-
fileName,
|
|
393
|
-
uri
|
|
394
|
-
}, statement.moduleSpecifier.text);
|
|
395
|
-
if (!importedDocument) continue;
|
|
396
|
-
const importedSourceFile = ts$1.createSourceFile(importedDocument.fileName, importedDocument.text, ts$1.ScriptTarget.Latest, true, getScriptKind$3(importedDocument.fileName));
|
|
397
|
-
sourceFileUris.set(importedSourceFile, importedDocument.uri);
|
|
398
|
-
const importedBindings = collectBindings(importedSourceFile, importedDocument.uri, importedDocument.fileName, options, cache);
|
|
399
|
-
for (const element of namedBindings.elements) {
|
|
400
|
-
const importedName = element.propertyName?.text ?? element.name.text;
|
|
401
|
-
const binding = importedBindings.get(importedName);
|
|
402
|
-
if (binding) bindings.set(element.name.text, binding);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
function collectResolvedTransitionFunctions(root, bindings) {
|
|
407
|
-
const found = /* @__PURE__ */ new Map();
|
|
408
|
-
const visitExpression = (expression, sourcePath) => {
|
|
409
|
-
const current = unwrapExpression$2(expression);
|
|
410
|
-
if (ts$1.isIdentifier(current)) {
|
|
411
|
-
const binding = bindings.get(current.text);
|
|
412
|
-
if (binding?.kind === "transitionFunction") {
|
|
413
|
-
const resolved = found.get(current.text) ?? {
|
|
414
|
-
binding,
|
|
415
|
-
sourcePaths: []
|
|
416
|
-
};
|
|
417
|
-
if (!resolved.sourcePaths.some((existing) => existing.length === sourcePath.length && existing.every((segment, index) => segment === sourcePath[index]))) resolved.sourcePaths.push(sourcePath);
|
|
418
|
-
found.set(current.text, resolved);
|
|
419
|
-
}
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
if (ts$1.isObjectLiteralExpression(current)) {
|
|
423
|
-
for (const property of current.properties) if (ts$1.isPropertyAssignment(property)) visitExpression(property.initializer, sourcePath);
|
|
424
|
-
else if (ts$1.isShorthandPropertyAssignment(property)) visitExpression(property.name, sourcePath);
|
|
425
|
-
else if (ts$1.isSpreadAssignment(property)) visitExpression(property.expression, sourcePath);
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
if (ts$1.isArrayLiteralExpression(current)) {
|
|
429
|
-
for (const element of current.elements) if (ts$1.isExpression(element)) visitExpression(element, sourcePath);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
ts$1.forEachChild(current, (child) => {
|
|
433
|
-
if (ts$1.isExpression(child)) visitExpression(child, sourcePath);
|
|
434
|
-
});
|
|
435
|
-
};
|
|
436
|
-
const visitState = (state, sourcePath) => {
|
|
437
|
-
for (const propertyName of [
|
|
438
|
-
"on",
|
|
439
|
-
"after",
|
|
440
|
-
"always",
|
|
441
|
-
"onDone",
|
|
442
|
-
"onError",
|
|
443
|
-
"onSnapshot",
|
|
444
|
-
"onTimeout"
|
|
445
|
-
]) {
|
|
446
|
-
const property = state.properties.find((candidate) => ts$1.isPropertyAssignment(candidate) && getPropertyNameText$2(candidate.name) === propertyName);
|
|
447
|
-
if (property) visitExpression(property.initializer, sourcePath);
|
|
448
|
-
}
|
|
449
|
-
const states = state.properties.find((candidate) => ts$1.isPropertyAssignment(candidate) && getPropertyNameText$2(candidate.name) === "states");
|
|
450
|
-
const statesObject = states && unwrapObjectLiteralExpression$1(states.initializer);
|
|
451
|
-
if (!statesObject) return;
|
|
452
|
-
for (const property of statesObject.properties) {
|
|
453
|
-
if (!ts$1.isPropertyAssignment(property)) continue;
|
|
454
|
-
const child = unwrapObjectLiteralExpression$1(property.initializer);
|
|
455
|
-
const key = getPropertyNameText$2(property.name);
|
|
456
|
-
if (child && key) visitState(child, [...sourcePath, key]);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
visitState(root, []);
|
|
460
|
-
return found;
|
|
461
|
-
}
|
|
462
|
-
function findMachineConfigObjectLiteral$1(sourceFile, bindings, machineIndex = 0) {
|
|
463
|
-
let found = null;
|
|
464
|
-
let currentMachineIndex = 0;
|
|
465
|
-
const visit = (node) => {
|
|
466
|
-
if (found) return;
|
|
467
|
-
if (ts$1.isCallExpression(node) && isCreateMachineExpression$2(node.expression)) {
|
|
468
|
-
const isSelectedMachine = currentMachineIndex === machineIndex;
|
|
469
|
-
currentMachineIndex += 1;
|
|
470
|
-
if (!isSelectedMachine) return;
|
|
471
|
-
const [firstArgument] = node.arguments;
|
|
472
|
-
if (!firstArgument || !ts$1.isExpression(firstArgument)) return;
|
|
473
|
-
found = unwrapObjectLiteralExpression$1(firstArgument) ?? resolveIdentifierObject(firstArgument, bindings);
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
ts$1.forEachChild(node, visit);
|
|
477
|
-
};
|
|
478
|
-
visit(sourceFile);
|
|
479
|
-
return found;
|
|
480
|
-
}
|
|
481
|
-
function collectStateLocations(stateObject, parentPath, locations, statesObjectLocations, bindings, staticValues, root) {
|
|
482
|
-
const statesObject = resolveObjectLiteralProperty(stateObject, "states", bindings);
|
|
483
|
-
if (!statesObject) return;
|
|
484
|
-
statesObjectLocations.push({
|
|
485
|
-
uri: statesObject.uri,
|
|
486
|
-
path: parentPath,
|
|
487
|
-
kind: statesObject.kind,
|
|
488
|
-
symbol: statesObject.symbol,
|
|
489
|
-
keyRange: statesObject.keyRange,
|
|
490
|
-
referenceRange: statesObject.referenceRange,
|
|
491
|
-
range: toSourceRange$1(statesObject.sourceFile, statesObject.object)
|
|
492
|
-
});
|
|
493
|
-
for (const property of statesObject.object.properties) {
|
|
494
|
-
const resolved = resolveStateProperty(property, statesObject.bindings, staticValues, root);
|
|
495
|
-
if (!resolved) continue;
|
|
496
|
-
const path = [...parentPath, resolved.key];
|
|
497
|
-
locations.push({
|
|
498
|
-
uri: resolved.uri,
|
|
499
|
-
path,
|
|
500
|
-
kind: resolved.kind,
|
|
501
|
-
symbol: resolved.symbol,
|
|
502
|
-
keyRange: resolved.keyRange,
|
|
503
|
-
referenceRange: resolved.referenceRange,
|
|
504
|
-
keySource: resolved.keySource,
|
|
505
|
-
keyReplacementText: resolved.keyReplacementText,
|
|
506
|
-
range: toSourceRange$1(resolved.sourceFile, resolved.stateObject)
|
|
507
|
-
});
|
|
508
|
-
collectStateLocations(resolved.stateObject, path, locations, statesObjectLocations, resolved.bindings, staticValues, root);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
function resolveStateProperty(property, bindings, staticValues, root) {
|
|
512
|
-
if (ts$1.isShorthandPropertyAssignment(property)) {
|
|
513
|
-
const binding = bindings.get(property.name.text);
|
|
514
|
-
if (!binding || binding.kind === "transitionFunction") return null;
|
|
515
|
-
return {
|
|
516
|
-
key: property.name.text,
|
|
517
|
-
kind: binding.kind,
|
|
518
|
-
symbol: property.name.text,
|
|
519
|
-
keyRange: toSourceRange$1(property.getSourceFile(), property.name),
|
|
520
|
-
referenceRange: toSourceRange$1(property.getSourceFile(), property.name),
|
|
521
|
-
stateObject: binding.node,
|
|
522
|
-
sourceFile: binding.sourceFile,
|
|
523
|
-
uri: binding.uri,
|
|
524
|
-
bindings: binding.bindings
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
if (!ts$1.isPropertyAssignment(property)) return null;
|
|
528
|
-
const resolvedKey = resolvePropertyKey(property.name, staticValues, root);
|
|
529
|
-
if (!resolvedKey) return null;
|
|
530
|
-
const inlineObject = unwrapObjectLiteralExpression$1(property.initializer);
|
|
531
|
-
if (inlineObject) return {
|
|
532
|
-
key: resolvedKey.key,
|
|
533
|
-
kind: "inline",
|
|
534
|
-
keyRange: toSourceRange$1(property.getSourceFile(), property.name),
|
|
535
|
-
keySource: resolvedKey.keySource,
|
|
536
|
-
keyReplacementText: resolvedKey.keyReplacementText,
|
|
537
|
-
stateObject: inlineObject,
|
|
538
|
-
sourceFile: property.getSourceFile(),
|
|
539
|
-
uri: getSourceFileUri(property.getSourceFile()),
|
|
540
|
-
bindings
|
|
541
|
-
};
|
|
542
|
-
if (ts$1.isIdentifier(property.initializer)) {
|
|
543
|
-
const binding = bindings.get(property.initializer.text);
|
|
544
|
-
if (!binding || binding.kind === "transitionFunction") return null;
|
|
545
|
-
return {
|
|
546
|
-
key: resolvedKey.key,
|
|
547
|
-
kind: binding.kind,
|
|
548
|
-
symbol: property.initializer.text,
|
|
549
|
-
keyRange: toSourceRange$1(property.getSourceFile(), property.name),
|
|
550
|
-
referenceRange: toSourceRange$1(property.getSourceFile(), property.initializer),
|
|
551
|
-
keySource: resolvedKey.keySource,
|
|
552
|
-
keyReplacementText: resolvedKey.keyReplacementText,
|
|
553
|
-
stateObject: binding.node,
|
|
554
|
-
sourceFile: binding.sourceFile,
|
|
555
|
-
uri: binding.uri,
|
|
556
|
-
bindings: binding.bindings
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
return null;
|
|
560
|
-
}
|
|
561
|
-
function getSourceFileUri(sourceFile) {
|
|
562
|
-
return sourceFileUris.get(sourceFile) ?? sourceFile.fileName;
|
|
563
|
-
}
|
|
564
|
-
function unwrapCreateConfigArgument(expression) {
|
|
565
|
-
const unwrapped = unwrapExpression$2(expression);
|
|
566
|
-
if (!ts$1.isCallExpression(unwrapped)) return null;
|
|
567
|
-
const callee = unwrapped.expression;
|
|
568
|
-
if (!ts$1.isPropertyAccessExpression(callee) || !isSetupCreateConfigName(callee.name.text)) return null;
|
|
569
|
-
const [firstArgument] = unwrapped.arguments;
|
|
570
|
-
if (!firstArgument || !ts$1.isExpression(firstArgument)) return null;
|
|
571
|
-
return unwrapObjectLiteralExpression$1(firstArgument);
|
|
572
|
-
}
|
|
573
|
-
function isSetupCreateConfigName(name) {
|
|
574
|
-
return /^create[A-Z_$][\w$]*Config$/.test(name);
|
|
575
|
-
}
|
|
576
|
-
function resolveIdentifierObject(expression, bindings) {
|
|
577
|
-
const unwrapped = unwrapExpression$2(expression);
|
|
578
|
-
if (!ts$1.isIdentifier(unwrapped)) return null;
|
|
579
|
-
const binding = bindings.get(unwrapped.text);
|
|
580
|
-
return binding?.kind === "objectReference" ? binding.node : null;
|
|
581
|
-
}
|
|
582
|
-
function resolveObjectLiteralProperty(objectLiteral, propertyName, bindings) {
|
|
583
|
-
for (const property of objectLiteral.properties) {
|
|
584
|
-
if (!ts$1.isPropertyAssignment(property) && !ts$1.isShorthandPropertyAssignment(property)) continue;
|
|
585
|
-
if (getPropertyNameText$2(property.name) !== propertyName) continue;
|
|
586
|
-
const initializer = ts$1.isShorthandPropertyAssignment(property) ? property.name : property.initializer;
|
|
587
|
-
const inlineObject = unwrapObjectLiteralExpression$1(initializer);
|
|
588
|
-
if (inlineObject) return {
|
|
589
|
-
object: inlineObject,
|
|
590
|
-
kind: "inline",
|
|
591
|
-
keyRange: toSourceRange$1(property.getSourceFile(), property.name),
|
|
592
|
-
sourceFile: property.getSourceFile(),
|
|
593
|
-
uri: getSourceFileUri(property.getSourceFile()),
|
|
594
|
-
bindings
|
|
595
|
-
};
|
|
596
|
-
if (ts$1.isIdentifier(initializer)) {
|
|
597
|
-
const binding = bindings.get(initializer.text);
|
|
598
|
-
if (binding?.kind !== "objectReference") return null;
|
|
599
|
-
return {
|
|
600
|
-
object: binding.node,
|
|
601
|
-
kind: binding.kind,
|
|
602
|
-
symbol: initializer.text,
|
|
603
|
-
keyRange: toSourceRange$1(property.getSourceFile(), property.name),
|
|
604
|
-
referenceRange: toSourceRange$1(property.getSourceFile(), initializer),
|
|
605
|
-
sourceFile: binding.sourceFile,
|
|
606
|
-
uri: binding.uri,
|
|
607
|
-
bindings: binding.bindings
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
return null;
|
|
611
|
-
}
|
|
612
|
-
return null;
|
|
613
|
-
}
|
|
614
|
-
function getPropertyNameText$2(name) {
|
|
615
|
-
if (ts$1.isIdentifier(name) || ts$1.isStringLiteral(name) || ts$1.isNumericLiteral(name)) return name.text;
|
|
616
|
-
if (ts$1.isComputedPropertyName(name)) {
|
|
617
|
-
const expression = name.expression;
|
|
618
|
-
if (ts$1.isStringLiteral(expression) || ts$1.isNoSubstitutionTemplateLiteral(expression) || ts$1.isNumericLiteral(expression)) return expression.text;
|
|
619
|
-
}
|
|
620
|
-
return null;
|
|
621
|
-
}
|
|
622
|
-
function resolvePropertyKey(name, staticValues, root) {
|
|
623
|
-
const literalKey = getPropertyNameText$2(name);
|
|
624
|
-
if (literalKey !== null) {
|
|
625
|
-
if (ts$1.isComputedPropertyName(name)) return {
|
|
626
|
-
key: literalKey,
|
|
627
|
-
keySource: { kind: "computedLiteral" },
|
|
628
|
-
keyReplacementText: JSON.stringify(literalKey)
|
|
629
|
-
};
|
|
630
|
-
return { key: literalKey };
|
|
631
|
-
}
|
|
632
|
-
if (!ts$1.isComputedPropertyName(name)) return null;
|
|
633
|
-
const binding = getStaticValueBindingForExpression(name.expression, staticValues);
|
|
634
|
-
if (!binding) return null;
|
|
635
|
-
return {
|
|
636
|
-
key: binding.value,
|
|
637
|
-
keySource: {
|
|
638
|
-
kind: binding.kind,
|
|
639
|
-
symbol: binding.symbol,
|
|
640
|
-
valueRange: binding.valueRange,
|
|
641
|
-
machineLocal: isStaticValueMachineLocal(name.getSourceFile(), binding, root)
|
|
642
|
-
},
|
|
643
|
-
keyReplacementText: JSON.stringify(binding.value)
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
function getStaticValueBindingForExpression(expression, staticValues) {
|
|
647
|
-
const unwrapped = unwrapExpression$2(expression);
|
|
648
|
-
if (ts$1.isIdentifier(unwrapped)) return staticValues.get(unwrapped.text) ?? null;
|
|
649
|
-
if (ts$1.isPropertyAccessExpression(unwrapped) && ts$1.isIdentifier(unwrapped.expression)) return staticValues.get(`${unwrapped.expression.text}.${unwrapped.name.text}`) ?? null;
|
|
650
|
-
return null;
|
|
651
|
-
}
|
|
652
|
-
function collectStaticValueReferences(sourceFile, uri, root, staticValues) {
|
|
653
|
-
const references = [];
|
|
654
|
-
if (staticValues.size === 0) return references;
|
|
655
|
-
const rootStart = root.getStart(sourceFile);
|
|
656
|
-
const rootEnd = root.getEnd();
|
|
657
|
-
const visit = (node) => {
|
|
658
|
-
if (ts$1.isComputedPropertyName(node)) {
|
|
659
|
-
const binding = getStaticValueBindingForExpression(node.expression, staticValues);
|
|
660
|
-
if (binding && !isStateKeyComputedPropertyName(node)) references.push({
|
|
661
|
-
uri,
|
|
662
|
-
range: toSourceRange$1(sourceFile, node),
|
|
663
|
-
value: binding.value
|
|
664
|
-
});
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
if (!(node.getStart(sourceFile) >= rootStart && node.getEnd() <= rootEnd)) {
|
|
668
|
-
ts$1.forEachChild(node, visit);
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
const binding = getStaticValueReferenceBinding(node, staticValues);
|
|
672
|
-
if (binding) {
|
|
673
|
-
references.push({
|
|
674
|
-
uri,
|
|
675
|
-
range: toSourceRange$1(sourceFile, node),
|
|
676
|
-
value: binding.value
|
|
677
|
-
});
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
ts$1.forEachChild(node, visit);
|
|
681
|
-
};
|
|
682
|
-
visit(root);
|
|
683
|
-
return references;
|
|
684
|
-
}
|
|
685
|
-
function getStaticValueReferenceBinding(node, staticValues) {
|
|
686
|
-
if (ts$1.isIdentifier(node)) {
|
|
687
|
-
if (ts$1.isVariableDeclaration(node.parent) && node.parent.name === node) return null;
|
|
688
|
-
if (ts$1.isPropertyAssignment(node.parent) && node.parent.name === node) return null;
|
|
689
|
-
if (ts$1.isPropertyAccessExpression(node.parent)) return null;
|
|
690
|
-
return staticValues.get(node.text) ?? null;
|
|
691
|
-
}
|
|
692
|
-
if (ts$1.isPropertyAccessExpression(node) && ts$1.isIdentifier(node.expression)) {
|
|
693
|
-
if (ts$1.isComputedPropertyName(node.parent) && ts$1.isEnumMember(node.parent.parent)) return null;
|
|
694
|
-
return staticValues.get(`${node.expression.text}.${node.name.text}`) ?? null;
|
|
695
|
-
}
|
|
696
|
-
return null;
|
|
697
|
-
}
|
|
698
|
-
function isStaticValueMachineLocal(sourceFile, binding, root) {
|
|
699
|
-
const rootStart = root.getStart(sourceFile);
|
|
700
|
-
const rootEnd = root.getEnd();
|
|
701
|
-
let referenceCount = 0;
|
|
702
|
-
let hasOutsideReference = false;
|
|
703
|
-
const visit = (node) => {
|
|
704
|
-
if (getStaticValueReferenceBinding(node, new Map([[binding.symbol, binding]]))) {
|
|
705
|
-
referenceCount += 1;
|
|
706
|
-
if (node.getStart(sourceFile) < rootStart || node.getEnd() > rootEnd) hasOutsideReference = true;
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
ts$1.forEachChild(node, visit);
|
|
710
|
-
};
|
|
711
|
-
visit(sourceFile);
|
|
712
|
-
return referenceCount > 0 && !hasOutsideReference;
|
|
713
|
-
}
|
|
714
|
-
function isStateKeyComputedPropertyName(name) {
|
|
715
|
-
const property = name.parent;
|
|
716
|
-
if (!ts$1.isPropertyAssignment(property)) return false;
|
|
717
|
-
const objectLiteral = property.parent;
|
|
718
|
-
if (!ts$1.isObjectLiteralExpression(objectLiteral)) return false;
|
|
719
|
-
const parentProperty = objectLiteral.parent;
|
|
720
|
-
if (!ts$1.isPropertyAssignment(parentProperty)) return false;
|
|
721
|
-
return getPropertyNameText$2(parentProperty.name) === "states";
|
|
722
|
-
}
|
|
723
|
-
function getStaticStringValue(expression) {
|
|
724
|
-
const unwrapped = unwrapExpression$2(expression);
|
|
725
|
-
if (ts$1.isStringLiteral(unwrapped) || ts$1.isNoSubstitutionTemplateLiteral(unwrapped) || ts$1.isNumericLiteral(unwrapped)) return unwrapped.text;
|
|
726
|
-
return null;
|
|
727
|
-
}
|
|
728
|
-
function getEnumMemberName(name) {
|
|
729
|
-
if (ts$1.isIdentifier(name) || ts$1.isStringLiteral(name) || ts$1.isNumericLiteral(name)) return name.text;
|
|
730
|
-
return null;
|
|
731
|
-
}
|
|
732
|
-
function isCreateMachineExpression$2(expression) {
|
|
733
|
-
const isMachineFactoryName = (name) => name === "createMachine" || name === "createMachineFromConfig";
|
|
734
|
-
return ts$1.isIdentifier(expression) && isMachineFactoryName(expression.text) || ts$1.isPropertyAccessExpression(expression) && isMachineFactoryName(expression.name.text);
|
|
735
|
-
}
|
|
736
|
-
function unwrapObjectLiteralExpression$1(expression) {
|
|
737
|
-
const unwrapped = unwrapExpression$2(expression);
|
|
738
|
-
return ts$1.isObjectLiteralExpression(unwrapped) ? unwrapped : null;
|
|
739
|
-
}
|
|
740
|
-
function unwrapExpression$2(expression) {
|
|
741
|
-
let current = expression;
|
|
742
|
-
for (;;) {
|
|
743
|
-
if (ts$1.isParenthesizedExpression(current)) {
|
|
744
|
-
current = current.expression;
|
|
745
|
-
continue;
|
|
746
|
-
}
|
|
747
|
-
if (ts$1.isAsExpression(current) || ts$1.isSatisfiesExpression(current)) {
|
|
748
|
-
current = current.expression;
|
|
749
|
-
continue;
|
|
750
|
-
}
|
|
751
|
-
if (ts$1.isTypeAssertionExpression(current) || ts$1.isNonNullExpression(current)) {
|
|
752
|
-
current = current.expression;
|
|
753
|
-
continue;
|
|
754
|
-
}
|
|
755
|
-
return current;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
function toSourceRange$1(sourceFile, node) {
|
|
759
|
-
const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
760
|
-
const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
761
|
-
return {
|
|
762
|
-
startLine: start.line,
|
|
763
|
-
startChar: start.character,
|
|
764
|
-
endLine: end.line,
|
|
765
|
-
endChar: end.character
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
function getScriptKind$3(fileName) {
|
|
769
|
-
if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
|
|
770
|
-
if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
|
|
771
|
-
if (fileName.endsWith(".js")) return ts$1.ScriptKind.JS;
|
|
772
|
-
return ts$1.ScriptKind.TS;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
//#endregion
|
|
776
|
-
//#region ../editor-sync/src/internal/codeToGraph.ts
|
|
777
|
-
/**
|
|
778
|
-
* Returns source text for the embed to parse.
|
|
779
|
-
*
|
|
780
|
-
* The returned text is still a temporary parse payload, not the source we write
|
|
781
|
-
* back to. Directly imported modular states are hydrated into the root
|
|
782
|
-
* `states` object so Viz can see their transition bodies even though it only
|
|
783
|
-
* receives one string.
|
|
784
|
-
*/
|
|
785
|
-
function parseSourceToMachine(text, document, options = {}) {
|
|
786
|
-
const sourceLocations = collectXStateSourceLocations(text, document, options);
|
|
787
|
-
if (!sourceLocations) return text;
|
|
788
|
-
const staticValueReplacements = (sourceLocations.staticValues ?? []).map((location) => ({
|
|
789
|
-
range: location.range,
|
|
790
|
-
newText: JSON.stringify(location.value)
|
|
791
|
-
}));
|
|
792
|
-
const stateReplacements = sourceLocations.states.filter((location) => location.path.length === 1 && location.keyRange && !sourceLocations.statesObject?.referenceRange).flatMap((location) => {
|
|
793
|
-
const keyRange = location.keyRange;
|
|
794
|
-
const keyReplacement = location.keyReplacementText ? [{
|
|
795
|
-
range: keyRange,
|
|
796
|
-
newText: location.keyReplacementText
|
|
797
|
-
}] : [];
|
|
798
|
-
const stateText = readSourceRange(location.uri, location.range, options.resolver);
|
|
799
|
-
if (!stateText) return keyReplacement;
|
|
800
|
-
if (location.referenceRange && !areSourceRangesEqual(location.referenceRange, keyRange)) return [...keyReplacement, {
|
|
801
|
-
range: location.referenceRange,
|
|
802
|
-
newText: stateText
|
|
803
|
-
}];
|
|
804
|
-
if (!canHydrateShorthandState(text, keyRange)) return keyReplacement;
|
|
805
|
-
return [...keyReplacement, {
|
|
806
|
-
range: keyRange,
|
|
807
|
-
newText: `${location.path[0]}: ${stateText}`
|
|
808
|
-
}];
|
|
809
|
-
});
|
|
810
|
-
const statesObjectReplacements = sourceLocations.statesObject?.referenceRange && sourceLocations.statesObject.kind !== "inline" ? (() => {
|
|
811
|
-
const statesText = readHydratedStatesObject(sourceLocations.statesObject.uri, sourceLocations.statesObject.range, sourceLocations.states, options.resolver);
|
|
812
|
-
return statesText ? [{
|
|
813
|
-
range: sourceLocations.statesObject.referenceRange,
|
|
814
|
-
newText: areSourceRangesEqual(sourceLocations.statesObject.referenceRange, sourceLocations.statesObject.keyRange) ? `states: ${statesText}` : statesText
|
|
815
|
-
}] : [];
|
|
816
|
-
})() : [];
|
|
817
|
-
const importReplacements = collectHydratedImportReplacements(text, document, new Set([...sourceLocations.states.filter((location) => location.path.length === 1 && location.referenceRange).map((location) => location.symbol).filter((symbol) => typeof symbol === "string"), ...sourceLocations.statesObject?.symbol ? [sourceLocations.statesObject.symbol] : []]));
|
|
818
|
-
const hydratedText = applySourceRangeReplacements(text, [
|
|
819
|
-
...staticValueReplacements,
|
|
820
|
-
...stateReplacements,
|
|
821
|
-
...statesObjectReplacements,
|
|
822
|
-
...importReplacements
|
|
823
|
-
].filter(removeOverlappingRanges).sort(compareSourceRangesDescending));
|
|
824
|
-
if (sourceLocations.profile === "xstate-v6-alpha" || options.machineIndex !== void 0) {
|
|
825
|
-
const importedTransitionSources = collectImportedXStateTransitionSources(text, document, options);
|
|
826
|
-
const parseText = importedTransitionSources.length ? `${hydratedText}\n${importedTransitionSources.join("\n")}` : hydratedText;
|
|
827
|
-
const selectedMachineIndex = options.machineIndex ?? 0;
|
|
828
|
-
const config = extractXStateMachineConfigEntries(parseText, { profile: sourceLocations.profile }).find((entry) => entry.machineIndex === selectedMachineIndex)?.config;
|
|
829
|
-
if (!config) throw new Error(`No machine at index ${selectedMachineIndex} was found in ${document.fileName}.`);
|
|
830
|
-
return `import { createMachine } from "xstate";
|
|
831
|
-
|
|
832
|
-
export const machine = createMachine(${serializeJS(config, 0)});
|
|
833
|
-
`;
|
|
834
|
-
}
|
|
835
|
-
return hydratedText;
|
|
836
|
-
}
|
|
837
|
-
function readHydratedStatesObject(uri, range, states, resolver) {
|
|
838
|
-
if (!resolver) return null;
|
|
839
|
-
const document = resolver.read(uri);
|
|
840
|
-
if (!document) return null;
|
|
841
|
-
const replacements = states.filter((state) => state.path.length === 1 && state.referenceRange).flatMap((state) => {
|
|
842
|
-
const stateText = readSourceRange(state.uri, state.range, resolver);
|
|
843
|
-
if (!stateText || !state.referenceRange) return [];
|
|
844
|
-
const isShorthand = state.keyRange && areSourceRangesEqual(state.referenceRange, state.keyRange);
|
|
845
|
-
return [{
|
|
846
|
-
range: state.referenceRange,
|
|
847
|
-
newText: isShorthand ? `${state.path[0]}: ${stateText}` : stateText
|
|
848
|
-
}];
|
|
849
|
-
});
|
|
850
|
-
return applySourceRangeReplacementsToSlice(document.text, range, replacements);
|
|
851
|
-
}
|
|
852
|
-
function applySourceRangeReplacementsToSlice(text, range, replacements) {
|
|
853
|
-
const sliceStart = offsetAtRangePosition$2(text, range.startLine, range.startChar);
|
|
854
|
-
const sliceEnd = offsetAtRangePosition$2(text, range.endLine, range.endChar);
|
|
855
|
-
let updated = text.slice(sliceStart, sliceEnd);
|
|
856
|
-
for (const replacement of replacements.sort(compareSourceRangesDescending)) {
|
|
857
|
-
const start = offsetAtRangePosition$2(text, replacement.range.startLine, replacement.range.startChar);
|
|
858
|
-
const end = offsetAtRangePosition$2(text, replacement.range.endLine, replacement.range.endChar);
|
|
859
|
-
if (start < sliceStart || end > sliceEnd) continue;
|
|
860
|
-
const localStart = start - sliceStart;
|
|
861
|
-
const localEnd = end - sliceStart;
|
|
862
|
-
updated = `${updated.slice(0, localStart)}${replacement.newText}${updated.slice(localEnd)}`;
|
|
863
|
-
}
|
|
864
|
-
return updated;
|
|
865
|
-
}
|
|
866
|
-
function collectHydratedImportReplacements(text, document, hydratedSymbols) {
|
|
867
|
-
if (hydratedSymbols.size === 0) return [];
|
|
868
|
-
const sourceFile = ts$1.createSourceFile(document.fileName, text, ts$1.ScriptTarget.Latest, true, getScriptKind$2(document.fileName));
|
|
869
|
-
const replacements = [];
|
|
870
|
-
for (const statement of sourceFile.statements) {
|
|
871
|
-
if (!ts$1.isImportDeclaration(statement) || !ts$1.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
872
|
-
if (!statement.moduleSpecifier.text.startsWith(".")) continue;
|
|
873
|
-
const namedBindings = statement.importClause?.namedBindings;
|
|
874
|
-
if (!namedBindings || !ts$1.isNamedImports(namedBindings)) continue;
|
|
875
|
-
const importedNames = namedBindings.elements.map((element) => element.name.text);
|
|
876
|
-
if (importedNames.length === 0 || !importedNames.every((name) => hydratedSymbols.has(name))) continue;
|
|
877
|
-
replacements.push({
|
|
878
|
-
range: toSourceRange(sourceFile, statement, { includeTrailingNewline: true }),
|
|
879
|
-
newText: ""
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
return replacements;
|
|
883
|
-
}
|
|
884
|
-
function toSourceRange(sourceFile, node, options = {}) {
|
|
885
|
-
const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
886
|
-
let endOffset = node.getEnd();
|
|
887
|
-
if (options.includeTrailingNewline) {
|
|
888
|
-
const match = sourceFile.text.slice(endOffset).match(/^\r?\n/);
|
|
889
|
-
if (match) endOffset += match[0].length;
|
|
890
|
-
}
|
|
891
|
-
const end = sourceFile.getLineAndCharacterOfPosition(endOffset);
|
|
892
|
-
return {
|
|
893
|
-
startLine: start.line,
|
|
894
|
-
startChar: start.character,
|
|
895
|
-
endLine: end.line,
|
|
896
|
-
endChar: end.character
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
function getScriptKind$2(fileName) {
|
|
900
|
-
if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
|
|
901
|
-
if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
|
|
902
|
-
if (fileName.endsWith(".js")) return ts$1.ScriptKind.JS;
|
|
903
|
-
return ts$1.ScriptKind.TS;
|
|
904
|
-
}
|
|
905
|
-
function removeOverlappingRanges(replacement, index, replacements) {
|
|
906
|
-
return !replacements.some((candidate, candidateIndex) => {
|
|
907
|
-
if (candidateIndex === index) return false;
|
|
908
|
-
return compareSourceRangesDescending(candidate, replacement) < 0 && rangesOverlap$1(candidate.range, replacement.range);
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
function rangesOverlap$1(left, right) {
|
|
912
|
-
const leftStart = rangePositionKey(left.startLine, left.startChar);
|
|
913
|
-
const leftEnd = rangePositionKey(left.endLine, left.endChar);
|
|
914
|
-
const rightStart = rangePositionKey(right.startLine, right.startChar);
|
|
915
|
-
return leftStart < rangePositionKey(right.endLine, right.endChar) && rightStart < leftEnd;
|
|
916
|
-
}
|
|
917
|
-
function rangePositionKey(line, character) {
|
|
918
|
-
return line * 1e6 + character;
|
|
919
|
-
}
|
|
920
|
-
function areSourceRangesEqual(left, right) {
|
|
921
|
-
return left.startLine === right.startLine && left.startChar === right.startChar && left.endLine === right.endLine && left.endChar === right.endChar;
|
|
922
|
-
}
|
|
923
|
-
function canHydrateShorthandState(text, range) {
|
|
924
|
-
const end = offsetAtRangePosition$2(text, range.endLine, range.endChar);
|
|
925
|
-
return text.slice(end).match(/\S/)?.[0] !== ":";
|
|
926
|
-
}
|
|
927
|
-
function readSourceRange(uri, range, resolver) {
|
|
928
|
-
if (!resolver) return null;
|
|
929
|
-
const document = resolver.read(uri);
|
|
930
|
-
if (!document) return null;
|
|
931
|
-
const start = offsetAtRangePosition$2(document.text, range.startLine, range.startChar);
|
|
932
|
-
const end = offsetAtRangePosition$2(document.text, range.endLine, range.endChar);
|
|
933
|
-
return document.text.slice(start, end);
|
|
934
|
-
}
|
|
935
|
-
function applySourceRangeReplacements(text, replacements) {
|
|
936
|
-
let updated = text;
|
|
937
|
-
for (const replacement of replacements) {
|
|
938
|
-
const start = offsetAtRangePosition$2(updated, replacement.range.startLine, replacement.range.startChar);
|
|
939
|
-
const end = offsetAtRangePosition$2(updated, replacement.range.endLine, replacement.range.endChar);
|
|
940
|
-
updated = `${updated.slice(0, start)}${replacement.newText}${updated.slice(end)}`;
|
|
941
|
-
}
|
|
942
|
-
return updated;
|
|
943
|
-
}
|
|
944
|
-
function compareSourceRangesDescending(left, right) {
|
|
945
|
-
if (left.range.startLine !== right.range.startLine) return right.range.startLine - left.range.startLine;
|
|
946
|
-
return right.range.startChar - left.range.startChar;
|
|
947
|
-
}
|
|
948
|
-
function offsetAtRangePosition$2(text, line, character) {
|
|
949
|
-
if (line <= 0) return character;
|
|
950
|
-
let currentLine = 0;
|
|
951
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
952
|
-
if (text.charCodeAt(index) !== 10) continue;
|
|
953
|
-
currentLine += 1;
|
|
954
|
-
if (currentLine === line) return index + 1 + character;
|
|
955
|
-
}
|
|
956
|
-
return text.length;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
//#endregion
|
|
960
|
-
//#region ../editor-sync/src/internal/editTargets.ts
|
|
961
|
-
function createSourceFile(document, sourceText) {
|
|
962
|
-
return ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$1(document.fileName));
|
|
963
|
-
}
|
|
964
|
-
function getMappedRootObjectLiteral(document, sourceFile, sourceLocations) {
|
|
965
|
-
if (!sourceLocations?.root || !isSourceLocationForDocument(sourceLocations.root.uri, document)) return null;
|
|
966
|
-
return findObjectLiteralByRange(sourceFile, sourceLocations.root.range);
|
|
967
|
-
}
|
|
968
|
-
function getMappedStateObjectLiteral(document, sourceFile, sourceLocations, path) {
|
|
969
|
-
if (!sourceLocations) return null;
|
|
970
|
-
const location = sourceLocations.states.find((item) => arePathsEqual$1(item.path, path));
|
|
971
|
-
if (!location || !isSourceLocationForDocument(location.uri, document)) return null;
|
|
972
|
-
return findObjectLiteralByRange(sourceFile, location.range);
|
|
973
|
-
}
|
|
974
|
-
async function getMappedStateReplacementTarget(rootDocument, rootSourceFile, sourceLocations, path, openTextDocument) {
|
|
975
|
-
if (!sourceLocations) return null;
|
|
976
|
-
const location = sourceLocations.states.find((item) => arePathsEqual$1(item.path, path));
|
|
977
|
-
if (!location) return null;
|
|
978
|
-
return getObjectTargetFromLocation(rootDocument, rootSourceFile, location.uri, location.range, openTextDocument);
|
|
979
|
-
}
|
|
980
|
-
function getStateMembershipParentTarget(document, sourceFile, machineConfigNode, sourceLocations, parentPath, getStateObjectLiteralByPath) {
|
|
981
|
-
if (parentPath.length === 0) return getMappedStatesObjectTarget(document, sourceFile, sourceLocations) ?? {
|
|
982
|
-
document,
|
|
983
|
-
sourceFile,
|
|
984
|
-
node: machineConfigNode,
|
|
985
|
-
nodeKind: "machineConfig"
|
|
986
|
-
};
|
|
987
|
-
const node = getMappedStateObjectLiteral(document, sourceFile, sourceLocations, parentPath) ?? getStateObjectLiteralByPath(machineConfigNode, parentPath);
|
|
988
|
-
return node ? {
|
|
989
|
-
document,
|
|
990
|
-
sourceFile,
|
|
991
|
-
node,
|
|
992
|
-
nodeKind: "machineConfig"
|
|
993
|
-
} : null;
|
|
994
|
-
}
|
|
995
|
-
async function getStateMembershipParentTargetForDocuments(document, sourceFile, machineConfigNode, sourceLocations, parentPath, openTextDocument, getStateObjectLiteralByPath) {
|
|
996
|
-
if (parentPath.length === 0) return await getMappedStatesObjectTargetForDocuments(document, sourceFile, sourceLocations, openTextDocument) ?? {
|
|
997
|
-
document,
|
|
998
|
-
sourceFile,
|
|
999
|
-
node: machineConfigNode,
|
|
1000
|
-
nodeKind: "machineConfig"
|
|
1001
|
-
};
|
|
1002
|
-
const mappedTarget = await getMappedStateReplacementTarget(document, sourceFile, sourceLocations, parentPath, openTextDocument);
|
|
1003
|
-
if (mappedTarget) return {
|
|
1004
|
-
...mappedTarget,
|
|
1005
|
-
nodeKind: "machineConfig"
|
|
1006
|
-
};
|
|
1007
|
-
const node = getStateObjectLiteralByPath(machineConfigNode, parentPath);
|
|
1008
|
-
return node ? {
|
|
1009
|
-
document,
|
|
1010
|
-
sourceFile,
|
|
1011
|
-
node,
|
|
1012
|
-
nodeKind: "machineConfig"
|
|
1013
|
-
} : null;
|
|
1014
|
-
}
|
|
1015
|
-
async function openDocumentBySourceUri(uri, openTextDocument) {
|
|
1016
|
-
if (!openTextDocument) return null;
|
|
1017
|
-
try {
|
|
1018
|
-
return await openTextDocument(uri);
|
|
1019
|
-
} catch {
|
|
1020
|
-
return null;
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
function isSourceLocationForDocument(uri, document) {
|
|
1024
|
-
return uri === (typeof document.uri === "string" ? document.uri : document.uri?.toString()) || uri === document.fileName;
|
|
1025
|
-
}
|
|
1026
|
-
function findObjectLiteralByRange(sourceFile, range) {
|
|
1027
|
-
const start = offsetAtRangePosition$1(sourceFile.text, range.startLine, range.startChar);
|
|
1028
|
-
const end = offsetAtRangePosition$1(sourceFile.text, range.endLine, range.endChar);
|
|
1029
|
-
let found = null;
|
|
1030
|
-
const visit = (node) => {
|
|
1031
|
-
if (found) return;
|
|
1032
|
-
if (ts$1.isObjectLiteralExpression(node) && node.getStart(sourceFile) === start && node.getEnd() >= end) {
|
|
1033
|
-
found = node;
|
|
1034
|
-
return;
|
|
1035
|
-
}
|
|
1036
|
-
ts$1.forEachChild(node, visit);
|
|
1037
|
-
};
|
|
1038
|
-
visit(sourceFile);
|
|
1039
|
-
return found;
|
|
1040
|
-
}
|
|
1041
|
-
function getMappedStatesObjectTarget(rootDocument, rootSourceFile, sourceLocations) {
|
|
1042
|
-
const location = sourceLocations?.statesObject;
|
|
1043
|
-
if (!location || !isSourceLocationForDocument(location.uri, rootDocument)) return null;
|
|
1044
|
-
const node = findObjectLiteralByRange(rootSourceFile, location.range);
|
|
1045
|
-
return node ? {
|
|
1046
|
-
document: rootDocument,
|
|
1047
|
-
sourceFile: rootSourceFile,
|
|
1048
|
-
node,
|
|
1049
|
-
nodeKind: "statesObject"
|
|
1050
|
-
} : null;
|
|
1051
|
-
}
|
|
1052
|
-
async function getMappedStatesObjectTargetForDocuments(rootDocument, rootSourceFile, sourceLocations, openTextDocument) {
|
|
1053
|
-
const location = sourceLocations?.statesObject;
|
|
1054
|
-
if (!location) return null;
|
|
1055
|
-
const target = await getObjectTargetFromLocation(rootDocument, rootSourceFile, location.uri, location.range, openTextDocument);
|
|
1056
|
-
return target ? {
|
|
1057
|
-
...target,
|
|
1058
|
-
nodeKind: "statesObject"
|
|
1059
|
-
} : null;
|
|
1060
|
-
}
|
|
1061
|
-
async function getObjectTargetFromLocation(rootDocument, rootSourceFile, uri, range, openTextDocument) {
|
|
1062
|
-
const targetDocument = isSourceLocationForDocument(uri, rootDocument) ? rootDocument : await openDocumentBySourceUri(uri, openTextDocument);
|
|
1063
|
-
if (!targetDocument) return null;
|
|
1064
|
-
const targetSourceFile = isSourceLocationForDocument(uri, rootDocument) ? rootSourceFile : createSourceFile(targetDocument, targetDocument.getText());
|
|
1065
|
-
const node = findObjectLiteralByRange(targetSourceFile, range);
|
|
1066
|
-
if (!node) return null;
|
|
1067
|
-
return {
|
|
1068
|
-
document: targetDocument,
|
|
1069
|
-
sourceFile: targetSourceFile,
|
|
1070
|
-
node
|
|
1071
|
-
};
|
|
1072
|
-
}
|
|
1073
|
-
function getScriptKind$1(fileName) {
|
|
1074
|
-
if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
|
|
1075
|
-
if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
|
|
1076
|
-
if (fileName.endsWith(".js")) return ts$1.ScriptKind.JS;
|
|
1077
|
-
return ts$1.ScriptKind.TS;
|
|
1078
|
-
}
|
|
1079
|
-
function arePathsEqual$1(left, right) {
|
|
1080
|
-
return left.length === right.length && left.every((segment, index) => segment === right[index]);
|
|
1081
|
-
}
|
|
1082
|
-
function offsetAtRangePosition$1(sourceText, line, character) {
|
|
1083
|
-
let currentLine = 0;
|
|
1084
|
-
let lineStart = 0;
|
|
1085
|
-
for (let index = 0; index < sourceText.length; index += 1) {
|
|
1086
|
-
if (currentLine === line) return Math.min(lineStart + character, sourceText.length);
|
|
1087
|
-
if (sourceText.charCodeAt(index) === 10) {
|
|
1088
|
-
currentLine += 1;
|
|
1089
|
-
lineStart = index + 1;
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
return Math.min(lineStart + character, sourceText.length);
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
//#endregion
|
|
1096
|
-
//#region ../editor-sync/src/internal/graphToCode.ts
|
|
1097
|
-
function applyMachineToSource(machineConfig, document, options = {}) {
|
|
1098
|
-
if (!isMachineConfig(machineConfig)) return [];
|
|
1099
|
-
const sourceFile = createSourceFile(document, document.getText());
|
|
1100
|
-
const machineConfigNode = getMappedRootObjectLiteral(document, sourceFile, options.sourceLocations) ?? findMachineConfigObjectLiteral(sourceFile, options.machineIndex);
|
|
1101
|
-
if (!machineConfigNode) return [];
|
|
1102
|
-
if (options.sourceUpdateStrategy === "rewrite") return addCodeImportReplacements(document, sourceFile, machineConfig, [createRewriteReplacement(document, sourceFile, machineConfigNode, machineConfig)]).sort(compareReplacementRangesDescending);
|
|
1103
|
-
const parsedPatches = parseGraphPatches(options.patches);
|
|
1104
|
-
const prevGraph = parseGraph(options.prevGraph);
|
|
1105
|
-
const nextGraph = parseGraph(options.nextGraph);
|
|
1106
|
-
const rootNode = machineConfigNode;
|
|
1107
|
-
if (!parsedPatches || !prevGraph || !nextGraph || parsedPatches.length === 0) {
|
|
1108
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1109
|
-
return [createReplacement(document, sourceFile, rootNode, machineConfig)];
|
|
1110
|
-
}
|
|
1111
|
-
if (hasNestedStateRenameBatch(parsedPatches, prevGraph, nextGraph)) return [];
|
|
1112
|
-
const semanticReplacements = tryCreateSemanticReplacements(document, sourceFile, machineConfigNode, parsedPatches, prevGraph, nextGraph);
|
|
1113
|
-
if (semanticReplacements) return semanticReplacements.sort(compareReplacementRangesDescending);
|
|
1114
|
-
let patchesForSplit = parsedPatches;
|
|
1115
|
-
const implementationSplit = tryCreateImplementationPatchSplit(document, sourceFile, patchesForSplit, nextGraph, options.machineIndex);
|
|
1116
|
-
if (implementationSplit) {
|
|
1117
|
-
const implementationRemainingPatches = dropCoveredSchemaPatches(implementationSplit.remainingPatches, implementationSplit.replacements.length > 0);
|
|
1118
|
-
if (implementationRemainingPatches.length === 0) return implementationSplit.replacements.sort(compareReplacementRangesDescending);
|
|
1119
|
-
const remainingPlan = deriveReplacementPlan(implementationRemainingPatches, prevGraph, nextGraph);
|
|
1120
|
-
if (remainingPlan.type === "root") {
|
|
1121
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1122
|
-
return [createReplacement(document, sourceFile, rootNode, machineConfig)];
|
|
1123
|
-
}
|
|
1124
|
-
const replacements = [...implementationSplit.replacements];
|
|
1125
|
-
for (const path of remainingPlan.paths) {
|
|
1126
|
-
const currentNode = getMappedStateObjectLiteral(document, sourceFile, options.sourceLocations, path) ?? getStateObjectLiteralByPath(machineConfigNode, path);
|
|
1127
|
-
const nextValue = getStateConfigByPath(machineConfig, path);
|
|
1128
|
-
if (!currentNode || !isMachineConfig(nextValue)) {
|
|
1129
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1130
|
-
return [createReplacement(document, sourceFile, rootNode, machineConfig)];
|
|
1131
|
-
}
|
|
1132
|
-
replacements.push(createReplacement(document, sourceFile, currentNode, nextValue));
|
|
1133
|
-
}
|
|
1134
|
-
return replacements.sort(compareReplacementRangesDescending);
|
|
1135
|
-
}
|
|
1136
|
-
patchesForSplit = dropCoveredSchemaPatches(patchesForSplit, false);
|
|
1137
|
-
const stateMembershipSplit = tryCreateStateMembershipPatchSplit(document, sourceFile, rootNode, machineConfig, patchesForSplit, prevGraph, nextGraph, options.sourceLocations);
|
|
1138
|
-
if (stateMembershipSplit) {
|
|
1139
|
-
const stateMembershipRemainingPatches = dropCoveredSchemaPatches(stateMembershipSplit.remainingPatches, stateMembershipSplit.replacements.length > 0);
|
|
1140
|
-
if (stateMembershipRemainingPatches.length === 0) return stateMembershipSplit.replacements;
|
|
1141
|
-
const remainingPlan = deriveReplacementPlan(stateMembershipRemainingPatches, prevGraph, nextGraph);
|
|
1142
|
-
if (remainingPlan.type === "root") {
|
|
1143
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1144
|
-
return [createReplacement(document, sourceFile, rootNode, machineConfig)];
|
|
1145
|
-
}
|
|
1146
|
-
const replacements = [...stateMembershipSplit.replacements];
|
|
1147
|
-
for (const path of remainingPlan.paths) {
|
|
1148
|
-
const currentNode = getMappedStateObjectLiteral(document, sourceFile, options.sourceLocations, path) ?? getStateObjectLiteralByPath(machineConfigNode, path);
|
|
1149
|
-
const nextValue = getStateConfigByPath(machineConfig, path);
|
|
1150
|
-
if (!currentNode || !isMachineConfig(nextValue)) {
|
|
1151
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1152
|
-
return [createReplacement(document, sourceFile, rootNode, machineConfig)];
|
|
1153
|
-
}
|
|
1154
|
-
replacements.push(createReplacement(document, sourceFile, currentNode, nextValue));
|
|
1155
|
-
}
|
|
1156
|
-
return replacements.sort(compareReplacementRangesDescending);
|
|
1157
|
-
}
|
|
1158
|
-
const plan = deriveReplacementPlan(patchesForSplit, prevGraph, nextGraph);
|
|
1159
|
-
if (plan.type === "root") {
|
|
1160
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1161
|
-
return [createReplacement(document, sourceFile, rootNode, machineConfig)];
|
|
1162
|
-
}
|
|
1163
|
-
const replacements = [];
|
|
1164
|
-
for (const path of plan.paths) {
|
|
1165
|
-
const currentNode = getMappedStateObjectLiteral(document, sourceFile, options.sourceLocations, path) ?? getStateObjectLiteralByPath(machineConfigNode, path);
|
|
1166
|
-
const nextValue = getStateConfigByPath(machineConfig, path);
|
|
1167
|
-
if (!currentNode || !isMachineConfig(nextValue)) {
|
|
1168
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1169
|
-
return [createReplacement(document, sourceFile, rootNode, machineConfig)];
|
|
1170
|
-
}
|
|
1171
|
-
replacements.push(createReplacement(document, sourceFile, currentNode, nextValue));
|
|
1172
|
-
}
|
|
1173
|
-
return replacements.sort(compareReplacementRangesDescending);
|
|
1174
|
-
}
|
|
1175
|
-
async function applyMachineToSourceDocuments(machineConfig, document, options = {}) {
|
|
1176
|
-
if (!isMachineConfig(machineConfig)) return [];
|
|
1177
|
-
const sourceFile = createSourceFile(document, document.getText());
|
|
1178
|
-
const machineConfigNode = getMappedRootObjectLiteral(document, sourceFile, options.sourceLocations) ?? findMachineConfigObjectLiteral(sourceFile, options.machineIndex);
|
|
1179
|
-
if (!machineConfigNode) return [];
|
|
1180
|
-
if (options.sourceUpdateStrategy === "rewrite") return addCodeImportReplacements(document, sourceFile, machineConfig, [createRewriteReplacement(document, sourceFile, machineConfigNode, machineConfig)]).sort(compareReplacementRangesDescending);
|
|
1181
|
-
const parsedPatches = parseGraphPatches(options.patches);
|
|
1182
|
-
const prevGraph = parseGraph(options.prevGraph);
|
|
1183
|
-
const nextGraph = parseGraph(options.nextGraph);
|
|
1184
|
-
const finalize = (replacements) => {
|
|
1185
|
-
return normalizeOverlappingReplacements(addCodeImportReplacements(document, sourceFile, machineConfig, replacements)).sort(compareReplacementRangesDescending);
|
|
1186
|
-
};
|
|
1187
|
-
if (!parsedPatches || !prevGraph || !nextGraph || parsedPatches.length === 0) {
|
|
1188
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1189
|
-
return finalize([createReplacement(document, sourceFile, machineConfigNode, machineConfig)]);
|
|
1190
|
-
}
|
|
1191
|
-
if (hasNestedStateRenameBatch(parsedPatches, prevGraph, nextGraph)) return [];
|
|
1192
|
-
const semanticSplit = await tryCreateSemanticPatchSplitForDocuments(document, sourceFile, machineConfigNode, parsedPatches, prevGraph, nextGraph, options.sourceLocations, options.openTextDocument);
|
|
1193
|
-
if (semanticSplit && semanticSplit.remainingPatches.length === 0) return finalize(semanticSplit.replacements);
|
|
1194
|
-
const activePatches = dropCoveredSchemaPatches(semanticSplit?.remainingPatches ?? parsedPatches, (semanticSplit?.replacements.length ?? 0) > 0);
|
|
1195
|
-
const activeSourceLocations = semanticSplit?.sourceLocations ?? options.sourceLocations;
|
|
1196
|
-
let semanticReplacements = semanticSplit?.replacements ?? [];
|
|
1197
|
-
if (activePatches.length === 0) return finalize(semanticReplacements);
|
|
1198
|
-
const transitionSplit = await tryCreateTransitionPatchSplitForDocuments(document, sourceFile, machineConfigNode, machineConfig, activePatches, prevGraph, nextGraph, activeSourceLocations, options.openTextDocument);
|
|
1199
|
-
if (transitionSplit && transitionSplit.remainingPatches.length === 0) return finalize([...semanticReplacements, ...transitionSplit.replacements]);
|
|
1200
|
-
if (transitionSplit) semanticReplacements = [...semanticReplacements, ...transitionSplit.replacements];
|
|
1201
|
-
const implementationSplit = tryCreateImplementationPatchSplit(document, sourceFile, transitionSplit?.remainingPatches ?? activePatches, nextGraph, options.machineIndex);
|
|
1202
|
-
if (implementationSplit && implementationSplit.remainingPatches.length === 0) return finalize([...semanticReplacements, ...implementationSplit.replacements]);
|
|
1203
|
-
if (implementationSplit) semanticReplacements = [...semanticReplacements, ...implementationSplit.replacements];
|
|
1204
|
-
const patchesAfterImplementationSplit = dropCoveredSchemaPatches(implementationSplit?.remainingPatches ?? activePatches, semanticReplacements.length > 0);
|
|
1205
|
-
if (patchesAfterImplementationSplit.length === 0) return finalize(semanticReplacements);
|
|
1206
|
-
const stateMembershipSplit = await tryCreateStateMembershipPatchSplitForDocuments(document, sourceFile, machineConfigNode, machineConfig, patchesAfterImplementationSplit, prevGraph, nextGraph, activeSourceLocations, options.openTextDocument);
|
|
1207
|
-
if (stateMembershipSplit) {
|
|
1208
|
-
const stateMembershipRemainingPatches = dropCoveredSchemaPatches(stateMembershipSplit.remainingPatches, semanticReplacements.length + stateMembershipSplit.replacements.length > 0);
|
|
1209
|
-
if (stateMembershipRemainingPatches.length === 0) return finalize([...semanticReplacements, ...stateMembershipSplit.replacements]);
|
|
1210
|
-
const remainingPlan = deriveReplacementPlan(stateMembershipRemainingPatches, prevGraph, nextGraph);
|
|
1211
|
-
if (remainingPlan.type === "root") {
|
|
1212
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1213
|
-
return finalize([createReplacement(document, sourceFile, machineConfigNode, machineConfig)]);
|
|
1214
|
-
}
|
|
1215
|
-
const replacements = [...semanticReplacements, ...stateMembershipSplit.replacements];
|
|
1216
|
-
for (const path of remainingPlan.paths) {
|
|
1217
|
-
const nextValue = getStateConfigByPath(machineConfig, path);
|
|
1218
|
-
if (!isMachineConfig(nextValue)) return fallbackRootReplacement(document, sourceFile, machineConfigNode, machineConfig, activeSourceLocations);
|
|
1219
|
-
const mappedTarget = await getMappedStateReplacementTarget(document, sourceFile, activeSourceLocations, path, options.openTextDocument);
|
|
1220
|
-
const currentNode = mappedTarget?.node ?? getStateObjectLiteralByPath(machineConfigNode, path);
|
|
1221
|
-
const targetDocument = mappedTarget?.document ?? document;
|
|
1222
|
-
const targetSourceFile = mappedTarget?.sourceFile ?? sourceFile;
|
|
1223
|
-
if (!currentNode) return fallbackRootReplacement(document, sourceFile, machineConfigNode, machineConfig, activeSourceLocations);
|
|
1224
|
-
replacements.push(createReplacement(targetDocument, targetSourceFile, currentNode, nextValue));
|
|
1225
|
-
}
|
|
1226
|
-
return finalize(replacements);
|
|
1227
|
-
}
|
|
1228
|
-
const plan = deriveReplacementPlan(patchesAfterImplementationSplit, prevGraph, nextGraph);
|
|
1229
|
-
if (plan.type === "root") {
|
|
1230
|
-
if (wouldDeModularizeRootReplacement(options.sourceLocations)) return [];
|
|
1231
|
-
return finalize([createReplacement(document, sourceFile, machineConfigNode, machineConfig)]);
|
|
1232
|
-
}
|
|
1233
|
-
const replacements = [...semanticReplacements];
|
|
1234
|
-
for (const path of plan.paths) {
|
|
1235
|
-
const nextValue = getStateConfigByPath(machineConfig, path);
|
|
1236
|
-
if (!isMachineConfig(nextValue)) return fallbackRootReplacement(document, sourceFile, machineConfigNode, machineConfig, activeSourceLocations);
|
|
1237
|
-
const mappedTarget = await getMappedStateReplacementTarget(document, sourceFile, activeSourceLocations, path, options.openTextDocument);
|
|
1238
|
-
const currentNode = mappedTarget?.node ?? getStateObjectLiteralByPath(machineConfigNode, path);
|
|
1239
|
-
const targetDocument = mappedTarget?.document ?? document;
|
|
1240
|
-
const targetSourceFile = mappedTarget?.sourceFile ?? sourceFile;
|
|
1241
|
-
if (!currentNode) return fallbackRootReplacement(document, sourceFile, machineConfigNode, machineConfig, activeSourceLocations);
|
|
1242
|
-
replacements.push(createReplacement(targetDocument, targetSourceFile, currentNode, nextValue));
|
|
1243
|
-
}
|
|
1244
|
-
return finalize(replacements);
|
|
1245
|
-
}
|
|
1246
|
-
function fallbackRootReplacement(document, sourceFile, machineConfigNode, machineConfig, sourceLocations) {
|
|
1247
|
-
if (wouldDeModularizeRootReplacement(sourceLocations)) return [];
|
|
1248
|
-
return addCodeImportReplacements(document, sourceFile, machineConfig, [createReplacement(document, sourceFile, machineConfigNode, machineConfig)]).sort(compareReplacementRangesDescending);
|
|
1249
|
-
}
|
|
1250
|
-
async function tryCreateTransitionPatchSplitForDocuments(document, sourceFile, machineConfigNode, machineConfig, patches, prevGraph, nextGraph, sourceLocations, openTextDocument) {
|
|
1251
|
-
if (isXStateV6Source(sourceLocations)) {
|
|
1252
|
-
const v6Split = await tryCreateV6TransitionPatchSplitForDocuments(document, sourceFile, machineConfigNode, machineConfig, patches, prevGraph, nextGraph, sourceLocations, openTextDocument);
|
|
1253
|
-
if (v6Split) return v6Split;
|
|
1254
|
-
}
|
|
1255
|
-
const replacements = [];
|
|
1256
|
-
const remainingPatches = [];
|
|
1257
|
-
const handledTransitionKeys = /* @__PURE__ */ new Set();
|
|
1258
|
-
const handledStatePropertyKeys = /* @__PURE__ */ new Set();
|
|
1259
|
-
const deletedStatePaths = patches.flatMap((patch) => {
|
|
1260
|
-
if (patch.op !== "deleteState") return [];
|
|
1261
|
-
const path = resolveStatePath(prevGraph, patch.stateId);
|
|
1262
|
-
return path ? [path] : [];
|
|
1263
|
-
});
|
|
1264
|
-
for (const patch of patches) {
|
|
1265
|
-
const transitionTarget = getTransitionPatchTarget(patch, prevGraph, nextGraph);
|
|
1266
|
-
if (transitionTarget) {
|
|
1267
|
-
if (deletedStatePaths.some((deletedPath) => isPathEqualOrDescendant(transitionTarget.sourcePath, deletedPath))) continue;
|
|
1268
|
-
const transitionKey = `${transitionTarget.sourcePath.join(".")}:${transitionTarget.previousEventType}:${transitionTarget.index ?? "single"}`;
|
|
1269
|
-
if (handledTransitionKeys.has(transitionKey)) continue;
|
|
1270
|
-
const patchReplacements = patch.op === "deleteTransition" ? await createDeleteTransitionReplacementsForDocuments(document, sourceFile, machineConfigNode, transitionTarget, sourceLocations, openTextDocument) : await createTransitionReplacementForDocuments(document, sourceFile, machineConfigNode, machineConfig, transitionTarget, sourceLocations, openTextDocument);
|
|
1271
|
-
if (!patchReplacements) {
|
|
1272
|
-
remainingPatches.push(patch);
|
|
1273
|
-
continue;
|
|
1274
|
-
}
|
|
1275
|
-
handledTransitionKeys.add(transitionKey);
|
|
1276
|
-
replacements.push(...patchReplacements);
|
|
1277
|
-
continue;
|
|
1278
|
-
}
|
|
1279
|
-
const [statePropertyTarget] = getStatePropertyPatchTargets(patch, prevGraph, nextGraph);
|
|
1280
|
-
if (!statePropertyTarget) {
|
|
1281
|
-
remainingPatches.push(patch);
|
|
1282
|
-
continue;
|
|
1283
|
-
}
|
|
1284
|
-
const statePathKey = statePropertyTarget.path.join(".");
|
|
1285
|
-
const statePropertyKey = `${statePathKey}:${statePropertyTarget.propertyName}`;
|
|
1286
|
-
if (handledStatePropertyKeys.has(statePropertyKey)) continue;
|
|
1287
|
-
const propertyNames = /* @__PURE__ */ new Set();
|
|
1288
|
-
for (const candidate of patches) {
|
|
1289
|
-
const candidateTargets = getStatePropertyPatchTargets(candidate, prevGraph, nextGraph);
|
|
1290
|
-
for (const candidateTarget of candidateTargets) {
|
|
1291
|
-
if (candidateTarget.path.join(".") !== statePathKey) continue;
|
|
1292
|
-
const candidateKey = `${statePathKey}:${candidateTarget.propertyName}`;
|
|
1293
|
-
if (!handledStatePropertyKeys.has(candidateKey)) propertyNames.add(candidateTarget.propertyName);
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
const patchReplacements = await createStatePropertiesReplacementForDocuments(document, sourceFile, machineConfigNode, machineConfig, statePropertyTarget.path, [...propertyNames], sourceLocations, openTextDocument);
|
|
1297
|
-
if (!patchReplacements) {
|
|
1298
|
-
remainingPatches.push(patch);
|
|
1299
|
-
continue;
|
|
1300
|
-
}
|
|
1301
|
-
replacements.push(...patchReplacements);
|
|
1302
|
-
for (const propertyName of propertyNames) handledStatePropertyKeys.add(`${statePathKey}:${propertyName}`);
|
|
1303
|
-
}
|
|
1304
|
-
return replacements.length > 0 ? {
|
|
1305
|
-
replacements: replacements.sort(compareReplacementRangesDescending),
|
|
1306
|
-
remainingPatches
|
|
1307
|
-
} : null;
|
|
1308
|
-
}
|
|
1309
|
-
async function tryCreateV6TransitionPatchSplitForDocuments(document, sourceFile, machineConfigNode, machineConfig, patches, prevGraph, nextGraph, sourceLocations, openTextDocument) {
|
|
1310
|
-
applyV6FunctionDescriptionEdits(machineConfig, machineConfigNode, patches, prevGraph, nextGraph);
|
|
1311
|
-
const propertiesByPath = /* @__PURE__ */ new Map();
|
|
1312
|
-
const remainingPatches = [];
|
|
1313
|
-
const deletedStatePaths = patches.flatMap((patch) => {
|
|
1314
|
-
if (patch.op !== "deleteState") return [];
|
|
1315
|
-
const path = resolveStatePath(prevGraph, patch.stateId);
|
|
1316
|
-
return path ? [path] : [];
|
|
1317
|
-
});
|
|
1318
|
-
for (const patch of patches) {
|
|
1319
|
-
const edgeId = getTransitionPatchEdgeId(patch, prevGraph, nextGraph);
|
|
1320
|
-
if ((edgeId ? prevGraph.edges.find((edge) => edge.id === edgeId) ?? nextGraph.edges.find((edge) => edge.id === edgeId) : void 0)?.data?.meta?.[XSTATE_V6_FN_TRANSITION_META] === true && patch.op !== "deleteTransition") return {
|
|
1321
|
-
replacements: [],
|
|
1322
|
-
remainingPatches: []
|
|
1323
|
-
};
|
|
1324
|
-
const targets = getV6TransitionPropertyTargets(patch, prevGraph, nextGraph);
|
|
1325
|
-
if (targets.length === 0) {
|
|
1326
|
-
remainingPatches.push(patch);
|
|
1327
|
-
continue;
|
|
1328
|
-
}
|
|
1329
|
-
for (const target of targets) {
|
|
1330
|
-
if (deletedStatePaths.some((deletedPath) => isPathEqualOrDescendant(target.path, deletedPath))) continue;
|
|
1331
|
-
const key = target.path.join(".");
|
|
1332
|
-
const group = propertiesByPath.get(key) ?? {
|
|
1333
|
-
path: target.path,
|
|
1334
|
-
properties: /* @__PURE__ */ new Map()
|
|
1335
|
-
};
|
|
1336
|
-
const slots = group.properties.get(target.propertyName) ?? /* @__PURE__ */ new Set();
|
|
1337
|
-
if (target.slotKey !== null) slots.add(target.slotKey);
|
|
1338
|
-
group.properties.set(target.propertyName, slots);
|
|
1339
|
-
propertiesByPath.set(key, group);
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
if (propertiesByPath.size === 0) return null;
|
|
1343
|
-
const replacements = [];
|
|
1344
|
-
for (const { path, properties } of propertiesByPath.values()) {
|
|
1345
|
-
if (!canSerializeXStateV6TransitionProperties(machineConfig, path, properties)) return {
|
|
1346
|
-
replacements: [],
|
|
1347
|
-
remainingPatches: []
|
|
1348
|
-
};
|
|
1349
|
-
const propertyReplacements = await createStatePropertiesReplacementForDocuments(document, sourceFile, machineConfigNode, machineConfig, path, [...properties.keys()], sourceLocations, openTextDocument, properties);
|
|
1350
|
-
if (!propertyReplacements) return null;
|
|
1351
|
-
replacements.push(...propertyReplacements);
|
|
1352
|
-
}
|
|
1353
|
-
return {
|
|
1354
|
-
replacements: replacements.sort(compareReplacementRangesDescending),
|
|
1355
|
-
remainingPatches
|
|
1356
|
-
};
|
|
1357
|
-
}
|
|
1358
|
-
function applyV6FunctionDescriptionEdits(machineConfig, machineConfigNode, patches, prevGraph, nextGraph) {
|
|
1359
|
-
for (const patch of patches) {
|
|
1360
|
-
if (patch.op !== "updateTransition" || patch.transitionDescription === void 0 || Object.prototype.hasOwnProperty.call(patch.meta ?? {}, XSTATE_V6_BRANCH_DESCRIPTION_META)) continue;
|
|
1361
|
-
const target = getTransitionPatchTarget(patch, prevGraph, nextGraph);
|
|
1362
|
-
if (!target) continue;
|
|
1363
|
-
const stateObject = getStateObjectLiteralByPath(machineConfigNode, target.sourcePath);
|
|
1364
|
-
if (!stateObject || !isV6FunctionTransitionSource(stateObject, target.previousEventType)) continue;
|
|
1365
|
-
const stateConfig = getStateConfigByPath(machineConfig, target.sourcePath);
|
|
1366
|
-
const transitions = (isMachineConfig(stateConfig?.on) ? stateConfig.on : null)?.[target.eventType];
|
|
1367
|
-
if (!Array.isArray(transitions)) continue;
|
|
1368
|
-
for (const transition of transitions) {
|
|
1369
|
-
if (!isMachineConfig(transition)) continue;
|
|
1370
|
-
if (patch.transitionDescription === null) delete transition.description;
|
|
1371
|
-
else transition.description = patch.transitionDescription;
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
function isV6FunctionTransitionSource(stateObject, eventType) {
|
|
1376
|
-
const onObject = getObjectLiteralProperty(stateObject, "on");
|
|
1377
|
-
if (!onObject) return false;
|
|
1378
|
-
const eventProperty = findObjectLiteralProperty(onObject, eventType);
|
|
1379
|
-
if (!eventProperty || !ts$1.isPropertyAssignment(eventProperty)) return false;
|
|
1380
|
-
const initializer = unwrapExpression$1(eventProperty.initializer);
|
|
1381
|
-
if (ts$1.isArrowFunction(initializer) || ts$1.isFunctionExpression(initializer)) return true;
|
|
1382
|
-
if (!ts$1.isObjectLiteralExpression(initializer)) return false;
|
|
1383
|
-
return initializer.properties.some((property) => ts$1.isPropertyAssignment(property) && property.name.getText().replace(/^['"]|['"]$/g, "") === "to");
|
|
1384
|
-
}
|
|
1385
|
-
function canSerializeXStateV6TransitionProperties(machineConfig, path, properties) {
|
|
1386
|
-
const stateConfig = getStateConfigByPath(machineConfig, path);
|
|
1387
|
-
if (!stateConfig) return false;
|
|
1388
|
-
for (const [propertyName, slots] of properties) {
|
|
1389
|
-
const value = stateConfig[propertyName];
|
|
1390
|
-
if (value === void 0) continue;
|
|
1391
|
-
const converted = getXStateV6PropertyValue(propertyName, value);
|
|
1392
|
-
if (propertyName === "on" || propertyName === "after") {
|
|
1393
|
-
if (!isMachineConfig(converted)) return false;
|
|
1394
|
-
if ([...slots].some((slot) => Array.isArray(converted[slot]))) return false;
|
|
1395
|
-
continue;
|
|
1396
|
-
}
|
|
1397
|
-
if (propertyName === "invoke") {
|
|
1398
|
-
const invokes = Array.isArray(converted) ? converted : [converted];
|
|
1399
|
-
for (const slotRef of slots) {
|
|
1400
|
-
const match = /^@statelyai\.invoke\.(done|error|snapshot|timeout)\.(.+)$/.exec(slotRef);
|
|
1401
|
-
if (!match) return false;
|
|
1402
|
-
const invoke = invokes[findXStateV6InvokeIndex(invokes, match[2])];
|
|
1403
|
-
const slot = {
|
|
1404
|
-
done: "onDone",
|
|
1405
|
-
error: "onError",
|
|
1406
|
-
snapshot: "onSnapshot",
|
|
1407
|
-
timeout: "onTimeout"
|
|
1408
|
-
}[match[1]];
|
|
1409
|
-
if (!isMachineConfig(invoke) || Array.isArray(invoke[slot])) return false;
|
|
1410
|
-
}
|
|
1411
|
-
continue;
|
|
1412
|
-
}
|
|
1413
|
-
if (Array.isArray(converted)) return false;
|
|
1414
|
-
}
|
|
1415
|
-
return true;
|
|
1416
|
-
}
|
|
1417
|
-
async function createDeleteTransitionReplacementsForDocuments(document, sourceFile, machineConfigNode, transitionTarget, sourceLocations, openTextDocument) {
|
|
1418
|
-
const mappedTarget = await getMappedStateReplacementTarget(document, sourceFile, sourceLocations, transitionTarget.sourcePath, openTextDocument);
|
|
1419
|
-
const targetDocument = mappedTarget?.document ?? document;
|
|
1420
|
-
const targetSourceFile = mappedTarget?.sourceFile ?? sourceFile;
|
|
1421
|
-
const stateObject = mappedTarget?.node ?? getStateObjectLiteralByPath(machineConfigNode, transitionTarget.sourcePath);
|
|
1422
|
-
if (!stateObject) return null;
|
|
1423
|
-
const transitionSource = getTransitionSourceExpression(stateObject, transitionTarget.previousEventType, transitionTarget.index);
|
|
1424
|
-
if (!transitionSource) return null;
|
|
1425
|
-
if (transitionSource.array && transitionSource.element) return [createArrayElementDeletion(targetDocument, targetSourceFile, transitionSource.array, transitionSource.element)];
|
|
1426
|
-
return [createObjectLiteralPropertyDeletion(targetDocument, targetSourceFile, transitionSource.onObject, transitionSource.eventProperty)];
|
|
1427
|
-
}
|
|
1428
|
-
async function createTransitionReplacementForDocuments(document, sourceFile, machineConfigNode, machineConfig, transitionTarget, sourceLocations, openTextDocument) {
|
|
1429
|
-
const nextTransitionValue = getTransitionConfigValue(machineConfig, transitionTarget.sourcePath, transitionTarget.eventType, transitionTarget.index);
|
|
1430
|
-
if (nextTransitionValue === void 0) return null;
|
|
1431
|
-
const mappedTarget = await getMappedStateReplacementTarget(document, sourceFile, sourceLocations, transitionTarget.sourcePath, openTextDocument);
|
|
1432
|
-
const targetDocument = mappedTarget?.document ?? document;
|
|
1433
|
-
const targetSourceFile = mappedTarget?.sourceFile ?? sourceFile;
|
|
1434
|
-
const stateObject = mappedTarget?.node ?? getStateObjectLiteralByPath(machineConfigNode, transitionTarget.sourcePath);
|
|
1435
|
-
if (!stateObject) return null;
|
|
1436
|
-
const transitionSource = getTransitionSourceExpression(stateObject, transitionTarget.previousEventType, transitionTarget.index);
|
|
1437
|
-
if (transitionSource?.array && transitionSource.element) return [createExpressionReplacement(targetDocument, targetSourceFile, transitionSource.element, nextTransitionValue)];
|
|
1438
|
-
if (transitionSource && transitionTarget.previousEventType === transitionTarget.eventType) return [createPropertyValueReplacement(targetDocument, targetSourceFile, transitionSource.eventProperty, nextTransitionValue)];
|
|
1439
|
-
if (transitionSource) return [createPropertyAssignmentReplacement(targetDocument, targetSourceFile, transitionSource.eventProperty, transitionTarget.eventType, nextTransitionValue)];
|
|
1440
|
-
const onObject = getObjectLiteralProperty(stateObject, "on");
|
|
1441
|
-
if (onObject) return createObjectLiteralPropertyInsertion(targetDocument, targetSourceFile, onObject, transitionTarget.eventType, nextTransitionValue);
|
|
1442
|
-
return createObjectLiteralPropertyInsertion(targetDocument, targetSourceFile, stateObject, "on", { [transitionTarget.eventType]: nextTransitionValue });
|
|
1443
|
-
}
|
|
1444
|
-
async function createStatePropertiesReplacementForDocuments(document, sourceFile, machineConfigNode, machineConfig, path, propertyNames, sourceLocations, openTextDocument, affectedV6Slots) {
|
|
1445
|
-
const nextStateConfig = getStateConfigByPath(machineConfig, path);
|
|
1446
|
-
if (!isMachineConfig(nextStateConfig)) return null;
|
|
1447
|
-
const mappedTarget = await getMappedStateReplacementTarget(document, sourceFile, sourceLocations, path, openTextDocument);
|
|
1448
|
-
const targetDocument = mappedTarget?.document ?? document;
|
|
1449
|
-
const targetSourceFile = mappedTarget?.sourceFile ?? sourceFile;
|
|
1450
|
-
const stateObject = mappedTarget?.node ?? getStateObjectLiteralByPath(machineConfigNode, path);
|
|
1451
|
-
if (!stateObject) return null;
|
|
1452
|
-
const replacements = [];
|
|
1453
|
-
const insertionEntries = [];
|
|
1454
|
-
for (const propertyName of propertyNames) {
|
|
1455
|
-
const rawNextValue = nextStateConfig[propertyName];
|
|
1456
|
-
const property = findObjectLiteralProperty(stateObject, propertyName);
|
|
1457
|
-
const affectedSlots = affectedV6Slots?.get(propertyName);
|
|
1458
|
-
const nextValue = rawNextValue === void 0 || !isXStateV6Source(sourceLocations) ? rawNextValue : getXStateV6PropertyValue(propertyName, rawNextValue);
|
|
1459
|
-
if (affectedSlots && (propertyName === "on" || propertyName === "after") && property && ts$1.isPropertyAssignment(property) && isMachineConfig(nextValue)) {
|
|
1460
|
-
const existingMap = unwrapObjectLiteralExpression(property.initializer);
|
|
1461
|
-
if (existingMap) {
|
|
1462
|
-
const insertedSlots = [];
|
|
1463
|
-
for (const slot of affectedSlots) {
|
|
1464
|
-
const existingSlot = findObjectLiteralProperty(existingMap, slot);
|
|
1465
|
-
const nextSlotValue = nextValue[slot];
|
|
1466
|
-
if (nextSlotValue === void 0) {
|
|
1467
|
-
if (existingSlot) replacements.push(createObjectLiteralPropertyDeletion(targetDocument, targetSourceFile, existingMap, existingSlot));
|
|
1468
|
-
} else if (existingSlot) replacements.push(createPropertyValueReplacement(targetDocument, targetSourceFile, existingSlot, nextSlotValue));
|
|
1469
|
-
else insertedSlots.push({
|
|
1470
|
-
key: slot,
|
|
1471
|
-
config: nextSlotValue
|
|
1472
|
-
});
|
|
1473
|
-
}
|
|
1474
|
-
if (insertedSlots.length > 0) replacements.push(createObjectLiteralPropertiesInsertion(targetDocument, targetSourceFile, existingMap, insertedSlots));
|
|
1475
|
-
continue;
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
if (affectedSlots && propertyName === "invoke" && property && ts$1.isPropertyAssignment(property)) {
|
|
1479
|
-
const invokeReplacements = createXStateV6InvokeSlotReplacements(targetDocument, targetSourceFile, property.initializer, nextValue, affectedSlots);
|
|
1480
|
-
if (invokeReplacements) {
|
|
1481
|
-
replacements.push(...invokeReplacements);
|
|
1482
|
-
continue;
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
if (nextValue === void 0) {
|
|
1486
|
-
if (!property) continue;
|
|
1487
|
-
replacements.push(createObjectLiteralPropertyDeletion(targetDocument, targetSourceFile, stateObject, property));
|
|
1488
|
-
continue;
|
|
1489
|
-
}
|
|
1490
|
-
if (property) {
|
|
1491
|
-
replacements.push(createPropertyValueReplacement(targetDocument, targetSourceFile, property, nextValue));
|
|
1492
|
-
continue;
|
|
1493
|
-
}
|
|
1494
|
-
insertionEntries.push({
|
|
1495
|
-
key: propertyName,
|
|
1496
|
-
config: nextValue
|
|
1497
|
-
});
|
|
1498
|
-
}
|
|
1499
|
-
if (insertionEntries.length > 0) replacements.push(createObjectLiteralPropertiesInsertion(targetDocument, targetSourceFile, stateObject, insertionEntries));
|
|
1500
|
-
return replacements.length > 0 ? replacements.sort(compareReplacementRangesDescending) : [];
|
|
1501
|
-
}
|
|
1502
|
-
function createXStateV6InvokeSlotReplacements(document, sourceFile, initializer, nextValue, affectedSlots) {
|
|
1503
|
-
const expression = unwrapExpression$1(initializer);
|
|
1504
|
-
const existingInvokes = ts$1.isArrayLiteralExpression(expression) ? expression.elements.filter(ts$1.isExpression) : [expression];
|
|
1505
|
-
const nextInvokes = Array.isArray(nextValue) ? nextValue : [nextValue];
|
|
1506
|
-
const replacements = [];
|
|
1507
|
-
const insertions = /* @__PURE__ */ new Map();
|
|
1508
|
-
for (const slotRef of affectedSlots) {
|
|
1509
|
-
const match = /^@statelyai\.invoke\.(done|error|snapshot|timeout)\.(.+)$/.exec(slotRef);
|
|
1510
|
-
if (!match) return null;
|
|
1511
|
-
const propertyName = {
|
|
1512
|
-
done: "onDone",
|
|
1513
|
-
error: "onError",
|
|
1514
|
-
snapshot: "onSnapshot",
|
|
1515
|
-
timeout: "onTimeout"
|
|
1516
|
-
}[match[1]];
|
|
1517
|
-
const invokeId = match[2];
|
|
1518
|
-
const invokeIndex = findXStateV6InvokeIndex(nextInvokes, invokeId);
|
|
1519
|
-
const existingInvoke = existingInvokes[invokeIndex];
|
|
1520
|
-
const nextInvoke = nextInvokes[invokeIndex];
|
|
1521
|
-
if (invokeIndex < 0 || !existingInvoke || !nextInvoke || !isMachineConfig(nextInvoke)) return null;
|
|
1522
|
-
const existingObject = unwrapObjectLiteralExpression(existingInvoke);
|
|
1523
|
-
if (!existingObject) return null;
|
|
1524
|
-
const existingProperty = findObjectLiteralProperty(existingObject, propertyName);
|
|
1525
|
-
const nextSlotValue = nextInvoke[propertyName];
|
|
1526
|
-
if (nextSlotValue === void 0) {
|
|
1527
|
-
if (existingProperty) replacements.push(createObjectLiteralPropertyDeletion(document, sourceFile, existingObject, existingProperty));
|
|
1528
|
-
} else if (existingProperty) replacements.push(createPropertyValueReplacement(document, sourceFile, existingProperty, nextSlotValue));
|
|
1529
|
-
else {
|
|
1530
|
-
const entries = insertions.get(existingObject) ?? [];
|
|
1531
|
-
entries.push({
|
|
1532
|
-
key: propertyName,
|
|
1533
|
-
config: nextSlotValue
|
|
1534
|
-
});
|
|
1535
|
-
insertions.set(existingObject, entries);
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
for (const [existingObject, entries] of insertions) replacements.push(createObjectLiteralPropertiesInsertion(document, sourceFile, existingObject, entries));
|
|
1539
|
-
return replacements;
|
|
1540
|
-
}
|
|
1541
|
-
function findXStateV6InvokeIndex(invokes, invokeId) {
|
|
1542
|
-
return invokes.findIndex((candidate, index) => {
|
|
1543
|
-
if (!isMachineConfig(candidate)) return false;
|
|
1544
|
-
if (candidate.id === invokeId) return true;
|
|
1545
|
-
if (candidate.id !== void 0) return false;
|
|
1546
|
-
if (invokeId === `$auto-inline-${index}`) return true;
|
|
1547
|
-
return typeof candidate.src === "string" && invokeId === `$auto-invoke-${candidate.src}`;
|
|
1548
|
-
});
|
|
1549
|
-
}
|
|
1550
|
-
function getXStateV6PropertyValue(propertyName, value) {
|
|
1551
|
-
const converted = toXStateV6MachineConfig({ [propertyName]: value });
|
|
1552
|
-
return isMachineConfig(converted) ? converted[propertyName] : value;
|
|
1553
|
-
}
|
|
1554
|
-
function getTransitionPatchTarget(patch, prevGraph, nextGraph) {
|
|
1555
|
-
const patchRecord = patch;
|
|
1556
|
-
if (patch.op === "createTransition") {
|
|
1557
|
-
const edge = getCreatedTransitionEdge(patch, prevGraph, nextGraph);
|
|
1558
|
-
if (!edge) return null;
|
|
1559
|
-
const sourcePath = resolveStatePath(nextGraph, patch.sourceId);
|
|
1560
|
-
const eventType = edge?.data?.eventType ?? edge?.label ?? patchRecord.eventType;
|
|
1561
|
-
if (!sourcePath || !eventType) return null;
|
|
1562
|
-
return {
|
|
1563
|
-
sourcePath,
|
|
1564
|
-
eventType,
|
|
1565
|
-
previousEventType: eventType,
|
|
1566
|
-
transitionId: edge.id,
|
|
1567
|
-
index: getTransitionIndexFromId(edge.id)
|
|
1568
|
-
};
|
|
1569
|
-
}
|
|
1570
|
-
const edgeId = patch.op === "updateTransition" || patch.op === "deleteTransition" ? patch.transitionId : patch.op === "setGuard" || patch.op === "deleteGuard" ? patch.edgeId : patch.op === "createAction" || patch.op === "updateAction" || patch.op === "deleteAction" ? "edgeId" in patch.location ? patch.location.edgeId : null : null;
|
|
1571
|
-
if (!edgeId) return null;
|
|
1572
|
-
const nextEdge = nextGraph.edges.find((item) => item.id === edgeId);
|
|
1573
|
-
const prevEdge = prevGraph.edges.find((item) => item.id === edgeId);
|
|
1574
|
-
const sourcePath = resolveEdgeSourcePath(nextGraph, edgeId) ?? resolveEdgeSourcePath(prevGraph, edgeId);
|
|
1575
|
-
const eventType = nextEdge?.data?.eventType ?? nextEdge?.label ?? patchRecord.eventType ?? prevEdge?.data?.eventType ?? prevEdge?.label;
|
|
1576
|
-
const previousEventType = prevEdge?.data?.eventType ?? prevEdge?.label ?? eventType;
|
|
1577
|
-
if (!sourcePath || !eventType || !previousEventType) return null;
|
|
1578
|
-
return {
|
|
1579
|
-
sourcePath,
|
|
1580
|
-
eventType,
|
|
1581
|
-
previousEventType,
|
|
1582
|
-
transitionId: edgeId,
|
|
1583
|
-
index: getTransitionIndexFromId(edgeId)
|
|
1584
|
-
};
|
|
1585
|
-
}
|
|
1586
|
-
function isXStateV6Source(sourceLocations) {
|
|
1587
|
-
return sourceLocations?.profile === "xstate-v6-alpha" || sourceLocations?.profileRef?.startsWith("@statelyai/xstate@6") === true;
|
|
1588
|
-
}
|
|
1589
|
-
function getV6TransitionPropertyTargets(patch, prevGraph, nextGraph) {
|
|
1590
|
-
const edgeId = getTransitionPatchEdgeId(patch, prevGraph, nextGraph);
|
|
1591
|
-
if (!edgeId) return [];
|
|
1592
|
-
const candidates = [{
|
|
1593
|
-
edge: prevGraph.edges.find((edge) => edge.id === edgeId),
|
|
1594
|
-
graph: prevGraph
|
|
1595
|
-
}, {
|
|
1596
|
-
edge: nextGraph.edges.find((edge) => edge.id === edgeId),
|
|
1597
|
-
graph: nextGraph
|
|
1598
|
-
}];
|
|
1599
|
-
const targets = /* @__PURE__ */ new Map();
|
|
1600
|
-
for (const { edge, graph } of candidates) {
|
|
1601
|
-
if (!edge || edge.data?.route) continue;
|
|
1602
|
-
const path = resolveStatePath(graph, edge.sourceId);
|
|
1603
|
-
if (!path) continue;
|
|
1604
|
-
const eventType = edge.data?.eventType ?? edge.label ?? "";
|
|
1605
|
-
const sourceNode = graph.nodes.find((node) => node.id === edge.sourceId);
|
|
1606
|
-
let propertyName;
|
|
1607
|
-
let slotKey = null;
|
|
1608
|
-
if (eventType === "") propertyName = sourceNode?.data.type === "choice" ? "choice" : "always";
|
|
1609
|
-
else if (eventType.startsWith("xstate.after.")) {
|
|
1610
|
-
propertyName = "after";
|
|
1611
|
-
slotKey = getDelayKeyFromEventType(eventType, edge.sourceId);
|
|
1612
|
-
} else if (eventType.startsWith("@statelyai.invoke.")) {
|
|
1613
|
-
propertyName = "invoke";
|
|
1614
|
-
slotKey = eventType;
|
|
1615
|
-
} else if (eventType.startsWith("@statelyai.state.done.")) propertyName = "onDone";
|
|
1616
|
-
else if (eventType.startsWith("@statelyai.state.error.")) propertyName = "onError";
|
|
1617
|
-
else if (eventType.startsWith("@statelyai.state.timeout.")) propertyName = "onTimeout";
|
|
1618
|
-
else {
|
|
1619
|
-
propertyName = "on";
|
|
1620
|
-
slotKey = eventType;
|
|
1621
|
-
}
|
|
1622
|
-
targets.set(`${path.join(".")}:${propertyName}:${slotKey ?? ""}`, {
|
|
1623
|
-
path,
|
|
1624
|
-
propertyName,
|
|
1625
|
-
slotKey
|
|
1626
|
-
});
|
|
1627
|
-
}
|
|
1628
|
-
return [...targets.values()];
|
|
1629
|
-
}
|
|
1630
|
-
function getCreatedTransitionEdge(patch, prevGraph, nextGraph) {
|
|
1631
|
-
const previousIds = new Set(prevGraph.edges.map((edge) => edge.id));
|
|
1632
|
-
let candidates = nextGraph.edges.filter((edge) => !previousIds.has(edge.id) && edge.sourceId === patch.sourceId);
|
|
1633
|
-
const createdWithRequestedId = patch.id ? candidates.find((edge) => edge.id === patch.id) : void 0;
|
|
1634
|
-
if (createdWithRequestedId) return createdWithRequestedId;
|
|
1635
|
-
if (patch.eventType !== void 0) candidates = candidates.filter((edge) => (edge.data?.eventType ?? edge.label) === patch.eventType);
|
|
1636
|
-
if (patch.targetId !== void 0) candidates = candidates.filter((edge) => edge.targetId === patch.targetId);
|
|
1637
|
-
if (patch.transitionType !== void 0) candidates = candidates.filter((edge) => edge.data?.transitionType === patch.transitionType);
|
|
1638
|
-
if (patch.matches !== void 0) candidates = candidates.filter((edge) => JSON.stringify(edge.data?.matches) === JSON.stringify(patch.matches));
|
|
1639
|
-
return candidates.length === 1 ? candidates[0] : null;
|
|
1640
|
-
}
|
|
1641
|
-
function getTransitionPatchEdgeId(patch, prevGraph, nextGraph) {
|
|
1642
|
-
if (patch.op === "createTransition") return getCreatedTransitionEdge(patch, prevGraph, nextGraph)?.id ?? null;
|
|
1643
|
-
if (patch.op === "updateTransition" || patch.op === "deleteTransition") return patch.transitionId;
|
|
1644
|
-
if (patch.op === "setGuard" || patch.op === "deleteGuard") return patch.edgeId;
|
|
1645
|
-
if ((patch.op === "createAction" || patch.op === "updateAction" || patch.op === "deleteAction") && "edgeId" in patch.location) return patch.location.edgeId;
|
|
1646
|
-
return null;
|
|
1647
|
-
}
|
|
1648
|
-
function getStatePropertyPatchTargets(patch, prevGraph, nextGraph) {
|
|
1649
|
-
if (patch.op === "updateSchemas") return [{
|
|
1650
|
-
path: [],
|
|
1651
|
-
propertyName: "schemas"
|
|
1652
|
-
}];
|
|
1653
|
-
if (patch.op === "updateState") {
|
|
1654
|
-
const propertyNames = getStatePropertyNamesFromUpdatePatch(patch);
|
|
1655
|
-
if (propertyNames.length === 0) return [];
|
|
1656
|
-
const path = resolveStatePath(nextGraph, patch.stateId) ?? resolveStatePath(prevGraph, patch.stateId);
|
|
1657
|
-
return path ? propertyNames.map((propertyName) => ({
|
|
1658
|
-
path,
|
|
1659
|
-
propertyName
|
|
1660
|
-
})) : [];
|
|
1661
|
-
}
|
|
1662
|
-
if (patch.op === "createAction" || patch.op === "updateAction" || patch.op === "deleteAction") {
|
|
1663
|
-
if (!("nodeId" in patch.location)) return [];
|
|
1664
|
-
const path = resolveStatePath(nextGraph, patch.location.nodeId) ?? resolveStatePath(prevGraph, patch.location.nodeId);
|
|
1665
|
-
return path ? [{
|
|
1666
|
-
path,
|
|
1667
|
-
propertyName: patch.location.group
|
|
1668
|
-
}] : [];
|
|
1669
|
-
}
|
|
1670
|
-
if (patch.op === "createInvoke" || patch.op === "updateInvoke" || patch.op === "deleteInvoke") {
|
|
1671
|
-
const path = resolveStatePath(nextGraph, patch.nodeId) ?? resolveStatePath(prevGraph, patch.nodeId);
|
|
1672
|
-
return path ? [{
|
|
1673
|
-
path,
|
|
1674
|
-
propertyName: "invoke"
|
|
1675
|
-
}] : [];
|
|
1676
|
-
}
|
|
1677
|
-
if (patch.op === "createTag" || patch.op === "deleteTag") {
|
|
1678
|
-
const path = resolveStatePath(nextGraph, patch.nodeId) ?? resolveStatePath(prevGraph, patch.nodeId);
|
|
1679
|
-
return path ? [{
|
|
1680
|
-
path,
|
|
1681
|
-
propertyName: "tags"
|
|
1682
|
-
}] : [];
|
|
1683
|
-
}
|
|
1684
|
-
return [];
|
|
1685
|
-
}
|
|
1686
|
-
function getStatePropertyNamesFromUpdatePatch(patch) {
|
|
1687
|
-
if (patch.op !== "updateState") return [];
|
|
1688
|
-
const patchRecord = patch;
|
|
1689
|
-
const propertyNames = [];
|
|
1690
|
-
if (patchRecord.stateDescription !== void 0) propertyNames.push("description");
|
|
1691
|
-
if (patchRecord.meta !== void 0) propertyNames.push("meta");
|
|
1692
|
-
if (patchRecord.type !== void 0) propertyNames.push("type");
|
|
1693
|
-
if (patchRecord.history !== void 0) propertyNames.push("history");
|
|
1694
|
-
if (patchRecord.initialId !== void 0 || patchRecord.initialInput !== void 0) propertyNames.push("initial");
|
|
1695
|
-
if (patchRecord.timeout !== void 0) propertyNames.push("timeout");
|
|
1696
|
-
if (patchRecord.route !== void 0) propertyNames.push("route");
|
|
1697
|
-
return propertyNames;
|
|
1698
|
-
}
|
|
1699
|
-
function getTransitionConfigValue(machineConfig, sourcePath, eventType, index) {
|
|
1700
|
-
const sourceConfig = getStateConfigByPath(machineConfig, sourcePath);
|
|
1701
|
-
const eventConfig = (isMachineConfig(sourceConfig?.on) ? sourceConfig.on : null)?.[eventType];
|
|
1702
|
-
if (Array.isArray(eventConfig) && index !== null) return eventConfig[index];
|
|
1703
|
-
return eventConfig;
|
|
1704
|
-
}
|
|
1705
|
-
function getTransitionSourceExpression(stateObject, eventType, index) {
|
|
1706
|
-
const onObject = getObjectLiteralProperty(stateObject, "on");
|
|
1707
|
-
if (!onObject) return null;
|
|
1708
|
-
const eventProperty = findObjectLiteralProperty(onObject, eventType);
|
|
1709
|
-
if (!eventProperty) return null;
|
|
1710
|
-
if (!ts$1.isPropertyAssignment(eventProperty)) return {
|
|
1711
|
-
onObject,
|
|
1712
|
-
eventProperty
|
|
1713
|
-
};
|
|
1714
|
-
const initializer = unwrapExpression$1(eventProperty.initializer);
|
|
1715
|
-
if (ts$1.isArrayLiteralExpression(initializer) && index !== null) {
|
|
1716
|
-
const element = initializer.elements[index];
|
|
1717
|
-
if (element && ts$1.isExpression(element)) return {
|
|
1718
|
-
onObject,
|
|
1719
|
-
eventProperty,
|
|
1720
|
-
array: initializer,
|
|
1721
|
-
element
|
|
1722
|
-
};
|
|
1723
|
-
}
|
|
1724
|
-
return {
|
|
1725
|
-
onObject,
|
|
1726
|
-
eventProperty
|
|
1727
|
-
};
|
|
1728
|
-
}
|
|
1729
|
-
function getTransitionIndexFromId(id) {
|
|
1730
|
-
const match = id.match(/\[(\d+)\](?:$|#)/);
|
|
1731
|
-
return match ? Number(match[1]) : null;
|
|
1732
|
-
}
|
|
1733
|
-
function createArrayElementDeletion(document, sourceFile, arrayLiteral, element) {
|
|
1734
|
-
const range = getArrayElementDeletionRange(sourceFile, arrayLiteral, element);
|
|
1735
|
-
return createOffsetReplacement(document, range.start, range.end, "");
|
|
1736
|
-
}
|
|
1737
|
-
function createExpressionReplacement(document, sourceFile, expression, nextValue) {
|
|
1738
|
-
const formatting = getFormattingContextForExpression(document, sourceFile, expression);
|
|
1739
|
-
const printed = containsRawCodeLike(nextValue) ? reindentPrintedExpression(serializeJS(nextValue), formatting) : printExpression(valueToExpression(nextValue, unwrapExpression$1(expression)), sourceFile, formatting);
|
|
1740
|
-
return createOffsetReplacement(document, expression.getStart(sourceFile), expression.getEnd(), preserveExpressionComments(expression, sourceFile, printed, formatting));
|
|
1741
|
-
}
|
|
1742
|
-
function preserveExpressionComments(expression, sourceFile, printed, formatting) {
|
|
1743
|
-
const original = sourceFile.text.slice(expression.getStart(sourceFile), expression.getEnd());
|
|
1744
|
-
const scanner = ts$1.createScanner(ts$1.ScriptTarget.Latest, false, ts$1.LanguageVariant.JSX, original);
|
|
1745
|
-
const comments = [];
|
|
1746
|
-
for (let token = scanner.scan(); token !== ts$1.SyntaxKind.EndOfFileToken; token = scanner.scan()) if (token === ts$1.SyntaxKind.SingleLineCommentTrivia || token === ts$1.SyntaxKind.MultiLineCommentTrivia) comments.push(scanner.getTokenText());
|
|
1747
|
-
const missing = comments.filter((comment) => !printed.includes(comment));
|
|
1748
|
-
if (missing.length === 0) return printed;
|
|
1749
|
-
const lineSeparator = formatting.newLineKind === ts$1.NewLineKind.CarriageReturnLineFeed ? "\r\n" : "\n";
|
|
1750
|
-
const bodyStart = printed.indexOf("=> {");
|
|
1751
|
-
if (bodyStart >= 0) {
|
|
1752
|
-
const insertion = bodyStart + 4;
|
|
1753
|
-
const indent = `${formatting.baseIndent}${formatting.indentUnit}`;
|
|
1754
|
-
return `${printed.slice(0, insertion)}${lineSeparator}${missing.map((comment) => `${indent}${comment}`).join(lineSeparator)}${printed.slice(insertion)}`;
|
|
1755
|
-
}
|
|
1756
|
-
return `${missing.join(lineSeparator)}${lineSeparator}${formatting.baseIndent}${printed}`;
|
|
1757
|
-
}
|
|
1758
|
-
function createPropertyValueReplacement(document, sourceFile, property, nextValue) {
|
|
1759
|
-
if (!ts$1.isPropertyAssignment(property)) return createPropertyAssignmentReplacement(document, sourceFile, property, getStatePropertyKeyText(property) ?? "value", nextValue);
|
|
1760
|
-
return createExpressionReplacement(document, sourceFile, property.initializer, nextValue);
|
|
1761
|
-
}
|
|
1762
|
-
function createPropertyAssignmentReplacement(document, sourceFile, property, key, nextValue) {
|
|
1763
|
-
const text = printObjectLiteralProperty(key, nextValue, sourceFile, getFormattingContextForExpression(document, sourceFile, property));
|
|
1764
|
-
return createOffsetReplacement(document, property.getStart(sourceFile), property.getEnd(), text);
|
|
1765
|
-
}
|
|
1766
|
-
function createObjectLiteralPropertyInsertion(document, sourceFile, objectLiteral, key, nextValue) {
|
|
1767
|
-
const propertyText = objectPropertyTextForInsertion(objectLiteral, printObjectLiteralProperty(key, nextValue, sourceFile, getFormattingContext(document, sourceFile, objectLiteral)));
|
|
1768
|
-
return createObjectPropertyInsertionReplacements(document, objectLiteral, getObjectLiteralPropertyInsertionOffset(sourceFile, objectLiteral), propertyText);
|
|
1769
|
-
}
|
|
1770
|
-
function getObjectLiteralPropertyInsertionOffset(sourceFile, objectLiteral) {
|
|
1771
|
-
const propertiesEnd = objectLiteral.properties.end;
|
|
1772
|
-
const objectEnd = objectLiteral.getEnd() - 1;
|
|
1773
|
-
const trailingTrivia = sourceFile.text.slice(propertiesEnd, objectEnd);
|
|
1774
|
-
const commentMatch = /^[ \t]*(\/\/|\/\*)/.exec(trailingTrivia);
|
|
1775
|
-
if (!commentMatch) return propertiesEnd;
|
|
1776
|
-
const commentStart = propertiesEnd + commentMatch.index + commentMatch[0].length - 2;
|
|
1777
|
-
if (commentMatch[1] === "//") {
|
|
1778
|
-
const lineFeed = sourceFile.text.indexOf("\n", commentStart);
|
|
1779
|
-
const lineEnd = lineFeed === -1 ? objectEnd : Math.min(lineFeed, objectEnd);
|
|
1780
|
-
return lineEnd > commentStart && sourceFile.text.charCodeAt(lineEnd - 1) === 13 ? lineEnd - 1 : lineEnd;
|
|
1781
|
-
}
|
|
1782
|
-
const commentEnd = sourceFile.text.indexOf("*/", commentStart + 2);
|
|
1783
|
-
return commentEnd === -1 ? propertiesEnd : Math.min(commentEnd + 2, objectEnd);
|
|
1784
|
-
}
|
|
1785
|
-
function createObjectPropertyInsertionReplacements(document, objectLiteral, insertionOffset, propertyText) {
|
|
1786
|
-
const propertiesEnd = objectLiteral.properties.end;
|
|
1787
|
-
const needsComma = objectLiteral.properties.length > 0 && !objectLiteral.properties.hasTrailingComma;
|
|
1788
|
-
const replacements = [createOffsetReplacement(document, insertionOffset, insertionOffset, `${needsComma && insertionOffset === propertiesEnd ? "," : ""}${propertyText}`)];
|
|
1789
|
-
if (needsComma && insertionOffset !== propertiesEnd) replacements.push(createOffsetReplacement(document, propertiesEnd, propertiesEnd, ","));
|
|
1790
|
-
return replacements;
|
|
1791
|
-
}
|
|
1792
|
-
function getArrayElementDeletionRange(sourceFile, arrayLiteral, element) {
|
|
1793
|
-
const elements = [...arrayLiteral.elements];
|
|
1794
|
-
const elementIndex = elements.indexOf(element);
|
|
1795
|
-
const elementStart = element.getFullStart();
|
|
1796
|
-
const elementEnd = element.getEnd();
|
|
1797
|
-
if (elements.length === 1) return {
|
|
1798
|
-
start: elementStart,
|
|
1799
|
-
end: elementEnd
|
|
1800
|
-
};
|
|
1801
|
-
if (elementIndex === elements.length - 1) {
|
|
1802
|
-
const previousElement = elements[elementIndex - 1];
|
|
1803
|
-
const commaOffset = sourceFile.text.lastIndexOf(",", elementStart - 1);
|
|
1804
|
-
return {
|
|
1805
|
-
start: commaOffset >= previousElement.getEnd() && commaOffset < elementStart ? commaOffset : elementStart,
|
|
1806
|
-
end: elementEnd
|
|
1807
|
-
};
|
|
1808
|
-
}
|
|
1809
|
-
const nextElement = elements[elementIndex + 1];
|
|
1810
|
-
const commaOffset = sourceFile.text.indexOf(",", elementEnd);
|
|
1811
|
-
return {
|
|
1812
|
-
start: elementStart,
|
|
1813
|
-
end: commaOffset >= elementEnd && commaOffset < nextElement.getFullStart() ? commaOffset + 1 : elementEnd
|
|
1814
|
-
};
|
|
1815
|
-
}
|
|
1816
|
-
function tryCreateImplementationPatchSplit(document, sourceFile, patches, nextGraph, machineIndex = 0) {
|
|
1817
|
-
const replacements = [];
|
|
1818
|
-
const remainingPatches = [];
|
|
1819
|
-
for (const patch of patches) {
|
|
1820
|
-
if (!isImplementationPatch(patch)) {
|
|
1821
|
-
remainingPatches.push(patch);
|
|
1822
|
-
continue;
|
|
1823
|
-
}
|
|
1824
|
-
const patchReplacements = createImplementationPatchReplacements(document, sourceFile, patch, nextGraph, machineIndex);
|
|
1825
|
-
if (!patchReplacements) {
|
|
1826
|
-
remainingPatches.push(patch);
|
|
1827
|
-
continue;
|
|
1828
|
-
}
|
|
1829
|
-
replacements.push(...patchReplacements);
|
|
1830
|
-
}
|
|
1831
|
-
return replacements.length > 0 ? {
|
|
1832
|
-
replacements: replacements.sort(compareReplacementRangesDescending),
|
|
1833
|
-
remainingPatches
|
|
1834
|
-
} : null;
|
|
1835
|
-
}
|
|
1836
|
-
function isImplementationPatch(patch) {
|
|
1837
|
-
return patch.op === "createImplementation" || patch.op === "updateImplementation" || patch.op === "deleteImplementation";
|
|
1838
|
-
}
|
|
1839
|
-
function createImplementationPatchReplacements(document, sourceFile, patch, nextGraph, machineIndex) {
|
|
1840
|
-
const createMachineCall = findCreateMachineCall(sourceFile, machineIndex);
|
|
1841
|
-
if (!createMachineCall) return null;
|
|
1842
|
-
const implementationRoot = findImplementationObjectLiteralForMachine(sourceFile, createMachineCall);
|
|
1843
|
-
const categoryName = implementationSourceTypeToCategoryName(patch.sourceType);
|
|
1844
|
-
const categoryObject = implementationRoot ? getObjectLiteralProperty(implementationRoot, categoryName) : null;
|
|
1845
|
-
if (patch.op === "createImplementation") {
|
|
1846
|
-
const implementation = getGraphImplementation(nextGraph, patch.sourceType, patch.implementation.id) ?? patch.implementation;
|
|
1847
|
-
if (!implementationRoot) return createMachineCall.arguments.length < 2 && !isObjectOfProvideCall(createMachineCall) ? createProvideCreateReplacements(document, sourceFile, createMachineCall, categoryName, implementation) : null;
|
|
1848
|
-
return createImplementationCreateReplacements(document, sourceFile, implementationRoot, categoryName, implementation);
|
|
1849
|
-
}
|
|
1850
|
-
if (!implementationRoot || !categoryObject) return null;
|
|
1851
|
-
const implementationProperty = findObjectLiteralProperty(categoryObject, patch.implementationId);
|
|
1852
|
-
if (!implementationProperty) return null;
|
|
1853
|
-
if (patch.op === "deleteImplementation") {
|
|
1854
|
-
const deletionRange = getObjectLiteralPropertyDeletionRange(sourceFile, categoryObject, implementationProperty);
|
|
1855
|
-
return [createOffsetReplacement(document, deletionRange.start, deletionRange.end, "")];
|
|
1856
|
-
}
|
|
1857
|
-
const nextImplementation = getGraphImplementation(nextGraph, patch.sourceType, patch.implementationId);
|
|
1858
|
-
const nextName = patch.data.name ?? nextImplementation?.name;
|
|
1859
|
-
const nextCode = patch.data.code ?? nextImplementation?.code?.body;
|
|
1860
|
-
const replacements = [];
|
|
1861
|
-
if (nextName && nextName !== patch.implementationId) {
|
|
1862
|
-
const nameNode = getObjectLiteralElementName(implementationProperty);
|
|
1863
|
-
if (!nameNode) return null;
|
|
1864
|
-
replacements.push(createOffsetReplacement(document, nameNode.getStart(sourceFile), nameNode.getEnd(), propertyNameTextForSource(nextName)));
|
|
1865
|
-
}
|
|
1866
|
-
if (nextCode !== void 0) {
|
|
1867
|
-
if (!ts$1.isPropertyAssignment(implementationProperty)) return null;
|
|
1868
|
-
replacements.push(createOffsetReplacement(document, implementationProperty.initializer.getStart(sourceFile), implementationProperty.initializer.getEnd(), nextCode));
|
|
1869
|
-
}
|
|
1870
|
-
return replacements.length > 0 ? replacements : null;
|
|
1871
|
-
}
|
|
1872
|
-
function createProvideCreateReplacements(document, sourceFile, createMachineCall, categoryName, implementation) {
|
|
1873
|
-
const implementationCode = implementation.code?.body;
|
|
1874
|
-
if (!implementation.id || !implementationCode) return null;
|
|
1875
|
-
const lineBreak = getLineBreak(sourceFile);
|
|
1876
|
-
const baseIndent = getLineIndent(sourceFile.text, createMachineCall.getStart(sourceFile));
|
|
1877
|
-
const propertyIndent = `${baseIndent} `;
|
|
1878
|
-
const nestedIndent = `${baseIndent} `;
|
|
1879
|
-
const text = `.provide({${lineBreak}${propertyIndent}${propertyNameTextForSource(categoryName)}: {${lineBreak}${nestedIndent}${propertyNameTextForSource(implementation.id)}: ${implementationCode},${lineBreak}${propertyIndent}},${lineBreak}${baseIndent}})`;
|
|
1880
|
-
return [createOffsetReplacement(document, createMachineCall.getEnd(), createMachineCall.getEnd(), text)];
|
|
1881
|
-
}
|
|
1882
|
-
function createImplementationCreateReplacements(document, sourceFile, implementationRoot, categoryName, implementation) {
|
|
1883
|
-
const implementationCode = implementation.code?.body;
|
|
1884
|
-
if (!implementation.id || !implementationCode) return null;
|
|
1885
|
-
const categoryObject = getObjectLiteralProperty(implementationRoot, categoryName);
|
|
1886
|
-
if (!categoryObject) {
|
|
1887
|
-
const propertyText = objectPropertyTextForInsertion(implementationRoot, `${propertyNameTextForSource(categoryName)}: {\n${getIndentForNewObjectProperty(sourceFile, implementationRoot)}${propertyNameTextForSource(implementation.id)}: ${implementationCode},\n${getIndentForObjectClose(sourceFile, implementationRoot)}}`);
|
|
1888
|
-
return createObjectPropertyInsertionReplacements(document, implementationRoot, getObjectLiteralPropertyInsertionOffset(sourceFile, implementationRoot), propertyText);
|
|
1889
|
-
}
|
|
1890
|
-
const propertyText = objectPropertyTextForInsertion(categoryObject, `${propertyNameTextForSource(implementation.id)}: ${implementationCode}`);
|
|
1891
|
-
return createObjectPropertyInsertionReplacements(document, categoryObject, getObjectLiteralPropertyInsertionOffset(sourceFile, categoryObject), propertyText);
|
|
1892
|
-
}
|
|
1893
|
-
function objectPropertyTextForInsertion(objectLiteral, propertyText) {
|
|
1894
|
-
const lineBreak = getLineBreak(objectLiteral.getSourceFile());
|
|
1895
|
-
const indent = getIndentForNewObjectProperty(objectLiteral.getSourceFile(), objectLiteral);
|
|
1896
|
-
return `${lineBreak}${indent}${propertyText.startsWith(indent) ? propertyText.slice(indent.length) : propertyText}`;
|
|
1897
|
-
}
|
|
1898
|
-
function getLineBreak(sourceFile) {
|
|
1899
|
-
return sourceFile.text.includes("\r\n") ? "\r\n" : "\n";
|
|
1900
|
-
}
|
|
1901
|
-
function getLineIndent(sourceText, offset) {
|
|
1902
|
-
const lineStart = getLineStartOffset(sourceText, offset);
|
|
1903
|
-
return sourceText.slice(lineStart, offset).match(/^[ \t]*/)?.[0] ?? "";
|
|
1904
|
-
}
|
|
1905
|
-
function getIndentForNewObjectProperty(sourceFile, objectLiteral) {
|
|
1906
|
-
const closeIndent = getIndentForObjectClose(sourceFile, objectLiteral);
|
|
1907
|
-
const existingProperty = objectLiteral.properties[0];
|
|
1908
|
-
if (existingProperty) {
|
|
1909
|
-
const propertyLineStart = getLineStartOffset(sourceFile.text, existingProperty.getStart(sourceFile));
|
|
1910
|
-
return sourceFile.text.slice(propertyLineStart, existingProperty.getStart(sourceFile)).match(/^[ \t]*/)?.[0] ?? `${closeIndent} `;
|
|
1911
|
-
}
|
|
1912
|
-
return `${closeIndent} `;
|
|
1913
|
-
}
|
|
1914
|
-
function getIndentForObjectClose(sourceFile, objectLiteral) {
|
|
1915
|
-
const objectStart = objectLiteral.getStart(sourceFile);
|
|
1916
|
-
const lineStart = getLineStartOffset(sourceFile.text, objectStart);
|
|
1917
|
-
return sourceFile.text.slice(lineStart, objectStart).match(/^[ \t]*/)?.[0] ?? "";
|
|
1918
|
-
}
|
|
1919
|
-
function createOffsetReplacement(document, startOffset, endOffset, newText) {
|
|
1920
|
-
const start = document.positionAt(startOffset);
|
|
1921
|
-
const end = document.positionAt(endOffset);
|
|
1922
|
-
return {
|
|
1923
|
-
uri: getDocumentUri(document),
|
|
1924
|
-
range: {
|
|
1925
|
-
startLine: start.line,
|
|
1926
|
-
startChar: start.character,
|
|
1927
|
-
endLine: end.line,
|
|
1928
|
-
endChar: end.character
|
|
1929
|
-
},
|
|
1930
|
-
newText
|
|
1931
|
-
};
|
|
1932
|
-
}
|
|
1933
|
-
function propertyNameTextForSource(name) {
|
|
1934
|
-
return isIdentifierText(name) ? name : JSON.stringify(name);
|
|
1935
|
-
}
|
|
1936
|
-
function getGraphImplementation(graph, sourceType, implementationId) {
|
|
1937
|
-
if (!implementationId) return;
|
|
1938
|
-
return graph.data?.implementations?.[implementationSourceTypeToCategoryName(sourceType)]?.find((implementation) => implementation.id === implementationId);
|
|
1939
|
-
}
|
|
1940
|
-
function implementationSourceTypeToCategoryName(sourceType) {
|
|
1941
|
-
switch (sourceType) {
|
|
1942
|
-
case "action": return "actions";
|
|
1943
|
-
case "guard": return "guards";
|
|
1944
|
-
case "actor": return "actors";
|
|
1945
|
-
case "delay": return "delays";
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
function tryCreateStateMembershipPatchSplit(document, sourceFile, machineConfigNode, machineConfig, patches, prevGraph, nextGraph, sourceLocations, allowDisjointDescendantReplacements = false) {
|
|
1949
|
-
const split = splitStateMembershipPatches(patches, prevGraph, nextGraph, sourceLocations, allowDisjointDescendantReplacements);
|
|
1950
|
-
if (!split) return null;
|
|
1951
|
-
const replacements = createStateMembershipReplacements(document, sourceFile, machineConfigNode, machineConfig, split.changes, sourceLocations);
|
|
1952
|
-
if (!replacements) return null;
|
|
1953
|
-
return {
|
|
1954
|
-
replacements,
|
|
1955
|
-
remainingPatches: split.remainingPatches
|
|
1956
|
-
};
|
|
1957
|
-
}
|
|
1958
|
-
async function tryCreateStateMembershipPatchSplitForDocuments(document, sourceFile, machineConfigNode, machineConfig, patches, prevGraph, nextGraph, sourceLocations, openTextDocument) {
|
|
1959
|
-
const split = splitStateMembershipPatches(patches, prevGraph, nextGraph, sourceLocations, true);
|
|
1960
|
-
if (!split) return null;
|
|
1961
|
-
const replacements = await createStateMembershipReplacementsForDocuments(document, sourceFile, machineConfigNode, machineConfig, split.changes, sourceLocations, openTextDocument);
|
|
1962
|
-
if (!replacements) return null;
|
|
1963
|
-
return {
|
|
1964
|
-
replacements,
|
|
1965
|
-
remainingPatches: split.remainingPatches
|
|
1966
|
-
};
|
|
1967
|
-
}
|
|
1968
|
-
function splitStateMembershipPatches(patches, prevGraph, nextGraph, sourceLocations, allowDisjointDescendantReplacements) {
|
|
1969
|
-
const createdById = /* @__PURE__ */ new Map();
|
|
1970
|
-
const changesByParent = /* @__PURE__ */ new Map();
|
|
1971
|
-
const remainingPatches = [];
|
|
1972
|
-
const getChange = (parentPath) => {
|
|
1973
|
-
const key = parentPath.join(".");
|
|
1974
|
-
const existing = changesByParent.get(key);
|
|
1975
|
-
if (existing) return existing;
|
|
1976
|
-
const change = {
|
|
1977
|
-
parentPath,
|
|
1978
|
-
createdPaths: [],
|
|
1979
|
-
deletedPaths: [],
|
|
1980
|
-
patches: []
|
|
1981
|
-
};
|
|
1982
|
-
changesByParent.set(key, change);
|
|
1983
|
-
return change;
|
|
1984
|
-
};
|
|
1985
|
-
for (const patch of patches) switch (patch.op) {
|
|
1986
|
-
case "createState": {
|
|
1987
|
-
if (!patch.id) {
|
|
1988
|
-
remainingPatches.push(patch);
|
|
1989
|
-
break;
|
|
1990
|
-
}
|
|
1991
|
-
const parentPath = resolveStatePath(nextGraph, patch.parentId);
|
|
1992
|
-
const nextPath = resolveStatePath(nextGraph, patch.id);
|
|
1993
|
-
if (!parentPath || !nextPath || nextPath.length === 0) {
|
|
1994
|
-
remainingPatches.push(patch);
|
|
1995
|
-
break;
|
|
1996
|
-
}
|
|
1997
|
-
const change = getChange(parentPath);
|
|
1998
|
-
createdById.set(patch.id, {
|
|
1999
|
-
parentPath,
|
|
2000
|
-
path: nextPath
|
|
2001
|
-
});
|
|
2002
|
-
change.createdPaths.push(nextPath);
|
|
2003
|
-
change.patches.push(patch);
|
|
2004
|
-
break;
|
|
2005
|
-
}
|
|
2006
|
-
case "updateState": {
|
|
2007
|
-
const created = createdById.get(patch.stateId);
|
|
2008
|
-
const nextPath = resolveStatePath(nextGraph, patch.stateId);
|
|
2009
|
-
if (created && nextPath && arePathsEqual(created.path, nextPath)) {
|
|
2010
|
-
getChange(created.parentPath).patches.push(patch);
|
|
2011
|
-
break;
|
|
2012
|
-
}
|
|
2013
|
-
remainingPatches.push(patch);
|
|
2014
|
-
break;
|
|
2015
|
-
}
|
|
2016
|
-
case "deleteState": {
|
|
2017
|
-
const prevPath = resolveStatePath(prevGraph, patch.stateId);
|
|
2018
|
-
if (!prevPath || prevPath.length === 0) {
|
|
2019
|
-
remainingPatches.push(patch);
|
|
2020
|
-
break;
|
|
2021
|
-
}
|
|
2022
|
-
const change = getChange(prevPath.slice(0, -1));
|
|
2023
|
-
change.deletedPaths.push(prevPath);
|
|
2024
|
-
change.patches.push(patch);
|
|
2025
|
-
break;
|
|
2026
|
-
}
|
|
2027
|
-
default: remainingPatches.push(patch);
|
|
2028
|
-
}
|
|
2029
|
-
const deletedPaths = [...changesByParent.values()].flatMap((change) => change.deletedPaths);
|
|
2030
|
-
const createdPaths = [...changesByParent.values()].flatMap((change) => change.createdPaths);
|
|
2031
|
-
const remainingPatchesWithSurvivingOwners = remainingPatches.filter((patch) => {
|
|
2032
|
-
const owner = resolvePatchOwner(patch, prevGraph, nextGraph);
|
|
2033
|
-
return owner.type !== "covered" && !isPatchOwnerCoveredByPaths(owner, deletedPaths) && !isPatchOwnerCoveredByPaths(owner, createdPaths);
|
|
2034
|
-
});
|
|
2035
|
-
const split = coalesceMembershipChangesWithRemainingPatches([...changesByParent.values()].filter((change) => !createdPaths.some((path) => isPathEqualOrDescendant(change.parentPath, path))), remainingPatchesWithSurvivingOwners, prevGraph, nextGraph, sourceLocations, allowDisjointDescendantReplacements);
|
|
2036
|
-
return split.changes.length > 0 ? split : null;
|
|
2037
|
-
}
|
|
2038
|
-
function isPatchOwnerCoveredByPaths(owner, paths) {
|
|
2039
|
-
if (owner.type !== "state" || paths.length === 0) return false;
|
|
2040
|
-
return paths.some((path) => isPathEqualOrDescendant(owner.path, path));
|
|
2041
|
-
}
|
|
2042
|
-
function coalesceMembershipChangesWithRemainingPatches(changes, remainingPatches, prevGraph, nextGraph, sourceLocations, allowDisjointDescendantReplacements) {
|
|
2043
|
-
if (remainingPatches.length === 0) return {
|
|
2044
|
-
changes,
|
|
2045
|
-
remainingPatches
|
|
2046
|
-
};
|
|
2047
|
-
const remainingPlan = deriveReplacementPlan(remainingPatches, prevGraph, nextGraph);
|
|
2048
|
-
const activeChanges = [];
|
|
2049
|
-
const activeRemainingPatches = [...remainingPatches];
|
|
2050
|
-
for (const change of changes) {
|
|
2051
|
-
if (shouldCoalesceMembershipChangeWithRemainingPlan(change, remainingPlan, sourceLocations, allowDisjointDescendantReplacements)) {
|
|
2052
|
-
activeRemainingPatches.push(...change.patches);
|
|
2053
|
-
continue;
|
|
2054
|
-
}
|
|
2055
|
-
activeChanges.push(change);
|
|
2056
|
-
}
|
|
2057
|
-
return {
|
|
2058
|
-
changes: activeChanges,
|
|
2059
|
-
remainingPatches: activeRemainingPatches
|
|
2060
|
-
};
|
|
2061
|
-
}
|
|
2062
|
-
function shouldCoalesceMembershipChangeWithRemainingPlan(change, remainingPlan, sourceLocations, allowDisjointDescendantReplacements) {
|
|
2063
|
-
if (change.parentPath.length === 0 && wouldDeModularizeRootReplacement(sourceLocations)) return false;
|
|
2064
|
-
if (remainingPlan.type === "root") return true;
|
|
2065
|
-
return remainingPlan.paths.some((path) => isPathEqualOrDescendant(change.parentPath, path) || !allowDisjointDescendantReplacements && isPathEqualOrDescendant(path, change.parentPath));
|
|
2066
|
-
}
|
|
2067
|
-
function isPathEqualOrDescendant(path, candidateParentPath) {
|
|
2068
|
-
return candidateParentPath.every((segment, index) => path[index] === segment);
|
|
2069
|
-
}
|
|
2070
|
-
function createStateMembershipReplacements(document, sourceFile, machineConfigNode, machineConfig, changes, sourceLocations) {
|
|
2071
|
-
const replacements = [];
|
|
2072
|
-
for (const change of changes) {
|
|
2073
|
-
const target = getStateMembershipParentTarget(document, sourceFile, machineConfigNode, sourceLocations, change.parentPath, getStateObjectLiteralByPath);
|
|
2074
|
-
if (!target) return null;
|
|
2075
|
-
const changeReplacements = createStateMembershipReplacementsForTarget(target.document, target.sourceFile, target.node, machineConfig, change, target.nodeKind);
|
|
2076
|
-
if (!changeReplacements) return null;
|
|
2077
|
-
replacements.push(...changeReplacements);
|
|
2078
|
-
replacements.push(...createStateMembershipInitialReplacements(change.parentPath.length === 0 ? document : target.document, change.parentPath.length === 0 ? sourceFile : target.sourceFile, change.parentPath.length === 0 ? machineConfigNode : target.node, machineConfig, change));
|
|
2079
|
-
}
|
|
2080
|
-
return replacements.sort(compareReplacementRangesDescending);
|
|
2081
|
-
}
|
|
2082
|
-
async function createStateMembershipReplacementsForDocuments(document, sourceFile, machineConfigNode, machineConfig, changes, sourceLocations, openTextDocument) {
|
|
2083
|
-
const replacements = [];
|
|
2084
|
-
for (const change of changes) {
|
|
2085
|
-
const target = await getStateMembershipParentTargetForDocuments(document, sourceFile, machineConfigNode, sourceLocations, change.parentPath, openTextDocument, getStateObjectLiteralByPath);
|
|
2086
|
-
if (!target) return null;
|
|
2087
|
-
const changeReplacements = createStateMembershipReplacementsForTarget(target.document, target.sourceFile, target.node, machineConfig, change, target.nodeKind);
|
|
2088
|
-
if (!changeReplacements) return null;
|
|
2089
|
-
replacements.push(...changeReplacements);
|
|
2090
|
-
replacements.push(...createStateMembershipInitialReplacements(change.parentPath.length === 0 ? document : target.document, change.parentPath.length === 0 ? sourceFile : target.sourceFile, change.parentPath.length === 0 ? machineConfigNode : target.node, machineConfig, change));
|
|
2091
|
-
}
|
|
2092
|
-
return replacements.sort(compareReplacementRangesDescending);
|
|
2093
|
-
}
|
|
2094
|
-
function createStateMembershipReplacementsForTarget(document, sourceFile, parentObject, machineConfig, change, nodeKind = "machineConfig") {
|
|
2095
|
-
const statesObject = nodeKind === "statesObject" ? parentObject : getObjectLiteralProperty(parentObject, "states");
|
|
2096
|
-
if (!statesObject) return null;
|
|
2097
|
-
const stateEntries = [];
|
|
2098
|
-
for (const path of change.createdPaths) {
|
|
2099
|
-
const key = path[path.length - 1];
|
|
2100
|
-
const stateConfig = getStateConfigByPath(machineConfig, path);
|
|
2101
|
-
if (!key || !isMachineConfig(stateConfig)) return null;
|
|
2102
|
-
stateEntries.push({
|
|
2103
|
-
key,
|
|
2104
|
-
config: stateConfig
|
|
2105
|
-
});
|
|
2106
|
-
}
|
|
2107
|
-
const deletionReplacements = [];
|
|
2108
|
-
for (const path of change.deletedPaths) {
|
|
2109
|
-
const key = path[path.length - 1];
|
|
2110
|
-
const property = key ? findObjectLiteralProperty(statesObject, key) : null;
|
|
2111
|
-
if (!property) return null;
|
|
2112
|
-
deletionReplacements.push(createObjectLiteralPropertyDeletion(document, sourceFile, statesObject, property));
|
|
2113
|
-
}
|
|
2114
|
-
return [...stateEntries.length > 0 ? [createObjectLiteralPropertiesInsertion(document, sourceFile, statesObject, stateEntries)] : [], ...deletionReplacements].sort(compareReplacementRangesDescending);
|
|
2115
|
-
}
|
|
2116
|
-
function createStateMembershipInitialReplacements(document, sourceFile, parentObject, machineConfig, change) {
|
|
2117
|
-
if (!change.patches.some((patch) => patch.op === "createState" && patch.initial === true || patch.op === "deleteState")) return [];
|
|
2118
|
-
const parentConfig = getStateConfigByPath(machineConfig, change.parentPath);
|
|
2119
|
-
if (!parentConfig) return [];
|
|
2120
|
-
const nextInitial = parentConfig.initial;
|
|
2121
|
-
const initialProperty = getPropertyAssignment(parentObject, "initial");
|
|
2122
|
-
if (nextInitial === void 0) return initialProperty ? [createObjectLiteralPropertyDeletion(document, sourceFile, parentObject, initialProperty)] : [];
|
|
2123
|
-
if (isMachineConfig(nextInitial)) {
|
|
2124
|
-
if (!initialProperty) return [createObjectLiteralPropertiesInsertion(document, sourceFile, parentObject, [{
|
|
2125
|
-
key: "initial",
|
|
2126
|
-
config: nextInitial
|
|
2127
|
-
}])];
|
|
2128
|
-
const initializer = unwrapExpression$1(initialProperty.initializer);
|
|
2129
|
-
const targetProperty = ts$1.isObjectLiteralExpression(initializer) ? getPropertyAssignment(initializer, "target") : null;
|
|
2130
|
-
if (targetProperty && typeof nextInitial.target === "string") {
|
|
2131
|
-
const targetInitializer = unwrapExpression$1(targetProperty.initializer);
|
|
2132
|
-
if (ts$1.isStringLiteralLike(targetInitializer) && targetInitializer.text === nextInitial.target) return [];
|
|
2133
|
-
return [createPropertyValueReplacement(document, sourceFile, targetProperty, nextInitial.target)];
|
|
2134
|
-
}
|
|
2135
|
-
return [createPropertyValueReplacement(document, sourceFile, initialProperty, nextInitial)];
|
|
2136
|
-
}
|
|
2137
|
-
if (typeof nextInitial !== "string") return [];
|
|
2138
|
-
if (initialProperty) {
|
|
2139
|
-
const initializer = unwrapExpression$1(initialProperty.initializer);
|
|
2140
|
-
if (ts$1.isStringLiteralLike(initializer) && initializer.text === nextInitial) return [];
|
|
2141
|
-
return [createPropertyValueReplacement(document, sourceFile, initialProperty, nextInitial)];
|
|
2142
|
-
}
|
|
2143
|
-
return [createObjectLiteralPropertiesInsertion(document, sourceFile, parentObject, [{
|
|
2144
|
-
key: "initial",
|
|
2145
|
-
config: nextInitial
|
|
2146
|
-
}])];
|
|
2147
|
-
}
|
|
2148
|
-
function createObjectLiteralPropertiesInsertion(document, sourceFile, objectLiteral, entries) {
|
|
2149
|
-
const formatting = getFormattingContext(document, sourceFile, objectLiteral);
|
|
2150
|
-
const lineSeparator = formatting.newLineKind === ts$1.NewLineKind.CarriageReturnLineFeed ? "\r\n" : "\n";
|
|
2151
|
-
const propertyTexts = entries.map((entry) => printObjectLiteralProperty(entry.key, entry.config, sourceFile, formatting));
|
|
2152
|
-
const existingProperties = objectLiteral.properties;
|
|
2153
|
-
const closeBraceOffset = objectLiteral.getEnd() - 1;
|
|
2154
|
-
const startOffset = existingProperties.length === 0 ? closeBraceOffset : existingProperties[existingProperties.length - 1].getEnd();
|
|
2155
|
-
const start = document.positionAt(startOffset);
|
|
2156
|
-
const end = document.positionAt(closeBraceOffset);
|
|
2157
|
-
const prefix = existingProperties.length === 0 ? lineSeparator : `,${lineSeparator}`;
|
|
2158
|
-
const suffix = `${lineSeparator}${formatting.baseIndent}`;
|
|
2159
|
-
return {
|
|
2160
|
-
uri: getDocumentUri(document),
|
|
2161
|
-
range: {
|
|
2162
|
-
startLine: start.line,
|
|
2163
|
-
startChar: start.character,
|
|
2164
|
-
endLine: end.line,
|
|
2165
|
-
endChar: end.character
|
|
2166
|
-
},
|
|
2167
|
-
newText: `${prefix}${propertyTexts.join(`,${lineSeparator}`)}${suffix}`
|
|
2168
|
-
};
|
|
2169
|
-
}
|
|
2170
|
-
function createObjectLiteralPropertyDeletion(document, sourceFile, objectLiteral, property) {
|
|
2171
|
-
const range = getObjectLiteralPropertyDeletionRange(sourceFile, objectLiteral, property);
|
|
2172
|
-
const start = document.positionAt(range.start);
|
|
2173
|
-
const end = document.positionAt(range.end);
|
|
2174
|
-
return {
|
|
2175
|
-
uri: getDocumentUri(document),
|
|
2176
|
-
range: {
|
|
2177
|
-
startLine: start.line,
|
|
2178
|
-
startChar: start.character,
|
|
2179
|
-
endLine: end.line,
|
|
2180
|
-
endChar: end.character
|
|
2181
|
-
},
|
|
2182
|
-
newText: ""
|
|
2183
|
-
};
|
|
2184
|
-
}
|
|
2185
|
-
function getObjectLiteralPropertyDeletionRange(sourceFile, objectLiteral, property) {
|
|
2186
|
-
const properties = [...objectLiteral.properties];
|
|
2187
|
-
const propertyIndex = properties.indexOf(property);
|
|
2188
|
-
const propertyStart = property.getFullStart();
|
|
2189
|
-
const propertyEnd = property.getEnd();
|
|
2190
|
-
if (properties.length === 1) return {
|
|
2191
|
-
start: propertyStart,
|
|
2192
|
-
end: objectLiteral.properties.hasTrailingComma ? objectLiteral.properties.end : propertyEnd
|
|
2193
|
-
};
|
|
2194
|
-
if (propertyIndex === properties.length - 1) {
|
|
2195
|
-
const previousProperty = properties[propertyIndex - 1];
|
|
2196
|
-
const commaOffset = sourceFile.text.lastIndexOf(",", propertyStart - 1);
|
|
2197
|
-
return {
|
|
2198
|
-
start: commaOffset >= previousProperty.getEnd() && commaOffset < propertyStart ? commaOffset : propertyStart,
|
|
2199
|
-
end: propertyEnd
|
|
2200
|
-
};
|
|
2201
|
-
}
|
|
2202
|
-
const nextProperty = properties[propertyIndex + 1];
|
|
2203
|
-
const commaOffset = sourceFile.text.indexOf(",", propertyEnd);
|
|
2204
|
-
return {
|
|
2205
|
-
start: propertyStart,
|
|
2206
|
-
end: commaOffset >= propertyEnd && commaOffset < nextProperty.getFullStart() ? commaOffset + 1 : propertyEnd
|
|
2207
|
-
};
|
|
2208
|
-
}
|
|
2209
|
-
function findObjectLiteralProperty(objectLiteral, propertyName) {
|
|
2210
|
-
for (const property of objectLiteral.properties) {
|
|
2211
|
-
if (ts$1.isShorthandPropertyAssignment(property) && property.name.text === propertyName) return property;
|
|
2212
|
-
if (ts$1.isPropertyAssignment(property)) {
|
|
2213
|
-
if (getPropertyNameText$1(property.name) === propertyName) return property;
|
|
2214
|
-
}
|
|
2215
|
-
}
|
|
2216
|
-
return null;
|
|
2217
|
-
}
|
|
2218
|
-
function findObjectLiteralPropertyByRange(sourceFile, objectLiteral, range) {
|
|
2219
|
-
if (!range) return null;
|
|
2220
|
-
const start = offsetAtRangePosition(sourceFile.text, range.startLine, range.startChar);
|
|
2221
|
-
const end = offsetAtRangePosition(sourceFile.text, range.endLine, range.endChar);
|
|
2222
|
-
for (const property of objectLiteral.properties) {
|
|
2223
|
-
const propertyName = getObjectLiteralElementName(property);
|
|
2224
|
-
if (propertyName && propertyName.getStart(sourceFile) === start && propertyName.getEnd() === end) return property;
|
|
2225
|
-
}
|
|
2226
|
-
return null;
|
|
2227
|
-
}
|
|
2228
|
-
function getObjectLiteralElementName(property) {
|
|
2229
|
-
if (ts$1.isPropertyAssignment(property) || ts$1.isShorthandPropertyAssignment(property) || ts$1.isMethodDeclaration(property) || ts$1.isGetAccessorDeclaration(property) || ts$1.isSetAccessorDeclaration(property)) return property.name;
|
|
2230
|
-
return null;
|
|
2231
|
-
}
|
|
2232
|
-
function getStatePropertyKeyText(property) {
|
|
2233
|
-
if (ts$1.isShorthandPropertyAssignment(property)) return property.name.text;
|
|
2234
|
-
if (ts$1.isPropertyAssignment(property)) return getPropertyNameText$1(property.name);
|
|
2235
|
-
return null;
|
|
2236
|
-
}
|
|
2237
|
-
function printObjectLiteralProperty(key, value, sourceFile, formatting) {
|
|
2238
|
-
const objectValue = { [key]: value };
|
|
2239
|
-
const objectText = containsRawCodeLike(value) ? reindentPrintedExpression(serializeJS(objectValue), formatting) : printExpression(valueToExpression(objectValue), sourceFile, formatting);
|
|
2240
|
-
const lineSeparator = formatting.newLineKind === ts$1.NewLineKind.CarriageReturnLineFeed ? "\r\n" : "\n";
|
|
2241
|
-
const lines = objectText.split(/\r?\n/);
|
|
2242
|
-
if (lines.length <= 2) return objectText.replace(/^\{\s*/, "").replace(/\s*\}$/, "");
|
|
2243
|
-
return lines.slice(1, -1).join(lineSeparator);
|
|
2244
|
-
}
|
|
2245
|
-
function wouldDeModularizeRootReplacement(sourceLocations) {
|
|
2246
|
-
const root = sourceLocations?.root;
|
|
2247
|
-
return sourceLocations?.states.some((location) => location.kind !== "inline" || (root ? !isSourceLocationInsideRoot(location, root) : false)) ?? false;
|
|
2248
|
-
}
|
|
2249
|
-
function isSourceLocationInsideRoot(location, root) {
|
|
2250
|
-
if (location.uri !== root.uri) return false;
|
|
2251
|
-
const startsAfterRootStart = location.range.startLine > root.range.startLine || location.range.startLine === root.range.startLine && location.range.startChar >= root.range.startChar;
|
|
2252
|
-
const endsBeforeRootEnd = location.range.endLine < root.range.endLine || location.range.endLine === root.range.endLine && location.range.endChar <= root.range.endChar;
|
|
2253
|
-
return startsAfterRootStart && endsBeforeRootEnd;
|
|
2254
|
-
}
|
|
2255
|
-
function parseGraphPatches(value) {
|
|
2256
|
-
if (!Array.isArray(value)) return null;
|
|
2257
|
-
return value.filter(isGraphPatch);
|
|
2258
|
-
}
|
|
2259
|
-
function parseGraph(value) {
|
|
2260
|
-
if (!value || typeof value !== "object") return null;
|
|
2261
|
-
const candidate = value;
|
|
2262
|
-
if (!Array.isArray(candidate.nodes) || !Array.isArray(candidate.edges)) return null;
|
|
2263
|
-
return candidate;
|
|
2264
|
-
}
|
|
2265
|
-
function isGraphPatch(value) {
|
|
2266
|
-
return Boolean(value && typeof value === "object" && "op" in value && typeof value.op === "string");
|
|
2267
|
-
}
|
|
2268
|
-
function isMachineConfig(value) {
|
|
2269
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2270
|
-
}
|
|
2271
|
-
function findMachineConfigObjectLiteral(sourceFile, machineIndex = 0) {
|
|
2272
|
-
const firstArgument = findCreateMachineCall(sourceFile, machineIndex)?.arguments[0];
|
|
2273
|
-
return firstArgument ? unwrapObjectLiteralExpression(firstArgument) : null;
|
|
2274
|
-
}
|
|
2275
|
-
function findCreateMachineCall(sourceFile, machineIndex = 0) {
|
|
2276
|
-
let found = null;
|
|
2277
|
-
let currentMachineIndex = 0;
|
|
2278
|
-
const visit = (node) => {
|
|
2279
|
-
if (found) return;
|
|
2280
|
-
if (ts$1.isCallExpression(node)) {
|
|
2281
|
-
const expression = node.expression;
|
|
2282
|
-
if (isCreateMachineExpression$1(expression)) {
|
|
2283
|
-
const isSelectedMachine = currentMachineIndex === machineIndex;
|
|
2284
|
-
currentMachineIndex += 1;
|
|
2285
|
-
if (!isSelectedMachine) return;
|
|
2286
|
-
found = node;
|
|
2287
|
-
return;
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2290
|
-
ts$1.forEachChild(node, visit);
|
|
2291
|
-
};
|
|
2292
|
-
visit(sourceFile);
|
|
2293
|
-
return found;
|
|
2294
|
-
}
|
|
2295
|
-
function findImplementationObjectLiteralForMachine(sourceFile, createMachineCall) {
|
|
2296
|
-
const setupObject = findSetupObjectLiteralForMachine(sourceFile, createMachineCall);
|
|
2297
|
-
if (setupObject) return setupObject;
|
|
2298
|
-
const secondArgument = createMachineCall.arguments[1];
|
|
2299
|
-
if (secondArgument) {
|
|
2300
|
-
const implementationObject = unwrapObjectLiteralExpression(secondArgument);
|
|
2301
|
-
if (implementationObject) return implementationObject;
|
|
2302
|
-
}
|
|
2303
|
-
if (isObjectOfProvideCall(createMachineCall)) {
|
|
2304
|
-
const provideCall = createMachineCall.parent.parent;
|
|
2305
|
-
if (ts$1.isCallExpression(provideCall)) {
|
|
2306
|
-
const firstArgument = provideCall.arguments[0];
|
|
2307
|
-
return firstArgument ? unwrapObjectLiteralExpression(firstArgument) : null;
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
return null;
|
|
2311
|
-
}
|
|
2312
|
-
function findSetupObjectLiteralForMachine(sourceFile, createMachineCall) {
|
|
2313
|
-
const expression = createMachineCall.expression;
|
|
2314
|
-
if (!ts$1.isPropertyAccessExpression(expression)) return null;
|
|
2315
|
-
const receiver = unwrapExpression$1(expression.expression);
|
|
2316
|
-
if (ts$1.isCallExpression(receiver) && isSetupCallExpression(receiver)) {
|
|
2317
|
-
const setupArgument = receiver.arguments[0];
|
|
2318
|
-
return setupArgument ? unwrapObjectLiteralExpression(setupArgument) : null;
|
|
2319
|
-
}
|
|
2320
|
-
if (!ts$1.isIdentifier(receiver)) return null;
|
|
2321
|
-
for (const statement of sourceFile.statements) {
|
|
2322
|
-
if (!ts$1.isVariableStatement(statement)) continue;
|
|
2323
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
2324
|
-
if (!ts$1.isIdentifier(declaration.name) || declaration.name.text !== receiver.text || !declaration.initializer) continue;
|
|
2325
|
-
const initializer = unwrapExpression$1(declaration.initializer);
|
|
2326
|
-
if (!ts$1.isCallExpression(initializer) || !isSetupCallExpression(initializer)) return null;
|
|
2327
|
-
const setupArgument = initializer.arguments[0];
|
|
2328
|
-
return setupArgument ? unwrapObjectLiteralExpression(setupArgument) : null;
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
return null;
|
|
2332
|
-
}
|
|
2333
|
-
function isObjectOfProvideCall(node) {
|
|
2334
|
-
const parent = node.parent;
|
|
2335
|
-
const grandparent = parent?.parent;
|
|
2336
|
-
return !!parent && !!grandparent && ts$1.isPropertyAccessExpression(parent) && parent.expression === node && parent.name.text === "provide" && ts$1.isCallExpression(grandparent) && grandparent.expression === parent;
|
|
2337
|
-
}
|
|
2338
|
-
function isCreateMachineExpression$1(expression) {
|
|
2339
|
-
const isMachineFactoryName = (name) => name === "createMachine" || name === "createMachineFromConfig";
|
|
2340
|
-
return ts$1.isIdentifier(expression) && isMachineFactoryName(expression.text) || ts$1.isPropertyAccessExpression(expression) && isMachineFactoryName(expression.name.text);
|
|
2341
|
-
}
|
|
2342
|
-
function isSetupCallExpression(node) {
|
|
2343
|
-
const expression = node.expression;
|
|
2344
|
-
return ts$1.isIdentifier(expression) && expression.text === "setup" || ts$1.isPropertyAccessExpression(expression) && expression.name.text === "setup";
|
|
2345
|
-
}
|
|
2346
|
-
function unwrapObjectLiteralExpression(expression) {
|
|
2347
|
-
const current = unwrapExpression$1(expression);
|
|
2348
|
-
return ts$1.isObjectLiteralExpression(current) ? current : null;
|
|
2349
|
-
}
|
|
2350
|
-
function unwrapExpression$1(expression) {
|
|
2351
|
-
let current = expression;
|
|
2352
|
-
for (;;) {
|
|
2353
|
-
if (ts$1.isParenthesizedExpression(current)) {
|
|
2354
|
-
current = current.expression;
|
|
2355
|
-
continue;
|
|
2356
|
-
}
|
|
2357
|
-
if (ts$1.isAsExpression(current) || ts$1.isSatisfiesExpression(current)) {
|
|
2358
|
-
current = current.expression;
|
|
2359
|
-
continue;
|
|
2360
|
-
}
|
|
2361
|
-
if (ts$1.isTypeAssertionExpression(current)) {
|
|
2362
|
-
current = current.expression;
|
|
2363
|
-
continue;
|
|
2364
|
-
}
|
|
2365
|
-
break;
|
|
2366
|
-
}
|
|
2367
|
-
return current;
|
|
2368
|
-
}
|
|
2369
|
-
function resolveStatePath(graph, nodeId) {
|
|
2370
|
-
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
2371
|
-
const segments = [];
|
|
2372
|
-
let current = nodeById.get(nodeId);
|
|
2373
|
-
if (!current) return null;
|
|
2374
|
-
while (current && current.parentId !== null) {
|
|
2375
|
-
segments.push(current.data.key);
|
|
2376
|
-
current = nodeById.get(current.parentId);
|
|
2377
|
-
}
|
|
2378
|
-
return segments.reverse();
|
|
2379
|
-
}
|
|
2380
|
-
function getEdgeSourceId(graph, edgeId) {
|
|
2381
|
-
return graph.edges.find((edge) => edge.id === edgeId)?.sourceId ?? null;
|
|
2382
|
-
}
|
|
2383
|
-
function resolveEdgeSourcePath(graph, edgeId) {
|
|
2384
|
-
const sourceId = getEdgeSourceId(graph, edgeId);
|
|
2385
|
-
return sourceId ? resolveStatePath(graph, sourceId) : null;
|
|
2386
|
-
}
|
|
2387
|
-
function resolvePatchOwner(patch, prevGraph, nextGraph) {
|
|
2388
|
-
switch (patch.op) {
|
|
2389
|
-
case "createState": {
|
|
2390
|
-
const parentPath = resolveStatePath(nextGraph, patch.parentId);
|
|
2391
|
-
return parentPath ? {
|
|
2392
|
-
type: "state",
|
|
2393
|
-
path: parentPath
|
|
2394
|
-
} : { type: "covered" };
|
|
2395
|
-
}
|
|
2396
|
-
case "updateState": {
|
|
2397
|
-
const nextPath = resolveStatePath(nextGraph, patch.stateId);
|
|
2398
|
-
const prevPath = resolveStatePath(prevGraph, patch.stateId);
|
|
2399
|
-
if (patch.key !== void 0 && prevPath && nextPath) return { type: "root" };
|
|
2400
|
-
const path = nextPath ?? prevPath;
|
|
2401
|
-
return path ? {
|
|
2402
|
-
type: "state",
|
|
2403
|
-
path
|
|
2404
|
-
} : { type: "covered" };
|
|
2405
|
-
}
|
|
2406
|
-
case "deleteState": {
|
|
2407
|
-
const node = prevGraph.nodes.find((item) => item.id === patch.stateId);
|
|
2408
|
-
if (!node) return { type: "covered" };
|
|
2409
|
-
if (!node.parentId) return { type: "root" };
|
|
2410
|
-
const parentPath = resolveStatePath(prevGraph, node.parentId);
|
|
2411
|
-
return parentPath ? {
|
|
2412
|
-
type: "state",
|
|
2413
|
-
path: parentPath
|
|
2414
|
-
} : { type: "root" };
|
|
2415
|
-
}
|
|
2416
|
-
case "createTransition": {
|
|
2417
|
-
const path = resolveStatePath(nextGraph, patch.sourceId);
|
|
2418
|
-
return path ? {
|
|
2419
|
-
type: "state",
|
|
2420
|
-
path
|
|
2421
|
-
} : { type: "covered" };
|
|
2422
|
-
}
|
|
2423
|
-
case "updateTransition": {
|
|
2424
|
-
const path = resolveEdgeSourcePath(nextGraph, patch.transitionId) ?? resolveEdgeSourcePath(prevGraph, patch.transitionId);
|
|
2425
|
-
return path ? {
|
|
2426
|
-
type: "state",
|
|
2427
|
-
path
|
|
2428
|
-
} : { type: "covered" };
|
|
2429
|
-
}
|
|
2430
|
-
case "deleteTransition":
|
|
2431
|
-
case "setGuard":
|
|
2432
|
-
case "deleteGuard": {
|
|
2433
|
-
const edgeId = "transitionId" in patch ? patch.transitionId : patch.edgeId;
|
|
2434
|
-
const path = resolveEdgeSourcePath(nextGraph, edgeId) ?? resolveEdgeSourcePath(prevGraph, edgeId);
|
|
2435
|
-
return path ? {
|
|
2436
|
-
type: "state",
|
|
2437
|
-
path
|
|
2438
|
-
} : { type: "covered" };
|
|
2439
|
-
}
|
|
2440
|
-
case "createAction":
|
|
2441
|
-
case "updateAction":
|
|
2442
|
-
case "deleteAction": {
|
|
2443
|
-
if ("nodeId" in patch.location) {
|
|
2444
|
-
const path = resolveStatePath(nextGraph, patch.location.nodeId) ?? resolveStatePath(prevGraph, patch.location.nodeId);
|
|
2445
|
-
return path ? {
|
|
2446
|
-
type: "state",
|
|
2447
|
-
path
|
|
2448
|
-
} : { type: "covered" };
|
|
2449
|
-
}
|
|
2450
|
-
const path = resolveEdgeSourcePath(nextGraph, patch.location.edgeId) ?? resolveEdgeSourcePath(prevGraph, patch.location.edgeId);
|
|
2451
|
-
return path ? {
|
|
2452
|
-
type: "state",
|
|
2453
|
-
path
|
|
2454
|
-
} : { type: "covered" };
|
|
2455
|
-
}
|
|
2456
|
-
case "createInvoke":
|
|
2457
|
-
case "updateInvoke":
|
|
2458
|
-
case "deleteInvoke":
|
|
2459
|
-
case "createTag":
|
|
2460
|
-
case "deleteTag": {
|
|
2461
|
-
const path = resolveStatePath(nextGraph, patch.nodeId) ?? resolveStatePath(prevGraph, patch.nodeId);
|
|
2462
|
-
return path ? {
|
|
2463
|
-
type: "state",
|
|
2464
|
-
path
|
|
2465
|
-
} : { type: "covered" };
|
|
2466
|
-
}
|
|
2467
|
-
case "updateSchemas": return { type: "root" };
|
|
2468
|
-
default: return { type: "root" };
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
|
-
function deriveReplacementPlan(patches, prevGraph, nextGraph) {
|
|
2472
|
-
const rawPaths = [];
|
|
2473
|
-
const hasConcretePatch = patches.some((patch) => patch.op !== "updateSchemas");
|
|
2474
|
-
for (const patch of patches) {
|
|
2475
|
-
if (patch.op === "updateSchemas" && hasConcretePatch) continue;
|
|
2476
|
-
const owner = resolvePatchOwner(patch, prevGraph, nextGraph);
|
|
2477
|
-
if (owner.type === "root") return { type: "root" };
|
|
2478
|
-
if (owner.type === "covered") continue;
|
|
2479
|
-
rawPaths.push(owner.path);
|
|
2480
|
-
}
|
|
2481
|
-
const reducedPaths = reducePaths(rawPaths);
|
|
2482
|
-
if (reducedPaths.length === 0 || reducedPaths.some((path) => path.length === 0)) return { type: "root" };
|
|
2483
|
-
return {
|
|
2484
|
-
type: "states",
|
|
2485
|
-
paths: reducedPaths
|
|
2486
|
-
};
|
|
2487
|
-
}
|
|
2488
|
-
function dropCoveredSchemaPatches(patches, hasConcreteReplacement) {
|
|
2489
|
-
return hasConcreteReplacement ? patches.filter((patch) => patch.op !== "updateSchemas") : patches;
|
|
2490
|
-
}
|
|
2491
|
-
function reducePaths(paths) {
|
|
2492
|
-
const uniquePaths = Array.from(new Set(paths.map((path) => path.join(".")))).map((value) => value === "" ? [] : value.split(".")).sort((a, b) => a.length - b.length);
|
|
2493
|
-
return uniquePaths.filter((path, index) => {
|
|
2494
|
-
return !uniquePaths.some((candidate, candidateIndex) => {
|
|
2495
|
-
if (candidateIndex === index || candidate.length >= path.length) return false;
|
|
2496
|
-
return candidate.every((segment, segmentIndex) => segment === path[segmentIndex]);
|
|
2497
|
-
});
|
|
2498
|
-
});
|
|
2499
|
-
}
|
|
2500
|
-
function getStateConfigByPath(machineConfig, path) {
|
|
2501
|
-
let current = machineConfig;
|
|
2502
|
-
for (const segment of path) {
|
|
2503
|
-
const states = current?.states;
|
|
2504
|
-
if (!isMachineConfig(states)) return;
|
|
2505
|
-
const next = states[segment];
|
|
2506
|
-
if (!isMachineConfig(next)) return;
|
|
2507
|
-
current = next;
|
|
2508
|
-
}
|
|
2509
|
-
return current;
|
|
2510
|
-
}
|
|
2511
|
-
function getStateObjectLiteralByPath(root, path) {
|
|
2512
|
-
let current = root;
|
|
2513
|
-
for (const segment of path) {
|
|
2514
|
-
if (!current) return null;
|
|
2515
|
-
const statesProperty = getObjectLiteralProperty(current, "states");
|
|
2516
|
-
current = statesProperty && getObjectLiteralProperty(statesProperty, segment);
|
|
2517
|
-
}
|
|
2518
|
-
return current;
|
|
2519
|
-
}
|
|
2520
|
-
function offsetAtRangePosition(sourceText, line, character) {
|
|
2521
|
-
if (line <= 0) return character;
|
|
2522
|
-
let currentLine = 0;
|
|
2523
|
-
for (let index = 0; index < sourceText.length; index += 1) {
|
|
2524
|
-
if (sourceText.charCodeAt(index) !== 10) continue;
|
|
2525
|
-
currentLine += 1;
|
|
2526
|
-
if (currentLine === line) return index + 1 + character;
|
|
2527
|
-
}
|
|
2528
|
-
return sourceText.length;
|
|
2529
|
-
}
|
|
2530
|
-
function getObjectLiteralProperty(objectLiteral, propertyName) {
|
|
2531
|
-
for (const property of objectLiteral.properties) {
|
|
2532
|
-
if (!ts$1.isPropertyAssignment(property)) continue;
|
|
2533
|
-
if (getPropertyNameText$1(property.name) !== propertyName) continue;
|
|
2534
|
-
return unwrapObjectLiteralExpression(property.initializer) ?? null;
|
|
2535
|
-
}
|
|
2536
|
-
return null;
|
|
2537
|
-
}
|
|
2538
|
-
function getPropertyNameText$1(name) {
|
|
2539
|
-
if (ts$1.isIdentifier(name) || ts$1.isStringLiteral(name) || ts$1.isNumericLiteral(name)) return name.text;
|
|
2540
|
-
if (ts$1.isComputedPropertyName(name)) {
|
|
2541
|
-
const expression = name.expression;
|
|
2542
|
-
if (ts$1.isStringLiteral(expression) || ts$1.isNoSubstitutionTemplateLiteral(expression) || ts$1.isNumericLiteral(expression)) return expression.text;
|
|
2543
|
-
}
|
|
2544
|
-
return null;
|
|
2545
|
-
}
|
|
2546
|
-
function createReplacement(document, sourceFile, node, value) {
|
|
2547
|
-
const start = document.positionAt(node.getStart(sourceFile));
|
|
2548
|
-
const end = document.positionAt(node.getEnd());
|
|
2549
|
-
const formatting = getFormattingContext(document, sourceFile, node);
|
|
2550
|
-
return {
|
|
2551
|
-
uri: getDocumentUri(document),
|
|
2552
|
-
range: {
|
|
2553
|
-
startLine: start.line,
|
|
2554
|
-
startChar: start.character,
|
|
2555
|
-
endLine: end.line,
|
|
2556
|
-
endChar: end.character
|
|
2557
|
-
},
|
|
2558
|
-
newText: printExpression(valueToExpression(value, node), sourceFile, formatting)
|
|
2559
|
-
};
|
|
2560
|
-
}
|
|
2561
|
-
function createRewriteReplacement(document, sourceFile, node, value) {
|
|
2562
|
-
const start = document.positionAt(node.getStart(sourceFile));
|
|
2563
|
-
const end = document.positionAt(node.getEnd());
|
|
2564
|
-
const formatting = getFormattingContext(document, sourceFile, node);
|
|
2565
|
-
return {
|
|
2566
|
-
uri: getDocumentUri(document),
|
|
2567
|
-
range: {
|
|
2568
|
-
startLine: start.line,
|
|
2569
|
-
startChar: start.character,
|
|
2570
|
-
endLine: end.line,
|
|
2571
|
-
endChar: end.character
|
|
2572
|
-
},
|
|
2573
|
-
newText: printExpression(valueToExpression(value), sourceFile, formatting)
|
|
2574
|
-
};
|
|
2575
|
-
}
|
|
2576
|
-
function addCodeImportReplacements(document, sourceFile, machineConfig, replacements) {
|
|
2577
|
-
const importReplacements = createCodeImportReplacements(document, sourceFile, collectCodeImports(machineConfig));
|
|
2578
|
-
return [...replacements, ...importReplacements];
|
|
2579
|
-
}
|
|
2580
|
-
function collectCodeImports(value) {
|
|
2581
|
-
const imports = [];
|
|
2582
|
-
const visit = (item) => {
|
|
2583
|
-
if (!item || typeof item !== "object") return;
|
|
2584
|
-
if (Array.isArray(item)) {
|
|
2585
|
-
for (const child of item) visit(child);
|
|
2586
|
-
return;
|
|
2587
|
-
}
|
|
2588
|
-
if (isCodeExpression(item)) {
|
|
2589
|
-
const itemImports = item.imports;
|
|
2590
|
-
if (Array.isArray(itemImports)) {
|
|
2591
|
-
for (const candidate of itemImports) if (isCodeImport(candidate)) imports.push(candidate);
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
for (const child of Object.values(item)) visit(child);
|
|
2595
|
-
};
|
|
2596
|
-
visit(value);
|
|
2597
|
-
return imports;
|
|
2598
|
-
}
|
|
2599
|
-
function isCodeImport(value) {
|
|
2600
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2601
|
-
const candidate = value;
|
|
2602
|
-
return typeof candidate.package === "string" && typeof candidate.import === "string" && (!candidate.version || typeof candidate.version === "string") && (!candidate.importKind || candidate.importKind === "named" || candidate.importKind === "default" || candidate.importKind === "namespace");
|
|
2603
|
-
}
|
|
2604
|
-
function createCodeImportReplacements(document, sourceFile, imports) {
|
|
2605
|
-
const namedImportsByPackage = /* @__PURE__ */ new Map();
|
|
2606
|
-
for (const codeImport of imports) {
|
|
2607
|
-
if ((codeImport.importKind ?? "named") !== "named") continue;
|
|
2608
|
-
const namedImports = namedImportsByPackage.get(codeImport.package) ?? /* @__PURE__ */ new Set();
|
|
2609
|
-
namedImports.add(codeImport.import);
|
|
2610
|
-
namedImportsByPackage.set(codeImport.package, namedImports);
|
|
2611
|
-
}
|
|
2612
|
-
const replacements = [];
|
|
2613
|
-
for (const [packageName, importNames] of namedImportsByPackage) {
|
|
2614
|
-
const missing = new Set(importNames);
|
|
2615
|
-
const importDeclaration = findImportDeclaration(sourceFile, packageName);
|
|
2616
|
-
if (importDeclaration) {
|
|
2617
|
-
const namedBindings = importDeclaration.importClause?.namedBindings;
|
|
2618
|
-
if (namedBindings && ts$1.isNamedImports(namedBindings)) {
|
|
2619
|
-
for (const element of namedBindings.elements) missing.delete(element.name.text);
|
|
2620
|
-
if (missing.size > 0) {
|
|
2621
|
-
const insertion = `${namedBindings.elements.length > 0 ? ", " : ""}${[...missing].sort().join(", ")}`;
|
|
2622
|
-
replacements.push(createOffsetReplacement(document, namedBindings.elements.end, namedBindings.elements.end, insertion));
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
continue;
|
|
2626
|
-
}
|
|
2627
|
-
replacements.push(createOffsetReplacement(document, getImportInsertionOffset(sourceFile), getImportInsertionOffset(sourceFile), `import { ${[...missing].sort().join(", ")} } from "${packageName}";${getLineBreak(sourceFile)}`));
|
|
2628
|
-
}
|
|
2629
|
-
return replacements;
|
|
2630
|
-
}
|
|
2631
|
-
function findImportDeclaration(sourceFile, packageName) {
|
|
2632
|
-
for (const statement of sourceFile.statements) if (ts$1.isImportDeclaration(statement) && ts$1.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === packageName) return statement;
|
|
2633
|
-
return null;
|
|
2634
|
-
}
|
|
2635
|
-
function getImportInsertionOffset(sourceFile) {
|
|
2636
|
-
let offset = 0;
|
|
2637
|
-
for (const statement of sourceFile.statements) {
|
|
2638
|
-
if (!ts$1.isImportDeclaration(statement)) break;
|
|
2639
|
-
offset = statement.getEnd();
|
|
2640
|
-
const nextLineStart = getLineStartOffset(sourceFile.text, offset + 1);
|
|
2641
|
-
if (nextLineStart > offset) offset = nextLineStart;
|
|
2642
|
-
}
|
|
2643
|
-
return offset;
|
|
2644
|
-
}
|
|
2645
|
-
function getDocumentUri(document) {
|
|
2646
|
-
return (typeof document.uri === "string" ? document.uri : document.uri?.toString()) ?? document.fileName;
|
|
2647
|
-
}
|
|
2648
|
-
function tryCreateSemanticReplacements(document, sourceFile, machineConfigNode, patches, prevGraph, nextGraph) {
|
|
2649
|
-
const split = getSemanticStateRenamePatchSplit(patches, prevGraph, nextGraph);
|
|
2650
|
-
if (!split || split.remainingPatches.length > 0) return null;
|
|
2651
|
-
const replacements = [];
|
|
2652
|
-
const targetLiterals = collectTargetStringLiterals(machineConfigNode).map((target) => ({
|
|
2653
|
-
...target,
|
|
2654
|
-
document,
|
|
2655
|
-
sourceFile
|
|
2656
|
-
}));
|
|
2657
|
-
for (const rename of split.renames) {
|
|
2658
|
-
const parentObject = getStateObjectLiteralByPath(machineConfigNode, rename.parentPath);
|
|
2659
|
-
if (!parentObject) return null;
|
|
2660
|
-
const renameReplacements = createStateKeyRenameReplacementsForTarget(document, sourceFile, parentObject, rename, void 0, targetLiterals);
|
|
2661
|
-
if (!renameReplacements || hasDuplicateReplacementRange([...replacements, ...renameReplacements])) return null;
|
|
2662
|
-
replacements.push(...renameReplacements);
|
|
2663
|
-
}
|
|
2664
|
-
return replacements.sort(compareReplacementRangesDescending);
|
|
2665
|
-
}
|
|
2666
|
-
async function tryCreateSemanticPatchSplitForDocuments(document, sourceFile, machineConfigNode, patches, prevGraph, nextGraph, sourceLocations, openTextDocument) {
|
|
2667
|
-
const descriptionSplit = isXStateV6Source(sourceLocations) ? await tryCreateV6WrapperDescriptionPatchSplitForDocuments(document, sourceFile, machineConfigNode, patches, prevGraph, nextGraph, sourceLocations, openTextDocument) : null;
|
|
2668
|
-
const split = getSemanticStateRenamePatchSplit(descriptionSplit?.remainingPatches ?? patches, prevGraph, nextGraph);
|
|
2669
|
-
if (!split) return descriptionSplit;
|
|
2670
|
-
const replacements = [...descriptionSplit?.replacements ?? []];
|
|
2671
|
-
const targetLiterals = await collectMappedTargetStringLiterals(document, sourceFile, machineConfigNode, sourceLocations, openTextDocument);
|
|
2672
|
-
let activeSourceLocations = sourceLocations;
|
|
2673
|
-
for (const rename of split.renames) {
|
|
2674
|
-
const parentTarget = await getStateMembershipParentTargetForDocuments(document, sourceFile, machineConfigNode, activeSourceLocations, rename.parentPath, openTextDocument, getStateObjectLiteralByPath);
|
|
2675
|
-
if (!parentTarget) return null;
|
|
2676
|
-
const renameReplacements = createStateKeyRenameReplacementsForTarget(parentTarget.document, parentTarget.sourceFile, parentTarget.node, rename, activeSourceLocations, targetLiterals, parentTarget.nodeKind, parentTarget.nodeKind === "statesObject" && rename.parentPath.length === 0 ? machineConfigNode : parentTarget.node);
|
|
2677
|
-
if (!renameReplacements || hasDuplicateReplacementRange([...replacements, ...renameReplacements])) return null;
|
|
2678
|
-
replacements.push(...renameReplacements);
|
|
2679
|
-
activeSourceLocations = createRenamedSourceLocations(activeSourceLocations, rename);
|
|
2680
|
-
}
|
|
2681
|
-
return {
|
|
2682
|
-
replacements: replacements.sort(compareReplacementRangesDescending),
|
|
2683
|
-
remainingPatches: split.remainingPatches,
|
|
2684
|
-
sourceLocations: activeSourceLocations
|
|
2685
|
-
};
|
|
2686
|
-
}
|
|
2687
|
-
async function tryCreateV6WrapperDescriptionPatchSplitForDocuments(document, sourceFile, machineConfigNode, patches, prevGraph, nextGraph, sourceLocations, openTextDocument) {
|
|
2688
|
-
const replacements = [];
|
|
2689
|
-
const remainingPatches = [];
|
|
2690
|
-
const descriptionsByTarget = /* @__PURE__ */ new Map();
|
|
2691
|
-
for (const patch of patches) {
|
|
2692
|
-
if (patch.op !== "updateTransition" || patch.transitionDescription === void 0 || Object.entries(patch).some(([key, value]) => value !== void 0 && ![
|
|
2693
|
-
"op",
|
|
2694
|
-
"transitionId",
|
|
2695
|
-
"transitionDescription"
|
|
2696
|
-
].includes(key))) {
|
|
2697
|
-
remainingPatches.push(patch);
|
|
2698
|
-
continue;
|
|
2699
|
-
}
|
|
2700
|
-
const target = getTransitionPatchTarget(patch, prevGraph, nextGraph);
|
|
2701
|
-
if (!target || target.previousEventType !== target.eventType) {
|
|
2702
|
-
remainingPatches.push(patch);
|
|
2703
|
-
continue;
|
|
2704
|
-
}
|
|
2705
|
-
const targetKey = `${target.sourcePath.join(".")}:${target.eventType}`;
|
|
2706
|
-
if (descriptionsByTarget.has(targetKey)) {
|
|
2707
|
-
if (descriptionsByTarget.get(targetKey) !== patch.transitionDescription) return null;
|
|
2708
|
-
continue;
|
|
2709
|
-
}
|
|
2710
|
-
descriptionsByTarget.set(targetKey, patch.transitionDescription);
|
|
2711
|
-
const mappedTarget = await getMappedStateReplacementTarget(document, sourceFile, sourceLocations, target.sourcePath, openTextDocument);
|
|
2712
|
-
const targetDocument = mappedTarget?.document ?? document;
|
|
2713
|
-
const targetSourceFile = mappedTarget?.sourceFile ?? sourceFile;
|
|
2714
|
-
const stateObject = mappedTarget?.node ?? getStateObjectLiteralByPath(machineConfigNode, target.sourcePath);
|
|
2715
|
-
const onObject = stateObject ? getObjectLiteralProperty(stateObject, "on") : null;
|
|
2716
|
-
const eventProperty = onObject ? findObjectLiteralProperty(onObject, target.eventType) : null;
|
|
2717
|
-
const initializer = eventProperty && ts$1.isPropertyAssignment(eventProperty) ? unwrapExpression$1(eventProperty.initializer) : null;
|
|
2718
|
-
const wrapper = initializer && ts$1.isObjectLiteralExpression(initializer) ? initializer : null;
|
|
2719
|
-
const toProperty = wrapper ? getPropertyAssignment(wrapper, "to") : null;
|
|
2720
|
-
if (!wrapper || !toProperty) {
|
|
2721
|
-
remainingPatches.push(patch);
|
|
2722
|
-
continue;
|
|
2723
|
-
}
|
|
2724
|
-
const descriptionProperty = getPropertyAssignment(wrapper, "description");
|
|
2725
|
-
if (patch.transitionDescription === null) {
|
|
2726
|
-
if (descriptionProperty) replacements.push(createObjectLiteralPropertyDeletion(targetDocument, targetSourceFile, wrapper, descriptionProperty));
|
|
2727
|
-
} else if (descriptionProperty) replacements.push(createPropertyValueReplacement(targetDocument, targetSourceFile, descriptionProperty, patch.transitionDescription));
|
|
2728
|
-
else replacements.push(...createObjectLiteralPropertyInsertion(targetDocument, targetSourceFile, wrapper, "description", patch.transitionDescription));
|
|
2729
|
-
}
|
|
2730
|
-
return replacements.length > 0 ? {
|
|
2731
|
-
replacements,
|
|
2732
|
-
remainingPatches,
|
|
2733
|
-
sourceLocations
|
|
2734
|
-
} : null;
|
|
2735
|
-
}
|
|
2736
|
-
function getSemanticStateRenameFromPatch(patch, prevGraph, nextGraph) {
|
|
2737
|
-
if (patch.op !== "updateState" || patch.key === void 0) return null;
|
|
2738
|
-
const prevPath = resolveStatePath(prevGraph, patch.stateId);
|
|
2739
|
-
const nextPath = resolveStatePath(nextGraph, patch.stateId);
|
|
2740
|
-
if (!prevPath || !nextPath || prevPath.length !== nextPath.length || prevPath.length === 0) return null;
|
|
2741
|
-
const oldKey = prevPath[prevPath.length - 1];
|
|
2742
|
-
const newKey = nextPath[nextPath.length - 1];
|
|
2743
|
-
const parentPath = prevPath.slice(0, -1);
|
|
2744
|
-
if (oldKey === newKey) return null;
|
|
2745
|
-
return {
|
|
2746
|
-
oldKey,
|
|
2747
|
-
newKey,
|
|
2748
|
-
oldPath: prevPath,
|
|
2749
|
-
newPath: nextPath,
|
|
2750
|
-
parentPath,
|
|
2751
|
-
oldAbsolutePath: getAbsoluteStatePath(prevGraph, patch.stateId),
|
|
2752
|
-
newAbsolutePath: getAbsoluteStatePath(nextGraph, patch.stateId)
|
|
2753
|
-
};
|
|
2754
|
-
}
|
|
2755
|
-
function getSemanticStateRenamePatchSplit(patches, prevGraph, nextGraph) {
|
|
2756
|
-
const renames = [];
|
|
2757
|
-
const remainingPatches = [];
|
|
2758
|
-
for (const patch of patches) {
|
|
2759
|
-
const patchRename = getSemanticStateRenameFromPatch(patch, prevGraph, nextGraph);
|
|
2760
|
-
if (patchRename) {
|
|
2761
|
-
renames.push(patchRename);
|
|
2762
|
-
if (getStatePropertyNamesFromUpdatePatch(patch).length > 0) remainingPatches.push(patch);
|
|
2763
|
-
continue;
|
|
2764
|
-
}
|
|
2765
|
-
remainingPatches.push(patch);
|
|
2766
|
-
}
|
|
2767
|
-
return renames.length > 0 ? {
|
|
2768
|
-
renames,
|
|
2769
|
-
remainingPatches
|
|
2770
|
-
} : null;
|
|
2771
|
-
}
|
|
2772
|
-
function hasNestedStateRenameBatch(patches, prevGraph, nextGraph) {
|
|
2773
|
-
const renames = patches.flatMap((patch) => {
|
|
2774
|
-
const rename = getSemanticStateRenameFromPatch(patch, prevGraph, nextGraph);
|
|
2775
|
-
return rename ? [rename] : [];
|
|
2776
|
-
});
|
|
2777
|
-
return renames.some((ancestor, ancestorIndex) => renames.some((descendant, descendantIndex) => ancestorIndex !== descendantIndex && ancestor.oldPath.length < descendant.oldPath.length && isPathEqualOrDescendant(descendant.oldPath, ancestor.oldPath)));
|
|
2778
|
-
}
|
|
2779
|
-
function hasDuplicateReplacementRange(replacements) {
|
|
2780
|
-
const ranges = /* @__PURE__ */ new Set();
|
|
2781
|
-
for (const replacement of replacements) {
|
|
2782
|
-
const key = `${replacement.uri ?? ""}:${replacement.range.startLine}:${replacement.range.startChar}:${replacement.range.endLine}:${replacement.range.endChar}`;
|
|
2783
|
-
if (ranges.has(key)) return true;
|
|
2784
|
-
ranges.add(key);
|
|
2785
|
-
}
|
|
2786
|
-
return false;
|
|
2787
|
-
}
|
|
2788
|
-
function createRenamedSourceLocations(sourceLocations, rename) {
|
|
2789
|
-
if (!sourceLocations) return;
|
|
2790
|
-
return {
|
|
2791
|
-
...sourceLocations,
|
|
2792
|
-
states: sourceLocations.states.map((location) => {
|
|
2793
|
-
if (!isPathEqualOrDescendant(location.path, rename.oldPath)) return location;
|
|
2794
|
-
return {
|
|
2795
|
-
...location,
|
|
2796
|
-
path: [...rename.newPath, ...location.path.slice(rename.oldPath.length)]
|
|
2797
|
-
};
|
|
2798
|
-
}),
|
|
2799
|
-
transitionFunctions: sourceLocations.transitionFunctions?.map((location) => ({
|
|
2800
|
-
...location,
|
|
2801
|
-
sourcePaths: location.sourcePaths.map((path) => isPathEqualOrDescendant(path, rename.oldPath) ? [...rename.newPath, ...path.slice(rename.oldPath.length)] : path)
|
|
2802
|
-
}))
|
|
2803
|
-
};
|
|
2804
|
-
}
|
|
2805
|
-
function createStateKeyRenameReplacementsForTarget(document, sourceFile, parentObject, rename, sourceLocations, targetLiterals, nodeKind = "machineConfig", initialOwnerObject = parentObject) {
|
|
2806
|
-
const statesObject = nodeKind === "statesObject" ? parentObject : getObjectLiteralProperty(parentObject, "states");
|
|
2807
|
-
const location = sourceLocations?.states.find((item) => rename.oldPath && arePathsEqual(item.path, rename.oldPath));
|
|
2808
|
-
const stateProperty = statesObject && (findObjectLiteralProperty(statesObject, rename.oldKey) ?? findObjectLiteralPropertyByRange(sourceFile, statesObject, location?.keyRange));
|
|
2809
|
-
if (!statesObject || !stateProperty) return null;
|
|
2810
|
-
const keyReplacement = createStatePropertyRenameReplacement(document, sourceFile, stateProperty, rename.oldKey, rename.newKey, location?.keySource);
|
|
2811
|
-
if (!keyReplacement) return null;
|
|
2812
|
-
const replacements = [keyReplacement];
|
|
2813
|
-
const initialProperty = getPropertyAssignment(initialOwnerObject, "initial");
|
|
2814
|
-
if (initialProperty) {
|
|
2815
|
-
const initialReplacement = createStringInitializerRenameReplacement(document, sourceFile, initialProperty.initializer, rename.oldKey, rename.newKey);
|
|
2816
|
-
if (initialReplacement) replacements.push(initialReplacement);
|
|
2817
|
-
}
|
|
2818
|
-
const supportedScope = new Set([rename.parentPath.join("."), ...getDirectChildStatePaths(nodeKind === "statesObject" ? initialOwnerObject : parentObject, rename.parentPath).map((path) => path.join("."))]);
|
|
2819
|
-
for (const target of targetLiterals) {
|
|
2820
|
-
if (!referencesRenamedState(target.node.text, rename)) continue;
|
|
2821
|
-
if (!referencesRenamedAbsoluteState(target.node.text, rename)) {
|
|
2822
|
-
const initialSourcePaths = target.initialSourcePaths ?? [];
|
|
2823
|
-
if (initialSourcePaths.length > 0) {
|
|
2824
|
-
if (!target.sourcePaths.every((sourcePath) => arePathsEqual(sourcePath, rename.parentPath))) {
|
|
2825
|
-
if (initialSourcePaths.length === target.sourcePaths.length && !initialSourcePaths.some((sourcePath) => arePathsEqual(sourcePath, rename.parentPath))) continue;
|
|
2826
|
-
return null;
|
|
2827
|
-
}
|
|
2828
|
-
} else for (const sourcePath of target.sourcePaths) {
|
|
2829
|
-
const sourceKey = sourcePath.join(".");
|
|
2830
|
-
if (!(sourcePath.length === rename.parentPath.length || arePathsEqual(sourcePath.slice(0, -1), rename.parentPath)) || !supportedScope.has(sourceKey)) return null;
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
|
-
const replacement = createStringLiteralReplacement(target.document, target.sourceFile, target.node, renameStateReference(target.node.text, rename));
|
|
2834
|
-
replacements.push(replacement);
|
|
2835
|
-
}
|
|
2836
|
-
return replacements;
|
|
2837
|
-
}
|
|
2838
|
-
function getDirectChildStatePaths(stateObject, parentPath) {
|
|
2839
|
-
const statesObject = getObjectLiteralProperty(stateObject, "states");
|
|
2840
|
-
if (!statesObject) return [];
|
|
2841
|
-
return statesObject.properties.flatMap((property) => {
|
|
2842
|
-
const key = getStatePropertyKeyText(property);
|
|
2843
|
-
if (!key) return [];
|
|
2844
|
-
return [[...parentPath, key]];
|
|
2845
|
-
});
|
|
2846
|
-
}
|
|
2847
|
-
function collectTargetStringLiterals(machineConfigNode) {
|
|
2848
|
-
const literals = [];
|
|
2849
|
-
const bindings = collectTopLevelExpressionBindings(machineConfigNode.getSourceFile());
|
|
2850
|
-
const visitStateObject = (stateObject, sourcePath) => {
|
|
2851
|
-
collectInitialTargetStringLiterals(stateObject, sourcePath, literals, bindings);
|
|
2852
|
-
for (const propertyName of [
|
|
2853
|
-
"always",
|
|
2854
|
-
"after",
|
|
2855
|
-
"on",
|
|
2856
|
-
"onDone"
|
|
2857
|
-
]) {
|
|
2858
|
-
const property = getPropertyAssignment(stateObject, propertyName);
|
|
2859
|
-
if (!property) continue;
|
|
2860
|
-
collectTargetStringLiteralsFromExpression(property.initializer, sourcePath, literals, bindings);
|
|
2861
|
-
}
|
|
2862
|
-
const statesObject = getObjectLiteralProperty(stateObject, "states");
|
|
2863
|
-
if (!statesObject) return;
|
|
2864
|
-
for (const property of statesObject.properties) {
|
|
2865
|
-
if (!ts$1.isPropertyAssignment(property)) continue;
|
|
2866
|
-
const key = getPropertyNameText$1(property.name);
|
|
2867
|
-
const childState = unwrapObjectLiteralExpression(property.initializer);
|
|
2868
|
-
if (!key || !childState) continue;
|
|
2869
|
-
visitStateObject(childState, [...sourcePath, key]);
|
|
2870
|
-
}
|
|
2871
|
-
};
|
|
2872
|
-
visitStateObject(machineConfigNode, []);
|
|
2873
|
-
return literals;
|
|
2874
|
-
}
|
|
2875
|
-
function collectInitialTargetStringLiterals(stateObject, sourcePath, literals, bindings) {
|
|
2876
|
-
const initialProperty = getPropertyAssignment(stateObject, "initial");
|
|
2877
|
-
if (!initialProperty) return;
|
|
2878
|
-
const initializer = unwrapExpression$1(initialProperty.initializer);
|
|
2879
|
-
if (ts$1.isObjectLiteralExpression(initializer)) {
|
|
2880
|
-
const initialLiterals = [];
|
|
2881
|
-
collectTargetStringLiteralsFromExpression(initializer, sourcePath, initialLiterals, bindings);
|
|
2882
|
-
for (const literal of initialLiterals) for (const literalSourcePath of literal.sourcePaths) addTargetLiteral(literals, literal.node, literalSourcePath, true);
|
|
2883
|
-
}
|
|
2884
|
-
}
|
|
2885
|
-
async function collectMappedTargetStringLiterals(rootDocument, rootSourceFile, machineConfigNode, sourceLocations, openTextDocument) {
|
|
2886
|
-
const literals = collectTargetStringLiterals(machineConfigNode).map((target) => ({
|
|
2887
|
-
...target,
|
|
2888
|
-
document: rootDocument,
|
|
2889
|
-
sourceFile: rootSourceFile
|
|
2890
|
-
}));
|
|
2891
|
-
if (!sourceLocations) return literals;
|
|
2892
|
-
const seen = new Set(literals.map((target) => getTargetLiteralReplacementKey(rootDocument, rootSourceFile, target.node)));
|
|
2893
|
-
const targetByKey = new Map(literals.map((target) => [getTargetLiteralReplacementKey(rootDocument, rootSourceFile, target.node), target]));
|
|
2894
|
-
for (const location of sourceLocations.states) {
|
|
2895
|
-
const targetDocument = isSourceLocationForDocument(location.uri, rootDocument) ? rootDocument : await openDocumentBySourceUri(location.uri, openTextDocument);
|
|
2896
|
-
if (!targetDocument) continue;
|
|
2897
|
-
const targetSourceFile = isSourceLocationForDocument(location.uri, rootDocument) ? rootSourceFile : createSourceFile(targetDocument, targetDocument.getText());
|
|
2898
|
-
const stateObject = findObjectLiteralByRange(targetSourceFile, location.range);
|
|
2899
|
-
if (!stateObject) continue;
|
|
2900
|
-
const stateLiterals = [];
|
|
2901
|
-
collectTargetStringLiteralsFromStateObject(stateObject, location.path, stateLiterals, collectTopLevelExpressionBindings(targetSourceFile));
|
|
2902
|
-
for (const target of stateLiterals) {
|
|
2903
|
-
const key = getTargetLiteralReplacementKey(targetDocument, targetSourceFile, target.node);
|
|
2904
|
-
if (seen.has(key)) {
|
|
2905
|
-
const existing = targetByKey.get(key);
|
|
2906
|
-
if (existing) {
|
|
2907
|
-
mergeTargetSourcePaths(existing, target.sourcePaths);
|
|
2908
|
-
mergeInitialSourcePaths(existing, target.initialSourcePaths ?? []);
|
|
2909
|
-
}
|
|
2910
|
-
continue;
|
|
2911
|
-
}
|
|
2912
|
-
seen.add(key);
|
|
2913
|
-
const mappedTarget = {
|
|
2914
|
-
...target,
|
|
2915
|
-
document: targetDocument,
|
|
2916
|
-
sourceFile: targetSourceFile
|
|
2917
|
-
};
|
|
2918
|
-
literals.push(mappedTarget);
|
|
2919
|
-
targetByKey.set(key, mappedTarget);
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
for (const location of sourceLocations.transitionFunctions ?? []) {
|
|
2923
|
-
const targetDocument = isSourceLocationForDocument(location.uri, rootDocument) ? rootDocument : await openDocumentBySourceUri(location.uri, openTextDocument);
|
|
2924
|
-
if (!targetDocument) continue;
|
|
2925
|
-
const targetSourceFile = isSourceLocationForDocument(location.uri, rootDocument) ? rootSourceFile : createSourceFile(targetDocument, targetDocument.getText());
|
|
2926
|
-
const fn = findTransitionFunctionByRange(targetSourceFile, location.range);
|
|
2927
|
-
if (!fn) continue;
|
|
2928
|
-
const targetParameterIndexes = getTransitionTargetParameterIndexes(fn);
|
|
2929
|
-
if (targetParameterIndexes.length > 0) {
|
|
2930
|
-
const callTargets = collectTransitionCallArgumentLiterals(machineConfigNode, location.symbol, targetParameterIndexes);
|
|
2931
|
-
for (const target of callTargets) {
|
|
2932
|
-
const key = getTargetLiteralReplacementKey(rootDocument, rootSourceFile, target.node);
|
|
2933
|
-
if (seen.has(key)) {
|
|
2934
|
-
const existing = targetByKey.get(key);
|
|
2935
|
-
if (existing) {
|
|
2936
|
-
mergeTargetSourcePaths(existing, target.sourcePaths);
|
|
2937
|
-
mergeInitialSourcePaths(existing, target.initialSourcePaths ?? []);
|
|
2938
|
-
}
|
|
2939
|
-
continue;
|
|
2940
|
-
}
|
|
2941
|
-
seen.add(key);
|
|
2942
|
-
const mappedTarget = {
|
|
2943
|
-
...target,
|
|
2944
|
-
document: rootDocument,
|
|
2945
|
-
sourceFile: rootSourceFile
|
|
2946
|
-
};
|
|
2947
|
-
literals.push(mappedTarget);
|
|
2948
|
-
targetByKey.set(key, mappedTarget);
|
|
2949
|
-
}
|
|
2950
|
-
}
|
|
2951
|
-
const functionLiterals = [];
|
|
2952
|
-
const bindings = collectTopLevelExpressionBindings(targetSourceFile);
|
|
2953
|
-
for (const sourcePath of location.sourcePaths) collectTargetStringLiteralsFromFunction(fn, sourcePath, functionLiterals, bindings, new Set([location.symbol]));
|
|
2954
|
-
for (const target of functionLiterals) {
|
|
2955
|
-
const key = getTargetLiteralReplacementKey(targetDocument, targetSourceFile, target.node);
|
|
2956
|
-
if (seen.has(key)) {
|
|
2957
|
-
const existing = targetByKey.get(key);
|
|
2958
|
-
if (existing) {
|
|
2959
|
-
mergeTargetSourcePaths(existing, target.sourcePaths);
|
|
2960
|
-
mergeInitialSourcePaths(existing, target.initialSourcePaths ?? []);
|
|
2961
|
-
}
|
|
2962
|
-
continue;
|
|
2963
|
-
}
|
|
2964
|
-
seen.add(key);
|
|
2965
|
-
const mappedTarget = {
|
|
2966
|
-
...target,
|
|
2967
|
-
document: targetDocument,
|
|
2968
|
-
sourceFile: targetSourceFile
|
|
2969
|
-
};
|
|
2970
|
-
literals.push(mappedTarget);
|
|
2971
|
-
targetByKey.set(key, mappedTarget);
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
return literals;
|
|
2975
|
-
}
|
|
2976
|
-
function findTransitionFunctionByRange(sourceFile, range) {
|
|
2977
|
-
const start = offsetAtRangePosition(sourceFile.text, range.startLine, range.startChar);
|
|
2978
|
-
const end = offsetAtRangePosition(sourceFile.text, range.endLine, range.endChar);
|
|
2979
|
-
let found = null;
|
|
2980
|
-
const visit = (node) => {
|
|
2981
|
-
if (found) return;
|
|
2982
|
-
if ((ts$1.isArrowFunction(node) || ts$1.isFunctionExpression(node) || ts$1.isFunctionDeclaration(node)) && node.getStart(sourceFile) === start && node.getEnd() >= end) {
|
|
2983
|
-
found = node;
|
|
2984
|
-
return;
|
|
2985
|
-
}
|
|
2986
|
-
ts$1.forEachChild(node, visit);
|
|
2987
|
-
};
|
|
2988
|
-
visit(sourceFile);
|
|
2989
|
-
return found;
|
|
2990
|
-
}
|
|
2991
|
-
function getTransitionTargetParameterIndexes(fn) {
|
|
2992
|
-
const parameterIndexes = /* @__PURE__ */ new Map();
|
|
2993
|
-
for (const [index, parameter] of fn.parameters.entries()) if (ts$1.isIdentifier(parameter.name)) parameterIndexes.set(parameter.name.text, index);
|
|
2994
|
-
const indexes = /* @__PURE__ */ new Set();
|
|
2995
|
-
const visit = (node) => {
|
|
2996
|
-
if (ts$1.isObjectLiteralExpression(node)) for (const property of node.properties) {
|
|
2997
|
-
const targetIdentifier = ts$1.isShorthandPropertyAssignment(property) && property.name.text === "target" ? property.name : ts$1.isPropertyAssignment(property) && getPropertyNameText$1(property.name) === "target" && ts$1.isIdentifier(unwrapExpression$1(property.initializer)) ? unwrapExpression$1(property.initializer) : null;
|
|
2998
|
-
if (targetIdentifier) {
|
|
2999
|
-
const index = parameterIndexes.get(targetIdentifier.text);
|
|
3000
|
-
if (index !== void 0) indexes.add(index);
|
|
3001
|
-
}
|
|
3002
|
-
}
|
|
3003
|
-
ts$1.forEachChild(node, visit);
|
|
3004
|
-
};
|
|
3005
|
-
visit(fn);
|
|
3006
|
-
return [...indexes];
|
|
3007
|
-
}
|
|
3008
|
-
function collectTransitionCallArgumentLiterals(machineConfigNode, symbol, targetParameterIndexes) {
|
|
3009
|
-
const literals = [];
|
|
3010
|
-
const visitExpression = (expression, sourcePath) => {
|
|
3011
|
-
const current = unwrapExpression$1(expression);
|
|
3012
|
-
if (ts$1.isCallExpression(current) && ts$1.isIdentifier(current.expression) && current.expression.text === symbol) {
|
|
3013
|
-
for (const index of targetParameterIndexes) {
|
|
3014
|
-
const argument = current.arguments[index];
|
|
3015
|
-
if (argument && ts$1.isStringLiteralLike(unwrapExpression$1(argument))) addTargetLiteral(literals, unwrapExpression$1(argument), sourcePath);
|
|
3016
|
-
}
|
|
3017
|
-
return;
|
|
3018
|
-
}
|
|
3019
|
-
if (ts$1.isObjectLiteralExpression(current)) {
|
|
3020
|
-
for (const property of current.properties) if (ts$1.isPropertyAssignment(property)) visitExpression(property.initializer, sourcePath);
|
|
3021
|
-
else if (ts$1.isShorthandPropertyAssignment(property)) visitExpression(property.name, sourcePath);
|
|
3022
|
-
else if (ts$1.isSpreadAssignment(property)) visitExpression(property.expression, sourcePath);
|
|
3023
|
-
return;
|
|
3024
|
-
}
|
|
3025
|
-
if (ts$1.isArrayLiteralExpression(current)) {
|
|
3026
|
-
for (const element of current.elements) if (ts$1.isExpression(element)) visitExpression(element, sourcePath);
|
|
3027
|
-
return;
|
|
3028
|
-
}
|
|
3029
|
-
ts$1.forEachChild(current, (child) => {
|
|
3030
|
-
if (ts$1.isExpression(child)) visitExpression(child, sourcePath);
|
|
3031
|
-
});
|
|
3032
|
-
};
|
|
3033
|
-
const visitState = (stateObject, sourcePath) => {
|
|
3034
|
-
for (const propertyName of [
|
|
3035
|
-
"always",
|
|
3036
|
-
"after",
|
|
3037
|
-
"on",
|
|
3038
|
-
"onDone"
|
|
3039
|
-
]) {
|
|
3040
|
-
const property = getPropertyAssignment(stateObject, propertyName);
|
|
3041
|
-
if (property) visitExpression(property.initializer, sourcePath);
|
|
3042
|
-
}
|
|
3043
|
-
const states = getObjectLiteralProperty(stateObject, "states");
|
|
3044
|
-
if (!states) return;
|
|
3045
|
-
for (const property of states.properties) {
|
|
3046
|
-
if (!ts$1.isPropertyAssignment(property)) continue;
|
|
3047
|
-
const key = getPropertyNameText$1(property.name);
|
|
3048
|
-
const child = unwrapObjectLiteralExpression(property.initializer);
|
|
3049
|
-
if (key && child) visitState(child, [...sourcePath, key]);
|
|
3050
|
-
}
|
|
3051
|
-
};
|
|
3052
|
-
visitState(machineConfigNode, []);
|
|
3053
|
-
return literals;
|
|
3054
|
-
}
|
|
3055
|
-
function getTargetLiteralReplacementKey(document, sourceFile, node) {
|
|
3056
|
-
return `${getDocumentUri(document)}:${node.getStart(sourceFile)}:${node.getEnd()}`;
|
|
3057
|
-
}
|
|
3058
|
-
function collectTargetStringLiteralsFromStateObject(stateObject, sourcePath, literals, bindings) {
|
|
3059
|
-
collectInitialTargetStringLiterals(stateObject, sourcePath, literals, bindings);
|
|
3060
|
-
for (const propertyName of [
|
|
3061
|
-
"always",
|
|
3062
|
-
"after",
|
|
3063
|
-
"on",
|
|
3064
|
-
"onDone"
|
|
3065
|
-
]) {
|
|
3066
|
-
const property = getPropertyAssignment(stateObject, propertyName);
|
|
3067
|
-
if (!property) continue;
|
|
3068
|
-
collectTargetStringLiteralsFromExpression(property.initializer, sourcePath, literals, bindings);
|
|
3069
|
-
}
|
|
3070
|
-
const statesObject = getObjectLiteralProperty(stateObject, "states");
|
|
3071
|
-
if (!statesObject) return;
|
|
3072
|
-
for (const property of statesObject.properties) {
|
|
3073
|
-
if (!ts$1.isPropertyAssignment(property)) continue;
|
|
3074
|
-
const key = getPropertyNameText$1(property.name);
|
|
3075
|
-
const childState = unwrapObjectLiteralExpression(property.initializer);
|
|
3076
|
-
if (!key || !childState) continue;
|
|
3077
|
-
collectTargetStringLiteralsFromStateObject(childState, [...sourcePath, key], literals, bindings);
|
|
3078
|
-
}
|
|
3079
|
-
}
|
|
3080
|
-
function collectTargetStringLiteralsFromExpression(expression, sourcePath, literals, bindings, seenBindings = /* @__PURE__ */ new Set()) {
|
|
3081
|
-
const current = unwrapExpression$1(expression);
|
|
3082
|
-
if (ts$1.isStringLiteralLike(current)) {
|
|
3083
|
-
addTargetLiteral(literals, current, sourcePath);
|
|
3084
|
-
return;
|
|
3085
|
-
}
|
|
3086
|
-
if (ts$1.isIdentifier(current)) {
|
|
3087
|
-
if (seenBindings.has(current.text)) return;
|
|
3088
|
-
const binding = bindings.get(current.text);
|
|
3089
|
-
if (binding) {
|
|
3090
|
-
const nextSeenBindings = new Set([...seenBindings, current.text]);
|
|
3091
|
-
if (isTransitionFunctionBinding(binding)) collectTargetStringLiteralsFromFunction(binding, sourcePath, literals, bindings, nextSeenBindings);
|
|
3092
|
-
else collectTargetStringLiteralsFromExpression(binding, sourcePath, literals, bindings, nextSeenBindings);
|
|
3093
|
-
}
|
|
3094
|
-
return;
|
|
3095
|
-
}
|
|
3096
|
-
if (ts$1.isArrowFunction(current) || ts$1.isFunctionExpression(current)) {
|
|
3097
|
-
collectTargetStringLiteralsFromFunction(current, sourcePath, literals, bindings, seenBindings);
|
|
3098
|
-
return;
|
|
3099
|
-
}
|
|
3100
|
-
if (ts$1.isCallExpression(current) && ts$1.isIdentifier(current.expression)) {
|
|
3101
|
-
const binding = bindings.get(current.expression.text);
|
|
3102
|
-
if (binding && isTransitionFunctionBinding(binding)) {
|
|
3103
|
-
const scopedBindings = new Map(bindings);
|
|
3104
|
-
for (const [index, parameter] of binding.parameters.entries()) {
|
|
3105
|
-
if (!ts$1.isIdentifier(parameter.name)) continue;
|
|
3106
|
-
const argument = current.arguments[index];
|
|
3107
|
-
if (argument) scopedBindings.set(parameter.name.text, argument);
|
|
3108
|
-
else if (parameter.initializer) scopedBindings.set(parameter.name.text, parameter.initializer);
|
|
3109
|
-
}
|
|
3110
|
-
collectTargetStringLiteralsFromFunction(binding, sourcePath, literals, scopedBindings, seenBindings);
|
|
3111
|
-
}
|
|
3112
|
-
return;
|
|
3113
|
-
}
|
|
3114
|
-
if (ts$1.isArrayLiteralExpression(current)) {
|
|
3115
|
-
for (const element of current.elements) if (ts$1.isExpression(element)) collectTargetStringLiteralsFromExpression(element, sourcePath, literals, bindings, seenBindings);
|
|
3116
|
-
return;
|
|
3117
|
-
}
|
|
3118
|
-
if (!ts$1.isObjectLiteralExpression(current)) return;
|
|
3119
|
-
const targetProperty = current.properties.find((property) => ts$1.isPropertyAssignment(property) && getPropertyNameText$1(property.name) === "target" || ts$1.isShorthandPropertyAssignment(property) && property.name.text === "target");
|
|
3120
|
-
if (targetProperty) {
|
|
3121
|
-
collectTargetStringLiteralsFromExpression(ts$1.isPropertyAssignment(targetProperty) ? targetProperty.initializer : targetProperty.name, sourcePath, literals, bindings, seenBindings);
|
|
3122
|
-
return;
|
|
3123
|
-
}
|
|
3124
|
-
for (const property of current.properties) {
|
|
3125
|
-
if (!ts$1.isPropertyAssignment(property)) continue;
|
|
3126
|
-
collectTargetStringLiteralsFromExpression(property.initializer, sourcePath, literals, bindings, seenBindings);
|
|
3127
|
-
}
|
|
3128
|
-
}
|
|
3129
|
-
function collectTargetStringLiteralsFromFunctionBody(body, sourcePath, literals, bindings, seenBindings) {
|
|
3130
|
-
const visit = (node) => {
|
|
3131
|
-
if (ts$1.isFunctionLike(node)) return;
|
|
3132
|
-
if (ts$1.isReturnStatement(node) && node.expression) {
|
|
3133
|
-
collectTargetStringLiteralsFromExpression(node.expression, sourcePath, literals, bindings, seenBindings);
|
|
3134
|
-
return;
|
|
3135
|
-
}
|
|
3136
|
-
ts$1.forEachChild(node, visit);
|
|
3137
|
-
};
|
|
3138
|
-
ts$1.forEachChild(body, visit);
|
|
3139
|
-
}
|
|
3140
|
-
function isTransitionFunctionBinding(binding) {
|
|
3141
|
-
return ts$1.isArrowFunction(binding) || ts$1.isFunctionExpression(binding) || ts$1.isFunctionDeclaration(binding);
|
|
3142
|
-
}
|
|
3143
|
-
function collectTargetStringLiteralsFromFunction(fn, sourcePath, literals, bindings, seenBindings) {
|
|
3144
|
-
if (!fn.body) return;
|
|
3145
|
-
if (ts$1.isBlock(fn.body)) {
|
|
3146
|
-
collectTargetStringLiteralsFromFunctionBody(fn.body, sourcePath, literals, bindings, seenBindings);
|
|
3147
|
-
return;
|
|
3148
|
-
}
|
|
3149
|
-
collectTargetStringLiteralsFromExpression(fn.body, sourcePath, literals, bindings, seenBindings);
|
|
3150
|
-
}
|
|
3151
|
-
function collectTopLevelExpressionBindings(sourceFile) {
|
|
3152
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
3153
|
-
for (const statement of sourceFile.statements) {
|
|
3154
|
-
if (ts$1.isFunctionDeclaration(statement) && statement.name) {
|
|
3155
|
-
bindings.set(statement.name.text, statement);
|
|
3156
|
-
continue;
|
|
3157
|
-
}
|
|
3158
|
-
if (!ts$1.isVariableStatement(statement)) continue;
|
|
3159
|
-
for (const declaration of statement.declarationList.declarations) if (ts$1.isIdentifier(declaration.name) && declaration.initializer) bindings.set(declaration.name.text, declaration.initializer);
|
|
3160
|
-
}
|
|
3161
|
-
return bindings;
|
|
3162
|
-
}
|
|
3163
|
-
function addTargetLiteral(literals, node, sourcePath, isInitialTarget = false) {
|
|
3164
|
-
const existing = literals.find((literal) => literal.node.getStart() === node.getStart() && literal.node.getEnd() === node.getEnd());
|
|
3165
|
-
if (existing) {
|
|
3166
|
-
mergeTargetSourcePaths(existing, [sourcePath]);
|
|
3167
|
-
if (isInitialTarget) mergeInitialSourcePaths(existing, [sourcePath]);
|
|
3168
|
-
return;
|
|
3169
|
-
}
|
|
3170
|
-
literals.push({
|
|
3171
|
-
node,
|
|
3172
|
-
sourcePaths: [sourcePath],
|
|
3173
|
-
...isInitialTarget ? { initialSourcePaths: [sourcePath] } : {}
|
|
3174
|
-
});
|
|
3175
|
-
}
|
|
3176
|
-
function mergeInitialSourcePaths(target, initialSourcePaths) {
|
|
3177
|
-
if (initialSourcePaths.length === 0) return;
|
|
3178
|
-
const merged = target.initialSourcePaths ?? [];
|
|
3179
|
-
for (const sourcePath of initialSourcePaths) if (!merged.some((existing) => arePathsEqual(existing, sourcePath))) merged.push(sourcePath);
|
|
3180
|
-
target.initialSourcePaths = merged;
|
|
3181
|
-
}
|
|
3182
|
-
function mergeTargetSourcePaths(target, sourcePaths) {
|
|
3183
|
-
for (const sourcePath of sourcePaths) if (!target.sourcePaths.some((existing) => arePathsEqual(existing, sourcePath))) target.sourcePaths.push(sourcePath);
|
|
3184
|
-
}
|
|
3185
|
-
function referencesRenamedState(value, rename) {
|
|
3186
|
-
if (referencesRenamedSibling(value, rename.oldKey)) return true;
|
|
3187
|
-
return referencesRenamedAbsoluteState(value, rename);
|
|
3188
|
-
}
|
|
3189
|
-
function referencesRenamedAbsoluteState(value, rename) {
|
|
3190
|
-
const oldAbsolutePath = rename.oldAbsolutePath;
|
|
3191
|
-
return oldAbsolutePath !== void 0 && (value === oldAbsolutePath || value.startsWith(`${oldAbsolutePath}.`));
|
|
3192
|
-
}
|
|
3193
|
-
function renameStateReference(value, rename) {
|
|
3194
|
-
const oldAbsolutePath = rename.oldAbsolutePath;
|
|
3195
|
-
const newAbsolutePath = rename.newAbsolutePath;
|
|
3196
|
-
if (oldAbsolutePath && newAbsolutePath) {
|
|
3197
|
-
if (value === oldAbsolutePath) return newAbsolutePath;
|
|
3198
|
-
if (value.startsWith(`${oldAbsolutePath}.`)) return `${newAbsolutePath}${value.slice(oldAbsolutePath.length)}`;
|
|
3199
|
-
}
|
|
3200
|
-
return renameSiblingReference(value, rename.oldKey, rename.newKey);
|
|
3201
|
-
}
|
|
3202
|
-
function referencesRenamedSibling(value, oldKey) {
|
|
3203
|
-
return value === oldKey || value.startsWith(`${oldKey}.`);
|
|
3204
|
-
}
|
|
3205
|
-
function getAbsoluteStatePath(graph, nodeId) {
|
|
3206
|
-
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
3207
|
-
const node = nodeById.get(nodeId);
|
|
3208
|
-
if (!node) return;
|
|
3209
|
-
const resolvedId = resolveGraphStateId(node, nodeById);
|
|
3210
|
-
return resolvedId ? `#${resolvedId}` : void 0;
|
|
3211
|
-
}
|
|
3212
|
-
function resolveGraphStateId(node, nodeById) {
|
|
3213
|
-
const authoredId = node.data.nodeId;
|
|
3214
|
-
if (authoredId && !authoredId.startsWith("$auto-")) return authoredId.replace(/^#/, "");
|
|
3215
|
-
if (node.parentId === null) return node.data.key;
|
|
3216
|
-
const parent = nodeById.get(node.parentId);
|
|
3217
|
-
const parentId = parent && resolveGraphStateId(parent, nodeById);
|
|
3218
|
-
return parentId ? `${parentId}.${node.data.key}` : node.data.key;
|
|
3219
|
-
}
|
|
3220
|
-
function renameSiblingReference(value, oldKey, newKey) {
|
|
3221
|
-
if (value === oldKey) return newKey;
|
|
3222
|
-
return value.startsWith(`${oldKey}.`) ? `${newKey}${value.slice(oldKey.length)}` : value;
|
|
3223
|
-
}
|
|
3224
|
-
function arePathsEqual(left, right) {
|
|
3225
|
-
return left.length === right.length && left.every((segment, index) => segment === right[index]);
|
|
3226
|
-
}
|
|
3227
|
-
function createPropertyNameReplacement(document, sourceFile, propertyName, nextName) {
|
|
3228
|
-
if (!isSupportedPropertyName(propertyName)) return null;
|
|
3229
|
-
const start = document.positionAt(propertyName.getStart(sourceFile));
|
|
3230
|
-
const end = document.positionAt(propertyName.getEnd());
|
|
3231
|
-
const sourceText = sourceFile.text.slice(propertyName.getStart(sourceFile), propertyName.getEnd());
|
|
3232
|
-
return {
|
|
3233
|
-
range: {
|
|
3234
|
-
startLine: start.line,
|
|
3235
|
-
startChar: start.character,
|
|
3236
|
-
endLine: end.line,
|
|
3237
|
-
endChar: end.character
|
|
3238
|
-
},
|
|
3239
|
-
uri: getDocumentUri(document),
|
|
3240
|
-
newText: formatPropertyNameText(propertyName, nextName, sourceText)
|
|
3241
|
-
};
|
|
3242
|
-
}
|
|
3243
|
-
function createStatePropertyRenameReplacement(document, sourceFile, property, oldKey, newKey, keySource) {
|
|
3244
|
-
if (keySource?.machineLocal && keySource.valueRange && isSourceRangeInDocument(sourceFile, keySource.valueRange)) return createSourceRangeReplacement(document, sourceFile, keySource.valueRange, formatStringLiteralText(sourceFile.text.slice(offsetAtRangePosition(sourceFile.text, keySource.valueRange.startLine, keySource.valueRange.startChar), offsetAtRangePosition(sourceFile.text, keySource.valueRange.endLine, keySource.valueRange.endChar)), newKey));
|
|
3245
|
-
if (ts$1.isPropertyAssignment(property)) {
|
|
3246
|
-
if (ts$1.isComputedPropertyName(property.name)) return createPropertyNameLiteralReplacement(document, sourceFile, property.name, newKey);
|
|
3247
|
-
return createPropertyNameReplacement(document, sourceFile, property.name, newKey);
|
|
3248
|
-
}
|
|
3249
|
-
if (!ts$1.isShorthandPropertyAssignment(property)) return null;
|
|
3250
|
-
const start = document.positionAt(property.name.getStart(sourceFile));
|
|
3251
|
-
const end = document.positionAt(property.name.getEnd());
|
|
3252
|
-
const sourceText = sourceFile.text.slice(property.name.getStart(sourceFile), property.name.getEnd());
|
|
3253
|
-
const nextPropertyName = formatPropertyNameText(property.name, newKey, sourceText);
|
|
3254
|
-
return {
|
|
3255
|
-
range: {
|
|
3256
|
-
startLine: start.line,
|
|
3257
|
-
startChar: start.character,
|
|
3258
|
-
endLine: end.line,
|
|
3259
|
-
endChar: end.character
|
|
3260
|
-
},
|
|
3261
|
-
uri: getDocumentUri(document),
|
|
3262
|
-
newText: `${nextPropertyName}: ${oldKey}`
|
|
3263
|
-
};
|
|
3264
|
-
}
|
|
3265
|
-
function createPropertyNameLiteralReplacement(document, sourceFile, propertyName, nextName) {
|
|
3266
|
-
const start = document.positionAt(propertyName.getStart(sourceFile));
|
|
3267
|
-
const end = document.positionAt(propertyName.getEnd());
|
|
3268
|
-
return {
|
|
3269
|
-
range: {
|
|
3270
|
-
startLine: start.line,
|
|
3271
|
-
startChar: start.character,
|
|
3272
|
-
endLine: end.line,
|
|
3273
|
-
endChar: end.character
|
|
3274
|
-
},
|
|
3275
|
-
uri: getDocumentUri(document),
|
|
3276
|
-
newText: isIdentifierText(nextName) ? nextName : JSON.stringify(nextName)
|
|
3277
|
-
};
|
|
3278
|
-
}
|
|
3279
|
-
function createStringInitializerRenameReplacement(document, sourceFile, expression, oldValue, newValue) {
|
|
3280
|
-
if (!ts$1.isStringLiteralLike(expression) || expression.text !== oldValue) return null;
|
|
3281
|
-
return createStringLiteralReplacement(document, sourceFile, expression, newValue);
|
|
3282
|
-
}
|
|
3283
|
-
function createStringLiteralReplacement(document, sourceFile, expression, nextValue) {
|
|
3284
|
-
const start = document.positionAt(expression.getStart(sourceFile));
|
|
3285
|
-
const end = document.positionAt(expression.getEnd());
|
|
3286
|
-
const sourceText = sourceFile.text.slice(expression.getStart(sourceFile), expression.getEnd());
|
|
3287
|
-
return {
|
|
3288
|
-
range: {
|
|
3289
|
-
startLine: start.line,
|
|
3290
|
-
startChar: start.character,
|
|
3291
|
-
endLine: end.line,
|
|
3292
|
-
endChar: end.character
|
|
3293
|
-
},
|
|
3294
|
-
uri: getDocumentUri(document),
|
|
3295
|
-
newText: formatStringLiteralText(sourceText, nextValue)
|
|
3296
|
-
};
|
|
3297
|
-
}
|
|
3298
|
-
function createSourceRangeReplacement(document, sourceFile, range, newText) {
|
|
3299
|
-
const startOffset = offsetAtRangePosition(sourceFile.text, range.startLine, range.startChar);
|
|
3300
|
-
const endOffset = offsetAtRangePosition(sourceFile.text, range.endLine, range.endChar);
|
|
3301
|
-
const start = document.positionAt(startOffset);
|
|
3302
|
-
const end = document.positionAt(endOffset);
|
|
3303
|
-
return {
|
|
3304
|
-
range: {
|
|
3305
|
-
startLine: start.line,
|
|
3306
|
-
startChar: start.character,
|
|
3307
|
-
endLine: end.line,
|
|
3308
|
-
endChar: end.character
|
|
3309
|
-
},
|
|
3310
|
-
uri: getDocumentUri(document),
|
|
3311
|
-
newText
|
|
3312
|
-
};
|
|
3313
|
-
}
|
|
3314
|
-
function isSourceRangeInDocument(sourceFile, range) {
|
|
3315
|
-
return offsetAtRangePosition(sourceFile.text, range.startLine, range.startChar) <= sourceFile.text.length;
|
|
3316
|
-
}
|
|
3317
|
-
function isSupportedPropertyName(propertyName) {
|
|
3318
|
-
if (ts$1.isIdentifier(propertyName) || ts$1.isStringLiteral(propertyName) || ts$1.isNumericLiteral(propertyName)) return true;
|
|
3319
|
-
if (ts$1.isComputedPropertyName(propertyName)) {
|
|
3320
|
-
const expression = propertyName.expression;
|
|
3321
|
-
return ts$1.isStringLiteral(expression) || ts$1.isNoSubstitutionTemplateLiteral(expression) || ts$1.isNumericLiteral(expression);
|
|
3322
|
-
}
|
|
3323
|
-
return false;
|
|
3324
|
-
}
|
|
3325
|
-
function formatPropertyNameText(propertyName, nextName, sourceText) {
|
|
3326
|
-
if (ts$1.isIdentifier(propertyName) && isIdentifierText(nextName)) return nextName;
|
|
3327
|
-
if (ts$1.isComputedPropertyName(propertyName)) return `[${formatStringLiteralText(sourceText.slice(1, -1), nextName)}]`;
|
|
3328
|
-
return formatStringLiteralText(sourceText, nextName);
|
|
3329
|
-
}
|
|
3330
|
-
function formatStringLiteralText(sourceText, nextValue) {
|
|
3331
|
-
const quote = sourceText.startsWith("'") ? "'" : sourceText.startsWith("`") ? "`" : "\"";
|
|
3332
|
-
return `${quote}${quote === "'" ? nextValue.replace(/\\/g, "\\\\").replace(/'/g, "\\'") : quote === "\"" ? nextValue.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") : nextValue.replace(/\\/g, "\\\\").replace(/`/g, "\\`")}${quote}`;
|
|
3333
|
-
}
|
|
3334
|
-
function compareReplacementRangesDescending(a, b) {
|
|
3335
|
-
if (a.range.startLine !== b.range.startLine) return b.range.startLine - a.range.startLine;
|
|
3336
|
-
return b.range.startChar - a.range.startChar;
|
|
3337
|
-
}
|
|
3338
|
-
function normalizeOverlappingReplacements(replacements) {
|
|
3339
|
-
const result = [];
|
|
3340
|
-
for (const candidate of replacements) {
|
|
3341
|
-
if (replacements.find((other) => other !== candidate && other.uri === candidate.uri && rangeContains(other.range, candidate.range))) continue;
|
|
3342
|
-
const overlappingDeletion = result.find((other) => other.uri === candidate.uri && other.newText === "" && candidate.newText === "" && rangesOverlap(other.range, candidate.range));
|
|
3343
|
-
if (overlappingDeletion) {
|
|
3344
|
-
overlappingDeletion.range = unionRanges(overlappingDeletion.range, candidate.range);
|
|
3345
|
-
continue;
|
|
3346
|
-
}
|
|
3347
|
-
result.push(candidate);
|
|
3348
|
-
}
|
|
3349
|
-
return result;
|
|
3350
|
-
}
|
|
3351
|
-
const position = (line, char) => [line, char];
|
|
3352
|
-
const comparePosition = (a, b) => a[0] - b[0] || a[1] - b[1];
|
|
3353
|
-
const rangeStart = (range) => position(range.startLine, range.startChar);
|
|
3354
|
-
const rangeEnd = (range) => position(range.endLine, range.endChar);
|
|
3355
|
-
const rangeContains = (outer, inner) => comparePosition(rangeStart(outer), rangeStart(inner)) <= 0 && comparePosition(rangeEnd(outer), rangeEnd(inner)) >= 0;
|
|
3356
|
-
const rangesOverlap = (a, b) => comparePosition(rangeStart(a), rangeEnd(b)) <= 0 && comparePosition(rangeStart(b), rangeEnd(a)) <= 0;
|
|
3357
|
-
function unionRanges(a, b) {
|
|
3358
|
-
const start = comparePosition(rangeStart(a), rangeStart(b)) <= 0 ? a : b;
|
|
3359
|
-
const end = comparePosition(rangeEnd(a), rangeEnd(b)) >= 0 ? a : b;
|
|
3360
|
-
return {
|
|
3361
|
-
startLine: start.startLine,
|
|
3362
|
-
startChar: start.startChar,
|
|
3363
|
-
endLine: end.endLine,
|
|
3364
|
-
endChar: end.endChar
|
|
3365
|
-
};
|
|
3366
|
-
}
|
|
3367
|
-
function printExpression(expression, sourceFile, formatting) {
|
|
3368
|
-
return reindentPrintedExpression(ts$1.createPrinter({ newLine: formatting.newLineKind }).printNode(ts$1.EmitHint.Expression, expression, sourceFile), formatting);
|
|
3369
|
-
}
|
|
3370
|
-
function reindentPrintedExpression(printed, formatting) {
|
|
3371
|
-
const lineSeparator = formatting.newLineKind === ts$1.NewLineKind.CarriageReturnLineFeed ? "\r\n" : "\n";
|
|
3372
|
-
const lines = printed.split(/\r?\n/);
|
|
3373
|
-
if (lines.length <= 1) return printed;
|
|
3374
|
-
const printedIndentUnit = Math.min(...lines.slice(1).map((line) => line.match(/^ */)?.[0].length ?? 0).filter((indent) => indent > 0), 4);
|
|
3375
|
-
return lines.map((line, index) => {
|
|
3376
|
-
if (index === 0) return line;
|
|
3377
|
-
if (line.trim() === "") return "";
|
|
3378
|
-
const leadingSpaces = line.match(/^ */)?.[0].length ?? 0;
|
|
3379
|
-
const indentLevel = Math.floor(leadingSpaces / printedIndentUnit);
|
|
3380
|
-
return `${formatting.baseIndent}${formatting.indentUnit.repeat(indentLevel)}${line.slice(leadingSpaces)}`;
|
|
3381
|
-
}).join(lineSeparator);
|
|
3382
|
-
}
|
|
3383
|
-
function getFormattingContext(document, sourceFile, node) {
|
|
3384
|
-
const sourceText = sourceFile.text;
|
|
3385
|
-
const lineStart = getLineStartOffset(sourceText, node.getStart(sourceFile));
|
|
3386
|
-
const lineIndent = sourceText.slice(lineStart).match(/^[ \t]*/)?.[0] ?? "";
|
|
3387
|
-
const editorIndentUnit = getEditorIndentUnit(document);
|
|
3388
|
-
const inferredIndentUnit = inferIndentUnitFromNode(sourceText, node, sourceFile, lineIndent) ?? inferIndentUnitFromSource(sourceText);
|
|
3389
|
-
return {
|
|
3390
|
-
baseIndent: lineIndent,
|
|
3391
|
-
indentUnit: editorIndentUnit ?? inferredIndentUnit ?? " ",
|
|
3392
|
-
newLineKind: sourceText.includes("\r\n") ? ts$1.NewLineKind.CarriageReturnLineFeed : ts$1.NewLineKind.LineFeed
|
|
3393
|
-
};
|
|
3394
|
-
}
|
|
3395
|
-
function getFormattingContextForExpression(document, sourceFile, node) {
|
|
3396
|
-
const sourceText = sourceFile.text;
|
|
3397
|
-
const nodeStart = node.getStart(sourceFile);
|
|
3398
|
-
const lineStart = getLineStartOffset(sourceText, nodeStart);
|
|
3399
|
-
return {
|
|
3400
|
-
baseIndent: sourceText.slice(lineStart, nodeStart).match(/^[ \t]*/)?.[0] ?? "",
|
|
3401
|
-
indentUnit: getEditorIndentUnit(document) ?? inferIndentUnitFromSource(sourceText) ?? " ",
|
|
3402
|
-
newLineKind: sourceText.includes("\r\n") ? ts$1.NewLineKind.CarriageReturnLineFeed : ts$1.NewLineKind.LineFeed
|
|
3403
|
-
};
|
|
3404
|
-
}
|
|
3405
|
-
function getEditorIndentUnit(document) {
|
|
3406
|
-
return document.editorIndentUnit ?? null;
|
|
3407
|
-
}
|
|
3408
|
-
function inferIndentUnitFromNode(sourceText, node, sourceFile, baseIndent) {
|
|
3409
|
-
return getShortestIndent(sourceText.slice(node.getStart(sourceFile), node.getEnd()).split(/\r?\n/).slice(1).map((line) => line.match(/^[ \t]*/)?.[0] ?? "").filter((indent) => indent.startsWith(baseIndent)).map((indent) => indent.slice(baseIndent.length)).filter((indent) => indent.length > 0));
|
|
3410
|
-
}
|
|
3411
|
-
function inferIndentUnitFromSource(sourceText) {
|
|
3412
|
-
return getShortestIndent(sourceText.split(/\r?\n/).map((line) => line.match(/^[ \t]+(?=\S)/)?.[0] ?? "").filter(Boolean));
|
|
3413
|
-
}
|
|
3414
|
-
function getShortestIndent(indents) {
|
|
3415
|
-
if (indents.length === 0) return null;
|
|
3416
|
-
return indents.reduce((shortest, current) => current.length < shortest.length ? current : shortest);
|
|
3417
|
-
}
|
|
3418
|
-
function getLineStartOffset(sourceText, offset) {
|
|
3419
|
-
const lastNewline = sourceText.lastIndexOf("\n", offset - 1);
|
|
3420
|
-
if (lastNewline === -1) return 0;
|
|
3421
|
-
return lastNewline + 1;
|
|
3422
|
-
}
|
|
3423
|
-
function valueToExpression(value, existingExpression) {
|
|
3424
|
-
const rawCodeExpression = rawCodeToExpression(value);
|
|
3425
|
-
if (rawCodeExpression) return rawCodeExpression;
|
|
3426
|
-
const inlineExpression = inlineExpressionDirectiveToExpression(value);
|
|
3427
|
-
if (inlineExpression) return inlineExpression;
|
|
3428
|
-
if (value === null) return ts$1.factory.createNull();
|
|
3429
|
-
if (typeof value === "string") return ts$1.factory.createStringLiteral(value);
|
|
3430
|
-
if (typeof value === "number") {
|
|
3431
|
-
if (value < 0) return ts$1.factory.createPrefixUnaryExpression(ts$1.SyntaxKind.MinusToken, ts$1.factory.createNumericLiteral(Math.abs(value)));
|
|
3432
|
-
return ts$1.factory.createNumericLiteral(value);
|
|
3433
|
-
}
|
|
3434
|
-
if (typeof value === "boolean") return value ? ts$1.factory.createTrue() : ts$1.factory.createFalse();
|
|
3435
|
-
if (Array.isArray(value)) {
|
|
3436
|
-
const existingArray = existingExpression && ts$1.isArrayLiteralExpression(existingExpression) ? existingExpression : void 0;
|
|
3437
|
-
const elements = value.map((item, index) => valueToExpression(item, existingArray && ts$1.isExpression(existingArray.elements[index]) ? existingArray.elements[index] : void 0));
|
|
3438
|
-
if (existingArray) {
|
|
3439
|
-
const updatedArray = ts$1.factory.createArrayLiteralExpression(elements, true);
|
|
3440
|
-
ts$1.setOriginalNode(updatedArray, existingArray);
|
|
3441
|
-
ts$1.setTextRange(updatedArray, existingArray);
|
|
3442
|
-
return updatedArray;
|
|
3443
|
-
}
|
|
3444
|
-
return ts$1.factory.createArrayLiteralExpression(elements, true);
|
|
3445
|
-
}
|
|
3446
|
-
if (isMachineConfig(value)) {
|
|
3447
|
-
const existingObject = existingExpression && ts$1.isObjectLiteralExpression(existingExpression) ? existingExpression : void 0;
|
|
3448
|
-
const entries = Object.entries(value).filter(([, propertyValue]) => propertyValue !== void 0);
|
|
3449
|
-
if (existingObject) {
|
|
3450
|
-
const existingOrder = new Map(existingObject.properties.map((property, index) => [getStatePropertyKeyText(property), index]));
|
|
3451
|
-
entries.sort(([left], [right]) => (existingOrder.get(left) ?? Number.MAX_SAFE_INTEGER) - (existingOrder.get(right) ?? Number.MAX_SAFE_INTEGER));
|
|
3452
|
-
}
|
|
3453
|
-
const properties = entries.map(([key, propertyValue]) => {
|
|
3454
|
-
const existingProperty = existingObject ? getPropertyAssignment(existingObject, key) : null;
|
|
3455
|
-
const nextInitializer = getPreservedPropertyInitializer(key, propertyValue, existingProperty?.initializer) ?? valueToExpression(propertyValue, existingProperty?.initializer);
|
|
3456
|
-
if (existingProperty) return ts$1.factory.updatePropertyAssignment(existingProperty, existingProperty.name, nextInitializer);
|
|
3457
|
-
return ts$1.factory.createPropertyAssignment(toPropertyName(key), nextInitializer);
|
|
3458
|
-
});
|
|
3459
|
-
if (existingObject) {
|
|
3460
|
-
const updatedObject = ts$1.factory.createObjectLiteralExpression(properties, true);
|
|
3461
|
-
ts$1.setOriginalNode(updatedObject, existingObject);
|
|
3462
|
-
ts$1.setTextRange(updatedObject, existingObject);
|
|
3463
|
-
return updatedObject;
|
|
3464
|
-
}
|
|
3465
|
-
return ts$1.factory.createObjectLiteralExpression(properties, true);
|
|
3466
|
-
}
|
|
3467
|
-
return ts$1.factory.createIdentifier("undefined");
|
|
3468
|
-
}
|
|
3469
|
-
function rawCodeToExpression(value) {
|
|
3470
|
-
if (!isRawCodeLike(value)) return null;
|
|
3471
|
-
return ts$1.factory.createIdentifier(value.code);
|
|
3472
|
-
}
|
|
3473
|
-
function isRawCodeLike(value) {
|
|
3474
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3475
|
-
const raw = value;
|
|
3476
|
-
return raw.constructor?.name === "RawCode" && typeof raw.code === "string";
|
|
3477
|
-
}
|
|
3478
|
-
function containsRawCodeLike(value) {
|
|
3479
|
-
if (isRawCodeLike(value)) return true;
|
|
3480
|
-
if (Array.isArray(value)) return value.some(containsRawCodeLike);
|
|
3481
|
-
return Boolean(value) && typeof value === "object" && Object.values(value).some(containsRawCodeLike);
|
|
3482
|
-
}
|
|
3483
|
-
function inlineExpressionDirectiveToExpression(value) {
|
|
3484
|
-
if (!isCodeExpression(value)) return null;
|
|
3485
|
-
return ts$1.factory.createIdentifier(value.expr);
|
|
3486
|
-
}
|
|
3487
|
-
function getPreservedPropertyInitializer(propertyName, nextValue, existingInitializer) {
|
|
3488
|
-
if (propertyName !== "description" || typeof nextValue !== "string" || !existingInitializer) return null;
|
|
3489
|
-
if (ts$1.isStringLiteralLike(existingInitializer)) return existingInitializer.text === nextValue ? existingInitializer : null;
|
|
3490
|
-
return existingInitializer;
|
|
3491
|
-
}
|
|
3492
|
-
function getPropertyAssignment(objectLiteral, propertyName) {
|
|
3493
|
-
for (const property of objectLiteral.properties) {
|
|
3494
|
-
if (!ts$1.isPropertyAssignment(property)) continue;
|
|
3495
|
-
if (getPropertyNameText$1(property.name) === propertyName) return property;
|
|
3496
|
-
}
|
|
3497
|
-
return null;
|
|
3498
|
-
}
|
|
3499
|
-
function toPropertyName(name) {
|
|
3500
|
-
return isIdentifierText(name) ? ts$1.factory.createIdentifier(name) : ts$1.factory.createStringLiteral(name);
|
|
3501
|
-
}
|
|
3502
|
-
function isIdentifierText(value) {
|
|
3503
|
-
return /^[$A-Z_][0-9A-Z_$]*$/i.test(value);
|
|
3504
|
-
}
|
|
3505
|
-
|
|
3506
|
-
//#endregion
|
|
3507
|
-
//#region ../editor-sync/src/internal/statelyMetadata.ts
|
|
3508
|
-
const STATELY_METADATA_PREFIX = "@statelyai.";
|
|
3509
|
-
function isPlainObject(value) {
|
|
3510
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3511
|
-
}
|
|
3512
|
-
function stripStatelyMetadata(value) {
|
|
3513
|
-
if (Array.isArray(value)) return value.map((item) => stripStatelyMetadata(item));
|
|
3514
|
-
if (!isPlainObject(value)) return value;
|
|
3515
|
-
const result = {};
|
|
3516
|
-
for (const [key, child] of Object.entries(value)) {
|
|
3517
|
-
if (key.startsWith(STATELY_METADATA_PREFIX)) continue;
|
|
3518
|
-
const nextChild = stripStatelyMetadata(child);
|
|
3519
|
-
if (isPlainObject(nextChild) && Object.keys(nextChild).length === 0 && isPlainObject(child)) continue;
|
|
3520
|
-
result[key] = nextChild;
|
|
3521
|
-
}
|
|
3522
|
-
return result;
|
|
3523
|
-
}
|
|
3524
|
-
|
|
3525
|
-
//#endregion
|
|
3526
|
-
//#region ../editor-sync/src/internal/adapters.ts
|
|
3527
|
-
const xstateAdapter = {
|
|
3528
|
-
id: "xstate",
|
|
3529
|
-
format: "xstate",
|
|
3530
|
-
matches(document, text) {
|
|
3531
|
-
return isJavaScriptOrTypeScriptFile(document.fileName) && looksLikeXStateMachine(text);
|
|
3532
|
-
},
|
|
3533
|
-
matchesFallback(document) {
|
|
3534
|
-
return isJavaScriptOrTypeScriptFile(document.fileName);
|
|
3535
|
-
},
|
|
3536
|
-
parseSourceToMachine(text, document, options) {
|
|
3537
|
-
return parseSourceToMachine(text, document, options);
|
|
3538
|
-
},
|
|
3539
|
-
collectSourceLocations(text, document, options) {
|
|
3540
|
-
return collectXStateSourceLocations(text, document, options);
|
|
3541
|
-
},
|
|
3542
|
-
async applyMachineToDocument(machineConfig, document, options) {
|
|
3543
|
-
const detectedSourceLocations = collectXStateSourceLocations(document.getText(), document, { machineIndex: options?.machineIndex });
|
|
3544
|
-
const rootSourceLocation = options?.sourceLocations?.root ?? detectedSourceLocations?.root;
|
|
3545
|
-
const sanitizedMachineConfig = options?.preserveStatelyMetadata === false ? stripStatelyMetadata(machineConfig) : machineConfig;
|
|
3546
|
-
if (!options?.patches?.length) return [];
|
|
3547
|
-
const replacements = await applyMachineToSourceDocuments(sanitizedMachineConfig, document, options);
|
|
3548
|
-
if (replacements.some(containsXStateExpressionPlaceholder) || options?.sourceUpdateStrategy !== "rewrite" && replacements.some((replacement) => isImplicitRootReplacement(replacement, rootSourceLocation))) return [];
|
|
3549
|
-
return replacements;
|
|
3550
|
-
}
|
|
3551
|
-
};
|
|
3552
|
-
function containsXStateExpressionPlaceholder(replacement) {
|
|
3553
|
-
return /["']\{\{\s*[^}]+\s*\}\}["']/.test(replacement.newText);
|
|
3554
|
-
}
|
|
3555
|
-
function isImplicitRootReplacement(replacement, root) {
|
|
3556
|
-
if (!root) return false;
|
|
3557
|
-
if (replacement.uri && replacement.uri !== root.uri) return false;
|
|
3558
|
-
const startsBeforeRoot = replacement.range.startLine < root.range.startLine || replacement.range.startLine === root.range.startLine && replacement.range.startChar <= root.range.startChar;
|
|
3559
|
-
const endsAfterRoot = replacement.range.endLine > root.range.endLine || replacement.range.endLine === root.range.endLine && replacement.range.endChar >= root.range.endChar;
|
|
3560
|
-
return startsBeforeRoot && endsAfterRoot;
|
|
3561
|
-
}
|
|
3562
|
-
function createSerializedExportAdapter(format, matches) {
|
|
3563
|
-
return {
|
|
3564
|
-
id: format,
|
|
3565
|
-
format,
|
|
3566
|
-
matches,
|
|
3567
|
-
parseSourceToMachine(text) {
|
|
3568
|
-
return text;
|
|
3569
|
-
},
|
|
3570
|
-
async applyMachineToDocument(_machineConfig, document, options) {
|
|
3571
|
-
return createSerializedDocumentReplacement(document, options, format);
|
|
3572
|
-
}
|
|
3573
|
-
};
|
|
3574
|
-
}
|
|
3575
|
-
async function createSerializedDocumentReplacement(document, options, format) {
|
|
3576
|
-
const serialized = await options?.requestSerialized?.(format);
|
|
3577
|
-
if (typeof serialized !== "string") return [];
|
|
3578
|
-
const fullText = document.getText();
|
|
3579
|
-
const end = document.positionAt(fullText.length);
|
|
3580
|
-
return [{
|
|
3581
|
-
range: {
|
|
3582
|
-
startLine: 0,
|
|
3583
|
-
startChar: 0,
|
|
3584
|
-
endLine: end.line,
|
|
3585
|
-
endChar: end.character
|
|
3586
|
-
},
|
|
3587
|
-
newText: serialized
|
|
3588
|
-
}];
|
|
3589
|
-
}
|
|
3590
|
-
const xstateJsonAdapter = createSerializedExportAdapter("xstate-json", (document, text) => isJsonFile(document.fileName) && looksLikeXStateJson(text));
|
|
3591
|
-
const asyncAdapter = createSerializedExportAdapter("async", (document, text) => isJavaScriptOrTypeScriptFile(document.fileName) && looksLikeAsyncWorkflow(text));
|
|
3592
|
-
const xgraphAdapter = createSerializedExportAdapter("xgraph", (document, text) => isJsonFile(document.fileName) && looksLikeXGraphJson(text));
|
|
3593
|
-
const mermaidAdapter = createSerializedExportAdapter("mermaid", (document, text) => isMermaidFile(document.fileName) || looksLikeMermaid(text));
|
|
3594
|
-
const aslJsonAdapter = createSerializedExportAdapter("asl-json", (document, text) => (hasAslExtension(document.fileName) || looksLikeAmazonStatesLanguage(text)) && !isYamlFile(document.fileName));
|
|
3595
|
-
const aslYamlAdapter = createSerializedExportAdapter("asl-yaml", (document, text) => (hasAslExtension(document.fileName) || looksLikeAmazonStatesLanguage(text)) && isYamlFile(document.fileName));
|
|
3596
|
-
const adapters = [
|
|
3597
|
-
xstateAdapter,
|
|
3598
|
-
asyncAdapter,
|
|
3599
|
-
createSerializedExportAdapter("redux", (document, text) => isJavaScriptOrTypeScriptFile(document.fileName) && looksLikeReduxToolkit(text)),
|
|
3600
|
-
createSerializedExportAdapter("zustand", (document, text) => isJavaScriptOrTypeScriptFile(document.fileName) && looksLikeZustand(text)),
|
|
3601
|
-
aslYamlAdapter,
|
|
3602
|
-
aslJsonAdapter,
|
|
3603
|
-
mermaidAdapter,
|
|
3604
|
-
xgraphAdapter,
|
|
3605
|
-
xstateJsonAdapter
|
|
3606
|
-
];
|
|
3607
|
-
function isJavaScriptOrTypeScriptFile(fileName) {
|
|
3608
|
-
return /\.(?:c|m)?(?:jsx?|tsx?)$/i.test(fileName);
|
|
3609
|
-
}
|
|
3610
|
-
function isYamlFile(fileName) {
|
|
3611
|
-
return /\.(?:asl\.)?ya?ml$/i.test(fileName);
|
|
3612
|
-
}
|
|
3613
|
-
function hasAslExtension(fileName) {
|
|
3614
|
-
return /\.asl\.(?:jsonc?|ya?ml)$/i.test(fileName);
|
|
3615
|
-
}
|
|
3616
|
-
function isJsonFile(fileName) {
|
|
3617
|
-
return /\.jsonc?$/i.test(fileName);
|
|
3618
|
-
}
|
|
3619
|
-
function isMermaidFile(fileName) {
|
|
3620
|
-
return /\.(?:mmd|mermaid)$/i.test(fileName);
|
|
3621
|
-
}
|
|
3622
|
-
function stripMermaidFrontmatter(text) {
|
|
3623
|
-
return text.replace(/^\s*---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
3624
|
-
}
|
|
3625
|
-
function looksLikeMermaid(text) {
|
|
3626
|
-
return /^\s*(?:stateDiagram(?:-v2)?|flowchart(?:\s+(?:TB|TD|BT|LR|RL))?|graph\s+(?:TB|TD|BT|LR|RL))\b/.test(stripMermaidFrontmatter(text));
|
|
3627
|
-
}
|
|
3628
|
-
function looksLikeAmazonStatesLanguage(text) {
|
|
3629
|
-
return /(^|\s|["'])StartAt["']?\s*:/m.test(text) && /(^|\s|["'])States["']?\s*:/m.test(text);
|
|
3630
|
-
}
|
|
3631
|
-
function looksLikeXStateJson(text) {
|
|
3632
|
-
try {
|
|
3633
|
-
const value = JSON.parse(text);
|
|
3634
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "states" in value);
|
|
3635
|
-
} catch {
|
|
3636
|
-
return false;
|
|
3637
|
-
}
|
|
3638
|
-
}
|
|
3639
|
-
function looksLikeXGraphJson(text) {
|
|
3640
|
-
try {
|
|
3641
|
-
const value = JSON.parse(text);
|
|
3642
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Array.isArray(value.nodes) && Array.isArray(value.edges));
|
|
3643
|
-
} catch {
|
|
3644
|
-
return false;
|
|
3645
|
-
}
|
|
3646
|
-
}
|
|
3647
|
-
function looksLikeReduxToolkit(text) {
|
|
3648
|
-
return /\bcreateSlice\s*\(/.test(text) && /@reduxjs\/toolkit/.test(text);
|
|
3649
|
-
}
|
|
3650
|
-
function looksLikeZustand(text) {
|
|
3651
|
-
return /\bcreate\s*\(\s*\(/.test(text) && /from\s+["']zustand["']/.test(text);
|
|
3652
|
-
}
|
|
3653
|
-
function looksLikeAsyncWorkflow(text) {
|
|
3654
|
-
return /\bawait\b/.test(text) && (/\basync\s+function\b/.test(text) || /\basync\s*\([^)]*\)\s*=>/.test(text) || /\basync\s+\w+\s*=>/.test(text));
|
|
3655
|
-
}
|
|
3656
|
-
function looksLikeXStateMachine(text) {
|
|
3657
|
-
return /\b(?:createMachine|createMachineFromConfig)\s*\(/.test(text) || /\bMachine\s*\(/.test(text) || /\.createMachine\s*\(/.test(text);
|
|
3658
|
-
}
|
|
3659
|
-
function getSyncAdapter(document, text = "") {
|
|
3660
|
-
const resolvedText = text || document.getText?.() || "";
|
|
3661
|
-
const matched = adapters.find((adapter) => adapter.matches(document, resolvedText));
|
|
3662
|
-
if (matched) return matched;
|
|
3663
|
-
const fallback = adapters.find((adapter) => adapter.matchesFallback?.(document, resolvedText));
|
|
3664
|
-
if (fallback) return fallback;
|
|
3665
|
-
throw new Error(`Unsupported document for Stately sync: ${document.fileName}`);
|
|
3666
|
-
}
|
|
3667
|
-
function parseDocumentToMachine(document, options = {}) {
|
|
3668
|
-
const text = document.getText();
|
|
3669
|
-
const adapter = getSyncAdapter(document, text);
|
|
3670
|
-
return {
|
|
3671
|
-
adapter,
|
|
3672
|
-
format: adapter.format,
|
|
3673
|
-
machine: adapter.parseSourceToMachine(text, document, options),
|
|
3674
|
-
sourceLocations: adapter.collectSourceLocations?.(text, document, options)
|
|
3675
|
-
};
|
|
3676
|
-
}
|
|
3677
|
-
|
|
3678
|
-
//#endregion
|
|
3679
|
-
//#region src/pushMerge.ts
|
|
3680
|
-
const GEOMETRY_KEYS = [
|
|
3681
|
-
"x",
|
|
3682
|
-
"y",
|
|
3683
|
-
"width",
|
|
3684
|
-
"height"
|
|
3685
|
-
];
|
|
3686
|
-
const DEFAULT_NODE_SIZE = {
|
|
3687
|
-
width: 160,
|
|
3688
|
-
height: 64
|
|
3689
|
-
};
|
|
3690
|
-
const DEFAULT_ANNOTATION_SIZE = {
|
|
3691
|
-
width: 240,
|
|
3692
|
-
height: 120
|
|
3693
|
-
};
|
|
3694
|
-
const NEW_NODE_GAP = 80;
|
|
3695
|
-
const MAX_CONCURRENT_MERGE_ATTEMPTS = 3;
|
|
3696
|
-
function indexBySemanticId(normalized, original) {
|
|
3697
|
-
const bySemanticId = /* @__PURE__ */ new Map();
|
|
3698
|
-
normalized.forEach((entity, index) => {
|
|
3699
|
-
const source = original[index];
|
|
3700
|
-
if (source && !bySemanticId.has(entity.id)) bySemanticId.set(entity.id, source);
|
|
3701
|
-
});
|
|
3702
|
-
return bySemanticId;
|
|
3703
|
-
}
|
|
3704
|
-
function carryGeometry(target, remote) {
|
|
3705
|
-
for (const key of GEOMETRY_KEYS) if (remote[key] !== void 0) target[key] = remote[key];
|
|
3706
|
-
}
|
|
3707
|
-
function collectDigraphNodes(node, into) {
|
|
3708
|
-
into.set(node.id, node);
|
|
3709
|
-
node.nodes.forEach((child) => collectDigraphNodes(child, into));
|
|
3710
|
-
return into;
|
|
3711
|
-
}
|
|
3712
|
-
function restoreNodeFields(node, remoteNodesById, remoteNodeIdByLocalId) {
|
|
3713
|
-
const structuralChildren = [...node.nodes];
|
|
3714
|
-
const remoteId = remoteNodeIdByLocalId.get(node.id);
|
|
3715
|
-
const remote = remoteId ? remoteNodesById.get(remoteId) : void 0;
|
|
3716
|
-
if (remote) {
|
|
3717
|
-
if (remote.uniqueId !== void 0) node.uniqueId = remote.uniqueId;
|
|
3718
|
-
if (remote.data.assets !== void 0) node.data.assets = structuredClone(remote.data.assets);
|
|
3719
|
-
if (remote.data.blockFlowDirection !== void 0) node.data.blockFlowDirection = remote.data.blockFlowDirection;
|
|
3720
|
-
if (remote.data.order !== void 0) node.data.order = remote.data.order;
|
|
3721
|
-
const existingIds = new Set(node.nodes.map((child) => child.id));
|
|
3722
|
-
const annotations = remote.nodes.filter((child) => child.data.type === "annotation" && !existingIds.has(child.id));
|
|
3723
|
-
node.nodes.push(...annotations.map((annotation) => structuredClone(annotation)));
|
|
3724
|
-
}
|
|
3725
|
-
structuralChildren.forEach((child) => restoreNodeFields(child, remoteNodesById, remoteNodeIdByLocalId));
|
|
3726
|
-
}
|
|
3727
|
-
function restoreEdgeFields(edges, remoteEdgesById, remoteEdgeIdByLocalId) {
|
|
3728
|
-
for (const edge of edges) {
|
|
3729
|
-
const remoteId = remoteEdgeIdByLocalId.get(edge.id);
|
|
3730
|
-
const remote = remoteId ? remoteEdgesById.get(remoteId) : void 0;
|
|
3731
|
-
if (remote) {
|
|
3732
|
-
if (remote.uniqueId !== void 0) edge.uniqueId = remote.uniqueId;
|
|
3733
|
-
if (remote.data.assets !== void 0) edge.data.assets = structuredClone(remote.data.assets);
|
|
3734
|
-
}
|
|
3735
|
-
}
|
|
3736
|
-
}
|
|
3737
|
-
function hasFinitePoint(value) {
|
|
3738
|
-
return Boolean(value && Number.isFinite(value.x) && Number.isFinite(value.y));
|
|
3739
|
-
}
|
|
3740
|
-
function hasFiniteSize(value) {
|
|
3741
|
-
return Boolean(value && Number.isFinite(value.width) && Number.isFinite(value.height) && value.width > 0 && value.height > 0);
|
|
3742
|
-
}
|
|
3743
|
-
function ensureCompleteNodeGeometry(rootNode) {
|
|
3744
|
-
if (!hasFinitePoint(rootNode.position)) rootNode.position = {
|
|
3745
|
-
x: 0,
|
|
3746
|
-
y: 0
|
|
3747
|
-
};
|
|
3748
|
-
if (!hasFiniteSize(rootNode.size)) rootNode.size = {
|
|
3749
|
-
width: 320,
|
|
3750
|
-
height: 200
|
|
3751
|
-
};
|
|
3752
|
-
const visit = (parent) => {
|
|
3753
|
-
const parentPosition = parent.position;
|
|
3754
|
-
let nextX = parentPosition.x + 40;
|
|
3755
|
-
for (const child of parent.nodes) if (hasFinitePoint(child.position) && hasFiniteSize(child.size)) nextX = Math.max(nextX, child.position.x + child.size.width + NEW_NODE_GAP);
|
|
3756
|
-
for (const child of parent.nodes) {
|
|
3757
|
-
const childSize = hasFiniteSize(child.size) ? child.size : child.data.type === "annotation" ? { ...DEFAULT_ANNOTATION_SIZE } : { ...DEFAULT_NODE_SIZE };
|
|
3758
|
-
child.size = childSize;
|
|
3759
|
-
if (!hasFinitePoint(child.position)) {
|
|
3760
|
-
child.position = {
|
|
3761
|
-
x: nextX,
|
|
3762
|
-
y: parentPosition.y + 80
|
|
3763
|
-
};
|
|
3764
|
-
nextX += childSize.width + NEW_NODE_GAP;
|
|
3765
|
-
}
|
|
3766
|
-
visit(child);
|
|
3767
|
-
}
|
|
3768
|
-
};
|
|
3769
|
-
visit(rootNode);
|
|
3770
|
-
}
|
|
3771
|
-
function mergeLocalGraphIntoRemoteDefinition(localGraph, remoteDefinition) {
|
|
3772
|
-
const remoteNodesById = collectDigraphNodes(remoteDefinition.rootNode, /* @__PURE__ */ new Map());
|
|
3773
|
-
const annotationIds = new Set([...remoteNodesById.values()].filter((node) => node.data.type === "annotation").map((node) => node.id));
|
|
3774
|
-
const remoteGraph = fromStudioMachine(remoteDefinition);
|
|
3775
|
-
remoteGraph.nodes = remoteGraph.nodes.filter((node) => !annotationIds.has(node.id));
|
|
3776
|
-
remoteGraph.edges = remoteGraph.edges.filter((edge) => !annotationIds.has(edge.sourceId) && !annotationIds.has(edge.targetId));
|
|
3777
|
-
const { base, compare, summary } = diffGraphsSemantically(remoteGraph, localGraph);
|
|
3778
|
-
const remoteNodesBySemanticId = indexBySemanticId(base.nodes, remoteGraph.nodes);
|
|
3779
|
-
const remoteEdgesBySemanticId = indexBySemanticId(base.edges, remoteGraph.edges);
|
|
3780
|
-
const merged = structuredClone(localGraph);
|
|
3781
|
-
const remoteNodeIdByLocalId = /* @__PURE__ */ new Map();
|
|
3782
|
-
const remoteEdgeIdByLocalId = /* @__PURE__ */ new Map();
|
|
3783
|
-
merged.nodes.forEach((node, index) => {
|
|
3784
|
-
const semanticId = compare.nodes[index]?.id;
|
|
3785
|
-
const remote = semanticId ? remoteNodesBySemanticId.get(semanticId) : void 0;
|
|
3786
|
-
if (!remote) return;
|
|
3787
|
-
remoteNodeIdByLocalId.set(node.id, remote.id);
|
|
3788
|
-
carryGeometry(node, remote);
|
|
3789
|
-
if (node.data.color === void 0 && remote.data?.color !== void 0) node.data.color = remote.data.color;
|
|
3790
|
-
});
|
|
3791
|
-
merged.edges.forEach((edge, index) => {
|
|
3792
|
-
const semanticId = compare.edges[index]?.id;
|
|
3793
|
-
const remote = semanticId ? remoteEdgesBySemanticId.get(semanticId) : void 0;
|
|
3794
|
-
if (!remote) return;
|
|
3795
|
-
remoteEdgeIdByLocalId.set(edge.id, remote.id);
|
|
3796
|
-
carryGeometry(edge, remote);
|
|
3797
|
-
if (edge.data.color === void 0 && remote.data?.color !== void 0) edge.data.color = remote.data.color;
|
|
3798
|
-
});
|
|
3799
|
-
const definition = toStudioMachine(merged);
|
|
3800
|
-
restoreNodeFields(definition.rootNode, remoteNodesById, remoteNodeIdByLocalId);
|
|
3801
|
-
restoreEdgeFields(definition.edges, new Map(remoteDefinition.edges.map((edge) => [edge.id, edge])), remoteEdgeIdByLocalId);
|
|
3802
|
-
ensureCompleteNodeGeometry(definition.rootNode);
|
|
3803
|
-
return {
|
|
3804
|
-
definition: {
|
|
3805
|
-
...remoteDefinition,
|
|
3806
|
-
...definition,
|
|
3807
|
-
context: remoteDefinition.context
|
|
3808
|
-
},
|
|
3809
|
-
summary
|
|
3810
|
-
};
|
|
3811
|
-
}
|
|
3812
|
-
function isRecord(value) {
|
|
3813
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3814
|
-
}
|
|
3815
|
-
function isDigraphNodeConfig(value) {
|
|
3816
|
-
if (!isRecord(value) || typeof value.id !== "string") return false;
|
|
3817
|
-
if (!isRecord(value.data) || typeof value.data.key !== "string") return false;
|
|
3818
|
-
if (!Array.isArray(value.data.entry) || !Array.isArray(value.data.exit) || !Array.isArray(value.data.invoke) || !Array.isArray(value.data.tags) || !Array.isArray(value.nodes)) return false;
|
|
3819
|
-
return value.nodes.every(isDigraphNodeConfig);
|
|
3820
|
-
}
|
|
3821
|
-
function isDigraphEdgeConfig(value) {
|
|
3822
|
-
return isRecord(value) && typeof value.id === "string" && typeof value.source === "string" && (value.target === void 0 || typeof value.target === "string") && isRecord(value.data) && isRecord(value.data.eventTypeData) && Array.isArray(value.data.actions);
|
|
3823
|
-
}
|
|
3824
|
-
function isDigraphConfig(value) {
|
|
3825
|
-
if (!isRecord(value) || typeof value.id !== "string" || !isDigraphNodeConfig(value.rootNode) || !Array.isArray(value.edges) || !value.edges.every(isDigraphEdgeConfig)) return false;
|
|
3826
|
-
if (value.implementations !== void 0) {
|
|
3827
|
-
if (!isRecord(value.implementations)) return false;
|
|
3828
|
-
if (!isRecord(value.implementations.actions) || !isRecord(value.implementations.actors) || !isRecord(value.implementations.guards)) return false;
|
|
3829
|
-
}
|
|
3830
|
-
return true;
|
|
3831
|
-
}
|
|
3832
|
-
function requireDigraphConfig(machineId, value) {
|
|
3833
|
-
if (!isDigraphConfig(value)) throw new Error(`Cannot safely push machine "${machineId}": its remote definition is invalid.`);
|
|
3834
|
-
return value;
|
|
3835
|
-
}
|
|
3836
|
-
function stableStringify(value) {
|
|
3837
|
-
return JSON.stringify(value, (_key, item) => {
|
|
3838
|
-
if (isRecord(item)) return Object.fromEntries(Object.entries(item).sort(([left], [right]) => left.localeCompare(right)));
|
|
3839
|
-
return item;
|
|
3840
|
-
});
|
|
3841
|
-
}
|
|
3842
|
-
function definitionsMatch(left, right) {
|
|
3843
|
-
return stableStringify(left) === stableStringify(right);
|
|
3844
|
-
}
|
|
3845
|
-
async function updateMachineDefinition(client, machineId, localGraph) {
|
|
3846
|
-
if (typeof client.machines?.get !== "function") return client.machines.update({
|
|
3847
|
-
id: machineId,
|
|
3848
|
-
definition: toStudioMachine(localGraph)
|
|
3849
|
-
});
|
|
3850
|
-
let remoteRecord = await client.machines.get(machineId);
|
|
3851
|
-
for (let attempt = 0; attempt < MAX_CONCURRENT_MERGE_ATTEMPTS; attempt += 1) {
|
|
3852
|
-
const remoteDefinition = requireDigraphConfig(machineId, remoteRecord.definition);
|
|
3853
|
-
const mergedDefinition = mergeLocalGraphIntoRemoteDefinition(localGraph, remoteDefinition).definition;
|
|
3854
|
-
const latestRecord = await client.machines.get(machineId);
|
|
3855
|
-
requireDigraphConfig(machineId, latestRecord.definition);
|
|
3856
|
-
if (definitionsMatch(remoteDefinition, latestRecord.definition)) return client.machines.update({
|
|
3857
|
-
id: machineId,
|
|
3858
|
-
definition: mergedDefinition
|
|
3859
|
-
});
|
|
3860
|
-
remoteRecord = latestRecord;
|
|
3861
|
-
}
|
|
3862
|
-
throw new Error(`Cannot safely push machine "${machineId}": its remote definition kept changing during the push. Try again after Studio edits have settled.`);
|
|
3863
|
-
}
|
|
3864
|
-
|
|
3865
|
-
//#endregion
|
|
3866
|
-
//#region src/sync.ts
|
|
3867
|
-
const execFileAsync = promisify(execFile);
|
|
3868
|
-
const MAX_CAPTURED_LOCAL_MACHINES = 100;
|
|
3869
|
-
var PullSourceReconciliationError = class extends Error {
|
|
3870
|
-
code = "source_reconciliation_failed";
|
|
3871
|
-
constructor(message) {
|
|
3872
|
-
super(message);
|
|
3873
|
-
this.name = "PullSourceReconciliationError";
|
|
3874
|
-
}
|
|
3875
|
-
};
|
|
3876
|
-
const INLINE_EXPRESSION_FIELD_SET = new Set(INLINE_EXPRESSION_DIRECTIVE_FIELDS);
|
|
3877
|
-
async function extractMachinesFromLocalSource(sourcePath, contents, machineIndex) {
|
|
3878
|
-
try {
|
|
3879
|
-
return { machines: extractMachineConfigsFromAst(sourcePath, parseSourceToMachine(contents, createTextDocument(sourcePath, contents), {
|
|
3880
|
-
machineIndex,
|
|
3881
|
-
resolver: createFileSourceDocumentResolver()
|
|
3882
|
-
}), machineIndex === void 0 ? void 0 : 0) };
|
|
3883
|
-
} catch (error) {
|
|
3884
|
-
return {
|
|
3885
|
-
machines: [],
|
|
3886
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3887
|
-
};
|
|
3888
|
-
}
|
|
3889
|
-
}
|
|
3890
|
-
function isInvalidLocalMachineDefinitionError(message) {
|
|
3891
|
-
return /No initial state specified for compound state node/.test(message) || /Try adding \{\s*initial:\s*['"`]/.test(message);
|
|
3892
|
-
}
|
|
3893
|
-
function formatLocalMachineExtractionIssue(message) {
|
|
3894
|
-
if (isInvalidLocalMachineDefinitionError(message)) return `Invalid local machine definition: ${message}`;
|
|
3895
|
-
return `Could not extract a local machine from this file: ${message}`;
|
|
3896
|
-
}
|
|
3897
|
-
function extractMachineConfigsFromAst(fileName, sourceText, selectedMachineIndex) {
|
|
3898
|
-
const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(fileName));
|
|
3899
|
-
const bindings = collectLocalBindings(sourceFile);
|
|
3900
|
-
const machines = [];
|
|
3901
|
-
let currentMachineIndex = 0;
|
|
3902
|
-
const visit = (node) => {
|
|
3903
|
-
if (selectedMachineIndex === void 0 && machines.length >= MAX_CAPTURED_LOCAL_MACHINES) return;
|
|
3904
|
-
if (!ts.isCallExpression(node) || !isCreateMachineExpression(node.expression)) {
|
|
3905
|
-
ts.forEachChild(node, visit);
|
|
3906
|
-
return;
|
|
3907
|
-
}
|
|
3908
|
-
const machineIndex = currentMachineIndex;
|
|
3909
|
-
currentMachineIndex += 1;
|
|
3910
|
-
if (selectedMachineIndex !== void 0 && machineIndex !== selectedMachineIndex) return;
|
|
3911
|
-
const [firstArgument] = node.arguments;
|
|
3912
|
-
if (!firstArgument || !ts.isExpression(firstArgument)) return;
|
|
3913
|
-
const config = evaluateExpression(firstArgument, sourceFile, bindings, /* @__PURE__ */ new Set());
|
|
3914
|
-
if (!config || typeof config !== "object" || Array.isArray(config)) return;
|
|
3915
|
-
machines.push({
|
|
3916
|
-
config,
|
|
3917
|
-
_type: ts.isPropertyAccessExpression(unwrapExpression(node.expression)) ? "setup.createMachine" : "createMachine"
|
|
3918
|
-
});
|
|
3919
|
-
};
|
|
3920
|
-
ts.forEachChild(sourceFile, visit);
|
|
3921
|
-
return machines;
|
|
3922
|
-
}
|
|
3923
|
-
function countSourceMachines(fileName, sourceText) {
|
|
3924
|
-
const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(fileName));
|
|
3925
|
-
let count = 0;
|
|
3926
|
-
const visit = (node) => {
|
|
3927
|
-
if (ts.isCallExpression(node) && isCreateMachineExpression(node.expression)) count += 1;
|
|
3928
|
-
ts.forEachChild(node, visit);
|
|
3929
|
-
};
|
|
3930
|
-
visit(sourceFile);
|
|
3931
|
-
return count;
|
|
3932
|
-
}
|
|
3933
|
-
function collectLocalBindings(sourceFile) {
|
|
3934
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
3935
|
-
for (const statement of sourceFile.statements) {
|
|
3936
|
-
if (ts.isVariableStatement(statement)) {
|
|
3937
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
3938
|
-
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
3939
|
-
bindings.set(declaration.name.text, {
|
|
3940
|
-
kind: "value",
|
|
3941
|
-
expression: declaration.initializer
|
|
3942
|
-
});
|
|
3943
|
-
}
|
|
3944
|
-
continue;
|
|
3945
|
-
}
|
|
3946
|
-
if (!ts.isEnumDeclaration(statement)) continue;
|
|
3947
|
-
let nextNumericValue = 0;
|
|
3948
|
-
for (const member of statement.members) {
|
|
3949
|
-
const memberName = getPropertyNameText(member.name);
|
|
3950
|
-
if (!memberName) continue;
|
|
3951
|
-
const symbol = `${statement.name.text}.${memberName}`;
|
|
3952
|
-
if (!member.initializer) {
|
|
3953
|
-
bindings.set(symbol, {
|
|
3954
|
-
kind: "enumMember",
|
|
3955
|
-
value: nextNumericValue
|
|
3956
|
-
});
|
|
3957
|
-
nextNumericValue += 1;
|
|
3958
|
-
continue;
|
|
3959
|
-
}
|
|
3960
|
-
const value = evaluateExpression(member.initializer, sourceFile, bindings, /* @__PURE__ */ new Set());
|
|
3961
|
-
if (typeof value === "string" || typeof value === "number") {
|
|
3962
|
-
bindings.set(symbol, {
|
|
3963
|
-
kind: "enumMember",
|
|
3964
|
-
value
|
|
3965
|
-
});
|
|
3966
|
-
if (typeof value === "number") nextNumericValue = value + 1;
|
|
3967
|
-
}
|
|
3968
|
-
}
|
|
3969
|
-
}
|
|
3970
|
-
return bindings;
|
|
3971
|
-
}
|
|
3972
|
-
function evaluateExpression(expression, sourceFile, bindings, seenBindings) {
|
|
3973
|
-
const unwrapped = unwrapExpression(expression);
|
|
3974
|
-
if (ts.isStringLiteral(unwrapped) || ts.isNoSubstitutionTemplateLiteral(unwrapped)) return unwrapped.text;
|
|
3975
|
-
if (ts.isNumericLiteral(unwrapped)) return Number(unwrapped.text);
|
|
3976
|
-
if (unwrapped.kind === ts.SyntaxKind.TrueKeyword) return true;
|
|
3977
|
-
if (unwrapped.kind === ts.SyntaxKind.FalseKeyword) return false;
|
|
3978
|
-
if (unwrapped.kind === ts.SyntaxKind.NullKeyword) return null;
|
|
3979
|
-
if (ts.isIdentifier(unwrapped)) {
|
|
3980
|
-
if (unwrapped.text === "undefined") return;
|
|
3981
|
-
const binding = bindings.get(unwrapped.text);
|
|
3982
|
-
if (!binding || binding.kind !== "value" || seenBindings.has(unwrapped.text)) return unwrapped.getText(sourceFile);
|
|
3983
|
-
const nextSeen = new Set(seenBindings);
|
|
3984
|
-
nextSeen.add(unwrapped.text);
|
|
3985
|
-
return evaluateExpression(binding.expression, sourceFile, bindings, nextSeen);
|
|
3986
|
-
}
|
|
3987
|
-
if (ts.isPropertyAccessExpression(unwrapped) && ts.isIdentifier(unwrapped.expression)) {
|
|
3988
|
-
const binding = bindings.get(`${unwrapped.expression.text}.${unwrapped.name.text}`);
|
|
3989
|
-
if (binding?.kind === "enumMember") return binding.value;
|
|
3990
|
-
return unwrapped.getText(sourceFile);
|
|
3991
|
-
}
|
|
3992
|
-
if (ts.isArrayLiteralExpression(unwrapped)) return unwrapped.elements.map((element) => ts.isSpreadElement(element) ? evaluateExpression(element.expression, sourceFile, bindings, seenBindings) : evaluateExpression(element, sourceFile, bindings, seenBindings));
|
|
3993
|
-
if (ts.isObjectLiteralExpression(unwrapped)) {
|
|
3994
|
-
const result = {};
|
|
3995
|
-
for (const property of unwrapped.properties) {
|
|
3996
|
-
if (ts.isSpreadAssignment(property)) {
|
|
3997
|
-
const spreadValue = evaluateExpression(property.expression, sourceFile, bindings, seenBindings);
|
|
3998
|
-
if (spreadValue && typeof spreadValue === "object" && !Array.isArray(spreadValue)) Object.assign(result, spreadValue);
|
|
3999
|
-
continue;
|
|
4000
|
-
}
|
|
4001
|
-
if (ts.isShorthandPropertyAssignment(property)) {
|
|
4002
|
-
result[property.name.text] = evaluateExpression(property.name, sourceFile, bindings, seenBindings);
|
|
4003
|
-
continue;
|
|
4004
|
-
}
|
|
4005
|
-
if (!ts.isPropertyAssignment(property)) continue;
|
|
4006
|
-
const key = resolvePropertyNameValue(property.name, sourceFile, bindings, seenBindings);
|
|
4007
|
-
if (key == null) continue;
|
|
4008
|
-
const fieldName = String(key);
|
|
4009
|
-
if (shouldPreserveInlineExpression(fieldName, property.initializer, sourceFile, bindings, seenBindings)) {
|
|
4010
|
-
result[fieldName] = createInlineExpressionDirective(property.initializer.getText(sourceFile));
|
|
4011
|
-
continue;
|
|
4012
|
-
}
|
|
4013
|
-
result[fieldName] = evaluateExpression(property.initializer, sourceFile, bindings, seenBindings);
|
|
4014
|
-
}
|
|
4015
|
-
return result;
|
|
4016
|
-
}
|
|
4017
|
-
if (ts.isPrefixUnaryExpression(unwrapped)) {
|
|
4018
|
-
const operand = evaluateExpression(unwrapped.operand, sourceFile, bindings, seenBindings);
|
|
4019
|
-
if (typeof operand !== "number") return unwrapped.getText(sourceFile);
|
|
4020
|
-
switch (unwrapped.operator) {
|
|
4021
|
-
case ts.SyntaxKind.MinusToken: return -operand;
|
|
4022
|
-
case ts.SyntaxKind.PlusToken: return operand;
|
|
4023
|
-
default: return unwrapped.getText(sourceFile);
|
|
4024
|
-
}
|
|
4025
|
-
}
|
|
4026
|
-
if (ts.isTemplateExpression(unwrapped)) {
|
|
4027
|
-
let value = unwrapped.head.text;
|
|
4028
|
-
for (const span of unwrapped.templateSpans) {
|
|
4029
|
-
const expressionValue = evaluateExpression(span.expression, sourceFile, bindings, seenBindings);
|
|
4030
|
-
value += String(expressionValue ?? "");
|
|
4031
|
-
value += span.literal.text;
|
|
4032
|
-
}
|
|
4033
|
-
return value;
|
|
4034
|
-
}
|
|
4035
|
-
return unwrapped.getText(sourceFile);
|
|
4036
|
-
}
|
|
4037
|
-
function shouldPreserveInlineExpression(fieldName, expression, sourceFile, bindings, seenBindings) {
|
|
4038
|
-
if (!INLINE_EXPRESSION_FIELD_SET.has(fieldName)) return false;
|
|
4039
|
-
const unwrapped = unwrapExpression(expression);
|
|
4040
|
-
if (ts.isIdentifier(unwrapped)) {
|
|
4041
|
-
const binding = bindings.get(unwrapped.text);
|
|
4042
|
-
if (!binding || binding.kind !== "value" || seenBindings.has(unwrapped.text)) return true;
|
|
4043
|
-
const nextSeen = new Set(seenBindings);
|
|
4044
|
-
nextSeen.add(unwrapped.text);
|
|
4045
|
-
return shouldPreserveInlineExpression(fieldName, binding.expression, sourceFile, bindings, nextSeen);
|
|
4046
|
-
}
|
|
4047
|
-
if (ts.isStringLiteral(unwrapped) || ts.isNoSubstitutionTemplateLiteral(unwrapped) || ts.isNumericLiteral(unwrapped) || unwrapped.kind === ts.SyntaxKind.TrueKeyword || unwrapped.kind === ts.SyntaxKind.FalseKeyword || unwrapped.kind === ts.SyntaxKind.NullKeyword || ts.isObjectLiteralExpression(unwrapped) || ts.isArrayLiteralExpression(unwrapped)) return false;
|
|
4048
|
-
return unwrapped.getText(sourceFile).trim().length > 0;
|
|
4049
|
-
}
|
|
4050
|
-
function createInlineExpressionDirective(expr) {
|
|
4051
|
-
return {
|
|
4052
|
-
"@type": "code",
|
|
4053
|
-
lang: "ts",
|
|
4054
|
-
expr
|
|
4055
|
-
};
|
|
4056
|
-
}
|
|
4057
|
-
function resolvePropertyNameValue(name, sourceFile, bindings, seenBindings) {
|
|
4058
|
-
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
|
|
4059
|
-
if (!ts.isComputedPropertyName(name)) return null;
|
|
4060
|
-
const value = evaluateExpression(name.expression, sourceFile, bindings, seenBindings);
|
|
4061
|
-
if (typeof value === "string" || typeof value === "number") return value;
|
|
4062
|
-
return name.getText(sourceFile);
|
|
4063
|
-
}
|
|
4064
|
-
function isCreateMachineExpression(expression) {
|
|
4065
|
-
const unwrapped = unwrapExpression(expression);
|
|
4066
|
-
return ts.isIdentifier(unwrapped) && unwrapped.text === "createMachine" || ts.isPropertyAccessExpression(unwrapped) && unwrapped.name.text === "createMachine";
|
|
4067
|
-
}
|
|
4068
|
-
function unwrapExpression(expression) {
|
|
4069
|
-
let current = expression;
|
|
4070
|
-
for (;;) {
|
|
4071
|
-
if (ts.isParenthesizedExpression(current)) {
|
|
4072
|
-
current = current.expression;
|
|
4073
|
-
continue;
|
|
4074
|
-
}
|
|
4075
|
-
if (ts.isAsExpression(current) || ts.isSatisfiesExpression(current)) {
|
|
4076
|
-
current = current.expression;
|
|
4077
|
-
continue;
|
|
4078
|
-
}
|
|
4079
|
-
if (ts.isTypeAssertionExpression(current) || ts.isNonNullExpression(current)) {
|
|
4080
|
-
current = current.expression;
|
|
4081
|
-
continue;
|
|
4082
|
-
}
|
|
4083
|
-
return current;
|
|
4084
|
-
}
|
|
4085
|
-
}
|
|
4086
|
-
function getPropertyNameText(name) {
|
|
4087
|
-
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
|
|
4088
|
-
return null;
|
|
4089
|
-
}
|
|
4090
|
-
function getScriptKind(fileName) {
|
|
4091
|
-
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
4092
|
-
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
4093
|
-
if (fileName.endsWith(".js")) return ts.ScriptKind.JS;
|
|
4094
|
-
return ts.ScriptKind.TS;
|
|
4095
|
-
}
|
|
4096
|
-
function isUrl(value) {
|
|
4097
|
-
try {
|
|
4098
|
-
const url = new URL(value);
|
|
4099
|
-
return url.protocol === "http:" || url.protocol === "https:";
|
|
4100
|
-
} catch {
|
|
4101
|
-
return false;
|
|
4102
|
-
}
|
|
4103
|
-
}
|
|
4104
|
-
async function fileExists(filePath) {
|
|
4105
|
-
try {
|
|
4106
|
-
await fs.access(filePath);
|
|
4107
|
-
return true;
|
|
4108
|
-
} catch {
|
|
4109
|
-
return false;
|
|
4110
|
-
}
|
|
4111
|
-
}
|
|
4112
|
-
function parseGitHubRemote(remoteUrl) {
|
|
4113
|
-
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
4114
|
-
if (httpsMatch) {
|
|
4115
|
-
const [, owner, repo] = httpsMatch;
|
|
4116
|
-
if (!owner || !repo) return null;
|
|
4117
|
-
return {
|
|
4118
|
-
url: `https://github.com/${owner}/${repo}`,
|
|
4119
|
-
owner,
|
|
4120
|
-
repo
|
|
4121
|
-
};
|
|
4122
|
-
}
|
|
4123
|
-
const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
4124
|
-
if (sshMatch) {
|
|
4125
|
-
const [, owner, repo] = sshMatch;
|
|
4126
|
-
if (!owner || !repo) return null;
|
|
4127
|
-
return {
|
|
4128
|
-
url: `https://github.com/${owner}/${repo}`,
|
|
4129
|
-
owner,
|
|
4130
|
-
repo
|
|
4131
|
-
};
|
|
4132
|
-
}
|
|
4133
|
-
return null;
|
|
4134
|
-
}
|
|
4135
|
-
async function inferConnectedRepo(filePath, cwd) {
|
|
4136
|
-
const workingDir = cwd ?? path.dirname(filePath);
|
|
4137
|
-
try {
|
|
4138
|
-
const [{ stdout: repoRootStdout }, { stdout: remoteStdout }, { stdout: branchStdout }, { stdout: treeShaStdout }] = await Promise.all([
|
|
4139
|
-
execFileAsync("git", ["rev-parse", "--show-toplevel"], { cwd: workingDir }),
|
|
4140
|
-
execFileAsync("git", [
|
|
4141
|
-
"remote",
|
|
4142
|
-
"get-url",
|
|
4143
|
-
"origin"
|
|
4144
|
-
], { cwd: workingDir }),
|
|
4145
|
-
execFileAsync("git", ["branch", "--show-current"], { cwd: workingDir }),
|
|
4146
|
-
execFileAsync("git", ["rev-parse", "HEAD"], { cwd: workingDir })
|
|
4147
|
-
]);
|
|
4148
|
-
const repoRoot = repoRootStdout.trim();
|
|
4149
|
-
const remote = parseGitHubRemote(remoteStdout.trim());
|
|
4150
|
-
const branch = branchStdout.trim();
|
|
4151
|
-
const treeSha = treeShaStdout.trim();
|
|
4152
|
-
if (!remote || !branch || !treeSha) return;
|
|
4153
|
-
const relativePath = path.relative(repoRoot, filePath).replace(/\\/g, "/");
|
|
4154
|
-
const relativeDir = path.dirname(relativePath).replace(/\\/g, "/");
|
|
4155
|
-
const selectedPaths = relativePath && relativePath !== "." ? [relativePath] : [];
|
|
4156
|
-
return {
|
|
4157
|
-
...remote,
|
|
4158
|
-
branch,
|
|
4159
|
-
treeSha,
|
|
4160
|
-
autoSync: false,
|
|
4161
|
-
pathForNewFiles: relativeDir && relativeDir !== "." ? relativeDir : "src/stately-studio",
|
|
4162
|
-
selectedPaths
|
|
4163
|
-
};
|
|
4164
|
-
} catch {
|
|
4165
|
-
return;
|
|
4166
|
-
}
|
|
4167
|
-
}
|
|
4168
|
-
function normalizeActions(value) {
|
|
4169
|
-
if (value == null) return [];
|
|
4170
|
-
return (Array.isArray(value) ? value : [value]).map((item) => {
|
|
4171
|
-
if (typeof item === "string") return { type: item };
|
|
4172
|
-
if (typeof item === "object" && item !== null && "type" in item) {
|
|
4173
|
-
const type = item.type;
|
|
4174
|
-
const params = "params" in item && typeof item.params === "object" && item.params ? item.params : void 0;
|
|
4175
|
-
return {
|
|
4176
|
-
type,
|
|
4177
|
-
...params ? { params } : {}
|
|
4178
|
-
};
|
|
4179
|
-
}
|
|
4180
|
-
return { type: String(item) };
|
|
4181
|
-
});
|
|
4182
|
-
}
|
|
4183
|
-
function normalizeTags(value) {
|
|
4184
|
-
if (!value) return [];
|
|
4185
|
-
if (Array.isArray(value)) return value.map((tag) => String(tag));
|
|
4186
|
-
return [String(value)];
|
|
4187
|
-
}
|
|
4188
|
-
function normalizeInvoke(value) {
|
|
4189
|
-
if (!value) return [];
|
|
4190
|
-
return (Array.isArray(value) ? value : [value]).map((invoke, index) => ({
|
|
4191
|
-
src: invoke.src,
|
|
4192
|
-
id: invoke.id ?? `invoke[${index}]`,
|
|
4193
|
-
...invoke.input ? { input: invoke.input } : {}
|
|
4194
|
-
}));
|
|
4195
|
-
}
|
|
4196
|
-
function resolveTargetId(sourceParentPath, target) {
|
|
4197
|
-
if (!target) return;
|
|
4198
|
-
if (target.startsWith("#")) {
|
|
4199
|
-
const stripped = target.slice(1);
|
|
4200
|
-
const machineIndex = stripped.indexOf(".");
|
|
4201
|
-
if (machineIndex >= 0) return `(machine).${stripped.slice(machineIndex + 1)}`;
|
|
4202
|
-
return `(machine).${stripped}`;
|
|
4203
|
-
}
|
|
4204
|
-
if (target.startsWith("(machine)")) return target;
|
|
4205
|
-
if (target.includes(".")) return `(machine).${target}`;
|
|
4206
|
-
return `${sourceParentPath}.${target}`;
|
|
4207
|
-
}
|
|
4208
|
-
function appendTransitionEdges(edges, sourceId, sourceParentPath, eventType, transition, edgeCounts) {
|
|
4209
|
-
const transitions = Array.isArray(transition) ? transition : [transition];
|
|
4210
|
-
for (const item of transitions) {
|
|
4211
|
-
const normalized = typeof item === "string" ? { target: item } : item;
|
|
4212
|
-
const targetId = resolveTargetId(sourceParentPath, Array.isArray(normalized.target) ? normalized.target[0] : normalized.target);
|
|
4213
|
-
const groupKey = `${sourceId}:${eventType}`;
|
|
4214
|
-
const index = edgeCounts.get(groupKey) ?? 0;
|
|
4215
|
-
edgeCounts.set(groupKey, index + 1);
|
|
4216
|
-
edges.push({
|
|
4217
|
-
type: "edge",
|
|
4218
|
-
id: `${sourceId}#${eventType}[${index}]`,
|
|
4219
|
-
sourceId,
|
|
4220
|
-
targetId: targetId ?? sourceId,
|
|
4221
|
-
label: eventType,
|
|
4222
|
-
data: {
|
|
4223
|
-
eventType,
|
|
4224
|
-
transitionType: normalized.reenter === true ? "reenter" : normalized.internal === true || !targetId ? "targetless" : "normal",
|
|
4225
|
-
...normalized.guard ? { guard: typeof normalized.guard === "string" ? { type: normalized.guard } : {
|
|
4226
|
-
type: normalized.guard.type,
|
|
4227
|
-
...normalized.guard.params ? { params: normalized.guard.params } : {}
|
|
4228
|
-
} } : {},
|
|
4229
|
-
actions: normalizeActions(normalized.actions),
|
|
4230
|
-
...normalized.description ? { description: normalized.description } : {}
|
|
4231
|
-
}
|
|
4232
|
-
});
|
|
4233
|
-
}
|
|
4234
|
-
}
|
|
4235
|
-
function fromXStateConfig(config) {
|
|
4236
|
-
const nodes = [];
|
|
4237
|
-
const edges = [];
|
|
4238
|
-
const edgeCounts = /* @__PURE__ */ new Map();
|
|
4239
|
-
function visitNode(key, nodeConfig, parentId, parentPath) {
|
|
4240
|
-
const nodeId = parentPath ? `${parentPath}.${key}` : "(machine)";
|
|
4241
|
-
const currentPath = parentPath ? nodeId : "(machine)";
|
|
4242
|
-
const initialId = nodeConfig.initial ? `${currentPath}.${nodeConfig.initial}` : void 0;
|
|
4243
|
-
nodes.push({
|
|
4244
|
-
type: "node",
|
|
4245
|
-
id: nodeId,
|
|
4246
|
-
parentId,
|
|
4247
|
-
label: key,
|
|
4248
|
-
...initialId ? { initialNodeId: initialId } : {},
|
|
4249
|
-
data: {
|
|
4250
|
-
key,
|
|
4251
|
-
...nodeConfig.type ? { type: nodeConfig.type } : {},
|
|
4252
|
-
...nodeConfig.history ? { history: nodeConfig.history } : {},
|
|
4253
|
-
...initialId ? { initialId } : {},
|
|
4254
|
-
entry: normalizeActions(nodeConfig.entry),
|
|
4255
|
-
exit: normalizeActions(nodeConfig.exit),
|
|
4256
|
-
invokes: normalizeInvoke(nodeConfig.invoke),
|
|
4257
|
-
tags: normalizeTags(nodeConfig.tags),
|
|
4258
|
-
...nodeConfig.description ? { description: nodeConfig.description } : {}
|
|
4259
|
-
}
|
|
4260
|
-
});
|
|
4261
|
-
for (const [eventType, transition] of Object.entries(nodeConfig.on ?? {})) appendTransitionEdges(edges, nodeId, parentPath ?? "(machine)", eventType, transition, edgeCounts);
|
|
4262
|
-
if (nodeConfig.always) appendTransitionEdges(edges, nodeId, parentPath ?? "(machine)", "", nodeConfig.always, edgeCounts);
|
|
4263
|
-
for (const [childKey, childConfig] of Object.entries(nodeConfig.states ?? {})) visitNode(childKey, childConfig, nodeId, currentPath);
|
|
4264
|
-
}
|
|
4265
|
-
visitNode("(machine)", config, null, null);
|
|
4266
|
-
return {
|
|
4267
|
-
id: config.id ?? "machine",
|
|
4268
|
-
nodes,
|
|
4269
|
-
edges,
|
|
4270
|
-
data: {}
|
|
4271
|
-
};
|
|
4272
|
-
}
|
|
4273
|
-
async function pushLocalMachineLinks(options) {
|
|
4274
|
-
const client = options.client ?? createStatelyClient({
|
|
4275
|
-
apiKey: options.apiKey,
|
|
4276
|
-
baseUrl: options.baseUrl,
|
|
4277
|
-
fetch: options.fetch
|
|
4278
|
-
});
|
|
4279
|
-
const sourcePath = path.resolve(options.cwd ?? process.cwd(), options.source);
|
|
4280
|
-
const project = await resolvePushProject(client, sourcePath, options);
|
|
4281
|
-
if (!project.projectVersionId) throw new Error("Resolved project is missing projectVersionId.");
|
|
4282
|
-
const contents = await fs.readFile(sourcePath, "utf8");
|
|
4283
|
-
const attachments = findStatelyPragmaAttachments(contents, sourcePath);
|
|
4284
|
-
const extracted = await extractMachinesFromLocalSource(sourcePath, contents);
|
|
4285
|
-
if (attachments.length === 0 || extracted.machines.length === 0) return {
|
|
4286
|
-
sourcePath,
|
|
4287
|
-
project,
|
|
4288
|
-
created: [],
|
|
4289
|
-
updated: [],
|
|
4290
|
-
skipped: [{
|
|
4291
|
-
machineIndex: 0,
|
|
4292
|
-
reason: extracted.error != null ? formatLocalMachineExtractionIssue(extracted.error) : "No local machines were discovered in this file."
|
|
4293
|
-
}]
|
|
4294
|
-
};
|
|
4295
|
-
if (attachments.length !== extracted.machines.length) return {
|
|
4296
|
-
sourcePath,
|
|
4297
|
-
project,
|
|
4298
|
-
created: [],
|
|
4299
|
-
updated: [],
|
|
4300
|
-
skipped: [{
|
|
4301
|
-
machineIndex: 0,
|
|
4302
|
-
reason: "The local machine extractor did not align with the source attachments for this file."
|
|
4303
|
-
}]
|
|
4304
|
-
};
|
|
4305
|
-
let nextContents = contents;
|
|
4306
|
-
let outputPath;
|
|
4307
|
-
const created = [];
|
|
4308
|
-
const updated = [];
|
|
4309
|
-
const skipped = [];
|
|
4310
|
-
for (const [machineIndex, attachment] of attachments.entries()) {
|
|
4311
|
-
const extractedMachine = extracted.machines[machineIndex];
|
|
4312
|
-
if (!extractedMachine?.config) {
|
|
4313
|
-
skipped.push({
|
|
4314
|
-
machineIndex,
|
|
4315
|
-
reason: "No extracted machine config was available for this source."
|
|
4316
|
-
});
|
|
4317
|
-
continue;
|
|
4318
|
-
}
|
|
4319
|
-
if (attachment.pragma?.id && !options.relink) {
|
|
4320
|
-
const localGraph = fromXStateConfig(extractedMachine.config);
|
|
4321
|
-
try {
|
|
4322
|
-
const updatedMachine = await updateMachineDefinition(client, attachment.pragma.id, localGraph);
|
|
4323
|
-
updated.push({
|
|
4324
|
-
machineIndex,
|
|
4325
|
-
machine: updatedMachine
|
|
4326
|
-
});
|
|
4327
|
-
} catch (error) {
|
|
4328
|
-
skipped.push({
|
|
4329
|
-
machineIndex,
|
|
4330
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
4331
|
-
});
|
|
4332
|
-
}
|
|
4333
|
-
continue;
|
|
4334
|
-
}
|
|
4335
|
-
const machine = await client.machines.create({
|
|
4336
|
-
projectVersionId: project.projectVersionId,
|
|
4337
|
-
definition: toStudioMachine(fromXStateConfig(extractedMachine.config)),
|
|
4338
|
-
xstateVersion: Math.max(5, options.xstateVersion ?? 5)
|
|
4339
|
-
});
|
|
4340
|
-
nextContents = upsertStatelyPragma(nextContents, machine.id, {
|
|
4341
|
-
fileName: sourcePath,
|
|
4342
|
-
machineIndex
|
|
4343
|
-
});
|
|
4344
|
-
created.push({
|
|
4345
|
-
machineIndex,
|
|
4346
|
-
machine
|
|
4347
|
-
});
|
|
4348
|
-
}
|
|
4349
|
-
if (nextContents !== contents) {
|
|
4350
|
-
await fs.writeFile(sourcePath, nextContents, "utf8");
|
|
4351
|
-
outputPath = sourcePath;
|
|
4352
|
-
}
|
|
4353
|
-
return {
|
|
4354
|
-
sourcePath,
|
|
4355
|
-
project,
|
|
4356
|
-
created,
|
|
4357
|
-
updated,
|
|
4358
|
-
skipped,
|
|
4359
|
-
...outputPath ? { outputPath } : {}
|
|
4360
|
-
};
|
|
4361
|
-
}
|
|
4362
|
-
async function resolveLocalFile(locator, options, machineIndex = 0) {
|
|
4363
|
-
const filePath = path.resolve(options.cwd ?? process.cwd(), locator);
|
|
4364
|
-
const contents = await fs.readFile(filePath, "utf8");
|
|
4365
|
-
const extension = path.extname(filePath).toLowerCase();
|
|
4366
|
-
if (extension === ".ts" || extension === ".tsx" || extension === ".js" || extension === ".jsx") {
|
|
4367
|
-
const localExtraction = await extractMachinesFromLocalSource(filePath, contents, machineIndex);
|
|
4368
|
-
let config = localExtraction.machines[0]?.config;
|
|
4369
|
-
const extractionIssue = localExtraction.error;
|
|
4370
|
-
if (!config) {
|
|
4371
|
-
const extracted = await (options.client ?? createStatelyClient({
|
|
4372
|
-
apiKey: options.apiKey,
|
|
4373
|
-
baseUrl: options.baseUrl,
|
|
4374
|
-
fetch: options.fetch
|
|
4375
|
-
})).code.extractMachines(contents);
|
|
4376
|
-
const indexedMachine = extracted.machines.find((machine) => machine.machineIndex === machineIndex);
|
|
4377
|
-
config = extracted.machines.some((machine) => machine.machineIndex !== void 0) ? indexedMachine?.config : extracted.machines[machineIndex]?.config;
|
|
4378
|
-
}
|
|
4379
|
-
if (!config) throw new Error(extractionIssue ? `No machines extracted from ${filePath}: ${formatLocalMachineExtractionIssue(extractionIssue)}` : `No machine at index ${machineIndex} was extracted from ${filePath}`);
|
|
4380
|
-
return {
|
|
4381
|
-
kind: "local-file",
|
|
4382
|
-
locator: filePath,
|
|
4383
|
-
format: "xstate",
|
|
4384
|
-
graph: fromXStateConfig(config)
|
|
4385
|
-
};
|
|
4386
|
-
}
|
|
4387
|
-
const parsed = JSON.parse(contents);
|
|
4388
|
-
if ("rootNode" in parsed && "edges" in parsed) return {
|
|
4389
|
-
kind: "local-file",
|
|
4390
|
-
locator: filePath,
|
|
4391
|
-
format: "digraph",
|
|
4392
|
-
graph: fromStudioMachine(parsed)
|
|
4393
|
-
};
|
|
4394
|
-
if ("nodes" in parsed && "edges" in parsed) return {
|
|
4395
|
-
kind: "local-file",
|
|
4396
|
-
locator: filePath,
|
|
4397
|
-
format: "graph",
|
|
4398
|
-
graph: parsed
|
|
4399
|
-
};
|
|
4400
|
-
if ("states" in parsed) return {
|
|
4401
|
-
kind: "local-file",
|
|
4402
|
-
locator: filePath,
|
|
4403
|
-
format: "xstate",
|
|
4404
|
-
graph: fromXStateConfig(parsed)
|
|
4405
|
-
};
|
|
4406
|
-
throw new Error(`Unsupported local sync input: ${filePath}`);
|
|
4407
|
-
}
|
|
4408
|
-
function inferWritableTargetFormat(filePath) {
|
|
4409
|
-
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx") || filePath.endsWith(".js") || filePath.endsWith(".jsx")) return "xstate";
|
|
4410
|
-
if (filePath.endsWith(".digraph.json")) return "digraph";
|
|
4411
|
-
if (filePath.endsWith(".graph.json")) return "graph";
|
|
4412
|
-
return null;
|
|
4413
|
-
}
|
|
4414
|
-
function resolveLocalXStateMachineId(graph) {
|
|
4415
|
-
const rootKey = (graph.nodes.find((node) => node.parentId == null) ?? null)?.data?.key;
|
|
4416
|
-
if (rootKey && rootKey !== "(machine)") return rootKey;
|
|
4417
|
-
return graph.id;
|
|
4418
|
-
}
|
|
4419
|
-
function serializeGraph(graph, format, options = {}) {
|
|
4420
|
-
switch (format) {
|
|
4421
|
-
case "digraph": return `${JSON.stringify(toStudioMachine(graph), null, 2)}\n`;
|
|
4422
|
-
case "graph": return `${JSON.stringify(graph, null, 2)}\n`;
|
|
4423
|
-
case "xstate": {
|
|
4424
|
-
const targetLanguage = inferXStateTargetLanguage(options.targetPath);
|
|
4425
|
-
const source = graphToXStateTS({
|
|
4426
|
-
...graph,
|
|
4427
|
-
id: resolveLocalXStateMachineId(graph)
|
|
4428
|
-
}, { targetLanguage });
|
|
4429
|
-
if (!options.remoteMachineId) return source;
|
|
4430
|
-
return upsertStatelyPragma(source, options.remoteMachineId, options.targetPath ? { fileName: options.targetPath } : {});
|
|
4431
|
-
}
|
|
4432
|
-
default: {
|
|
4433
|
-
const exhaustive = format;
|
|
4434
|
-
throw new Error(`Unsupported sync output format: ${exhaustive}`);
|
|
4435
|
-
}
|
|
4436
|
-
}
|
|
4437
|
-
}
|
|
4438
|
-
function inferXStateTargetLanguage(targetPath) {
|
|
4439
|
-
if (!targetPath) return "typescript";
|
|
4440
|
-
return /\.(?:c|m)?jsx?$/i.test(targetPath) ? "javascript" : "typescript";
|
|
4441
|
-
}
|
|
4442
|
-
async function resolveRemoteMachine(machineId, baseUrl, kind, locator, options) {
|
|
4443
|
-
const digraph = (await (options.client ?? createStatelyClient({
|
|
4444
|
-
apiKey: options.apiKey,
|
|
4445
|
-
baseUrl,
|
|
4446
|
-
fetch: options.fetch
|
|
4447
|
-
})).machines.get(machineId)).definition;
|
|
4448
|
-
return {
|
|
4449
|
-
kind,
|
|
4450
|
-
locator,
|
|
4451
|
-
format: "digraph",
|
|
4452
|
-
graph: fromStudioMachine(digraph),
|
|
4453
|
-
remoteMachineId: machineId
|
|
4454
|
-
};
|
|
4455
|
-
}
|
|
4456
|
-
async function resolveSyncInput(locator, options) {
|
|
4457
|
-
if (await fileExists(path.resolve(options.cwd ?? process.cwd(), locator))) return resolveLocalFile(locator, options);
|
|
4458
|
-
if (isUrl(locator)) {
|
|
4459
|
-
const url = new URL(locator);
|
|
4460
|
-
const machineId = url.pathname.split("/").filter(Boolean).at(-1);
|
|
4461
|
-
if (!machineId) throw new Error(`Could not resolve machine ID from URL: ${locator}`);
|
|
4462
|
-
return resolveRemoteMachine(machineId, url.origin, "studio-url", locator, options);
|
|
4463
|
-
}
|
|
4464
|
-
return resolveRemoteMachine(locator, options.baseUrl, "studio-machine-id", locator, options);
|
|
4465
|
-
}
|
|
4466
|
-
function summarizeDiff(diff) {
|
|
4467
|
-
const nodeChanges = diff.nodes.added.length + diff.nodes.removed.length + diff.nodes.updated.length;
|
|
4468
|
-
const edgeChanges = diff.edges.added.length + diff.edges.removed.length + diff.edges.updated.length;
|
|
4469
|
-
return {
|
|
4470
|
-
hasChanges: !isEmptyDiff(diff),
|
|
4471
|
-
nodeChanges,
|
|
4472
|
-
edgeChanges
|
|
4473
|
-
};
|
|
4474
|
-
}
|
|
4475
|
-
async function planSync(options) {
|
|
4476
|
-
const source = await resolveSyncInput(options.source, options);
|
|
4477
|
-
const target = await resolveSyncInput(options.target, options);
|
|
4478
|
-
const { diff } = diffGraphsSemantically(source.graph, target.graph);
|
|
4479
|
-
return {
|
|
4480
|
-
source,
|
|
4481
|
-
target,
|
|
4482
|
-
diff,
|
|
4483
|
-
summary: summarizeDiff(diff),
|
|
4484
|
-
warnings: []
|
|
4485
|
-
};
|
|
4486
|
-
}
|
|
4487
|
-
async function pullSync(options) {
|
|
4488
|
-
const source = await resolveSyncInput(options.source, options);
|
|
4489
|
-
const outputPath = path.resolve(options.cwd ?? process.cwd(), options.target);
|
|
4490
|
-
const fallbackFormat = inferWritableTargetFormat(outputPath);
|
|
4491
|
-
let targetFormat = fallbackFormat;
|
|
4492
|
-
let existingTarget = null;
|
|
4493
|
-
const targetExists = await fileExists(outputPath);
|
|
4494
|
-
let machineIndex = options.machineIndex ?? 0;
|
|
4495
|
-
if (targetExists && options.machineIndex === void 0 && source.remoteMachineId) {
|
|
4496
|
-
const matchingIndex = findStatelyPragmaAttachments(await fs.readFile(outputPath, "utf8"), outputPath).findIndex((attachment) => attachment.pragma?.id === source.remoteMachineId);
|
|
4497
|
-
if (matchingIndex >= 0) machineIndex = matchingIndex;
|
|
4498
|
-
}
|
|
4499
|
-
if (targetExists) try {
|
|
4500
|
-
const target = await resolveLocalFile(options.target, options, machineIndex);
|
|
4501
|
-
existingTarget = target;
|
|
4502
|
-
targetFormat = target.format;
|
|
4503
|
-
} catch (error) {
|
|
4504
|
-
if (!fallbackFormat) throw error;
|
|
4505
|
-
const contents = await fs.readFile(outputPath, "utf8");
|
|
4506
|
-
if (fallbackFormat === "xstate" && contents.trim() !== "" && !options.force) throw new PullSourceReconciliationError(`Could not update ${outputPath} surgically: ${error instanceof Error ? error.message : String(error)} Pass --force to replace the file.`);
|
|
4507
|
-
}
|
|
4508
|
-
if (!targetFormat) throw new Error(`Could not infer a writable target format from ${outputPath}. Use an existing digraph/graph file or a .digraph.json/.graph.json target.`);
|
|
4509
|
-
let wroteSurgically = false;
|
|
4510
|
-
if (targetFormat === "xstate" && existingTarget?.format === "xstate") {
|
|
4511
|
-
const currentSource = await fs.readFile(outputPath, "utf8");
|
|
4512
|
-
const document = createTextDocument(outputPath, currentSource);
|
|
4513
|
-
let parsedTarget = null;
|
|
4514
|
-
try {
|
|
4515
|
-
parsedTarget = parseDocumentToMachine(document, {
|
|
4516
|
-
machineIndex,
|
|
4517
|
-
resolver: createFileSourceDocumentResolver()
|
|
4518
|
-
});
|
|
4519
|
-
} catch (error) {
|
|
4520
|
-
if (!options.force) throw new PullSourceReconciliationError(`Could not update ${outputPath} surgically: ${error instanceof Error ? error.message : String(error)} The source file was left unchanged. Pass --force to replace the file.`);
|
|
4521
|
-
}
|
|
4522
|
-
if (parsedTarget) {
|
|
4523
|
-
const semanticResult = diffGraphsSemantically(existingTarget.graph, source.graph);
|
|
4524
|
-
const alignedSource = alignCompareGraphIds(existingTarget.graph, source.graph, semanticResult.base, semanticResult.compare);
|
|
4525
|
-
const patchResult = graphChangeToPatchesResult(existingTarget.graph, alignedSource);
|
|
4526
|
-
const patches = patchResult.patches;
|
|
4527
|
-
if (patchResult.unsupportedChanges.length > 0) {
|
|
4528
|
-
if (!options.force) throw new PullSourceReconciliationError(`Could not update ${outputPath} surgically: ${patchResult.unsupportedChanges.join(" ")} The source file was left unchanged. Pass --force to replace the file.`);
|
|
4529
|
-
} else if (patches.length > 0) {
|
|
4530
|
-
const replacements = applyMachineToSource(graphToMachineConfig(alignedSource), document, {
|
|
4531
|
-
patches,
|
|
4532
|
-
prevGraph: existingTarget.graph,
|
|
4533
|
-
nextGraph: alignedSource,
|
|
4534
|
-
sourceLocations: parsedTarget.sourceLocations,
|
|
4535
|
-
machineIndex
|
|
4536
|
-
});
|
|
4537
|
-
if (replacements.length === 0) {
|
|
4538
|
-
if (!options.force) throw new PullSourceReconciliationError(`Could not write remote changes to ${outputPath}: source reconciliation produced no replacements. The source file was left unchanged. Pass --force to replace the file.`);
|
|
4539
|
-
} else {
|
|
4540
|
-
let updatedSource = applyReplacementsToText(currentSource, replacements);
|
|
4541
|
-
if (source.remoteMachineId) updatedSource = upsertStatelyPragma(updatedSource, source.remoteMachineId, {
|
|
4542
|
-
fileName: outputPath,
|
|
4543
|
-
machineIndex
|
|
4544
|
-
});
|
|
4545
|
-
await fs.writeFile(outputPath, updatedSource, "utf8");
|
|
4546
|
-
wroteSurgically = true;
|
|
4547
|
-
}
|
|
4548
|
-
} else {
|
|
4549
|
-
const updatedSource = source.remoteMachineId ? upsertStatelyPragma(currentSource, source.remoteMachineId, {
|
|
4550
|
-
fileName: outputPath,
|
|
4551
|
-
machineIndex
|
|
4552
|
-
}) : currentSource;
|
|
4553
|
-
if (updatedSource !== currentSource) await fs.writeFile(outputPath, updatedSource, "utf8");
|
|
4554
|
-
wroteSurgically = true;
|
|
4555
|
-
}
|
|
4556
|
-
}
|
|
4557
|
-
}
|
|
4558
|
-
if (!wroteSurgically) {
|
|
4559
|
-
if (targetFormat === "xstate" && targetExists) {
|
|
4560
|
-
const machineCount = countSourceMachines(outputPath, await fs.readFile(outputPath, "utf8"));
|
|
4561
|
-
if (machineCount > 1) throw new PullSourceReconciliationError(`Could not update ${outputPath} surgically, and full regeneration would remove the other ${machineCount - 1} machine(s) in this file. The source file was left unchanged.`);
|
|
4562
|
-
}
|
|
4563
|
-
const serialized = serializeGraph(source.graph, targetFormat, {
|
|
4564
|
-
remoteMachineId: source.remoteMachineId,
|
|
4565
|
-
targetPath: outputPath
|
|
4566
|
-
});
|
|
4567
|
-
await fs.writeFile(outputPath, serialized, "utf8");
|
|
4568
|
-
}
|
|
4569
|
-
return {
|
|
4570
|
-
source,
|
|
4571
|
-
target: {
|
|
4572
|
-
kind: "local-file",
|
|
4573
|
-
locator: outputPath,
|
|
4574
|
-
format: targetFormat,
|
|
4575
|
-
graph: source.graph
|
|
4576
|
-
},
|
|
4577
|
-
outputPath
|
|
4578
|
-
};
|
|
4579
|
-
}
|
|
4580
|
-
function inferDefaultProjectName(sourcePath, repo) {
|
|
4581
|
-
if (repo?.repo) return repo.repo;
|
|
4582
|
-
const parentDir = path.basename(path.dirname(sourcePath));
|
|
4583
|
-
if (parentDir && parentDir !== ".") return parentDir;
|
|
4584
|
-
return path.basename(sourcePath, path.extname(sourcePath));
|
|
4585
|
-
}
|
|
4586
|
-
async function resolvePushProject(client, sourcePath, options) {
|
|
4587
|
-
const explicitProject = options.project;
|
|
4588
|
-
if (explicitProject?.projectId) return client.projects.get(explicitProject.projectId);
|
|
4589
|
-
if (explicitProject?.projectVersionId) {
|
|
4590
|
-
const matchedProject = (await client.projects.list()).find((candidate) => candidate.projectVersionId === explicitProject.projectVersionId);
|
|
4591
|
-
if (!matchedProject) throw new Error(`No project found for projectVersionId "${explicitProject.projectVersionId}".`);
|
|
4592
|
-
return client.projects.get(matchedProject.projectId);
|
|
4593
|
-
}
|
|
4594
|
-
const inferredRepo = explicitProject?.repo ?? await inferConnectedRepo(sourcePath, options.cwd);
|
|
4595
|
-
const projectInput = {
|
|
4596
|
-
name: explicitProject?.name ?? inferDefaultProjectName(sourcePath, inferredRepo),
|
|
4597
|
-
visibility: explicitProject?.visibility ?? "private",
|
|
4598
|
-
...explicitProject?.description ? { description: explicitProject.description } : {},
|
|
4599
|
-
...explicitProject?.keywords ? { keywords: explicitProject.keywords } : {},
|
|
4600
|
-
...inferredRepo ? { repo: inferredRepo } : {}
|
|
4601
|
-
};
|
|
4602
|
-
return client.projects.ensure(projectInput);
|
|
4603
|
-
}
|
|
4604
|
-
async function pushSync(options) {
|
|
4605
|
-
const client = options.client ?? createStatelyClient({
|
|
4606
|
-
apiKey: options.apiKey,
|
|
4607
|
-
baseUrl: options.baseUrl,
|
|
4608
|
-
fetch: options.fetch
|
|
4609
|
-
});
|
|
4610
|
-
const source = await resolveSyncInput(options.source, {
|
|
4611
|
-
...options,
|
|
4612
|
-
target: options.source
|
|
4613
|
-
});
|
|
4614
|
-
if (source.kind !== "local-file") throw new Error("pushSync currently requires a local source file.");
|
|
4615
|
-
const sourcePath = source.locator;
|
|
4616
|
-
const project = await resolvePushProject(client, sourcePath, options);
|
|
4617
|
-
const existingMachineId = source.format === "xstate" ? getStatelyPragma(await fs.readFile(sourcePath, "utf8"), sourcePath)?.id : void 0;
|
|
4618
|
-
let machine;
|
|
4619
|
-
if (existingMachineId) machine = await updateMachineDefinition(client, existingMachineId, source.graph);
|
|
4620
|
-
else {
|
|
4621
|
-
if (!project.projectVersionId) throw new Error("Resolved project is missing projectVersionId.");
|
|
4622
|
-
machine = await client.machines.create({
|
|
4623
|
-
projectVersionId: project.projectVersionId,
|
|
4624
|
-
definition: toStudioMachine(source.graph),
|
|
4625
|
-
xstateVersion: options.xstateVersion ?? 5
|
|
4626
|
-
});
|
|
4627
|
-
}
|
|
4628
|
-
let outputPath;
|
|
4629
|
-
if (source.format === "xstate") {
|
|
4630
|
-
const contents = await fs.readFile(sourcePath, "utf8");
|
|
4631
|
-
const nextContents = upsertStatelyPragma(contents, machine.id, { fileName: sourcePath });
|
|
4632
|
-
if (nextContents !== contents) {
|
|
4633
|
-
await fs.writeFile(sourcePath, nextContents, "utf8");
|
|
4634
|
-
outputPath = sourcePath;
|
|
4635
|
-
}
|
|
4636
|
-
}
|
|
4637
|
-
return {
|
|
4638
|
-
source,
|
|
4639
|
-
project,
|
|
4640
|
-
machine,
|
|
4641
|
-
...outputPath ? { outputPath } : {}
|
|
4642
|
-
};
|
|
4643
|
-
}
|
|
4644
|
-
|
|
4645
|
-
//#endregion
|
|
4646
|
-
export { PullSourceReconciliationError, planSync, pullSync, pushLocalMachineLinks, pushSync };
|