@seed-design/cli 0.0.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.mjs +6 -2
- package/package.json +5 -5
- package/src/commands/add-all.ts +180 -0
- package/src/commands/add.ts +123 -151
- package/src/commands/init.ts +7 -8
- package/src/index.ts +2 -0
- package/src/schema.ts +38 -64
- package/src/tests/resolve-dependencies.test.ts +424 -0
- package/src/utils/fetch.ts +122 -0
- package/src/utils/get-config.ts +14 -48
- package/src/utils/install.ts +7 -12
- package/src/utils/resolve-dependencies.ts +77 -0
- package/src/utils/transformers/index.ts +2 -1
- package/src/utils/transformers/transform-jsx.ts +0 -1
- package/src/utils/transformers/transform-rsc.ts +21 -4
- package/src/utils/write.ts +75 -0
- package/src/test/add-relative-registries.test.ts +0 -182
- package/src/utils/add-relative-registries.ts +0 -43
- package/src/utils/get-metadata.ts +0 -75
package/src/commands/init.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
2
|
import fs from "fs-extra";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import
|
|
4
|
+
import { highlight } from "../utils/color";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
7
|
-
import type {
|
|
7
|
+
import type { Config } from "@/src/utils/get-config";
|
|
8
8
|
|
|
9
9
|
import type { CAC } from "cac";
|
|
10
10
|
|
|
@@ -21,12 +21,11 @@ export const initCommand = (cli: CAC) => {
|
|
|
21
21
|
})
|
|
22
22
|
.option("-y, --yes", "모든 질문에 대해 기본값으로 답변합니다.")
|
|
23
23
|
.action(async (opts) => {
|
|
24
|
-
|
|
25
|
-
p.intro(color.bgCyan("seed-design.json 파일 생성"));
|
|
24
|
+
p.intro("seed-design.json 파일 생성");
|
|
26
25
|
|
|
27
26
|
const options = initOptionsSchema.parse(opts);
|
|
28
27
|
const isYesOption = options.yes;
|
|
29
|
-
let config:
|
|
28
|
+
let config: Config = {
|
|
30
29
|
rsc: false,
|
|
31
30
|
tsx: true,
|
|
32
31
|
path: "./seed-design",
|
|
@@ -73,14 +72,14 @@ export const initCommand = (cli: CAC) => {
|
|
|
73
72
|
await fs.writeFile(targetPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
74
73
|
const relativePath = path.relative(process.cwd(), targetPath);
|
|
75
74
|
stop(`seed-design.json 파일이 ${highlight(relativePath)}에 생성됐어요.`);
|
|
76
|
-
p.log.info(
|
|
75
|
+
p.log.info(highlight("seed-design add {component} 명령어로 컴포넌트를 추가해보세요!"));
|
|
77
76
|
p.log.info(
|
|
78
|
-
|
|
77
|
+
highlight("seed-design add 명령어로 추가할 수 있는 모든 컴포넌트를 확인해보세요."),
|
|
79
78
|
);
|
|
80
79
|
p.outro("작업이 완료됐어요.");
|
|
81
80
|
} catch (error) {
|
|
82
81
|
p.log.error(`seed-design.json 파일 생성에 실패했어요. ${error}`);
|
|
83
|
-
p.outro(
|
|
82
|
+
p.outro(highlight("작업이 취소됐어요."));
|
|
84
83
|
process.exit(1);
|
|
85
84
|
}
|
|
86
85
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { addCommand } from "@/src/commands/add";
|
|
4
|
+
import { addAllCommand } from "@/src/commands/add-all";
|
|
4
5
|
import { initCommand } from "@/src/commands/init";
|
|
5
6
|
import { getPackageInfo } from "@/src/utils/get-package-info";
|
|
6
7
|
import { cac } from "cac";
|
|
@@ -13,6 +14,7 @@ async function main() {
|
|
|
13
14
|
|
|
14
15
|
/* Commands */
|
|
15
16
|
addCommand(CLI);
|
|
17
|
+
addAllCommand(CLI);
|
|
16
18
|
initCommand(CLI);
|
|
17
19
|
|
|
18
20
|
CLI.version(packageInfo.version || "1.0.0", "-v, --version");
|
package/src/schema.ts
CHANGED
|
@@ -1,81 +1,55 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* @example chip-tabs, alert-dialog
|
|
9
|
-
*/
|
|
10
|
-
name: z.string(),
|
|
3
|
+
/**
|
|
4
|
+
* this should be in sync with `docs/registry/schema.ts`
|
|
5
|
+
*/
|
|
6
|
+
export const publicRegistryItemSchema = z.object({
|
|
7
|
+
id: z.string(),
|
|
11
8
|
|
|
12
9
|
description: z.string().optional(),
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
* @description 레지스트리 의존성
|
|
16
|
-
* @example @seed-design/react-tabs
|
|
17
|
-
*/
|
|
18
|
-
dependencies: z.array(z.string()).optional(),
|
|
11
|
+
deprecated: z.boolean().optional(),
|
|
19
12
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
hideFromCLICatalog: z.boolean().optional(),
|
|
14
|
+
|
|
15
|
+
///////////////////////////////////////////////////////////////
|
|
16
|
+
|
|
17
|
+
dependencies: z.array(z.string()).optional(),
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
innerDependencies: z
|
|
20
|
+
.array(
|
|
21
|
+
z.object({
|
|
22
|
+
registryId: z.string(),
|
|
23
|
+
itemIds: z.array(z.string()),
|
|
24
|
+
}),
|
|
25
|
+
)
|
|
26
|
+
.optional(),
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
* @description
|
|
35
|
-
* 컴포넌트 코드 스니펫 경로, 여러 파일이 될 수 있어서 배열로 정의
|
|
36
|
-
* `:`를 기준으로 왼쪽은 {registryType}, 오른쪽은 파일 이름
|
|
37
|
-
* @example ui:alert-dialog.tsx
|
|
38
|
-
* @example ui:use-dismissible.ts
|
|
39
|
-
* @example lib:manner-temp-level.ts
|
|
40
|
-
*/
|
|
41
|
-
files: z.array(z.string()),
|
|
28
|
+
///////////////////////////////////////////////////////////////
|
|
42
29
|
|
|
43
|
-
|
|
44
|
-
* @description 컴포넌트 deprecated 여부
|
|
45
|
-
*/
|
|
46
|
-
deprecated: z.literal(true).optional(),
|
|
30
|
+
snippets: z.array(z.object({ path: z.string(), content: z.string() })),
|
|
47
31
|
});
|
|
48
|
-
export const registryUISchema = z.array(registryUIItemSchema);
|
|
49
32
|
|
|
50
33
|
/**
|
|
51
|
-
*
|
|
34
|
+
* this should be in sync with `packages/cli/src/schema.ts`
|
|
52
35
|
*/
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}),
|
|
36
|
+
export const publicRegistrySchema = z.object({
|
|
37
|
+
id: z.string(),
|
|
38
|
+
|
|
39
|
+
hideFromCLICatalog: z.boolean().optional(),
|
|
40
|
+
|
|
41
|
+
items: z.array(
|
|
42
|
+
publicRegistryItemSchema
|
|
43
|
+
.omit({ snippets: true })
|
|
44
|
+
.extend({ snippets: z.array(z.object({ path: z.string() })) }),
|
|
63
45
|
),
|
|
64
46
|
});
|
|
65
|
-
export const registryComponentMachineGeneratedSchema = z.array(
|
|
66
|
-
registryUIItemMachineGeneratedSchema,
|
|
67
|
-
);
|
|
68
47
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
export type RegistryLibItem = z.infer<typeof registryUIItemSchema>;
|
|
74
|
-
export type RegistryLib = z.infer<typeof registryUISchema>;
|
|
75
|
-
export type RegistryUIItem = z.infer<typeof registryUIItemSchema>;
|
|
76
|
-
export type RegistryUI = z.infer<typeof registryUISchema>;
|
|
48
|
+
/**
|
|
49
|
+
* this should be in sync with `packages/cli/src/schema.ts`
|
|
50
|
+
*/
|
|
51
|
+
export const publicAvailableRegistriesSchema = z.array(z.object({ id: z.string() }));
|
|
77
52
|
|
|
78
|
-
export type
|
|
79
|
-
export type
|
|
80
|
-
export type
|
|
81
|
-
export type RegistryLibMachineGenerated = z.infer<typeof registryComponentMachineGeneratedSchema>;
|
|
53
|
+
export type PublicRegistryItem = z.infer<typeof publicRegistryItemSchema>;
|
|
54
|
+
export type PublicRegistry = z.infer<typeof publicRegistrySchema>;
|
|
55
|
+
export type PublicAvailableRegistries = z.infer<typeof publicAvailableRegistriesSchema>;
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { resolveDependencies } from "../utils/resolve-dependencies";
|
|
3
|
+
import type { PublicRegistry } from "@/src/schema";
|
|
4
|
+
|
|
5
|
+
describe("resolveDependencies", () => {
|
|
6
|
+
it("should resolve a simple item without dependencies", () => {
|
|
7
|
+
const publicRegistries: PublicRegistry[] = [
|
|
8
|
+
{
|
|
9
|
+
id: "ui",
|
|
10
|
+
items: [
|
|
11
|
+
{
|
|
12
|
+
id: "button",
|
|
13
|
+
description: "Button component",
|
|
14
|
+
snippets: [{ path: "button.tsx" }],
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const result = resolveDependencies({
|
|
21
|
+
selectedItemKeys: ["ui:button"],
|
|
22
|
+
publicRegistries,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(result.registryItemsToAdd).toHaveLength(1);
|
|
26
|
+
expect(result.registryItemsToAdd[0]).toEqual({
|
|
27
|
+
registryId: "ui",
|
|
28
|
+
items: [
|
|
29
|
+
{
|
|
30
|
+
id: "button",
|
|
31
|
+
description: "Button component",
|
|
32
|
+
files: [{ path: "button.tsx" }],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
expect(result.npmDependenciesToAdd.size).toBe(0);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should resolve npm dependencies", () => {
|
|
40
|
+
const publicRegistries: PublicRegistry[] = [
|
|
41
|
+
{
|
|
42
|
+
id: "ui",
|
|
43
|
+
items: [
|
|
44
|
+
{
|
|
45
|
+
id: "tabs",
|
|
46
|
+
description: "Tabs component",
|
|
47
|
+
snippets: [{ path: "tabs.tsx" }],
|
|
48
|
+
dependencies: ["@seed-design/react-tabs", "clsx"],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const result = resolveDependencies({
|
|
55
|
+
selectedItemKeys: ["ui:tabs"],
|
|
56
|
+
publicRegistries,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(result.npmDependenciesToAdd.size).toBe(2);
|
|
60
|
+
expect(result.npmDependenciesToAdd.has("@seed-design/react-tabs")).toBe(true);
|
|
61
|
+
expect(result.npmDependenciesToAdd.has("clsx")).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should resolve inner dependencies recursively", () => {
|
|
65
|
+
const publicRegistries: PublicRegistry[] = [
|
|
66
|
+
{
|
|
67
|
+
id: "ui",
|
|
68
|
+
items: [
|
|
69
|
+
{
|
|
70
|
+
id: "dialog",
|
|
71
|
+
description: "Dialog component",
|
|
72
|
+
snippets: [{ path: "dialog.tsx" }],
|
|
73
|
+
innerDependencies: [
|
|
74
|
+
{
|
|
75
|
+
registryId: "breeze",
|
|
76
|
+
itemIds: ["animate-number"],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "breeze",
|
|
84
|
+
items: [
|
|
85
|
+
{
|
|
86
|
+
id: "animate-number",
|
|
87
|
+
description: "Animate number utility",
|
|
88
|
+
snippets: [{ path: "animate-number.ts" }],
|
|
89
|
+
dependencies: ["framer-motion"],
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const result = resolveDependencies({
|
|
96
|
+
selectedItemKeys: ["ui:dialog"],
|
|
97
|
+
publicRegistries,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(result.registryItemsToAdd).toHaveLength(2);
|
|
101
|
+
expect(result.registryItemsToAdd).toEqual([
|
|
102
|
+
{
|
|
103
|
+
registryId: "ui",
|
|
104
|
+
items: [
|
|
105
|
+
{
|
|
106
|
+
id: "dialog",
|
|
107
|
+
description: "Dialog component",
|
|
108
|
+
files: [{ path: "dialog.tsx" }],
|
|
109
|
+
innerDependencies: [
|
|
110
|
+
{
|
|
111
|
+
registryId: "breeze",
|
|
112
|
+
itemIds: ["animate-number"],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
registryId: "breeze",
|
|
120
|
+
items: [
|
|
121
|
+
{
|
|
122
|
+
id: "animate-number",
|
|
123
|
+
description: "Animate number utility",
|
|
124
|
+
files: [{ path: "animate-number.ts" }],
|
|
125
|
+
dependencies: ["framer-motion"],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
expect(result.npmDependenciesToAdd.size).toBe(1);
|
|
131
|
+
expect(result.npmDependenciesToAdd.has("framer-motion")).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should handle multiple selected items", () => {
|
|
135
|
+
const publicRegistries: PublicRegistry[] = [
|
|
136
|
+
{
|
|
137
|
+
id: "ui",
|
|
138
|
+
items: [
|
|
139
|
+
{
|
|
140
|
+
id: "button",
|
|
141
|
+
description: "Button component",
|
|
142
|
+
snippets: [{ path: "button.tsx" }],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "chip",
|
|
146
|
+
description: "Chip component",
|
|
147
|
+
snippets: [{ path: "chip.tsx" }],
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
const result = resolveDependencies({
|
|
154
|
+
selectedItemKeys: ["ui:button", "ui:chip"],
|
|
155
|
+
publicRegistries,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
expect(result.registryItemsToAdd).toHaveLength(1);
|
|
159
|
+
expect(result.registryItemsToAdd[0].registryId).toBe("ui");
|
|
160
|
+
expect(result.registryItemsToAdd[0].items).toHaveLength(2);
|
|
161
|
+
expect(result.registryItemsToAdd[0].items.map((i) => i.id)).toEqual(["button", "chip"]);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should prevent duplicate items", () => {
|
|
165
|
+
const publicRegistries: PublicRegistry[] = [
|
|
166
|
+
{
|
|
167
|
+
id: "ui",
|
|
168
|
+
items: [
|
|
169
|
+
{
|
|
170
|
+
id: "dialog",
|
|
171
|
+
description: "Dialog component",
|
|
172
|
+
snippets: [{ path: "dialog.tsx" }],
|
|
173
|
+
innerDependencies: [
|
|
174
|
+
{
|
|
175
|
+
registryId: "ui",
|
|
176
|
+
itemIds: ["button"],
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: "button",
|
|
182
|
+
description: "Button component",
|
|
183
|
+
snippets: [{ path: "button.tsx" }],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
// Select both dialog and button, but button is already a dependency of dialog
|
|
190
|
+
const result = resolveDependencies({
|
|
191
|
+
selectedItemKeys: ["ui:dialog", "ui:button"],
|
|
192
|
+
publicRegistries,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(result.registryItemsToAdd).toHaveLength(1);
|
|
196
|
+
expect(result.registryItemsToAdd[0].items).toHaveLength(2);
|
|
197
|
+
// Button should appear only once
|
|
198
|
+
const buttonCount = result.registryItemsToAdd[0].items.filter((i) => i.id === "button").length;
|
|
199
|
+
expect(buttonCount).toBe(1);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("should handle nested inner dependencies", () => {
|
|
203
|
+
const publicRegistries: PublicRegistry[] = [
|
|
204
|
+
{
|
|
205
|
+
id: "ui",
|
|
206
|
+
items: [
|
|
207
|
+
{
|
|
208
|
+
id: "complex",
|
|
209
|
+
description: "Complex component",
|
|
210
|
+
snippets: [{ path: "complex.tsx" }],
|
|
211
|
+
innerDependencies: [
|
|
212
|
+
{
|
|
213
|
+
registryId: "ui",
|
|
214
|
+
itemIds: ["dialog"],
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "dialog",
|
|
220
|
+
description: "Dialog component",
|
|
221
|
+
snippets: [{ path: "dialog.tsx" }],
|
|
222
|
+
innerDependencies: [
|
|
223
|
+
{
|
|
224
|
+
registryId: "breeze",
|
|
225
|
+
itemIds: ["animate"],
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
id: "breeze",
|
|
233
|
+
items: [
|
|
234
|
+
{
|
|
235
|
+
id: "animate",
|
|
236
|
+
description: "Animate utility",
|
|
237
|
+
snippets: [{ path: "animate.ts" }],
|
|
238
|
+
innerDependencies: [
|
|
239
|
+
{
|
|
240
|
+
registryId: "lib",
|
|
241
|
+
itemIds: ["utils"],
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: "lib",
|
|
249
|
+
items: [
|
|
250
|
+
{
|
|
251
|
+
id: "utils",
|
|
252
|
+
description: "Utility functions",
|
|
253
|
+
snippets: [{ path: "utils.ts" }],
|
|
254
|
+
dependencies: ["lodash"],
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
const result = resolveDependencies({
|
|
261
|
+
selectedItemKeys: ["ui:complex"],
|
|
262
|
+
publicRegistries,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
expect(result.registryItemsToAdd).toHaveLength(3);
|
|
266
|
+
expect(result.registryItemsToAdd.map((r) => r.registryId)).toEqual(["ui", "breeze", "lib"]);
|
|
267
|
+
expect(result.npmDependenciesToAdd.size).toBe(1);
|
|
268
|
+
expect(result.npmDependenciesToAdd.has("lodash")).toBe(true);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("should throw error for invalid snippet format", () => {
|
|
272
|
+
const publicRegistries: PublicRegistry[] = [];
|
|
273
|
+
|
|
274
|
+
expect(() =>
|
|
275
|
+
resolveDependencies({
|
|
276
|
+
selectedItemKeys: ["invalid-format"],
|
|
277
|
+
publicRegistries,
|
|
278
|
+
}),
|
|
279
|
+
).toThrowError('Invalid snippet format: "invalid-format"');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("should throw error for non-existent snippet", () => {
|
|
283
|
+
const publicRegistries: PublicRegistry[] = [
|
|
284
|
+
{
|
|
285
|
+
id: "ui",
|
|
286
|
+
items: [],
|
|
287
|
+
},
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
expect(() =>
|
|
291
|
+
resolveDependencies({
|
|
292
|
+
selectedItemKeys: ["ui:non-existent"],
|
|
293
|
+
publicRegistries,
|
|
294
|
+
}),
|
|
295
|
+
).toThrowError('Cannot find snippet: "ui:non-existent"');
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("should throw error for missing inner dependency", () => {
|
|
299
|
+
const publicRegistries: PublicRegistry[] = [
|
|
300
|
+
{
|
|
301
|
+
id: "ui",
|
|
302
|
+
items: [
|
|
303
|
+
{
|
|
304
|
+
id: "broken",
|
|
305
|
+
description: "Broken component",
|
|
306
|
+
snippets: [{ path: "broken.tsx" }],
|
|
307
|
+
innerDependencies: [
|
|
308
|
+
{
|
|
309
|
+
registryId: "breeze",
|
|
310
|
+
itemIds: ["missing"],
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
id: "breeze",
|
|
318
|
+
items: [],
|
|
319
|
+
},
|
|
320
|
+
];
|
|
321
|
+
|
|
322
|
+
expect(() =>
|
|
323
|
+
resolveDependencies({
|
|
324
|
+
selectedItemKeys: ["ui:broken"],
|
|
325
|
+
publicRegistries,
|
|
326
|
+
}),
|
|
327
|
+
).toThrowError("Cannot find dependency item: breeze:missing");
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("should handle multiple registries with multiple items", () => {
|
|
331
|
+
const publicRegistries: PublicRegistry[] = [
|
|
332
|
+
{
|
|
333
|
+
id: "ui",
|
|
334
|
+
items: [
|
|
335
|
+
{
|
|
336
|
+
id: "button",
|
|
337
|
+
description: "Button component",
|
|
338
|
+
snippets: [{ path: "button.tsx" }],
|
|
339
|
+
dependencies: ["clsx"],
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
id: "chip",
|
|
343
|
+
description: "Chip component",
|
|
344
|
+
snippets: [{ path: "chip.tsx" }],
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: "breeze",
|
|
350
|
+
items: [
|
|
351
|
+
{
|
|
352
|
+
id: "animate",
|
|
353
|
+
description: "Animate utility",
|
|
354
|
+
snippets: [{ path: "animate.ts" }],
|
|
355
|
+
dependencies: ["framer-motion"],
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
const result = resolveDependencies({
|
|
362
|
+
selectedItemKeys: ["ui:button", "breeze:animate", "ui:chip"],
|
|
363
|
+
publicRegistries,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
expect(result.registryItemsToAdd).toHaveLength(2);
|
|
367
|
+
|
|
368
|
+
const uiRegistry = result.registryItemsToAdd.find((r) => r.registryId === "ui");
|
|
369
|
+
expect(uiRegistry?.items).toHaveLength(2);
|
|
370
|
+
expect(uiRegistry?.items.map((i) => i.id)).toEqual(["button", "chip"]);
|
|
371
|
+
|
|
372
|
+
const breezeRegistry = result.registryItemsToAdd.find((r) => r.registryId === "breeze");
|
|
373
|
+
expect(breezeRegistry?.items).toHaveLength(1);
|
|
374
|
+
expect(breezeRegistry?.items[0].id).toBe("animate");
|
|
375
|
+
|
|
376
|
+
expect(result.npmDependenciesToAdd.size).toBe(2);
|
|
377
|
+
expect(result.npmDependenciesToAdd.has("clsx")).toBe(true);
|
|
378
|
+
expect(result.npmDependenciesToAdd.has("framer-motion")).toBe(true);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it("should collect all npm dependencies from nested dependencies", () => {
|
|
382
|
+
const publicRegistries: PublicRegistry[] = [
|
|
383
|
+
{
|
|
384
|
+
id: "ui",
|
|
385
|
+
items: [
|
|
386
|
+
{
|
|
387
|
+
id: "rich",
|
|
388
|
+
description: "Rich component",
|
|
389
|
+
snippets: [{ path: "rich.tsx" }],
|
|
390
|
+
dependencies: ["react-hook-form"],
|
|
391
|
+
innerDependencies: [
|
|
392
|
+
{
|
|
393
|
+
registryId: "ui",
|
|
394
|
+
itemIds: ["field", "label"],
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: "field",
|
|
400
|
+
description: "Field component",
|
|
401
|
+
snippets: [{ path: "field.tsx" }],
|
|
402
|
+
dependencies: ["clsx", "tailwind-merge"],
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
id: "label",
|
|
406
|
+
description: "Label component",
|
|
407
|
+
snippets: [{ path: "label.tsx" }],
|
|
408
|
+
dependencies: ["clsx"],
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
},
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
const result = resolveDependencies({
|
|
415
|
+
selectedItemKeys: ["ui:rich"],
|
|
416
|
+
publicRegistries,
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
expect(result.npmDependenciesToAdd.size).toBe(3);
|
|
420
|
+
expect(result.npmDependenciesToAdd.has("react-hook-form")).toBe(true);
|
|
421
|
+
expect(result.npmDependenciesToAdd.has("clsx")).toBe(true);
|
|
422
|
+
expect(result.npmDependenciesToAdd.has("tailwind-merge")).toBe(true);
|
|
423
|
+
});
|
|
424
|
+
});
|