@parlor/scaffold 0.0.0-stage → 0.2.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.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -2
  3. package/dist/add/addon.d.ts +103 -0
  4. package/dist/add/addon.d.ts.map +1 -0
  5. package/dist/add/addon.js +12 -0
  6. package/dist/add/addon.js.map +1 -0
  7. package/dist/add/plan.d.ts +72 -0
  8. package/dist/add/plan.d.ts.map +1 -0
  9. package/dist/add/plan.js +237 -0
  10. package/dist/add/plan.js.map +1 -0
  11. package/dist/add/run.d.ts +92 -0
  12. package/dist/add/run.d.ts.map +1 -0
  13. package/dist/add/run.js +167 -0
  14. package/dist/add/run.js.map +1 -0
  15. package/dist/add/transaction.d.ts +67 -0
  16. package/dist/add/transaction.d.ts.map +1 -0
  17. package/dist/add/transaction.js +181 -0
  18. package/dist/add/transaction.js.map +1 -0
  19. package/dist/addons/agent.d.ts +58 -0
  20. package/dist/addons/agent.d.ts.map +1 -0
  21. package/dist/addons/agent.js +50 -0
  22. package/dist/addons/agent.js.map +1 -0
  23. package/dist/addons/auth.d.ts +103 -0
  24. package/dist/addons/auth.d.ts.map +1 -0
  25. package/dist/addons/auth.js +93 -0
  26. package/dist/addons/auth.js.map +1 -0
  27. package/dist/addons/index.d.ts +159 -0
  28. package/dist/addons/index.d.ts.map +1 -0
  29. package/dist/addons/index.js +8 -0
  30. package/dist/addons/index.js.map +1 -0
  31. package/dist/catalog.d.ts +3 -0
  32. package/dist/catalog.d.ts.map +1 -0
  33. package/dist/catalog.js +23 -0
  34. package/dist/catalog.js.map +1 -0
  35. package/dist/create.d.ts +45 -0
  36. package/dist/create.d.ts.map +1 -0
  37. package/dist/create.js +162 -0
  38. package/dist/create.js.map +1 -0
  39. package/dist/docs.d.ts +12 -0
  40. package/dist/docs.d.ts.map +1 -0
  41. package/dist/docs.js +70 -0
  42. package/dist/docs.js.map +1 -0
  43. package/dist/edit/inspect.d.ts +15 -0
  44. package/dist/edit/inspect.d.ts.map +1 -0
  45. package/dist/edit/inspect.js +73 -0
  46. package/dist/edit/inspect.js.map +1 -0
  47. package/dist/edit/ops.d.ts +92 -0
  48. package/dist/edit/ops.d.ts.map +1 -0
  49. package/dist/edit/ops.js +516 -0
  50. package/dist/edit/ops.js.map +1 -0
  51. package/dist/edit/source.d.ts +24 -0
  52. package/dist/edit/source.d.ts.map +1 -0
  53. package/dist/edit/source.js +36 -0
  54. package/dist/edit/source.js.map +1 -0
  55. package/dist/edit/syntax.d.ts +39 -0
  56. package/dist/edit/syntax.d.ts.map +1 -0
  57. package/dist/edit/syntax.js +129 -0
  58. package/dist/edit/syntax.js.map +1 -0
  59. package/dist/errors.d.ts +5 -0
  60. package/dist/errors.d.ts.map +1 -0
  61. package/dist/errors.js +5 -0
  62. package/dist/errors.js.map +1 -0
  63. package/dist/files.d.ts +15 -0
  64. package/dist/files.d.ts.map +1 -0
  65. package/dist/files.js +65 -0
  66. package/dist/files.js.map +1 -0
  67. package/dist/fixtures.d.ts +28 -0
  68. package/dist/fixtures.d.ts.map +1 -0
  69. package/dist/fixtures.js +66 -0
  70. package/dist/fixtures.js.map +1 -0
  71. package/dist/git.d.ts +22 -0
  72. package/dist/git.d.ts.map +1 -0
  73. package/dist/git.js +56 -0
  74. package/dist/git.js.map +1 -0
  75. package/dist/identity.d.ts +9 -0
  76. package/dist/identity.d.ts.map +1 -0
  77. package/dist/identity.js +23 -0
  78. package/dist/identity.js.map +1 -0
  79. package/dist/index.d.ts +9 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +8 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/package-manager.d.ts +49 -0
  84. package/dist/package-manager.d.ts.map +1 -0
  85. package/dist/package-manager.js +118 -0
  86. package/dist/package-manager.js.map +1 -0
  87. package/dist/version.d.ts +6 -0
  88. package/dist/version.d.ts.map +1 -0
  89. package/dist/version.js +9 -0
  90. package/dist/version.js.map +1 -0
  91. package/package.json +46 -3
  92. package/templates/auth/auth.ts +19 -0
  93. package/templates/auth/schema/auth.ts +1 -0
  94. package/templates/auth/src/auth/client.ts +3 -0
  95. package/templates/auth/src/auth/sign-in-gate.tsx +17 -0
  96. package/templates/auth/src/auth/sign-out-button.tsx +17 -0
  97. package/templates/auth/src/routes/link.tsx +92 -0
  98. package/templates/auth/src/routes/sign-in.tsx +117 -0
  99. package/templates/default/_gitignore +7 -0
  100. package/templates/default/actions/index.ts +58 -0
  101. package/templates/default/actions/todos.spec.ts +38 -0
  102. package/templates/default/index.html +13 -0
  103. package/templates/default/migrations/0000_init.sql +9 -0
  104. package/templates/default/migrations/meta/0000_snapshot.json +87 -0
  105. package/templates/default/migrations/meta/_journal.json +13 -0
  106. package/templates/default/package.json +44 -0
  107. package/templates/default/parlor.config.ts +12 -0
  108. package/templates/default/schema/index.ts +1 -0
  109. package/templates/default/schema/todos.ts +13 -0
  110. package/templates/default/server.ts +13 -0
  111. package/templates/default/src/agent/agent-button.tsx +120 -0
  112. package/templates/default/src/agent/agent-sidebar.tsx +828 -0
  113. package/templates/default/src/agent/agent.css +5 -0
  114. package/templates/default/src/agent/confirmation-card.tsx +42 -0
  115. package/templates/default/src/agent/sidebar-view.ts +36 -0
  116. package/templates/default/src/agent/views.ts +9 -0
  117. package/templates/default/src/app.css +1 -0
  118. package/templates/default/src/header-actions.tsx +9 -0
  119. package/templates/default/src/main.tsx +27 -0
  120. package/templates/default/src/parlor.d.ts +9 -0
  121. package/templates/default/src/routeTree.gen.ts +59 -0
  122. package/templates/default/src/routes/__root.tsx +29 -0
  123. package/templates/default/src/routes/index.tsx +141 -0
  124. package/templates/default/tsconfig.json +8 -0
  125. package/templates/default/vitest.config.ts +5 -0
