assistant-ui 0.0.113 → 0.0.115
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/dist/codemods/v0-12/assistant-api-to-aui.js +1 -0
- package/dist/codemods/v0-12/assistant-api-to-aui.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 +7 -5
- package/dist/codemods/v0-12/primitive-if-to-aui-if.js.map +1 -1
- package/dist/commands/add.d.ts +0 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +0 -2
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/create.d.ts +9 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +20 -6
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +31 -16
- package/dist/commands/mcp.js.map +1 -1
- package/dist/lib/create-project.d.ts +10 -1
- package/dist/lib/create-project.d.ts.map +1 -1
- package/dist/lib/create-project.js +114 -5
- package/dist/lib/create-project.js.map +1 -1
- package/dist/lib/transform.js +1 -1
- package/dist/lib/transform.js.map +1 -1
- package/package.json +4 -4
- package/plugin/skills/assistant-ui/SKILL.md +1 -1
- package/src/codemods/v0-12/__tests__/primitive-if-to-aui-if.test.ts +48 -1
- package/src/codemods/v0-12/assistant-api-to-aui.ts +4 -1
- package/src/codemods/v0-12/primitive-if-to-aui-if.ts +18 -7
- package/src/commands/add.ts +0 -3
- package/src/commands/create.ts +43 -5
- package/src/commands/init.ts +1 -1
- package/src/commands/mcp.ts +37 -12
- package/src/lib/create-project.test.ts +175 -0
- package/src/lib/create-project.ts +170 -6
- package/src/lib/transform.test.ts +25 -0
- package/src/lib/transform.ts +1 -1
|
@@ -220,11 +220,8 @@ export async function transformProject(
|
|
|
220
220
|
shadcnUI &&
|
|
221
221
|
assistantUI
|
|
222
222
|
) {
|
|
223
|
-
const allShadcn = shadcnUI.includes("utils")
|
|
224
|
-
? shadcnUI
|
|
225
|
-
: [...shadcnUI, "utils"];
|
|
226
223
|
const auiComponents = assistantUI.map((c) => `@assistant-ui/${c}`);
|
|
227
|
-
const components = [...
|
|
224
|
+
const components = ["@assistant-ui/utils", ...shadcnUI, ...auiComponents];
|
|
228
225
|
logger.step(`Installing components: ${components.join(", ")}...`);
|
|
229
226
|
const failure = await installShadcnRegistry(
|
|
230
227
|
projectDir,
|
|
@@ -233,6 +230,7 @@ export async function transformProject(
|
|
|
233
230
|
pm,
|
|
234
231
|
);
|
|
235
232
|
if (failure) return { registryInstallFailure: failure };
|
|
233
|
+
await reconcileAssistantUIImportLayout(projectDir);
|
|
236
234
|
}
|
|
237
235
|
return {};
|
|
238
236
|
}
|
|
@@ -385,6 +383,166 @@ function stripImportExtension(component: string): string {
|
|
|
385
383
|
return component.replace(/\.[cm]?[tj]sx?$/, "");
|
|
386
384
|
}
|
|
387
385
|
|
|
386
|
+
const ASSISTANT_UI_OWNED_UI = new Set([
|
|
387
|
+
"accordion",
|
|
388
|
+
"badge",
|
|
389
|
+
"diff-viewer",
|
|
390
|
+
"direction",
|
|
391
|
+
"dot-matrix",
|
|
392
|
+
"number-roll",
|
|
393
|
+
"select",
|
|
394
|
+
"tabs",
|
|
395
|
+
]);
|
|
396
|
+
|
|
397
|
+
const BARE_ELEMENT_ITEMS = new Set([
|
|
398
|
+
"file",
|
|
399
|
+
"generative-ui",
|
|
400
|
+
"heat-graph",
|
|
401
|
+
"image",
|
|
402
|
+
"logos",
|
|
403
|
+
"markdown-text",
|
|
404
|
+
"syntax-highlighter",
|
|
405
|
+
"tooltip-icon-button",
|
|
406
|
+
]);
|
|
407
|
+
|
|
408
|
+
function toAssistantUIItem(specifier: string): string | null {
|
|
409
|
+
let name = stripImportExtension(specifier);
|
|
410
|
+
const inElements = name.startsWith("elements/");
|
|
411
|
+
if (inElements) {
|
|
412
|
+
name = name.slice("elements/".length);
|
|
413
|
+
} else if (name.includes("/")) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
if (name.endsWith(".aui")) {
|
|
417
|
+
return name.slice(0, -".aui".length);
|
|
418
|
+
}
|
|
419
|
+
return inElements && !BARE_ELEMENT_ITEMS.has(name)
|
|
420
|
+
? `elements-${name}`
|
|
421
|
+
: name;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Example snapshots are downloaded at a release tag while the shadcn registry
|
|
426
|
+
* is live, so a snapshot may import components at the legacy flat path
|
|
427
|
+
* (`@/components/assistant-ui/<name>`) after the registry has moved the file
|
|
428
|
+
* to `components/assistant-ui/elements/<name>.aui.tsx`. Resolve each legacy
|
|
429
|
+
* specifier against the files the registry actually installed and rewrite it
|
|
430
|
+
* only when the legacy path is absent and the elements layout has it.
|
|
431
|
+
*/
|
|
432
|
+
export async function reconcileAssistantUIImportLayout(
|
|
433
|
+
projectDir: string,
|
|
434
|
+
): Promise<void> {
|
|
435
|
+
const componentRoots = ["components", "src/components"]
|
|
436
|
+
.map((dir) => path.join(projectDir, dir, "assistant-ui"))
|
|
437
|
+
.filter((dir) => fs.existsSync(dir));
|
|
438
|
+
if (componentRoots.length === 0) return;
|
|
439
|
+
|
|
440
|
+
const resolvesAtLegacyPath = (name: string) =>
|
|
441
|
+
componentRoots.some((root) =>
|
|
442
|
+
[".tsx", ".ts", "/index.tsx", "/index.ts"].some((suffix) =>
|
|
443
|
+
fs.existsSync(path.join(root, `${name}${suffix}`)),
|
|
444
|
+
),
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
// Index the installed tree by import name so the rewrite follows whatever
|
|
448
|
+
// layout the registry delivered — some items install as
|
|
449
|
+
// elements/<name>.aui.tsx, others as elements/<name>.tsx, and a future
|
|
450
|
+
// layout move should not require new knowledge here.
|
|
451
|
+
const installedByName = new Map<string, string>();
|
|
452
|
+
for (const root of componentRoots) {
|
|
453
|
+
for (const { file } of readProjectFiles("**/*.{ts,tsx}", { cwd: root })) {
|
|
454
|
+
const normalized = file.split(path.sep).join("/");
|
|
455
|
+
if (!normalized.includes("/")) continue;
|
|
456
|
+
const specifier = normalized.replace(/\.[cm]?[tj]sx?$/, "");
|
|
457
|
+
const name = path.posix.basename(specifier).replace(/\.aui$/, "");
|
|
458
|
+
// A flat legacy import maps to the registry's `<name>` item, which is
|
|
459
|
+
// the `.aui` file; a colliding bare file with the same basename belongs
|
|
460
|
+
// to the distinct `elements-<name>` item, so the `.aui` variant wins.
|
|
461
|
+
const existing = installedByName.get(name);
|
|
462
|
+
if (
|
|
463
|
+
existing === undefined ||
|
|
464
|
+
(!existing.endsWith(".aui") && specifier.endsWith(".aui"))
|
|
465
|
+
) {
|
|
466
|
+
installedByName.set(name, specifier);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (installedByName.size === 0) return;
|
|
471
|
+
|
|
472
|
+
const { default: jscodeshift } = await import("jscodeshift");
|
|
473
|
+
const parsers = {
|
|
474
|
+
ts: jscodeshift.withParser("ts"),
|
|
475
|
+
tsx: jscodeshift.withParser("tsx"),
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
for (const { fullPath, content } of readProjectFiles("**/*.{ts,tsx}", {
|
|
479
|
+
cwd: projectDir,
|
|
480
|
+
ignore: LOCAL_PROJECT_ARTIFACT_GLOB_IGNORES,
|
|
481
|
+
})) {
|
|
482
|
+
if (!content.includes("@/components/assistant-ui/")) continue;
|
|
483
|
+
|
|
484
|
+
const replacements: Array<{ start: number; end: number; value: string }> =
|
|
485
|
+
[];
|
|
486
|
+
const collectReplacement = (source: {
|
|
487
|
+
value?: unknown;
|
|
488
|
+
start?: number | null;
|
|
489
|
+
end?: number | null;
|
|
490
|
+
}) => {
|
|
491
|
+
if (
|
|
492
|
+
typeof source.value !== "string" ||
|
|
493
|
+
source.start == null ||
|
|
494
|
+
source.end == null
|
|
495
|
+
) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const prefix = "@/components/assistant-ui/";
|
|
500
|
+
if (!source.value.startsWith(prefix)) return;
|
|
501
|
+
const specifier = source.value.slice(prefix.length);
|
|
502
|
+
if (specifier.includes("/")) return;
|
|
503
|
+
|
|
504
|
+
const name = stripImportExtension(specifier);
|
|
505
|
+
const installed = installedByName.get(name);
|
|
506
|
+
if (resolvesAtLegacyPath(name) || installed === undefined) return;
|
|
507
|
+
|
|
508
|
+
const raw = content.slice(source.start, source.end);
|
|
509
|
+
const quote = raw[0];
|
|
510
|
+
if ((quote !== '"' && quote !== "'") || raw.at(-1) !== quote) return;
|
|
511
|
+
replacements.push({
|
|
512
|
+
start: source.start,
|
|
513
|
+
end: source.end,
|
|
514
|
+
value: `${quote}@/components/assistant-ui/${installed}${quote}`,
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const j = fullPath.endsWith(".tsx") ? parsers.tsx : parsers.ts;
|
|
519
|
+
let root;
|
|
520
|
+
try {
|
|
521
|
+
root = j(content);
|
|
522
|
+
} catch {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
root
|
|
526
|
+
.find(j.ImportDeclaration)
|
|
527
|
+
.forEach(({ node }) => collectReplacement(node.source));
|
|
528
|
+
root
|
|
529
|
+
.find(j.ExportNamedDeclaration)
|
|
530
|
+
.forEach(({ node }) => node.source && collectReplacement(node.source));
|
|
531
|
+
root
|
|
532
|
+
.find(j.ExportAllDeclaration)
|
|
533
|
+
.forEach(({ node }) => collectReplacement(node.source));
|
|
534
|
+
|
|
535
|
+
let next = content;
|
|
536
|
+
for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
|
|
537
|
+
next =
|
|
538
|
+
next.slice(0, replacement.start) +
|
|
539
|
+
replacement.value +
|
|
540
|
+
next.slice(replacement.end);
|
|
541
|
+
}
|
|
542
|
+
if (next !== content) fs.writeFileSync(fullPath, next);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
388
546
|
function scanRequiredComponents(projectDir: string): RequiredComponents {
|
|
389
547
|
const assistantUIComponents = new Set<string>();
|
|
390
548
|
const shadcnUIComponents = new Set<string>();
|
|
@@ -396,12 +554,18 @@ function scanRequiredComponents(projectDir: string): RequiredComponents {
|
|
|
396
554
|
const assistantUIRegex =
|
|
397
555
|
/from\s+["']@\/components\/assistant-ui\/([^"']+)["']/g;
|
|
398
556
|
for (const match of content.matchAll(assistantUIRegex)) {
|
|
399
|
-
|
|
557
|
+
const item = toAssistantUIItem(match[1]!);
|
|
558
|
+
if (item) assistantUIComponents.add(item);
|
|
400
559
|
}
|
|
401
560
|
|
|
402
561
|
const uiRegex = /from\s+["']@\/components\/ui\/([^"']+)["']/g;
|
|
403
562
|
for (const match of content.matchAll(uiRegex)) {
|
|
404
|
-
|
|
563
|
+
const name = stripImportExtension(match[1]!);
|
|
564
|
+
if (ASSISTANT_UI_OWNED_UI.has(name)) {
|
|
565
|
+
assistantUIComponents.add(name);
|
|
566
|
+
} else {
|
|
567
|
+
shadcnUIComponents.add(name);
|
|
568
|
+
}
|
|
405
569
|
}
|
|
406
570
|
}
|
|
407
571
|
|
|
@@ -41,6 +41,31 @@ describe("transform", () => {
|
|
|
41
41
|
);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
it("fails a progress-enabled codemod that exits nonzero", async () => {
|
|
45
|
+
mocks.runSpawnCapture.mockResolvedValue({
|
|
46
|
+
code: 7,
|
|
47
|
+
signal: null,
|
|
48
|
+
stdout: "Processing file app.tsx\n",
|
|
49
|
+
stderr: "SyntaxError: Broken input\n",
|
|
50
|
+
});
|
|
51
|
+
const onProgress = vi.fn();
|
|
52
|
+
|
|
53
|
+
const failure = transform(
|
|
54
|
+
"v0-8/ui-package-split",
|
|
55
|
+
"/tmp/project",
|
|
56
|
+
{ dry: true },
|
|
57
|
+
{
|
|
58
|
+
logStatus: false,
|
|
59
|
+
onProgress,
|
|
60
|
+
relevantFiles: ["/tmp/project/app.tsx"],
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
await expect(failure).rejects.toBeInstanceOf(SpawnExitError);
|
|
65
|
+
await expect(failure).rejects.toThrow("SyntaxError: Broken input");
|
|
66
|
+
expect(onProgress).not.toHaveBeenCalled();
|
|
67
|
+
});
|
|
68
|
+
|
|
44
69
|
it("surfaces the codemod's stderr when it exits nonzero", async () => {
|
|
45
70
|
mocks.runSpawnCapture.mockResolvedValue({
|
|
46
71
|
code: 1,
|
package/src/lib/transform.ts
CHANGED
|
@@ -137,7 +137,7 @@ export async function transform(
|
|
|
137
137
|
if (result.signal !== null) {
|
|
138
138
|
throw new SpawnSignalError(result.signal, false);
|
|
139
139
|
}
|
|
140
|
-
if (
|
|
140
|
+
if (result.code !== 0) {
|
|
141
141
|
throw new SpawnExitError(result.code || 1, result.stderr);
|
|
142
142
|
}
|
|
143
143
|
|