assistant-ui 0.0.123 → 0.0.125
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 +7 -0
- package/dist/codemods/v0-11/content-part-to-message-part.d.ts.map +1 -1
- package/dist/codemods/v0-11/content-part-to-message-part.js +126 -40
- package/dist/codemods/v0-11/content-part-to-message-part.js.map +1 -1
- package/dist/codemods/v0-12/event-names-to-camelcase.d.ts.map +1 -1
- package/dist/codemods/v0-12/event-names-to-camelcase.js +2 -2
- package/dist/codemods/v0-12/event-names-to-camelcase.js.map +1 -1
- package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts.map +1 -1
- package/dist/codemods/v0-12/primitive-if-to-aui-if.js +57 -41
- package/dist/codemods/v0-12/primitive-if-to-aui-if.js.map +1 -1
- package/dist/codemods/v0-8/ui-package-split.d.ts.map +1 -1
- package/dist/codemods/v0-8/ui-package-split.js +11 -35
- package/dist/codemods/v0-8/ui-package-split.js.map +1 -1
- package/dist/codemods/v0-9/edge-package-split.d.ts.map +1 -1
- package/dist/codemods/v0-9/edge-package-split.js +15 -69
- package/dist/codemods/v0-9/edge-package-split.js.map +1 -1
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +12 -3
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/info.d.ts.map +1 -1
- package/dist/commands/info.js +8 -1
- package/dist/commands/info.js.map +1 -1
- package/dist/lib/cloud-auth.d.ts +1 -0
- package/dist/lib/cloud-auth.d.ts.map +1 -1
- package/dist/lib/cloud-auth.js +4 -3
- package/dist/lib/cloud-auth.js.map +1 -1
- package/dist/lib/cloud-remote.d.ts +22 -0
- package/dist/lib/cloud-remote.d.ts.map +1 -0
- package/dist/lib/cloud-remote.js +74 -0
- package/dist/lib/cloud-remote.js.map +1 -0
- package/dist/lib/utils/package-installer.d.ts.map +1 -1
- package/dist/lib/utils/package-installer.js +3 -2
- package/dist/lib/utils/package-installer.js.map +1 -1
- package/package.json +1 -1
- package/src/codemods/v0-11/__tests__/content-part-to-message-part.test.ts +268 -2
- package/src/codemods/v0-11/content-part-to-message-part.ts +211 -80
- package/src/codemods/v0-12/__tests__/event-names-to-camelcase.test.ts +23 -0
- package/src/codemods/v0-12/__tests__/primitive-if-to-aui-if.test.ts +150 -1
- package/src/codemods/v0-12/event-names-to-camelcase.ts +2 -8
- package/src/codemods/v0-12/primitive-if-to-aui-if.ts +92 -61
- package/src/codemods/v0-8/__tests__/ui-package-split.test.ts +83 -0
- package/src/codemods/v0-8/ui-package-split.ts +21 -59
- package/src/codemods/v0-9/edge-package-split.test.ts +100 -0
- package/src/codemods/v0-9/edge-package-split.ts +30 -139
- package/src/commands/cloud.test.ts +58 -0
- package/src/commands/cloud.ts +19 -2
- package/src/commands/info.ts +26 -1
- package/src/lib/cloud-auth.ts +14 -8
- package/src/lib/cloud-remote.test.ts +156 -0
- package/src/lib/cloud-remote.ts +150 -0
- package/src/lib/utils/package-installer.test.ts +39 -0
- package/src/lib/utils/package-installer.ts +3 -1
- package/templates/cloud-harness/components/assistant-ui/elements/tool-fallback.aui.tsx +17 -8
- package/templates/cloud-harness/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createTransformer } from "../utils/createTransformer";
|
|
2
|
+
import { resolveBinding } from "../utils/resolveBinding";
|
|
2
3
|
|
|
3
4
|
// Map of old ContentPart names to new MessagePart names
|
|
4
5
|
const typeRenamingMap: Record<string, string> = {
|
|
@@ -71,100 +72,230 @@ const typeRenamingMap: Record<string, string> = {
|
|
|
71
72
|
|
|
72
73
|
const migrateContentPartToMessagePart = createTransformer(
|
|
73
74
|
({ j, root, markAsChanged }) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
75
|
+
const renameExport = (path: any, newName: string) => {
|
|
76
|
+
const specifier = path.value;
|
|
77
|
+
const replacement: any = j.exportSpecifier.from({
|
|
78
|
+
local: j.identifier(newName),
|
|
79
|
+
exported: j.Identifier.check(specifier.exported)
|
|
80
|
+
? j.identifier(specifier.exported.name)
|
|
81
|
+
: specifier.exported,
|
|
82
|
+
});
|
|
83
|
+
replacement.exportKind = specifier.exportKind;
|
|
84
|
+
replacement.comments = specifier.comments;
|
|
85
|
+
path.replace(replacement);
|
|
86
|
+
};
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (j.ImportSpecifier.check(specifier)) {
|
|
82
|
-
const oldName = specifier.imported.name as string;
|
|
83
|
-
if (typeRenamingMap[oldName]) {
|
|
84
|
-
specifier.imported.name = typeRenamingMap[oldName];
|
|
85
|
-
if (specifier.local && specifier.local.name === oldName) {
|
|
86
|
-
specifier.local.name = typeRenamingMap[oldName];
|
|
87
|
-
}
|
|
88
|
-
markAsChanged();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
88
|
+
const declaredNames = new Set<string>();
|
|
89
|
+
root.find(j.TSTypeParameter).forEach((path: any) => {
|
|
90
|
+
declaredNames.add(path.value.name.name ?? path.value.name);
|
|
93
91
|
});
|
|
92
|
+
const identifiers = root.find(j.Identifier).paths();
|
|
93
|
+
for (const path of identifiers) {
|
|
94
|
+
if (path.parent.value.id === path.value)
|
|
95
|
+
declaredNames.add(path.value.name);
|
|
96
|
+
}
|
|
97
|
+
const usedNames = new Set(identifiers.map((path: any) => path.value.name));
|
|
98
|
+
const messageBindings = new Set<any>();
|
|
99
|
+
const namespaceBindings = new Set<any>();
|
|
100
|
+
const namespaceNames = new Set<string>();
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
path.value.object.name === "MessagePrimitive" &&
|
|
100
|
-
path.value.property &&
|
|
101
|
-
path.value.property.name === "Content"
|
|
102
|
-
) {
|
|
103
|
-
path.value.property.name = "Parts";
|
|
104
|
-
markAsChanged();
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// 3. Update JSX member expressions (e.g., MessagePrimitive.Content in JSX)
|
|
109
|
-
root.find(j.JSXMemberExpression).forEach((path: any) => {
|
|
110
|
-
if (
|
|
111
|
-
path.value.object &&
|
|
112
|
-
path.value.object.name === "MessagePrimitive" &&
|
|
113
|
-
path.value.property &&
|
|
114
|
-
path.value.property.name === "Content"
|
|
115
|
-
) {
|
|
116
|
-
path.value.property.name = "Parts";
|
|
117
|
-
markAsChanged();
|
|
118
|
-
}
|
|
119
|
-
});
|
|
102
|
+
root.find(j.ImportDeclaration).forEach((path: any) => {
|
|
103
|
+
const source = path.value.source.value;
|
|
104
|
+
if (typeof source !== "string" || !source.startsWith("@assistant-ui/"))
|
|
105
|
+
return;
|
|
120
106
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
markAsChanged();
|
|
107
|
+
path.value.specifiers?.forEach((specifier: any, index: number) => {
|
|
108
|
+
if (j.ImportNamespaceSpecifier.check(specifier)) {
|
|
109
|
+
namespaceBindings.add(specifier.local);
|
|
110
|
+
if (j.Identifier.check(specifier.local))
|
|
111
|
+
namespaceNames.add(specifier.local.name);
|
|
112
|
+
return;
|
|
128
113
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
114
|
+
if (
|
|
115
|
+
!j.ImportSpecifier.check(specifier) ||
|
|
116
|
+
!j.Identifier.check(specifier.imported) ||
|
|
117
|
+
!j.Identifier.check(specifier.local)
|
|
118
|
+
)
|
|
119
|
+
return;
|
|
120
|
+
const oldName = specifier.imported.name;
|
|
121
|
+
const importKind = (specifier as { importKind?: string }).importKind;
|
|
122
|
+
const typeOnly =
|
|
123
|
+
path.value.importKind === "type" || importKind === "type";
|
|
124
|
+
if (oldName === "MessagePrimitive" && !typeOnly) {
|
|
125
|
+
messageBindings.add(specifier.local);
|
|
126
|
+
}
|
|
127
|
+
const newName = typeRenamingMap[oldName];
|
|
128
|
+
if (!newName) return;
|
|
138
129
|
|
|
139
|
-
|
|
130
|
+
const references = identifiers.filter((reference: any) => {
|
|
131
|
+
const node = reference.value;
|
|
132
|
+
if (node.name !== oldName) return false;
|
|
133
|
+
const parent = reference.parent.value;
|
|
140
134
|
if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
135
|
+
parent.type.startsWith("Import") ||
|
|
136
|
+
parent.id === node ||
|
|
137
|
+
parent.label === node
|
|
138
|
+
)
|
|
139
|
+
return false;
|
|
140
|
+
if (parent.key === node && !parent.computed && parent.value !== node)
|
|
141
|
+
return false;
|
|
142
|
+
if (parent.property === node && !parent.computed) return false;
|
|
143
|
+
if (j.TSQualifiedName.check(parent) && parent.right === node)
|
|
144
|
+
return false;
|
|
145
|
+
if (j.JSXAttribute.check(parent) || j.JSXNamespacedName.check(parent))
|
|
146
|
+
return false;
|
|
148
147
|
if (
|
|
149
|
-
j.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
(j.JSXOpeningElement.check(parent) ||
|
|
149
|
+
j.JSXClosingElement.check(parent)) &&
|
|
150
|
+
/^[a-z]/.test(oldName)
|
|
151
|
+
)
|
|
152
|
+
return false;
|
|
153
|
+
if (
|
|
154
|
+
j.ExportSpecifier.check(parent) &&
|
|
155
|
+
(reference.parent.parent.value.source || parent.local !== node)
|
|
156
|
+
)
|
|
157
|
+
return false;
|
|
158
|
+
return true;
|
|
159
|
+
});
|
|
160
|
+
// Retain the local alias when a declaration in either TypeScript namespace
|
|
161
|
+
// could capture a renamed reference. The resolver covers value bindings.
|
|
162
|
+
const canRename =
|
|
163
|
+
specifier.local.name === oldName &&
|
|
164
|
+
!usedNames.has(newName) &&
|
|
165
|
+
!declaredNames.has(newName) &&
|
|
166
|
+
!declaredNames.has(oldName) &&
|
|
167
|
+
references.every((reference: any) => {
|
|
168
|
+
const binding = resolveBinding(j, reference, oldName);
|
|
169
|
+
return binding === specifier.local || (typeOnly && !binding);
|
|
170
|
+
});
|
|
171
|
+
if (canRename) {
|
|
172
|
+
for (const reference of references) {
|
|
173
|
+
const parent = reference.parent.value;
|
|
174
|
+
if (
|
|
175
|
+
(j.Property.check(parent) || j.ObjectProperty.check(parent)) &&
|
|
176
|
+
parent.shorthand
|
|
177
|
+
) {
|
|
178
|
+
parent.shorthand = false;
|
|
179
|
+
parent.key = j.identifier(oldName);
|
|
180
|
+
parent.value = j.identifier(newName);
|
|
181
|
+
} else if (j.ExportSpecifier.check(parent)) {
|
|
182
|
+
renameExport(reference.parent, newName);
|
|
183
|
+
} else {
|
|
184
|
+
reference.value.name = newName;
|
|
185
|
+
}
|
|
153
186
|
}
|
|
154
|
-
|
|
155
|
-
path.value.name = newName;
|
|
156
|
-
markAsChanged();
|
|
157
187
|
}
|
|
188
|
+
const replacement: any = j.importSpecifier(
|
|
189
|
+
j.identifier(newName),
|
|
190
|
+
j.identifier(canRename ? newName : specifier.local.name),
|
|
191
|
+
);
|
|
192
|
+
replacement.importKind = importKind;
|
|
193
|
+
replacement.comments = specifier.comments;
|
|
194
|
+
path.get("specifiers", index).replace(replacement);
|
|
195
|
+
markAsChanged();
|
|
158
196
|
});
|
|
197
|
+
});
|
|
159
198
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
199
|
+
root.find(j.ExportNamedDeclaration).forEach((path: any) => {
|
|
200
|
+
const source = path.value.source?.value;
|
|
201
|
+
if (typeof source !== "string" || !source.startsWith("@assistant-ui/"))
|
|
202
|
+
return;
|
|
203
|
+
path.value.specifiers?.forEach((specifier: any, index: number) => {
|
|
204
|
+
if (
|
|
205
|
+
!j.ExportSpecifier.check(specifier) ||
|
|
206
|
+
!j.Identifier.check(specifier.local)
|
|
207
|
+
)
|
|
208
|
+
return;
|
|
209
|
+
const newName = typeRenamingMap[specifier.local.name];
|
|
210
|
+
if (!newName) return;
|
|
211
|
+
renameExport(path.get("specifiers", index), newName);
|
|
212
|
+
markAsChanged();
|
|
166
213
|
});
|
|
167
214
|
});
|
|
215
|
+
|
|
216
|
+
const isTypeShadowed = (path: any, name: string) => {
|
|
217
|
+
if (!declaredNames.has(name)) return false;
|
|
218
|
+
for (let current = path.parent; current; current = current.parent) {
|
|
219
|
+
const node = current.value;
|
|
220
|
+
if (
|
|
221
|
+
node.type === "TSTypeParameterDeclaration" &&
|
|
222
|
+
node.params.some((param: any) => param.name === name)
|
|
223
|
+
)
|
|
224
|
+
return true;
|
|
225
|
+
if (node.type === "ClassExpression" && node.id?.name === name)
|
|
226
|
+
return true;
|
|
227
|
+
if (!["Program", "BlockStatement", "TSModuleBlock"].includes(node.type))
|
|
228
|
+
continue;
|
|
229
|
+
if (
|
|
230
|
+
node.body.some((statement: any) => {
|
|
231
|
+
const declaration = statement.declaration ?? statement;
|
|
232
|
+
return (
|
|
233
|
+
[
|
|
234
|
+
"TSInterfaceDeclaration",
|
|
235
|
+
"TSTypeAliasDeclaration",
|
|
236
|
+
"TSEnumDeclaration",
|
|
237
|
+
"ClassDeclaration",
|
|
238
|
+
"TSModuleDeclaration",
|
|
239
|
+
].includes(declaration.type) && declaration.id?.name === name
|
|
240
|
+
);
|
|
241
|
+
})
|
|
242
|
+
)
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
return false;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const isInTypeQuery = (path: any) => {
|
|
249
|
+
for (let current = path.parent; current; current = current.parent) {
|
|
250
|
+
if (j.TSTypeQuery.check(current.value)) return true;
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const renameNamespaceMember = (path: any) => {
|
|
256
|
+
const node = path.value;
|
|
257
|
+
const qualified = j.TSQualifiedName.check(node);
|
|
258
|
+
const object = qualified ? node.left : node.object;
|
|
259
|
+
const property = qualified ? node.right : node.property;
|
|
260
|
+
if (
|
|
261
|
+
node.computed ||
|
|
262
|
+
!j.Identifier.check(object) ||
|
|
263
|
+
!j.Identifier.check(property)
|
|
264
|
+
)
|
|
265
|
+
return;
|
|
266
|
+
const newName = typeRenamingMap[property.name];
|
|
267
|
+
if (!newName) return;
|
|
268
|
+
if (qualified && !isInTypeQuery(path)) {
|
|
269
|
+
if (
|
|
270
|
+
!namespaceNames.has(object.name) ||
|
|
271
|
+
isTypeShadowed(path, object.name)
|
|
272
|
+
)
|
|
273
|
+
return;
|
|
274
|
+
} else if (!namespaceBindings.has(resolveBinding(j, path, object.name))) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
property.name = newName;
|
|
278
|
+
markAsChanged();
|
|
279
|
+
};
|
|
280
|
+
root.find(j.TSQualifiedName).forEach(renameNamespaceMember);
|
|
281
|
+
root.find(j.JSXMemberExpression).forEach(renameNamespaceMember);
|
|
282
|
+
root.find(j.MemberExpression).forEach(renameNamespaceMember);
|
|
283
|
+
|
|
284
|
+
const renameContent = (path: any) => {
|
|
285
|
+
const { object, property, computed } = path.value;
|
|
286
|
+
if (
|
|
287
|
+
computed ||
|
|
288
|
+
!j.Identifier.check(object) ||
|
|
289
|
+
!j.Identifier.check(property) ||
|
|
290
|
+
property.name !== "Content" ||
|
|
291
|
+
!messageBindings.has(resolveBinding(j, path, object.name))
|
|
292
|
+
)
|
|
293
|
+
return;
|
|
294
|
+
property.name = "Parts";
|
|
295
|
+
markAsChanged();
|
|
296
|
+
};
|
|
297
|
+
root.find(j.MemberExpression).forEach(renameContent);
|
|
298
|
+
root.find(j.JSXMemberExpression).forEach(renameContent);
|
|
168
299
|
},
|
|
169
300
|
);
|
|
170
301
|
|
|
@@ -125,3 +125,26 @@ function MyComponent() {
|
|
|
125
125
|
);
|
|
126
126
|
});
|
|
127
127
|
});
|
|
128
|
+
|
|
129
|
+
describe("event-name migration preserves unrelated strings", () => {
|
|
130
|
+
const cases = [
|
|
131
|
+
"constructor",
|
|
132
|
+
"toString",
|
|
133
|
+
"__proto__",
|
|
134
|
+
"hasOwnProperty",
|
|
135
|
+
].flatMap((name) => [JSON.stringify(name), `\`${name}\``]);
|
|
136
|
+
|
|
137
|
+
it.each(cases)("leaves %s unchanged without event renames", (literal) => {
|
|
138
|
+
expectNoChange(`const key = ${literal};`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it.each(cases)("preserves %s beside an event rename", (literal) => {
|
|
142
|
+
const source = `import { useAuiEvent } from "@assistant-ui/react";
|
|
143
|
+
const key = ${literal};
|
|
144
|
+
useAuiEvent("thread.run-start", () => {});`;
|
|
145
|
+
const output = applyTransform(source);
|
|
146
|
+
expect(output).toBe(source.replace("thread.run-start", "thread.runStart"));
|
|
147
|
+
expect(() => j(output!)).not.toThrow();
|
|
148
|
+
expectNoChange(output!);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -969,7 +969,7 @@ function MyComponent() {
|
|
|
969
969
|
});
|
|
970
970
|
});
|
|
971
971
|
|
|
972
|
-
describe("condition
|
|
972
|
+
describe("condition semantics", () => {
|
|
973
973
|
const compileCondition = (jsx: string) => {
|
|
974
974
|
const output = applyTransform(`
|
|
975
975
|
import { MessagePrimitive, ComposerPrimitive } from "@assistant-ui/react";
|
|
@@ -982,6 +982,40 @@ const view = ${jsx};
|
|
|
982
982
|
) => boolean;
|
|
983
983
|
};
|
|
984
984
|
|
|
985
|
+
it.each(["hasBranches", "lastOrHover"])(
|
|
986
|
+
"keeps %s={false} unconditional",
|
|
987
|
+
(filter) => {
|
|
988
|
+
const condition = compileCondition(
|
|
989
|
+
`<MessagePrimitive.If ${filter}={false} />`,
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
for (const branchCount of [1, 2]) {
|
|
993
|
+
for (const isLast of [false, true]) {
|
|
994
|
+
for (const isHovering of [false, true]) {
|
|
995
|
+
expect(
|
|
996
|
+
condition({ message: { branchCount, isLast, isHovering } }),
|
|
997
|
+
).toBe(true);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
},
|
|
1002
|
+
);
|
|
1003
|
+
|
|
1004
|
+
it.each(["hasBranches", "lastOrHover"])(
|
|
1005
|
+
"keeps the role filter when %s={false}",
|
|
1006
|
+
(filter) => {
|
|
1007
|
+
const condition = compileCondition(
|
|
1008
|
+
`<MessagePrimitive.If ${filter}={false} assistant />`,
|
|
1009
|
+
);
|
|
1010
|
+
const message = { branchCount: 1, isLast: false, isHovering: false };
|
|
1011
|
+
|
|
1012
|
+
expect(condition({ message: { ...message, role: "assistant" } })).toBe(
|
|
1013
|
+
true,
|
|
1014
|
+
);
|
|
1015
|
+
expect(condition({ message: { ...message, role: "user" } })).toBe(false);
|
|
1016
|
+
},
|
|
1017
|
+
);
|
|
1018
|
+
|
|
985
1019
|
it.each([
|
|
986
1020
|
{
|
|
987
1021
|
jsx: "<MessagePrimitive.If speaking={false} />",
|
|
@@ -1015,3 +1049,118 @@ const view = ${jsx};
|
|
|
1015
1049
|
expect(condition(visible)).toBe(true);
|
|
1016
1050
|
});
|
|
1017
1051
|
});
|
|
1052
|
+
|
|
1053
|
+
describe("conditional import bindings", () => {
|
|
1054
|
+
it("adds AuiIf to the primitive's module when another package is imported later", () => {
|
|
1055
|
+
const output =
|
|
1056
|
+
applyTransform(`import { ThreadPrimitive } from "@assistant-ui/react";
|
|
1057
|
+
import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown";
|
|
1058
|
+
export const Empty = () => <ThreadPrimitive.If empty><MarkdownTextPrimitive /></ThreadPrimitive.If>;`)!;
|
|
1059
|
+
expect(output).toContain(
|
|
1060
|
+
'import { ThreadPrimitive, AuiIf } from "@assistant-ui/react"',
|
|
1061
|
+
);
|
|
1062
|
+
expect(output).toContain(
|
|
1063
|
+
'import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown"',
|
|
1064
|
+
);
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
it("uses an existing value import's local alias for both tags", () => {
|
|
1068
|
+
const output =
|
|
1069
|
+
applyTransform(`import { ThreadPrimitive, AuiIf as If } from "@assistant-ui/react";
|
|
1070
|
+
export const Empty = () => <ThreadPrimitive.If empty>Empty</ThreadPrimitive.If>;`)!;
|
|
1071
|
+
expect(output).toContain("<If condition=");
|
|
1072
|
+
expect(output).toContain("</If>");
|
|
1073
|
+
expect(
|
|
1074
|
+
j(output)
|
|
1075
|
+
.find(j.ImportSpecifier, { imported: { name: "AuiIf" } })
|
|
1076
|
+
.size(),
|
|
1077
|
+
).toBe(1);
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
it.each([
|
|
1081
|
+
'import type { AuiIf } from "@assistant-ui/react";',
|
|
1082
|
+
'import { type AuiIf } from "@assistant-ui/react";',
|
|
1083
|
+
'import { AuiIf } from "./custom-if";',
|
|
1084
|
+
'import { Other as AuiIf } from "@assistant-ui/react";',
|
|
1085
|
+
])(
|
|
1086
|
+
"keeps a separate value binding when the name is occupied: %s",
|
|
1087
|
+
(declaration) => {
|
|
1088
|
+
const output =
|
|
1089
|
+
applyTransform(`import { ThreadPrimitive } from "@assistant-ui/react";
|
|
1090
|
+
${declaration}
|
|
1091
|
+
export const Empty = () => <ThreadPrimitive.Empty>Empty</ThreadPrimitive.Empty>;`)!;
|
|
1092
|
+
expect(output).toContain("AuiIf as AuiIf1");
|
|
1093
|
+
expect(output).toContain("<AuiIf1 condition=");
|
|
1094
|
+
expect(output).toContain("</AuiIf1>");
|
|
1095
|
+
expect(output).toContain(declaration);
|
|
1096
|
+
},
|
|
1097
|
+
);
|
|
1098
|
+
|
|
1099
|
+
it("does not append a value component to a later type-only import", () => {
|
|
1100
|
+
const output =
|
|
1101
|
+
applyTransform(`import { ThreadPrimitive } from "@assistant-ui/react";
|
|
1102
|
+
import type { ThreadMessage } from "@assistant-ui/react";
|
|
1103
|
+
export const Empty = () => <ThreadPrimitive.If empty />;`)!;
|
|
1104
|
+
expect(output).toContain(
|
|
1105
|
+
'import { ThreadPrimitive, AuiIf } from "@assistant-ui/react"',
|
|
1106
|
+
);
|
|
1107
|
+
expect(output).toContain(
|
|
1108
|
+
'import type { ThreadMessage } from "@assistant-ui/react"',
|
|
1109
|
+
);
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
it("avoids shadowed aliases and reuses the fresh binding for multiple elements", () => {
|
|
1113
|
+
const output =
|
|
1114
|
+
applyTransform(`import { ThreadPrimitive, AuiIf as If } from "@assistant-ui/react";
|
|
1115
|
+
export function Empty(If, AuiIf) {
|
|
1116
|
+
return <ThreadPrimitive.If empty><ThreadPrimitive.Empty /></ThreadPrimitive.If>;
|
|
1117
|
+
}`)!;
|
|
1118
|
+
expect(output).toContain("AuiIf as AuiIf1");
|
|
1119
|
+
expect(output.match(/<AuiIf1 condition=/g)).toHaveLength(2);
|
|
1120
|
+
expect(output).toContain("</AuiIf1>");
|
|
1121
|
+
expect(
|
|
1122
|
+
j(output)
|
|
1123
|
+
.find(j.ImportSpecifier, { imported: { name: "AuiIf" } })
|
|
1124
|
+
.size(),
|
|
1125
|
+
).toBe(2);
|
|
1126
|
+
expect(applyTransform(output)).toBeNull();
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
it("keeps namespace imports intact", () => {
|
|
1130
|
+
const output =
|
|
1131
|
+
applyTransform(`import { ThreadPrimitive } from "@assistant-ui/react";
|
|
1132
|
+
import * as primitives from "@assistant-ui/react";
|
|
1133
|
+
export const Empty = () => <ThreadPrimitive.If empty />;`)!;
|
|
1134
|
+
expect(output).toContain(
|
|
1135
|
+
'import { ThreadPrimitive, AuiIf } from "@assistant-ui/react"',
|
|
1136
|
+
);
|
|
1137
|
+
expect(output).toContain(
|
|
1138
|
+
'import * as primitives from "@assistant-ui/react"',
|
|
1139
|
+
);
|
|
1140
|
+
expect(() => j(output)).not.toThrow();
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
it("uses a component name when an existing alias starts with lowercase", () => {
|
|
1144
|
+
const output =
|
|
1145
|
+
applyTransform(`import { ThreadPrimitive, AuiIf as conditional } from "@assistant-ui/react";
|
|
1146
|
+
export const Empty = () => <ThreadPrimitive.If empty />;`)!;
|
|
1147
|
+
expect(output).toContain("<AuiIf1 condition=");
|
|
1148
|
+
expect(output).toContain("AuiIf as conditional");
|
|
1149
|
+
expect(
|
|
1150
|
+
j(output)
|
|
1151
|
+
.find(j.ImportSpecifier, { local: { name: "AuiIf1" } })
|
|
1152
|
+
.size(),
|
|
1153
|
+
).toBe(1);
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
it("resolves aliased primitives without migrating their shadowed names", () => {
|
|
1157
|
+
const output =
|
|
1158
|
+
applyTransform(`import { ThreadPrimitive as Thread } from "@assistant-ui/react";
|
|
1159
|
+
export const Empty = () => <Thread.If empty />;
|
|
1160
|
+
export const Custom = (Thread) => <Thread.If empty />;`)!;
|
|
1161
|
+
expect(output).toContain("export const Empty = () => <AuiIf condition=");
|
|
1162
|
+
expect(output).toContain(
|
|
1163
|
+
"export const Custom = (Thread) => <Thread.If empty />;",
|
|
1164
|
+
);
|
|
1165
|
+
});
|
|
1166
|
+
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createTransformer } from "../utils/createTransformer";
|
|
2
2
|
|
|
3
|
-
// Map of old kebab-case event names to new camelCase event names
|
|
4
3
|
const eventNameMap: Record<string, string> = {
|
|
5
4
|
"thread.run-start": "thread.runStart",
|
|
6
5
|
"thread.run-end": "thread.runEnd",
|
|
@@ -12,16 +11,13 @@ const eventNameMap: Record<string, string> = {
|
|
|
12
11
|
|
|
13
12
|
const migrateEventNamesToCamelCase = createTransformer(
|
|
14
13
|
({ j, root, markAsChanged }) => {
|
|
15
|
-
// Find all string literals in the code
|
|
16
14
|
root.find(j.Literal).forEach((path: any) => {
|
|
17
15
|
const node = path.value;
|
|
18
16
|
|
|
19
|
-
// Check if this is a string literal
|
|
20
17
|
if (typeof node.value === "string") {
|
|
21
18
|
const oldEventName = node.value;
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
if (eventNameMap[oldEventName]) {
|
|
20
|
+
if (Object.hasOwn(eventNameMap, oldEventName)) {
|
|
25
21
|
const newEventName = eventNameMap[oldEventName];
|
|
26
22
|
node.value = newEventName;
|
|
27
23
|
markAsChanged();
|
|
@@ -29,16 +25,14 @@ const migrateEventNamesToCamelCase = createTransformer(
|
|
|
29
25
|
}
|
|
30
26
|
});
|
|
31
27
|
|
|
32
|
-
// Also handle template literals (in case event names are in template strings)
|
|
33
28
|
root.find(j.TemplateLiteral).forEach((path: any) => {
|
|
34
29
|
const node = path.value;
|
|
35
30
|
|
|
36
|
-
// Check quasi (string parts) in template literals
|
|
37
31
|
node.quasis.forEach((quasi: any) => {
|
|
38
32
|
if (quasi.value && typeof quasi.value.raw === "string") {
|
|
39
33
|
const oldEventName = quasi.value.raw;
|
|
40
34
|
|
|
41
|
-
if (eventNameMap
|
|
35
|
+
if (Object.hasOwn(eventNameMap, oldEventName)) {
|
|
42
36
|
const newEventName = eventNameMap[oldEventName];
|
|
43
37
|
quasi.value.raw = newEventName;
|
|
44
38
|
quasi.value.cooked = newEventName;
|