@@ -0,0 +1,516 @@
1
+ import { applySplices, parseSource } from "./source.js";
2
+ import { findCall, forEachNode, importedName, objectArgument, propertyOf, textOf, valueOf, } from "./syntax.js";
3
+ const DONE = { status: "done" };
4
+ const INLINE = { multiline: false, indent: "", comma: false };
5
+ const unsafe = (reason) => ({ status: "unsafe", reason });
6
+ const edit = (...splices) => ({ status: "edit", splices });
7
+ const squash = (text) => text.replace(/\s+/g, "");
8
+ const INDENT = " ";
9
+ function lineIndent(text, offset) {
10
+ return /^[ \t]*/.exec(text.slice(lineStart(text, offset)))?.[0] ?? "";
11
+ }
12
+ function lineStart(text, offset) {
13
+ return text.lastIndexOf("\n", offset - 1) + 1;
14
+ }
15
+ function lineEnd(text, offset) {
16
+ const end = text.indexOf("\n", offset);
17
+ return end === -1 ? text.length : end;
18
+ }
19
+ function entry(path, value, layout) {
20
+ const [key, ...rest] = path;
21
+ if (rest.length === 0)
22
+ return `${key}: ${value}`;
23
+ if (!layout.multiline)
24
+ return `${key}: { ${entry(rest, value, layout)} }`;
25
+ const inner = `${layout.indent}${INDENT}`;
26
+ const child = entry(rest, value, { ...layout, indent: inner });
27
+ return `${key}: {\n${inner}${child}${layout.comma ? "," : ""}\n${layout.indent}}`;
28
+ }
29
+ /** Appends to an object's properties or an array's elements in their own indentation and comma style. */
30
+ function appendEntry(file, container, render) {
31
+ const items = (container.type === "ObjectExpression" ? container.properties : container.elements).filter((item) => item !== null);
32
+ const last = items.at(-1);
33
+ const close = (container.end ?? 0) - 1;
34
+ if (!last) {
35
+ const text = render(INLINE);
36
+ return {
37
+ start: (container.start ?? 0) + 1,
38
+ end: close,
39
+ text: container.type === "ObjectExpression" ? ` ${text} ` : text,
40
+ };
41
+ }
42
+ const after = last.end ?? 0;
43
+ const between = file.text.slice(after, close);
44
+ if (!between.includes("\n"))
45
+ return { start: after, end: after, text: `, ${render(INLINE)}` };
46
+ const indent = lineIndent(file.text, last.start ?? 0);
47
+ const comma = between.indexOf(",");
48
+ const text = render({ multiline: true, indent, comma: comma !== -1 });
49
+ return comma === -1
50
+ ? { start: after, end: after, text: `,\n${indent}${text}` }
51
+ : { start: after + comma + 1, end: after + comma + 1, text: `\n${indent}${text},` };
52
+ }
53
+ function substitute(file, object, value) {
54
+ let missing;
55
+ const result = value.replace(/\$([A-Za-z_]\w*)/g, (_match, name) => {
56
+ const sibling = propertyOf(file, object, name);
57
+ if (!sibling.ok || !sibling.value) {
58
+ missing = name;
59
+ return name;
60
+ }
61
+ return textOf(file, valueOf(sibling.value));
62
+ });
63
+ return missing === undefined ? { value: result } : { missing };
64
+ }
65
+ /** The property at `op.path`, or where it would go; a parent that is not an object literal is unsafe. */
66
+ function locate(file, op) {
67
+ const call = findCall(file, op.call);
68
+ if (!call.ok)
69
+ return { reason: call.reason };
70
+ const argument = objectArgument(file, op.call, call.value);
71
+ if (!argument.ok)
72
+ return { reason: argument.reason };
73
+ if (argument.value.kind === "none") {
74
+ return { kind: "no-argument", close: (call.value.end ?? 0) - 1 };
75
+ }
76
+ let object = argument.value.node;
77
+ for (const [depth, key] of op.path.entries()) {
78
+ const property = propertyOf(file, object, key);
79
+ if (!property.ok)
80
+ return { reason: property.reason };
81
+ if (!property.value)
82
+ return { kind: "missing", object, rest: op.path.slice(depth) };
83
+ if (depth === op.path.length - 1)
84
+ return { kind: "found", object, property: property.value };
85
+ const value = valueOf(property.value);
86
+ if (value.type !== "ObjectExpression") {
87
+ return {
88
+ reason: `\`${key}\` is \`${textOf(file, value)}\`, not an object literal.`,
89
+ };
90
+ }
91
+ object = value;
92
+ }
93
+ return { reason: "the path is empty." };
94
+ }
95
+ function leadingName(text) {
96
+ return /^[A-Za-z_$][\w$]*/.exec(text.trim())?.[0];
97
+ }
98
+ function expressionName(node) {
99
+ if (node.type === "Identifier")
100
+ return node.name;
101
+ if (node.type === "CallExpression")
102
+ return expressionName(node.callee);
103
+ return undefined;
104
+ }
105
+ function evaluateSetProperty(file, op) {
106
+ const target = locate(file, op);
107
+ if ("reason" in target)
108
+ return unsafe(target.reason);
109
+ if (target.kind === "no-argument") {
110
+ if (/\$\w/.test(op.value))
111
+ return unsafe(`${op.call.callee}() has no options to read.`);
112
+ const text = entry(op.path, op.value, INLINE);
113
+ return edit({ start: target.close, end: target.close, text: `{ ${text} }` });
114
+ }
115
+ const value = substitute(file, target.object, op.value);
116
+ if ("missing" in value) {
117
+ return unsafe(`${op.call.callee}() has no \`${value.missing}\` to pass.`);
118
+ }
119
+ if (target.kind === "missing") {
120
+ return edit(appendEntry(file, target.object, (layout) => entry(target.rest, value.value, layout)));
121
+ }
122
+ const current = valueOf(target.property);
123
+ if (squash(textOf(file, current)) === squash(value.value))
124
+ return DONE;
125
+ if (op.onConflict === "unsafe") {
126
+ return unsafe(`\`${op.path.at(-1)}\` is already \`${textOf(file, current)}\`.`);
127
+ }
128
+ return edit({ start: current.start ?? 0, end: current.end ?? 0, text: value.value });
129
+ }
130
+ function evaluateAppendElement(file, op) {
131
+ const target = locate(file, op);
132
+ if ("reason" in target)
133
+ return unsafe(target.reason);
134
+ const array = `[${op.element}]`;
135
+ if (target.kind === "no-argument") {
136
+ const text = entry(op.path, array, INLINE);
137
+ return edit({ start: target.close, end: target.close, text: `{ ${text} }` });
138
+ }
139
+ if (target.kind === "missing") {
140
+ return edit(appendEntry(file, target.object, (layout) => entry(target.rest, array, layout)));
141
+ }
142
+ const key = op.path.at(-1);
143
+ const current = valueOf(target.property);
144
+ if (current.type !== "ArrayExpression") {
145
+ return unsafe(`\`${key}\` is \`${textOf(file, current)}\`, not an array literal.`);
146
+ }
147
+ const spread = current.elements.find((element) => element?.type === "SpreadElement");
148
+ if (spread) {
149
+ return unsafe(`\`${key}\` spreads \`${textOf(file, spread)}\`, which may already hold ${op.element}.`);
150
+ }
151
+ const wanted = leadingName(op.element);
152
+ if (current.elements.some((element) => element && expressionName(element) === wanted)) {
153
+ return DONE;
154
+ }
155
+ return edit(appendEntry(file, current, () => op.element));
156
+ }
157
+ function evaluateWrapProperty(file, op) {
158
+ const target = locate(file, op);
159
+ if ("reason" in target)
160
+ return unsafe(target.reason);
161
+ if (target.kind !== "found")
162
+ return unsafe(`${op.call.callee}() sets no \`${op.path.at(-1)}\`.`);
163
+ const current = valueOf(target.property);
164
+ if (current.type === "CallExpression" &&
165
+ current.callee.type === "Identifier" &&
166
+ current.callee.name === op.wrapper) {
167
+ return DONE;
168
+ }
169
+ return edit({
170
+ start: current.start ?? 0,
171
+ end: current.end ?? 0,
172
+ text: `${op.wrapper}(${textOf(file, current)})`,
173
+ });
174
+ }
175
+ /** Where `name` is bound at module scope: the module it is imported from, or this file. */
176
+ function topLevelBinding(file, name) {
177
+ for (const statement of file.ast.program.body) {
178
+ if (statement.type === "ImportDeclaration") {
179
+ if (statement.specifiers.some((specifier) => specifier.local.name === name)) {
180
+ return statement.source.value;
181
+ }
182
+ continue;
183
+ }
184
+ const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
185
+ if (!declaration)
186
+ continue;
187
+ if ((declaration.type === "FunctionDeclaration" ||
188
+ declaration.type === "ClassDeclaration") &&
189
+ declaration.id?.name === name) {
190
+ return file.path;
191
+ }
192
+ if (declaration.type === "VariableDeclaration" &&
193
+ declaration.declarations.some((declarator) => declarator.id.type === "Identifier" && declarator.id.name === name)) {
194
+ return file.path;
195
+ }
196
+ }
197
+ return undefined;
198
+ }
199
+ const isRelative = (source) => source.startsWith(".");
200
+ function importLine(op) {
201
+ return `import { ${op.names.join(", ")} } from "${op.from}";`;
202
+ }
203
+ function evaluateImport(file, op) {
204
+ const missing = op.names.filter((name) => importedName(file, name, op.from) !== name);
205
+ if (missing.length === 0)
206
+ return DONE;
207
+ for (const name of missing) {
208
+ const bound = topLevelBinding(file, name);
209
+ if (bound !== undefined) {
210
+ return unsafe(bound === file.path
211
+ ? `\`${name}\` is already declared in ${file.path}.`
212
+ : `\`${name}\` is already imported from ${bound}.`);
213
+ }
214
+ }
215
+ const imports = file.ast.program.body.filter((statement) => statement.type === "ImportDeclaration");
216
+ const existing = imports.find((statement) => statement.source.value === op.from &&
217
+ statement.importKind !== "type" &&
218
+ statement.specifiers.length > 0 &&
219
+ statement.specifiers.every((specifier) => specifier.type === "ImportSpecifier"));
220
+ if (existing) {
221
+ const at = existing.specifiers.at(-1)?.end ?? 0;
222
+ return edit({ start: at, end: at, text: `, ${missing.join(", ")}` });
223
+ }
224
+ const line = importLine({ ...op, names: missing });
225
+ const group = imports.filter((statement) => isRelative(statement.source.value) === isRelative(op.from));
226
+ const before = group.find((statement) => statement.source.value > op.from);
227
+ if (before) {
228
+ const start = lineStart(file.text, before.start ?? 0);
229
+ return edit({ start, end: start, text: `${line}\n` });
230
+ }
231
+ const lastOfGroup = group.at(-1);
232
+ if (lastOfGroup) {
233
+ const at = lineEnd(file.text, lastOfGroup.end ?? 0);
234
+ return edit({ start: at, end: at, text: `\n${line}` });
235
+ }
236
+ const lastImport = imports.at(-1);
237
+ if (isRelative(op.from) && lastImport) {
238
+ const at = lineEnd(file.text, lastImport.end ?? 0);
239
+ return edit({ start: at, end: at, text: `\n\n${line}` });
240
+ }
241
+ const start = imports[0] ? lineStart(file.text, imports[0].start ?? 0) : 0;
242
+ return edit({ start, end: start, text: `${line}\n\n` });
243
+ }
244
+ function evaluateExportAll(file, op) {
245
+ const present = file.ast.program.body.some((statement) => statement.type === "ExportAllDeclaration" && statement.source.value === op.from);
246
+ if (present)
247
+ return DONE;
248
+ const at = file.text.length;
249
+ const newline = at === 0 || file.text.endsWith("\n") ? "" : "\n";
250
+ return edit({ start: at, end: at, text: `${newline}export * from "${op.from}";\n` });
251
+ }
252
+ function openingElements(root, name) {
253
+ const found = [];
254
+ forEachNode(root, (node) => {
255
+ if (node.type === "JSXOpeningElement" &&
256
+ node.name.type === "JSXIdentifier" &&
257
+ node.name.name === name) {
258
+ found.push(node);
259
+ }
260
+ });
261
+ return found;
262
+ }
263
+ function evaluateJsxAttribute(file, op) {
264
+ const elements = openingElements(file.ast, op.element);
265
+ if (elements.length === 0)
266
+ return unsafe(`${file.path} renders no <${op.element}>.`);
267
+ if (elements.length > 1) {
268
+ return unsafe(`<${op.element}> appears more than once in ${file.path}.`);
269
+ }
270
+ const element = elements[0];
271
+ const existing = element.attributes.find((attribute) => attribute.type === "JSXAttribute" &&
272
+ attribute.name.type === "JSXIdentifier" &&
273
+ attribute.name.name === op.name);
274
+ if (existing) {
275
+ return squash(textOf(file, existing)) === squash(`${op.name}=${op.value}`)
276
+ ? DONE
277
+ : unsafe(`<${op.element}> already sets \`${textOf(file, existing)}\`.`);
278
+ }
279
+ const after = element.attributes.at(-1)?.end ?? element.name.end ?? 0;
280
+ return edit({ start: after, end: after, text: ` ${op.name}=${op.value}` });
281
+ }
282
+ function componentBody(file, name) {
283
+ for (const statement of file.ast.program.body) {
284
+ const declaration = statement.type === "ExportNamedDeclaration" ||
285
+ statement.type === "ExportDefaultDeclaration"
286
+ ? statement.declaration
287
+ : statement;
288
+ if (declaration?.type === "FunctionDeclaration" && declaration.id?.name === name) {
289
+ return declaration.body;
290
+ }
291
+ if (declaration?.type !== "VariableDeclaration")
292
+ continue;
293
+ for (const declarator of declaration.declarations) {
294
+ if (declarator.id.type === "Identifier" &&
295
+ declarator.id.name === name &&
296
+ (declarator.init?.type === "ArrowFunctionExpression" ||
297
+ declarator.init?.type === "FunctionExpression")) {
298
+ return declarator.init.body;
299
+ }
300
+ }
301
+ }
302
+ return undefined;
303
+ }
304
+ /** The values the component itself returns, not those of functions nested in it. */
305
+ function returnedValues(body) {
306
+ if (body.type !== "BlockStatement")
307
+ return [body];
308
+ const returned = [];
309
+ const visit = (node) => {
310
+ if (node.type === "FunctionDeclaration" ||
311
+ node.type === "FunctionExpression" ||
312
+ node.type === "ArrowFunctionExpression") {
313
+ return;
314
+ }
315
+ if (node.type === "ReturnStatement")
316
+ returned.push(node.argument);
317
+ for (const [key, value] of Object.entries(node)) {
318
+ if (key === "loc" || key.endsWith("Comments"))
319
+ continue;
320
+ for (const child of Array.isArray(value) ? value : [value]) {
321
+ if (child &&
322
+ typeof child === "object" &&
323
+ typeof child.type === "string") {
324
+ visit(child);
325
+ }
326
+ }
327
+ }
328
+ };
329
+ for (const statement of body.body)
330
+ visit(statement);
331
+ return returned;
332
+ }
333
+ function evaluateJsxChild(file, op) {
334
+ const body = componentBody(file, op.component);
335
+ if (!body)
336
+ return unsafe(`${file.path} has no component ${op.component}.`);
337
+ const returned = returnedValues(body);
338
+ const root = returned[0];
339
+ if (returned.length !== 1 ||
340
+ !root ||
341
+ (root.type !== "JSXElement" && root.type !== "JSXFragment")) {
342
+ return unsafe(`${op.component} must return exactly one JSX element to add <${op.element} />.`);
343
+ }
344
+ if (openingElements(root, op.element).length > 0)
345
+ return DONE;
346
+ const parent = root;
347
+ const closing = parent.type === "JSXElement" ? parent.closingElement : parent.closingFragment;
348
+ if (!closing)
349
+ return unsafe(`${op.component} returns a self-closing element.`);
350
+ const last = parent.children
351
+ .filter((child) => child.type !== "JSXText" || child.value.trim() !== "")
352
+ .at(-1);
353
+ const tag = `<${op.element} />`;
354
+ if (!last) {
355
+ const at = closing.start ?? 0;
356
+ return edit({ start: at, end: at, text: tag });
357
+ }
358
+ const at = last.end ?? 0;
359
+ return edit({
360
+ start: at,
361
+ end: at,
362
+ text: `\n${lineIndent(file.text, last.start ?? 0)}${tag}`,
363
+ });
364
+ }
365
+ export function evaluateOp(file, op) {
366
+ switch (op.kind) {
367
+ case "import":
368
+ return evaluateImport(file, op);
369
+ case "export-all":
370
+ return evaluateExportAll(file, op);
371
+ case "append-element":
372
+ return evaluateAppendElement(file, op);
373
+ case "set-property":
374
+ return evaluateSetProperty(file, op);
375
+ case "wrap-property":
376
+ return evaluateWrapProperty(file, op);
377
+ case "jsx-attribute":
378
+ return evaluateJsxAttribute(file, op);
379
+ case "jsx-child":
380
+ return evaluateJsxChild(file, op);
381
+ }
382
+ }
383
+ /** One short line for the printed plan, such as "auth: createAuth(database)"; none for an import. */
384
+ export function opSummary(file, op) {
385
+ switch (op.kind) {
386
+ case "import":
387
+ return undefined;
388
+ case "export-all":
389
+ return `export * from "${op.from}"`;
390
+ case "append-element":
391
+ return `${op.path.join(".")}: ${op.element}`;
392
+ case "set-property": {
393
+ const target = locate(file, op);
394
+ const value = "reason" in target || target.kind === "no-argument"
395
+ ? op.value
396
+ : (() => {
397
+ const substituted = substitute(file, target.object, op.value);
398
+ return "value" in substituted ? substituted.value : op.value;
399
+ })();
400
+ return `${op.path.join(".")}: ${value}`;
401
+ }
402
+ case "wrap-property": {
403
+ const target = locate(file, op);
404
+ const current = !("reason" in target) && target.kind === "found"
405
+ ? textOf(file, valueOf(target.property))
406
+ : "...";
407
+ return `${op.path.join(".")}: ${op.wrapper}(${current})`;
408
+ }
409
+ case "jsx-attribute":
410
+ return `<${op.element} ${op.name}=${op.value}>`;
411
+ case "jsx-child":
412
+ return `<${op.element} />`;
413
+ }
414
+ }
415
+ function callCode(op, value) {
416
+ const nested = (path, indent) => {
417
+ const [key, ...rest] = path;
418
+ if (rest.length === 0)
419
+ return `${indent}${key}: ${value},`;
420
+ return `${indent}${key}: {\n${nested(rest, `${indent}${INDENT}`)}\n${indent}},`;
421
+ };
422
+ return `${op.call.callee}({\n${INDENT}...\n${nested(op.path, INDENT)}\n});`;
423
+ }
424
+ /** The code to write by hand for `op`; a `$name` stands for the sibling the app passes. */
425
+ export function manualCode(op) {
426
+ switch (op.kind) {
427
+ case "import":
428
+ return importLine(op);
429
+ case "export-all":
430
+ return `export * from "${op.from}";`;
431
+ case "append-element":
432
+ return callCode(op, `[..., ${op.element}]`);
433
+ case "set-property":
434
+ return callCode(op, op.value.replace(/\$([A-Za-z_]\w*)/g, "$1"));
435
+ case "wrap-property":
436
+ return callCode(op, `${op.wrapper}(...)`);
437
+ case "jsx-attribute":
438
+ return `<${op.element} ... ${op.name}=${op.value}>`;
439
+ case "jsx-child":
440
+ return [
441
+ `function ${op.component}() {`,
442
+ `${INDENT}return (`,
443
+ `${INDENT}${INDENT}<...>`,
444
+ `${INDENT}${INDENT}${INDENT}...`,
445
+ `${INDENT}${INDENT}${INDENT}<${op.element} />`,
446
+ `${INDENT}${INDENT}</...>`,
447
+ `${INDENT});`,
448
+ "}",
449
+ ].join("\n");
450
+ }
451
+ }
452
+ function manualHeading(file, op) {
453
+ switch (op.kind) {
454
+ case "import":
455
+ return `In ${file}, import ${op.names.join(", ")} from ${op.from}:`;
456
+ case "export-all":
457
+ return `In ${file}, re-export ${op.from}:`;
458
+ case "append-element":
459
+ return `In ${file}, add ${op.element} to \`${op.path.join(".")}\` of ${op.call.callee}:`;
460
+ case "set-property":
461
+ return `In ${file}, pass \`${op.path.join(".")}\` to ${op.call.callee}:`;
462
+ case "wrap-property":
463
+ return `In ${file}, wrap \`${op.path.join(".")}\` of ${op.call.callee} in ${op.wrapper}:`;
464
+ case "jsx-attribute":
465
+ return `In ${file}, add \`${op.name}=${op.value}\` to <${op.element}>:`;
466
+ case "jsx-child":
467
+ return `In ${file}, render <${op.element} /> in ${op.component}:`;
468
+ }
469
+ }
470
+ export function indentLines(text, indent) {
471
+ return text
472
+ .split("\n")
473
+ .map((line) => (line ? `${indent}${line}` : line))
474
+ .join("\n");
475
+ }
476
+ /** The change to make by hand when `op` is unsafe. */
477
+ export function manualChange(file, op) {
478
+ return `${manualHeading(file, op)}\n\n${indentLines(manualCode(op), INDENT)}`;
479
+ }
480
+ export const UNCONFIRMED = "could not confirm the edit";
481
+ /**
482
+ * Applies `ops` in order, reparsing after each one, then proves the result: it must still
483
+ * parse and every op must now evaluate to `done`. A failed proof is `unsafe`, so a bug in an op
484
+ * can cost a manual step but never a broken or half-edited file, and a second run is a no-op
485
+ * by construction. Collects every unsafe op instead of stopping at the first.
486
+ */
487
+ export function editSource(file, ops) {
488
+ let current = file;
489
+ const failed = [];
490
+ for (const op of ops) {
491
+ const outcome = evaluateOp(current, op);
492
+ if (outcome.status === "unsafe")
493
+ failed.push({ op, reason: outcome.reason });
494
+ if (outcome.status === "edit") {
495
+ try {
496
+ current = parseSource(file.path, applySplices(current.text, outcome.splices));
497
+ }
498
+ catch {
499
+ failed.push({ op, reason: UNCONFIRMED });
500
+ }
501
+ }
502
+ }
503
+ if (failed.length > 0)
504
+ return { status: "unsafe", unsafe: failed };
505
+ if (current.text === file.text)
506
+ return { status: "done" };
507
+ const unconfirmed = ops.filter((op) => evaluateOp(current, op).status !== "done");
508
+ if (unconfirmed.length > 0) {
509
+ return {
510
+ status: "unsafe",
511
+ unsafe: unconfirmed.map((op) => ({ op, reason: UNCONFIRMED })),
512
+ };
513
+ }
514
+ return { status: "edit", text: current.text };
515
+ }
516
+ //# sourceMappingURL=ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ops.js","sourceRoot":"","sources":["../../src/edit/ops.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAgC,MAAM,aAAa,CAAC;AACtF,OAAO,EAEL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,cAAc,EACd,UAAU,EACV,MAAM,EACN,OAAO,GACR,MAAM,aAAa,CAAC;AA2DrB,MAAM,IAAI,GAAc,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC3C,MAAM,MAAM,GAAW,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACtE,MAAM,MAAM,GAAG,CAAC,MAAc,EAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7E,MAAM,IAAI,GAAG,CAAC,GAAG,OAAiB,EAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAChF,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC1D,MAAM,MAAM,GAAG,IAAI,CAAC;AAEpB,SAAS,UAAU,CAAC,IAAY,EAAE,MAAc;IAC9C,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACxE,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,MAAc;IAC7C,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,MAAc;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;AACxC,CAAC;AAED,SAAS,KAAK,CAAC,IAAuB,EAAE,KAAa,EAAE,MAAc;IACnE,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,KAAK,EAAE,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,GAAG,GAAG,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC;IAC1E,MAAM,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,OAAO,GAAG,GAAG,QAAQ,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC;AACpF,CAAC;AAED,yGAAyG;AACzG,SAAS,WAAW,CAClB,IAAgB,EAChB,SAA6C,EAC7C,MAAkC;IAElC,MAAM,KAAK,GAAG,CACZ,SAAS,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAClF,CAAC,MAAM,CAAC,CAAC,IAAI,EAAoC,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,KAAK,GAAG,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,OAAO;YACL,KAAK,EAAE,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC;YACjC,GAAG,EAAE,KAAK;YACV,IAAI,EAAE,SAAS,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI;SACjE,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QACzB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;IACnE,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IACtE,OAAO,KAAK,KAAK,CAAC,CAAC;QACjB,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,GAAG,IAAI,EAAE,EAAE;QAC3D,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,GAAG,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;AACxF,CAAC;AAED,SAAS,UAAU,CACjB,IAAgB,EAChB,MAAwB,EACxB,KAAa;IAEb,IAAI,OAA2B,CAAC;IAChC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;QACzE,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAClC,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;AACjE,CAAC;AAeD,yGAAyG;AACzG,SAAS,MAAM,CAAC,IAAgB,EAAE,EAAU;IAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3D,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACrD,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IACnE,CAAC;IACD,IAAI,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;IACjC,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrD,IAAI,CAAC,QAAQ,CAAC,KAAK;YAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACpF,IAAI,KAAK,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;YAC9B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;YACtC,OAAO;gBACL,MAAM,EAAE,KAAK,GAAG,WAAW,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,4BAA4B;aAC3E,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,cAAc,CAAC,IAAU;IAChC,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACjD,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,mBAAmB,CAC1B,IAAgB,EAChB,EAAyC;IAEzC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChC,IAAI,QAAQ,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;YACvB,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,4BAA4B,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IACxD,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;QACvB,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,eAAe,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,IAAI,CACT,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAC1C,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CACxC,CACF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,EAAE,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,mBAAmB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,qBAAqB,CAC5B,IAAgB,EAChB,EAA2C;IAE3C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChC,IAAI,QAAQ,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC,OAAO,GAAG,CAAC;IAChC,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,IAAI,CACT,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAChF,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,OAAO,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACvC,OAAO,MAAM,CAAC,KAAK,GAAG,WAAW,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,eAAe,CAAC,CAAC;IACrF,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CACX,KAAK,GAAG,gBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,8BAA8B,EAAE,CAAC,OAAO,GAAG,CACxF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,EAAE,CAAC;QACtF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,oBAAoB,CAC3B,IAAgB,EAChB,EAA0C;IAE1C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChC,IAAI,QAAQ,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;QACzB,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,gBAAgB,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzC,IACE,OAAO,CAAC,IAAI,KAAK,gBAAgB;QACjC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;QACpC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC,OAAO,EAClC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;QACV,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QACzB,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;QACrB,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG;KAChD,CAAC,CAAC;AACL,CAAC;AAED,2FAA2F;AAC3F,SAAS,eAAe,CAAC,IAAgB,EAAE,IAAY;IACrD,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,SAAS,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;YAC3C,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC5E,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC;YAChC,CAAC;YACD,SAAS;QACX,CAAC;QACD,MAAM,WAAW,GACf,SAAS,CAAC,IAAI,KAAK,wBAAwB,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QAClF,IAAI,CAAC,WAAW;YAAE,SAAS;QAC3B,IACE,CAAC,WAAW,CAAC,IAAI,KAAK,qBAAqB;YACzC,WAAW,CAAC,IAAI,KAAK,kBAAkB,CAAC;YAC1C,WAAW,CAAC,EAAE,EAAE,IAAI,KAAK,IAAI,EAC7B,CAAC;YACD,OAAO,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC;QACD,IACE,WAAW,CAAC,IAAI,KAAK,qBAAqB;YAC1C,WAAW,CAAC,YAAY,CAAC,IAAI,CAC3B,CAAC,UAAU,EAAE,EAAE,CACb,UAAU,CAAC,EAAE,CAAC,IAAI,KAAK,YAAY,IAAI,UAAU,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,CACrE,EACD,CAAC;YACD,OAAO,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AAE9D,SAAS,UAAU,CAAC,EAAmC;IACrD,OAAO,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,IAAI,IAAI,CAAC;AAChE,CAAC;AAED,SAAS,cAAc,CACrB,IAAgB,EAChB,EAAmC;IAEnC,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;IACtF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,MAAM,CACX,KAAK,KAAK,IAAI,CAAC,IAAI;gBACjB,CAAC,CAAC,KAAK,IAAI,6BAA6B,IAAI,CAAC,IAAI,GAAG;gBACpD,CAAC,CAAC,KAAK,IAAI,+BAA+B,KAAK,GAAG,CACrD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAC1C,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,mBAAmB,CACtD,CAAC;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAC3B,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,MAAM,CAAC,KAAK,KAAK,EAAE,CAAC,IAAI;QAClC,SAAS,CAAC,UAAU,KAAK,MAAM;QAC/B,SAAS,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAC/B,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,iBAAiB,CAAC,CAClF,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAC1B,CAAC,SAAS,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1E,CAAC;IACF,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC3E,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,IAAI,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;QACtC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,MAAM,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,iBAAiB,CACxB,IAAgB,EAChB,EAAuC;IAEvC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CACxC,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,IAAI,KAAK,sBAAsB,IAAI,SAAS,CAAC,MAAM,CAAC,KAAK,KAAK,EAAE,CAAC,IAAI,CAClF,CAAC;IACF,IAAI,OAAO;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC5B,MAAM,OAAO,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,kBAAkB,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,eAAe,CAAC,IAAU,EAAE,IAAY;IAC/C,MAAM,KAAK,GAAwB,EAAE,CAAC;IACtC,WAAW,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QACzB,IACE,IAAI,CAAC,IAAI,KAAK,mBAAmB;YACjC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,eAAe;YAClC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,EACvB,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,oBAAoB,CAC3B,IAAgB,EAChB,EAA0C;IAE1C,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,gBAAgB,EAAE,CAAC,OAAO,IAAI,CAAC,CAAC;IACrF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,+BAA+B,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CACtC,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,IAAI,KAAK,cAAc;QACjC,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,eAAe;QACvC,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,CAClC,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;YACxE,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,oBAAoB,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;IACtE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,aAAa,CAAC,IAAgB,EAAE,IAAY;IACnD,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC9C,MAAM,WAAW,GACf,SAAS,CAAC,IAAI,KAAK,wBAAwB;YAC3C,SAAS,CAAC,IAAI,KAAK,0BAA0B;YAC3C,CAAC,CAAC,SAAS,CAAC,WAAW;YACvB,CAAC,CAAC,SAAS,CAAC;QAChB,IAAI,WAAW,EAAE,IAAI,KAAK,qBAAqB,IAAI,WAAW,CAAC,EAAE,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;YACjF,OAAO,WAAW,CAAC,IAAI,CAAC;QAC1B,CAAC;QACD,IAAI,WAAW,EAAE,IAAI,KAAK,qBAAqB;YAAE,SAAS;QAC1D,KAAK,MAAM,UAAU,IAAI,WAAW,CAAC,YAAY,EAAE,CAAC;YAClD,IACE,UAAU,CAAC,EAAE,CAAC,IAAI,KAAK,YAAY;gBACnC,UAAU,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI;gBAC3B,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,KAAK,yBAAyB;oBAClD,UAAU,CAAC,IAAI,EAAE,IAAI,KAAK,oBAAoB,CAAC,EACjD,CAAC;gBACD,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,oFAAoF;AACpF,SAAS,cAAc,CAAC,IAAU;IAChC,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,CAAC,IAAkB,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAsC,EAAE,CAAC;IACvD,MAAM,KAAK,GAAG,CAAC,IAAU,EAAE,EAAE;QAC3B,IACE,IAAI,CAAC,IAAI,KAAK,qBAAqB;YACnC,IAAI,CAAC,IAAI,KAAK,oBAAoB;YAClC,IAAI,CAAC,IAAI,KAAK,yBAAyB,EACvC,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB;YAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAAE,SAAS;YACxD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3D,IACE,KAAK;oBACL,OAAO,KAAK,KAAK,QAAQ;oBACzB,OAAQ,KAAc,CAAC,IAAI,KAAK,QAAQ,EACxC,CAAC;oBACD,KAAK,CAAC,KAAa,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,IAAI;QAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACpD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,gBAAgB,CACvB,IAAgB,EAChB,EAAsC;IAEtC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,CAAC,IAAI;QAAE,OAAO,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,qBAAqB,EAAE,CAAC,SAAS,GAAG,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACzB,IACE,QAAQ,CAAC,MAAM,KAAK,CAAC;QACrB,CAAC,IAAI;QACL,CAAC,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,CAAC,EAC3D,CAAC;QACD,OAAO,MAAM,CACX,GAAG,EAAE,CAAC,SAAS,gDAAgD,EAAE,CAAC,OAAO,MAAM,CAChF,CAAC;IACJ,CAAC;IACD,IAAI,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9D,MAAM,MAAM,GAA6B,IAAI,CAAC;IAC9C,MAAM,OAAO,GACX,MAAM,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC;IAChF,IAAI,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,kCAAkC,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ;SACzB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;SACxE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACV,MAAM,GAAG,GAAG,IAAI,EAAE,CAAC,OAAO,KAAK,CAAC;IAChC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;QACV,KAAK,EAAE,EAAE;QACT,GAAG,EAAE,EAAE;QACP,IAAI,EAAE,KAAK,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE;KAC1D,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAgB,EAAE,EAAM;IACjD,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClC,KAAK,YAAY;YACf,OAAO,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrC,KAAK,gBAAgB;YACnB,OAAO,qBAAqB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACzC,KAAK,cAAc;YACjB,OAAO,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvC,KAAK,eAAe;YAClB,OAAO,oBAAoB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACxC,KAAK,eAAe;YAClB,OAAO,oBAAoB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACxC,KAAK,WAAW;YACd,OAAO,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,SAAS,CAAC,IAAgB,EAAE,EAAM;IAChD,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,SAAS,CAAC;QACnB,KAAK,YAAY;YACf,OAAO,kBAAkB,EAAE,CAAC,IAAI,GAAG,CAAC;QACtC,KAAK,gBAAgB;YACnB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC;QAC/C,KAAK,cAAc,EAAE,CAAC;YACpB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAChC,MAAM,KAAK,GACT,QAAQ,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa;gBACjD,CAAC,CAAC,EAAE,CAAC,KAAK;gBACV,CAAC,CAAC,CAAC,GAAG,EAAE;oBACJ,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;oBAC9D,OAAO,OAAO,IAAI,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;gBAC/D,CAAC,CAAC,EAAE,CAAC;YACX,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;QAC1C,CAAC;QACD,KAAK,eAAe,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAChC,MAAM,OAAO,GACX,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;gBAC9C,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACxC,CAAC,CAAC,KAAK,CAAC;YACZ,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,OAAO,IAAI,OAAO,GAAG,CAAC;QAC3D,CAAC;QACD,KAAK,eAAe;YAClB,OAAO,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,GAAG,CAAC;QAClD,KAAK,WAAW;YACd,OAAO,IAAI,EAAE,CAAC,OAAO,KAAK,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,EAAU,EAAE,KAAa;IACzC,MAAM,MAAM,GAAG,CAAC,IAAuB,EAAE,MAAc,EAAU,EAAE;QACjE,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,GAAG,MAAM,GAAG,GAAG,KAAK,KAAK,GAAG,CAAC;QAC3D,OAAO,GAAG,MAAM,GAAG,GAAG,QAAQ,MAAM,CAAC,IAAI,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,MAAM,IAAI,CAAC;IAClF,CAAC,CAAC;IACF,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,OAAO,MAAM,QAAQ,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC;AAC9E,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,UAAU,CAAC,EAAM;IAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC;QACxB,KAAK,YAAY;YACf,OAAO,kBAAkB,EAAE,CAAC,IAAI,IAAI,CAAC;QACvC,KAAK,gBAAgB;YACnB,OAAO,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC;QAC9C,KAAK,cAAc;YACjB,OAAO,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC,CAAC;QACnE,KAAK,eAAe;YAClB,OAAO,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,OAAO,CAAC,CAAC;QAC5C,KAAK,eAAe;YAClB,OAAO,IAAI,EAAE,CAAC,OAAO,QAAQ,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,GAAG,CAAC;QACtD,KAAK,WAAW;YACd,OAAO;gBACL,YAAY,EAAE,CAAC,SAAS,MAAM;gBAC9B,GAAG,MAAM,UAAU;gBACnB,GAAG,MAAM,GAAG,MAAM,OAAO;gBACzB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,KAAK;gBAChC,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC,OAAO,KAAK;gBAC9C,GAAG,MAAM,GAAG,MAAM,QAAQ;gBAC1B,GAAG,MAAM,IAAI;gBACb,GAAG;aACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,EAAM;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,MAAM,IAAI,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,GAAG,CAAC;QACtE,KAAK,YAAY;YACf,OAAO,MAAM,IAAI,eAAe,EAAE,CAAC,IAAI,GAAG,CAAC;QAC7C,KAAK,gBAAgB;YACnB,OAAO,MAAM,IAAI,SAAS,EAAE,CAAC,OAAO,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QAC3F,KAAK,cAAc;YACjB,OAAO,MAAM,IAAI,YAAY,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QAC3E,KAAK,eAAe;YAClB,OAAO,MAAM,IAAI,YAAY,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,MAAM,OAAO,EAAE,CAAC,OAAO,GAAG,CAAC;QAC5F,KAAK,eAAe;YAClB,OAAO,MAAM,IAAI,WAAW,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,UAAU,EAAE,CAAC,OAAO,IAAI,CAAC;QAC1E,KAAK,WAAW;YACd,OAAO,MAAM,IAAI,aAAa,EAAE,CAAC,OAAO,UAAU,EAAE,CAAC,SAAS,GAAG,CAAC;IACtE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAc;IACtD,OAAO,IAAI;SACR,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SACjD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,EAAM;IAC/C,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;AAChF,CAAC;AAYD,MAAM,CAAC,MAAM,WAAW,GAAG,4BAA4B,CAAC;AAExD;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAgB,EAAE,GAAkB;IAC7D,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACxC,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7E,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YAChF,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACnE,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC1D,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IAClF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO;YACL,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;SAC/D,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AAChD,CAAC","sourcesContent":["import type {\n ArrayExpression,\n Expression,\n JSXElement,\n JSXFragment,\n JSXOpeningElement,\n Node,\n ObjectExpression,\n ObjectProperty,\n} from \"@babel/types\";\n\nimport { applySplices, parseSource, type SourceFile, type Splice } from \"./source.js\";\nimport {\n type CallSite,\n findCall,\n forEachNode,\n importedName,\n objectArgument,\n propertyOf,\n textOf,\n valueOf,\n} from \"./syntax.js\";\n\nexport type { CallSite } from \"./syntax.js\";\n\n/**\n * The closed set of code edits. Each one knows how to tell that it is already done, how to\n * make it, and when it is unsafe. Add a kind here, with its tests, instead of editing text\n * anywhere else.\n */\nexport type Op =\n /** Joins an existing value import from `from`, else a new line where oxfmt's sorted imports put it. */\n | { readonly kind: \"import\"; readonly names: readonly string[]; readonly from: string }\n | { readonly kind: \"export-all\"; readonly from: string }\n /** Present when an element starts with the same identifier: `chat()` is present when `chat(...)` is. */\n | {\n readonly kind: \"append-element\";\n readonly call: CallSite;\n readonly path: readonly [string, ...string[]];\n readonly element: string;\n }\n /**\n * `$name` in `value` is replaced with the source text of the sibling property `name`, so\n * `createAuth($database)` follows whatever the app passes.\n */\n | {\n readonly kind: \"set-property\";\n readonly call: CallSite;\n readonly path: readonly [string, ...string[]];\n readonly value: string;\n /** `unsafe` for add-ons, which never overwrite; `replace` for create's own template. */\n readonly onConflict: \"unsafe\" | \"replace\";\n }\n | {\n readonly kind: \"wrap-property\";\n readonly call: CallSite;\n readonly path: readonly [string, ...string[]];\n readonly wrapper: string;\n }\n | {\n readonly kind: \"jsx-attribute\";\n readonly element: string;\n readonly name: string;\n readonly value: string;\n }\n /** The component must return exactly one JSX element or fragment. */\n | { readonly kind: \"jsx-child\"; readonly component: string; readonly element: string };\n\nexport type OpOutcome =\n | { readonly status: \"done\" }\n | { readonly status: \"edit\"; readonly splices: readonly Splice[] }\n | { readonly status: \"unsafe\"; readonly reason: string };\n\ntype CallOp = Extract<Op, { call: CallSite }>;\ntype Layout = {\n readonly multiline: boolean;\n readonly indent: string;\n readonly comma: boolean;\n};\n\nconst DONE: OpOutcome = { status: \"done\" };\nconst INLINE: Layout = { multiline: false, indent: \"\", comma: false };\nconst unsafe = (reason: string): OpOutcome => ({ status: \"unsafe\", reason });\nconst edit = (...splices: Splice[]): OpOutcome => ({ status: \"edit\", splices });\nconst squash = (text: string) => text.replace(/\\s+/g, \"\");\nconst INDENT = \" \";\n\nfunction lineIndent(text: string, offset: number): string {\n return /^[ \\t]*/.exec(text.slice(lineStart(text, offset)))?.[0] ?? \"\";\n}\n\nfunction lineStart(text: string, offset: number): number {\n return text.lastIndexOf(\"\\n\", offset - 1) + 1;\n}\n\nfunction lineEnd(text: string, offset: number): number {\n const end = text.indexOf(\"\\n\", offset);\n return end === -1 ? text.length : end;\n}\n\nfunction entry(path: readonly string[], value: string, layout: Layout): string {\n const [key, ...rest] = path;\n if (rest.length === 0) return `${key}: ${value}`;\n if (!layout.multiline) return `${key}: { ${entry(rest, value, layout)} }`;\n const inner = `${layout.indent}${INDENT}`;\n const child = entry(rest, value, { ...layout, indent: inner });\n return `${key}: {\\n${inner}${child}${layout.comma ? \",\" : \"\"}\\n${layout.indent}}`;\n}\n\n/** Appends to an object's properties or an array's elements in their own indentation and comma style. */\nfunction appendEntry(\n file: SourceFile,\n container: ObjectExpression | ArrayExpression,\n render: (layout: Layout) => string,\n): Splice {\n const items = (\n container.type === \"ObjectExpression\" ? container.properties : container.elements\n ).filter((item): item is NonNullable<typeof item> => item !== null);\n const last = items.at(-1);\n const close = (container.end ?? 0) - 1;\n if (!last) {\n const text = render(INLINE);\n return {\n start: (container.start ?? 0) + 1,\n end: close,\n text: container.type === \"ObjectExpression\" ? ` ${text} ` : text,\n };\n }\n const after = last.end ?? 0;\n const between = file.text.slice(after, close);\n if (!between.includes(\"\\n\"))\n return { start: after, end: after, text: `, ${render(INLINE)}` };\n const indent = lineIndent(file.text, last.start ?? 0);\n const comma = between.indexOf(\",\");\n const text = render({ multiline: true, indent, comma: comma !== -1 });\n return comma === -1\n ? { start: after, end: after, text: `,\\n${indent}${text}` }\n : { start: after + comma + 1, end: after + comma + 1, text: `\\n${indent}${text},` };\n}\n\nfunction substitute(\n file: SourceFile,\n object: ObjectExpression,\n value: string,\n): { readonly value: string } | { readonly missing: string } {\n let missing: string | undefined;\n const result = value.replace(/\\$([A-Za-z_]\\w*)/g, (_match, name: string) => {\n const sibling = propertyOf(file, object, name);\n if (!sibling.ok || !sibling.value) {\n missing = name;\n return name;\n }\n return textOf(file, valueOf(sibling.value));\n });\n return missing === undefined ? { value: result } : { missing };\n}\n\ntype Target =\n | { readonly kind: \"no-argument\"; readonly close: number }\n | {\n readonly kind: \"missing\";\n readonly object: ObjectExpression;\n readonly rest: readonly string[];\n }\n | {\n readonly kind: \"found\";\n readonly object: ObjectExpression;\n readonly property: ObjectProperty;\n };\n\n/** The property at `op.path`, or where it would go; a parent that is not an object literal is unsafe. */\nfunction locate(file: SourceFile, op: CallOp): Target | { readonly reason: string } {\n const call = findCall(file, op.call);\n if (!call.ok) return { reason: call.reason };\n const argument = objectArgument(file, op.call, call.value);\n if (!argument.ok) return { reason: argument.reason };\n if (argument.value.kind === \"none\") {\n return { kind: \"no-argument\", close: (call.value.end ?? 0) - 1 };\n }\n let object = argument.value.node;\n for (const [depth, key] of op.path.entries()) {\n const property = propertyOf(file, object, key);\n if (!property.ok) return { reason: property.reason };\n if (!property.value) return { kind: \"missing\", object, rest: op.path.slice(depth) };\n if (depth === op.path.length - 1)\n return { kind: \"found\", object, property: property.value };\n const value = valueOf(property.value);\n if (value.type !== \"ObjectExpression\") {\n return {\n reason: `\\`${key}\\` is \\`${textOf(file, value)}\\`, not an object literal.`,\n };\n }\n object = value;\n }\n return { reason: \"the path is empty.\" };\n}\n\nfunction leadingName(text: string): string | undefined {\n return /^[A-Za-z_$][\\w$]*/.exec(text.trim())?.[0];\n}\n\nfunction expressionName(node: Node): string | undefined {\n if (node.type === \"Identifier\") return node.name;\n if (node.type === \"CallExpression\") return expressionName(node.callee);\n return undefined;\n}\n\nfunction evaluateSetProperty(\n file: SourceFile,\n op: Extract<Op, { kind: \"set-property\" }>,\n): OpOutcome {\n const target = locate(file, op);\n if (\"reason\" in target) return unsafe(target.reason);\n if (target.kind === \"no-argument\") {\n if (/\\$\\w/.test(op.value))\n return unsafe(`${op.call.callee}() has no options to read.`);\n const text = entry(op.path, op.value, INLINE);\n return edit({ start: target.close, end: target.close, text: `{ ${text} }` });\n }\n const value = substitute(file, target.object, op.value);\n if (\"missing\" in value) {\n return unsafe(`${op.call.callee}() has no \\`${value.missing}\\` to pass.`);\n }\n if (target.kind === \"missing\") {\n return edit(\n appendEntry(file, target.object, (layout) =>\n entry(target.rest, value.value, layout),\n ),\n );\n }\n const current = valueOf(target.property);\n if (squash(textOf(file, current)) === squash(value.value)) return DONE;\n if (op.onConflict === \"unsafe\") {\n return unsafe(`\\`${op.path.at(-1)}\\` is already \\`${textOf(file, current)}\\`.`);\n }\n return edit({ start: current.start ?? 0, end: current.end ?? 0, text: value.value });\n}\n\nfunction evaluateAppendElement(\n file: SourceFile,\n op: Extract<Op, { kind: \"append-element\" }>,\n): OpOutcome {\n const target = locate(file, op);\n if (\"reason\" in target) return unsafe(target.reason);\n const array = `[${op.element}]`;\n if (target.kind === \"no-argument\") {\n const text = entry(op.path, array, INLINE);\n return edit({ start: target.close, end: target.close, text: `{ ${text} }` });\n }\n if (target.kind === \"missing\") {\n return edit(\n appendEntry(file, target.object, (layout) => entry(target.rest, array, layout)),\n );\n }\n const key = op.path.at(-1);\n const current = valueOf(target.property);\n if (current.type !== \"ArrayExpression\") {\n return unsafe(`\\`${key}\\` is \\`${textOf(file, current)}\\`, not an array literal.`);\n }\n const spread = current.elements.find((element) => element?.type === \"SpreadElement\");\n if (spread) {\n return unsafe(\n `\\`${key}\\` spreads \\`${textOf(file, spread)}\\`, which may already hold ${op.element}.`,\n );\n }\n const wanted = leadingName(op.element);\n if (current.elements.some((element) => element && expressionName(element) === wanted)) {\n return DONE;\n }\n return edit(appendEntry(file, current, () => op.element));\n}\n\nfunction evaluateWrapProperty(\n file: SourceFile,\n op: Extract<Op, { kind: \"wrap-property\" }>,\n): OpOutcome {\n const target = locate(file, op);\n if (\"reason\" in target) return unsafe(target.reason);\n if (target.kind !== \"found\")\n return unsafe(`${op.call.callee}() sets no \\`${op.path.at(-1)}\\`.`);\n const current = valueOf(target.property);\n if (\n current.type === \"CallExpression\" &&\n current.callee.type === \"Identifier\" &&\n current.callee.name === op.wrapper\n ) {\n return DONE;\n }\n return edit({\n start: current.start ?? 0,\n end: current.end ?? 0,\n text: `${op.wrapper}(${textOf(file, current)})`,\n });\n}\n\n/** Where `name` is bound at module scope: the module it is imported from, or this file. */\nfunction topLevelBinding(file: SourceFile, name: string): string | undefined {\n for (const statement of file.ast.program.body) {\n if (statement.type === \"ImportDeclaration\") {\n if (statement.specifiers.some((specifier) => specifier.local.name === name)) {\n return statement.source.value;\n }\n continue;\n }\n const declaration =\n statement.type === \"ExportNamedDeclaration\" ? statement.declaration : statement;\n if (!declaration) continue;\n if (\n (declaration.type === \"FunctionDeclaration\" ||\n declaration.type === \"ClassDeclaration\") &&\n declaration.id?.name === name\n ) {\n return file.path;\n }\n if (\n declaration.type === \"VariableDeclaration\" &&\n declaration.declarations.some(\n (declarator) =>\n declarator.id.type === \"Identifier\" && declarator.id.name === name,\n )\n ) {\n return file.path;\n }\n }\n return undefined;\n}\n\nconst isRelative = (source: string) => source.startsWith(\".\");\n\nfunction importLine(op: Extract<Op, { kind: \"import\" }>): string {\n return `import { ${op.names.join(\", \")} } from \"${op.from}\";`;\n}\n\nfunction evaluateImport(\n file: SourceFile,\n op: Extract<Op, { kind: \"import\" }>,\n): OpOutcome {\n const missing = op.names.filter((name) => importedName(file, name, op.from) !== name);\n if (missing.length === 0) return DONE;\n for (const name of missing) {\n const bound = topLevelBinding(file, name);\n if (bound !== undefined) {\n return unsafe(\n bound === file.path\n ? `\\`${name}\\` is already declared in ${file.path}.`\n : `\\`${name}\\` is already imported from ${bound}.`,\n );\n }\n }\n const imports = file.ast.program.body.filter(\n (statement) => statement.type === \"ImportDeclaration\",\n );\n const existing = imports.find(\n (statement) =>\n statement.source.value === op.from &&\n statement.importKind !== \"type\" &&\n statement.specifiers.length > 0 &&\n statement.specifiers.every((specifier) => specifier.type === \"ImportSpecifier\"),\n );\n if (existing) {\n const at = existing.specifiers.at(-1)?.end ?? 0;\n return edit({ start: at, end: at, text: `, ${missing.join(\", \")}` });\n }\n const line = importLine({ ...op, names: missing });\n const group = imports.filter(\n (statement) => isRelative(statement.source.value) === isRelative(op.from),\n );\n const before = group.find((statement) => statement.source.value > op.from);\n if (before) {\n const start = lineStart(file.text, before.start ?? 0);\n return edit({ start, end: start, text: `${line}\\n` });\n }\n const lastOfGroup = group.at(-1);\n if (lastOfGroup) {\n const at = lineEnd(file.text, lastOfGroup.end ?? 0);\n return edit({ start: at, end: at, text: `\\n${line}` });\n }\n const lastImport = imports.at(-1);\n if (isRelative(op.from) && lastImport) {\n const at = lineEnd(file.text, lastImport.end ?? 0);\n return edit({ start: at, end: at, text: `\\n\\n${line}` });\n }\n const start = imports[0] ? lineStart(file.text, imports[0].start ?? 0) : 0;\n return edit({ start, end: start, text: `${line}\\n\\n` });\n}\n\nfunction evaluateExportAll(\n file: SourceFile,\n op: Extract<Op, { kind: \"export-all\" }>,\n): OpOutcome {\n const present = file.ast.program.body.some(\n (statement) =>\n statement.type === \"ExportAllDeclaration\" && statement.source.value === op.from,\n );\n if (present) return DONE;\n const at = file.text.length;\n const newline = at === 0 || file.text.endsWith(\"\\n\") ? \"\" : \"\\n\";\n return edit({ start: at, end: at, text: `${newline}export * from \"${op.from}\";\\n` });\n}\n\nfunction openingElements(root: Node, name: string): JSXOpeningElement[] {\n const found: JSXOpeningElement[] = [];\n forEachNode(root, (node) => {\n if (\n node.type === \"JSXOpeningElement\" &&\n node.name.type === \"JSXIdentifier\" &&\n node.name.name === name\n ) {\n found.push(node);\n }\n });\n return found;\n}\n\nfunction evaluateJsxAttribute(\n file: SourceFile,\n op: Extract<Op, { kind: \"jsx-attribute\" }>,\n): OpOutcome {\n const elements = openingElements(file.ast, op.element);\n if (elements.length === 0) return unsafe(`${file.path} renders no <${op.element}>.`);\n if (elements.length > 1) {\n return unsafe(`<${op.element}> appears more than once in ${file.path}.`);\n }\n const element = elements[0]!;\n const existing = element.attributes.find(\n (attribute) =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.type === \"JSXIdentifier\" &&\n attribute.name.name === op.name,\n );\n if (existing) {\n return squash(textOf(file, existing)) === squash(`${op.name}=${op.value}`)\n ? DONE\n : unsafe(`<${op.element}> already sets \\`${textOf(file, existing)}\\`.`);\n }\n const after = element.attributes.at(-1)?.end ?? element.name.end ?? 0;\n return edit({ start: after, end: after, text: ` ${op.name}=${op.value}` });\n}\n\nfunction componentBody(file: SourceFile, name: string): Node | undefined {\n for (const statement of file.ast.program.body) {\n const declaration =\n statement.type === \"ExportNamedDeclaration\" ||\n statement.type === \"ExportDefaultDeclaration\"\n ? statement.declaration\n : statement;\n if (declaration?.type === \"FunctionDeclaration\" && declaration.id?.name === name) {\n return declaration.body;\n }\n if (declaration?.type !== \"VariableDeclaration\") continue;\n for (const declarator of declaration.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === name &&\n (declarator.init?.type === \"ArrowFunctionExpression\" ||\n declarator.init?.type === \"FunctionExpression\")\n ) {\n return declarator.init.body;\n }\n }\n }\n return undefined;\n}\n\n/** The values the component itself returns, not those of functions nested in it. */\nfunction returnedValues(body: Node): readonly (Expression | null | undefined)[] {\n if (body.type !== \"BlockStatement\") return [body as Expression];\n const returned: (Expression | null | undefined)[] = [];\n const visit = (node: Node) => {\n if (\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\"\n ) {\n return;\n }\n if (node.type === \"ReturnStatement\") returned.push(node.argument);\n for (const [key, value] of Object.entries(node)) {\n if (key === \"loc\" || key.endsWith(\"Comments\")) continue;\n for (const child of Array.isArray(value) ? value : [value]) {\n if (\n child &&\n typeof child === \"object\" &&\n typeof (child as Node).type === \"string\"\n ) {\n visit(child as Node);\n }\n }\n }\n };\n for (const statement of body.body) visit(statement);\n return returned;\n}\n\nfunction evaluateJsxChild(\n file: SourceFile,\n op: Extract<Op, { kind: \"jsx-child\" }>,\n): OpOutcome {\n const body = componentBody(file, op.component);\n if (!body) return unsafe(`${file.path} has no component ${op.component}.`);\n const returned = returnedValues(body);\n const root = returned[0];\n if (\n returned.length !== 1 ||\n !root ||\n (root.type !== \"JSXElement\" && root.type !== \"JSXFragment\")\n ) {\n return unsafe(\n `${op.component} must return exactly one JSX element to add <${op.element} />.`,\n );\n }\n if (openingElements(root, op.element).length > 0) return DONE;\n const parent: JSXElement | JSXFragment = root;\n const closing =\n parent.type === \"JSXElement\" ? parent.closingElement : parent.closingFragment;\n if (!closing) return unsafe(`${op.component} returns a self-closing element.`);\n const last = parent.children\n .filter((child) => child.type !== \"JSXText\" || child.value.trim() !== \"\")\n .at(-1);\n const tag = `<${op.element} />`;\n if (!last) {\n const at = closing.start ?? 0;\n return edit({ start: at, end: at, text: tag });\n }\n const at = last.end ?? 0;\n return edit({\n start: at,\n end: at,\n text: `\\n${lineIndent(file.text, last.start ?? 0)}${tag}`,\n });\n}\n\nexport function evaluateOp(file: SourceFile, op: Op): OpOutcome {\n switch (op.kind) {\n case \"import\":\n return evaluateImport(file, op);\n case \"export-all\":\n return evaluateExportAll(file, op);\n case \"append-element\":\n return evaluateAppendElement(file, op);\n case \"set-property\":\n return evaluateSetProperty(file, op);\n case \"wrap-property\":\n return evaluateWrapProperty(file, op);\n case \"jsx-attribute\":\n return evaluateJsxAttribute(file, op);\n case \"jsx-child\":\n return evaluateJsxChild(file, op);\n }\n}\n\n/** One short line for the printed plan, such as \"auth: createAuth(database)\"; none for an import. */\nexport function opSummary(file: SourceFile, op: Op): string | undefined {\n switch (op.kind) {\n case \"import\":\n return undefined;\n case \"export-all\":\n return `export * from \"${op.from}\"`;\n case \"append-element\":\n return `${op.path.join(\".\")}: ${op.element}`;\n case \"set-property\": {\n const target = locate(file, op);\n const value =\n \"reason\" in target || target.kind === \"no-argument\"\n ? op.value\n : (() => {\n const substituted = substitute(file, target.object, op.value);\n return \"value\" in substituted ? substituted.value : op.value;\n })();\n return `${op.path.join(\".\")}: ${value}`;\n }\n case \"wrap-property\": {\n const target = locate(file, op);\n const current =\n !(\"reason\" in target) && target.kind === \"found\"\n ? textOf(file, valueOf(target.property))\n : \"...\";\n return `${op.path.join(\".\")}: ${op.wrapper}(${current})`;\n }\n case \"jsx-attribute\":\n return `<${op.element} ${op.name}=${op.value}>`;\n case \"jsx-child\":\n return `<${op.element} />`;\n }\n}\n\nfunction callCode(op: CallOp, value: string): string {\n const nested = (path: readonly string[], indent: string): string => {\n const [key, ...rest] = path;\n if (rest.length === 0) return `${indent}${key}: ${value},`;\n return `${indent}${key}: {\\n${nested(rest, `${indent}${INDENT}`)}\\n${indent}},`;\n };\n return `${op.call.callee}({\\n${INDENT}...\\n${nested(op.path, INDENT)}\\n});`;\n}\n\n/** The code to write by hand for `op`; a `$name` stands for the sibling the app passes. */\nexport function manualCode(op: Op): string {\n switch (op.kind) {\n case \"import\":\n return importLine(op);\n case \"export-all\":\n return `export * from \"${op.from}\";`;\n case \"append-element\":\n return callCode(op, `[..., ${op.element}]`);\n case \"set-property\":\n return callCode(op, op.value.replace(/\\$([A-Za-z_]\\w*)/g, \"$1\"));\n case \"wrap-property\":\n return callCode(op, `${op.wrapper}(...)`);\n case \"jsx-attribute\":\n return `<${op.element} ... ${op.name}=${op.value}>`;\n case \"jsx-child\":\n return [\n `function ${op.component}() {`,\n `${INDENT}return (`,\n `${INDENT}${INDENT}<...>`,\n `${INDENT}${INDENT}${INDENT}...`,\n `${INDENT}${INDENT}${INDENT}<${op.element} />`,\n `${INDENT}${INDENT}</...>`,\n `${INDENT});`,\n \"}\",\n ].join(\"\\n\");\n }\n}\n\nfunction manualHeading(file: string, op: Op): string {\n switch (op.kind) {\n case \"import\":\n return `In ${file}, import ${op.names.join(\", \")} from ${op.from}:`;\n case \"export-all\":\n return `In ${file}, re-export ${op.from}:`;\n case \"append-element\":\n return `In ${file}, add ${op.element} to \\`${op.path.join(\".\")}\\` of ${op.call.callee}:`;\n case \"set-property\":\n return `In ${file}, pass \\`${op.path.join(\".\")}\\` to ${op.call.callee}:`;\n case \"wrap-property\":\n return `In ${file}, wrap \\`${op.path.join(\".\")}\\` of ${op.call.callee} in ${op.wrapper}:`;\n case \"jsx-attribute\":\n return `In ${file}, add \\`${op.name}=${op.value}\\` to <${op.element}>:`;\n case \"jsx-child\":\n return `In ${file}, render <${op.element} /> in ${op.component}:`;\n }\n}\n\nexport function indentLines(text: string, indent: string): string {\n return text\n .split(\"\\n\")\n .map((line) => (line ? `${indent}${line}` : line))\n .join(\"\\n\");\n}\n\n/** The change to make by hand when `op` is unsafe. */\nexport function manualChange(file: string, op: Op): string {\n return `${manualHeading(file, op)}\\n\\n${indentLines(manualCode(op), INDENT)}`;\n}\n\nexport type EditOutcome =\n | { readonly status: \"done\" }\n | { readonly status: \"edit\"; readonly text: string }\n | { readonly status: \"unsafe\"; readonly unsafe: readonly UnsafeOp[] };\n\nexport interface UnsafeOp {\n readonly op: Op;\n readonly reason: string;\n}\n\nexport const UNCONFIRMED = \"could not confirm the edit\";\n\n/**\n * Applies `ops` in order, reparsing after each one, then proves the result: it must still\n * parse and every op must now evaluate to `done`. A failed proof is `unsafe`, so a bug in an op\n * can cost a manual step but never a broken or half-edited file, and a second run is a no-op\n * by construction. Collects every unsafe op instead of stopping at the first.\n */\nexport function editSource(file: SourceFile, ops: readonly Op[]): EditOutcome {\n let current = file;\n const failed: UnsafeOp[] = [];\n for (const op of ops) {\n const outcome = evaluateOp(current, op);\n if (outcome.status === \"unsafe\") failed.push({ op, reason: outcome.reason });\n if (outcome.status === \"edit\") {\n try {\n current = parseSource(file.path, applySplices(current.text, outcome.splices));\n } catch {\n failed.push({ op, reason: UNCONFIRMED });\n }\n }\n }\n if (failed.length > 0) return { status: \"unsafe\", unsafe: failed };\n if (current.text === file.text) return { status: \"done\" };\n const unconfirmed = ops.filter((op) => evaluateOp(current, op).status !== \"done\");\n if (unconfirmed.length > 0) {\n return {\n status: \"unsafe\",\n unsafe: unconfirmed.map((op) => ({ op, reason: UNCONFIRMED })),\n };\n }\n return { status: \"edit\", text: current.text };\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { File } from "@babel/types";
2
+ /** A parsed TypeScript or TSX module. Edits never reprint it; they splice `text`. */
3
+ export interface SourceFile {
4
+ /** Project-relative, with `/` separators. */
5
+ readonly path: string;
6
+ readonly text: string;
7
+ readonly ast: File;
8
+ }
9
+ export declare class SourceSyntaxError extends Error {
10
+ readonly path: string;
11
+ readonly line: number;
12
+ readonly name = "SourceSyntaxError";
13
+ constructor(path: string, line: number, message: string);
14
+ }
15
+ export declare function parseSource(path: string, text: string): SourceFile;
16
+ /** Offsets index the text before any splice of the same batch. */
17
+ export interface Splice {
18
+ readonly start: number;
19
+ readonly end: number;
20
+ readonly text: string;
21
+ }
22
+ /** Splices must not overlap; two inserts at one offset keep their order. */
23
+ export declare function applySplices(text: string, splices: readonly Splice[]): string;
24
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/edit/source.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,qFAAqF;AACrF,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;CACpB;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAGxC,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;IAHvB,SAAkB,IAAI,uBAAuB;IAC7C,YACW,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EAGhB;CACF;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAWlE;AAED,kEAAkE;AAClE,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAS7E"}