@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
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { PublicRegistry } from "@/src/schema";
|
|
2
|
+
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import {
|
|
5
|
+
type PublicRegistryItem,
|
|
6
|
+
publicRegistrySchema,
|
|
7
|
+
publicRegistryItemSchema,
|
|
8
|
+
type PublicAvailableRegistries,
|
|
9
|
+
publicAvailableRegistriesSchema,
|
|
10
|
+
} from "@/src/schema";
|
|
11
|
+
|
|
12
|
+
export async function fetchAvailableRegistries({
|
|
13
|
+
baseUrl,
|
|
14
|
+
}: {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
}): Promise<PublicAvailableRegistries> {
|
|
17
|
+
// TODO: make this file public
|
|
18
|
+
const response = await fetch(`${baseUrl}/__registry__/index.json`);
|
|
19
|
+
|
|
20
|
+
if (!response.ok)
|
|
21
|
+
throw new Error(`Failed to fetch registries: ${response.status} ${response.statusText}`);
|
|
22
|
+
|
|
23
|
+
const registries = await response.json();
|
|
24
|
+
const {
|
|
25
|
+
success,
|
|
26
|
+
data: parsedRegistries,
|
|
27
|
+
error,
|
|
28
|
+
} = publicAvailableRegistriesSchema.safeParse(registries);
|
|
29
|
+
|
|
30
|
+
if (!success) throw new Error(`Failed to parse registries: ${error?.message}`);
|
|
31
|
+
|
|
32
|
+
return parsedRegistries;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function fetchRegistry({
|
|
36
|
+
baseUrl,
|
|
37
|
+
registryId,
|
|
38
|
+
}: {
|
|
39
|
+
baseUrl: string;
|
|
40
|
+
registryId: PublicRegistry["id"];
|
|
41
|
+
}): Promise<PublicRegistry> {
|
|
42
|
+
const response = await fetch(`${baseUrl}/__registry__/${registryId}/index.json`);
|
|
43
|
+
|
|
44
|
+
if (!response.ok)
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Failed to fetch ${registryId} registry: ${response.status} ${response.statusText}`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const index = await response.json();
|
|
50
|
+
const { success, data: parsedIndex, error } = publicRegistrySchema.safeParse(index);
|
|
51
|
+
|
|
52
|
+
if (!success) throw new Error(`Failed to parse ${registryId} registry: ${error?.message}`);
|
|
53
|
+
|
|
54
|
+
return parsedIndex;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function fetchRegistryItem({
|
|
58
|
+
baseUrl,
|
|
59
|
+
registryId,
|
|
60
|
+
registryItemId,
|
|
61
|
+
}: {
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
registryId: PublicRegistry["id"];
|
|
64
|
+
registryItemId: PublicRegistryItem["id"];
|
|
65
|
+
}): Promise<PublicRegistryItem> {
|
|
66
|
+
const response = await fetch(`${baseUrl}/__registry__/${registryId}/${registryItemId}.json`);
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`Failed to fetch ${registryItemId}: ${response.status} ${response.statusText}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const item = await response.json();
|
|
73
|
+
const { success, data: parsedItem, error } = publicRegistryItemSchema.safeParse(item);
|
|
74
|
+
|
|
75
|
+
if (!success) {
|
|
76
|
+
throw new Error(`Failed to parse ${registryItemId}: ${error?.message}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return parsedItem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function fetchRegistryItems({
|
|
83
|
+
baseUrl,
|
|
84
|
+
registryId,
|
|
85
|
+
registryItemIds,
|
|
86
|
+
}: {
|
|
87
|
+
baseUrl: string;
|
|
88
|
+
registryId: PublicRegistry["id"];
|
|
89
|
+
registryItemIds: PublicRegistryItem["id"][];
|
|
90
|
+
}): Promise<PublicRegistryItem[]> {
|
|
91
|
+
return await Promise.all(
|
|
92
|
+
registryItemIds.map(async (itemId) => {
|
|
93
|
+
try {
|
|
94
|
+
return await fetchRegistryItem({ baseUrl, registryId, registryItemId: itemId });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
// show available registry items in the registry
|
|
97
|
+
const response = await fetch(`${baseUrl}/__registry__/${registryId}/index.json`);
|
|
98
|
+
|
|
99
|
+
if (!response.ok)
|
|
100
|
+
throw new Error(
|
|
101
|
+
`${registryId} 레지스트리를 가져오지 못했어요: ${response.status} ${response.statusText}`,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const index = await response.json();
|
|
105
|
+
const { success, data: parsedIndex } = publicRegistrySchema.safeParse(index);
|
|
106
|
+
|
|
107
|
+
// fatal, should not happen
|
|
108
|
+
if (!success) throw new Error(`Failed to parse registry index for ${registryId}`);
|
|
109
|
+
|
|
110
|
+
p.log.error(`${itemId} 스니펫이 ${registryId} 레지스트리에 없어요.`);
|
|
111
|
+
p.log.info(
|
|
112
|
+
`${registryId} 레지스트리에 존재하는 스니펫:\n${parsedIndex.items
|
|
113
|
+
.map((component) => component.id)
|
|
114
|
+
.join("\n")}`,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// so fetchRegistryItems also can throw
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
}
|
package/src/utils/get-config.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
2
|
import { cosmiconfig } from "cosmiconfig";
|
|
3
3
|
import { execa } from "execa";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import path from "path";
|
|
6
4
|
import { z } from "zod";
|
|
7
5
|
import { highlight } from "./color";
|
|
8
6
|
import { getPackageManager } from "./get-package-manager";
|
|
@@ -13,7 +11,7 @@ const explorer = cosmiconfig(MODULE_NAME, {
|
|
|
13
11
|
searchPlaces: [`${MODULE_NAME}.json`],
|
|
14
12
|
});
|
|
15
13
|
|
|
16
|
-
export const
|
|
14
|
+
export const configSchema = z
|
|
17
15
|
.object({
|
|
18
16
|
$schema: z.string().optional(),
|
|
19
17
|
rsc: z.coerce.boolean().default(false),
|
|
@@ -22,65 +20,33 @@ export const rawConfigSchema = z
|
|
|
22
20
|
})
|
|
23
21
|
.strict();
|
|
24
22
|
|
|
25
|
-
export type
|
|
26
|
-
|
|
27
|
-
export const configSchema = rawConfigSchema.extend({
|
|
28
|
-
resolvedUIPaths: z.string(),
|
|
29
|
-
resolvedLibPaths: z.string(),
|
|
30
|
-
});
|
|
23
|
+
export type Config = z.infer<typeof configSchema>;
|
|
31
24
|
|
|
32
25
|
export async function getConfig(cwd: string) {
|
|
33
26
|
const config = await getRawConfig(cwd);
|
|
27
|
+
if (!config) return null;
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return await resolveConfigPaths(cwd, config);
|
|
29
|
+
return configSchema.parse(config);
|
|
40
30
|
}
|
|
41
31
|
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
export async function resolveConfigPaths(cwd: string, config: RawConfig) {
|
|
45
|
-
const seedComponentRootPath = path.resolve(cwd, config.path);
|
|
46
|
-
|
|
47
|
-
if (!fs.existsSync(seedComponentRootPath)) {
|
|
48
|
-
fs.mkdirSync(seedComponentRootPath, { recursive: true });
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const resolvedUIPaths = path.join(seedComponentRootPath, "ui");
|
|
52
|
-
const resolvedLibPaths = path.join(seedComponentRootPath, "lib");
|
|
53
|
-
|
|
54
|
-
if (!fs.existsSync(resolvedUIPaths)) {
|
|
55
|
-
fs.mkdirSync(resolvedUIPaths, { recursive: true });
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!fs.existsSync(resolvedLibPaths)) {
|
|
59
|
-
fs.mkdirSync(resolvedLibPaths, { recursive: true });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return configSchema.parse({
|
|
63
|
-
...config,
|
|
64
|
-
resolvedUIPaths,
|
|
65
|
-
resolvedLibPaths,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function getRawConfig(cwd: string): Promise<RawConfig | null> {
|
|
32
|
+
export async function getRawConfig(cwd: string): Promise<Config | null> {
|
|
70
33
|
try {
|
|
71
34
|
const configResult = await explorer.search(cwd);
|
|
72
|
-
return
|
|
35
|
+
return configSchema.parse(configResult.config);
|
|
73
36
|
} catch {
|
|
74
37
|
p.log.error("프로젝트 루트 경로에 `seed-design.json` 파일이 없어요.");
|
|
75
38
|
|
|
76
39
|
const isConfirm = await p.confirm({ message: "seed-design.json 파일을 생성하시겠어요?" });
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
await execa(packageManager, ["seed-design", "init", "--default"], { cwd });
|
|
80
|
-
p.log.message("seed-design.json 파일이 생성됐어요.");
|
|
81
|
-
} else {
|
|
40
|
+
|
|
41
|
+
if (!isConfirm) {
|
|
82
42
|
p.outro(highlight("작업이 취소됐어요."));
|
|
83
43
|
process.exit(1);
|
|
84
44
|
}
|
|
45
|
+
|
|
46
|
+
const packageManager = await getPackageManager(cwd);
|
|
47
|
+
|
|
48
|
+
await execa(packageManager, ["seed-design", "init", "--default"], { cwd });
|
|
49
|
+
|
|
50
|
+
p.log.message("seed-design.json 파일이 생성됐어요.");
|
|
85
51
|
}
|
|
86
52
|
}
|
package/src/utils/install.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
2
|
import { execa } from "execa";
|
|
3
|
-
import color from "picocolors";
|
|
4
3
|
import { getPackageManager } from "./get-package-manager";
|
|
5
4
|
import { getPackageInfo } from "./get-package-info";
|
|
6
5
|
|
|
@@ -18,20 +17,16 @@ export async function installDependencies({ cwd, deps, dev = false }: InstallDep
|
|
|
18
17
|
// 이미 설치된 의존성 필터링
|
|
19
18
|
const existingDeps = {
|
|
20
19
|
...packageInfo.dependencies,
|
|
21
|
-
...packageInfo.devDependencies,
|
|
20
|
+
// ...packageInfo.devDependencies,
|
|
21
|
+
// commented out because stated dependencies should be installed as actual dependencies even though they are listed in devDependencies
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const depsToInstall = deps.filter((dep) => !existingDeps[dep]);
|
|
25
|
-
const filteredDeps = deps.filter((dep) => existingDeps[dep]);
|
|
24
|
+
const depsToInstall = new Set(deps.filter((dep) => !existingDeps[dep]));
|
|
25
|
+
const filteredDeps = new Set(deps.filter((dep) => existingDeps[dep]));
|
|
26
26
|
|
|
27
|
-
if (!depsToInstall.
|
|
28
|
-
return {
|
|
29
|
-
installed: new Set(),
|
|
30
|
-
filtered: new Set(),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
27
|
+
if (!depsToInstall.size) return { installed: new Set<string>(), filtered: depsToInstall };
|
|
33
28
|
|
|
34
|
-
start(
|
|
29
|
+
start("의존성 설치중...");
|
|
35
30
|
|
|
36
31
|
const isDev = dev ? "-D" : null;
|
|
37
32
|
const addCommand = packageManager === "npm" ? "install" : "add";
|
|
@@ -44,7 +39,7 @@ export async function installDependencies({ cwd, deps, dev = false }: InstallDep
|
|
|
44
39
|
process.exit(1);
|
|
45
40
|
}
|
|
46
41
|
|
|
47
|
-
stop("의존성
|
|
42
|
+
stop("의존성 설치가 완료됐어요.");
|
|
48
43
|
|
|
49
44
|
return {
|
|
50
45
|
installed: depsToInstall,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { PublicRegistry, PublicRegistryItem } from "@/src/schema";
|
|
2
|
+
|
|
3
|
+
export function resolveDependencies({
|
|
4
|
+
selectedItemKeys,
|
|
5
|
+
publicRegistries,
|
|
6
|
+
}: {
|
|
7
|
+
/**
|
|
8
|
+
* @example ["breeze:animate-number", "ui:action-button"]
|
|
9
|
+
*/
|
|
10
|
+
selectedItemKeys: string[];
|
|
11
|
+
publicRegistries: PublicRegistry[];
|
|
12
|
+
}) {
|
|
13
|
+
const registryItemsToAdd: { registryId: string; items: PublicRegistry["items"] }[] = [];
|
|
14
|
+
const npmDependenciesToAdd = new Set<string>();
|
|
15
|
+
|
|
16
|
+
function collectRegistryItemsToAdd(registryId: string, item: PublicRegistryItem) {
|
|
17
|
+
const registryFoundToAdd = registryItemsToAdd.find((r) => r.registryId === registryId);
|
|
18
|
+
|
|
19
|
+
// if already added, skip
|
|
20
|
+
if (registryFoundToAdd?.items.some((i) => i.id === item.id)) return;
|
|
21
|
+
|
|
22
|
+
// Add the item to the list
|
|
23
|
+
if (registryFoundToAdd) {
|
|
24
|
+
registryFoundToAdd.items.push(item);
|
|
25
|
+
} else {
|
|
26
|
+
registryItemsToAdd.push({ registryId, items: [item] });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// process dependencies
|
|
30
|
+
if (item.dependencies?.length) {
|
|
31
|
+
for (const dep of item.dependencies) {
|
|
32
|
+
npmDependenciesToAdd.add(dep);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// process innerDependencies
|
|
37
|
+
if (item.innerDependencies?.length) {
|
|
38
|
+
for (const dependency of item.innerDependencies) {
|
|
39
|
+
for (const depItemId of dependency.itemIds) {
|
|
40
|
+
const depItem = publicRegistries
|
|
41
|
+
.find((r) => r.id === dependency.registryId)
|
|
42
|
+
?.items.find((i) => i.id === depItemId);
|
|
43
|
+
|
|
44
|
+
// should not happen
|
|
45
|
+
if (!depItem)
|
|
46
|
+
throw new Error(`Cannot find dependency item: ${dependency.registryId}:${depItemId}`);
|
|
47
|
+
|
|
48
|
+
collectRegistryItemsToAdd(dependency.registryId, depItem);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (const item of selectedItemKeys) {
|
|
55
|
+
const [registryId, ...rest] = item.split(":");
|
|
56
|
+
const itemId = rest.join(":");
|
|
57
|
+
|
|
58
|
+
if (!registryId || !itemId) {
|
|
59
|
+
throw new Error(`Invalid snippet format: "${item}"`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const foundItem = publicRegistries
|
|
63
|
+
.find((r) => r.id === registryId)
|
|
64
|
+
?.items.find((i) => i.id === itemId);
|
|
65
|
+
|
|
66
|
+
if (!foundItem) {
|
|
67
|
+
throw new Error(`Cannot find snippet: "${item}"`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
collectRegistryItemsToAdd(registryId, foundItem);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
registryItemsToAdd,
|
|
75
|
+
npmDependenciesToAdd,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -29,7 +29,8 @@ const project = new Project({
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
async function createTempSourceFile(filename: string) {
|
|
32
|
-
const dir = await fs.mkdtemp(path.join(tmpdir(), "seed-
|
|
32
|
+
const dir = await fs.mkdtemp(path.join(tmpdir(), "seed-design-"));
|
|
33
|
+
|
|
33
34
|
return path.join(dir, filename);
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -8,10 +8,27 @@ export const transformRsc: Transformer = async ({ sourceFile, config }) => {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
// Remove "use client" from the top of the file.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
// We need to be careful to only remove the directive itself, not any JSDoc comments
|
|
12
|
+
|
|
13
|
+
const firstExpressionStatement = sourceFile.getFirstChildByKind(SyntaxKind.ExpressionStatement);
|
|
14
|
+
if (!firstExpressionStatement) return sourceFile;
|
|
15
|
+
|
|
16
|
+
const expression = firstExpressionStatement.getExpression();
|
|
17
|
+
if (!expression) return sourceFile;
|
|
18
|
+
|
|
19
|
+
const expressionText = expression.getText().trim();
|
|
20
|
+
|
|
21
|
+
if (expressionText !== `"use client"` && expressionText !== `'use client'`) return sourceFile;
|
|
22
|
+
|
|
23
|
+
const expressionStatementText = firstExpressionStatement.getText();
|
|
24
|
+
const expressionStatementFullText = firstExpressionStatement.getFullText();
|
|
25
|
+
|
|
26
|
+
if (expressionStatementText.trim() === expressionStatementFullText.trim()) return sourceFile;
|
|
27
|
+
|
|
28
|
+
const triviaOnly = expressionStatementFullText.replace(expressionStatementText, "");
|
|
29
|
+
const cleanedTriviaOnly = triviaOnly.replace(/^\s*\n/, "").replace(/\n\s*$/, "");
|
|
30
|
+
|
|
31
|
+
firstExpressionStatement.replaceWithText(cleanedTriviaOnly);
|
|
15
32
|
|
|
16
33
|
return sourceFile;
|
|
17
34
|
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { fetchRegistryItems } from "@/src/utils/fetch";
|
|
2
|
+
import { transform } from "@/src/utils/transformers";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import fs from "fs-extra";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { highlight } from "./color";
|
|
7
|
+
import type { Config } from "@/src/utils/get-config";
|
|
8
|
+
import type { PublicRegistry } from "@/src/schema";
|
|
9
|
+
|
|
10
|
+
export async function writeRegistryItemSnippets({
|
|
11
|
+
registryItemsToAdd,
|
|
12
|
+
rootPath,
|
|
13
|
+
cwd,
|
|
14
|
+
baseUrl,
|
|
15
|
+
config,
|
|
16
|
+
}: {
|
|
17
|
+
registryItemsToAdd: { registryId: string; items: PublicRegistry["items"] }[];
|
|
18
|
+
rootPath: string;
|
|
19
|
+
cwd: string;
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
config: Config;
|
|
22
|
+
}) {
|
|
23
|
+
const registryResult: { name: string; path: string }[] = [];
|
|
24
|
+
|
|
25
|
+
for (const { registryId, items } of registryItemsToAdd) {
|
|
26
|
+
const registryPath = path.join(rootPath, registryId);
|
|
27
|
+
|
|
28
|
+
fs.ensureDirSync(registryPath);
|
|
29
|
+
|
|
30
|
+
const registryItems = await fetchRegistryItems({
|
|
31
|
+
baseUrl,
|
|
32
|
+
registryId,
|
|
33
|
+
registryItemIds: items.map((i) => i.id),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
for (const { id, snippets } of registryItems) {
|
|
37
|
+
const transformedSnippets = await Promise.all(
|
|
38
|
+
snippets.map(async (file) => {
|
|
39
|
+
const content = await transform({ filename: file.path, config, raw: file.content });
|
|
40
|
+
|
|
41
|
+
let filePath = path.join(registryPath, file.path);
|
|
42
|
+
if (!config.tsx) {
|
|
43
|
+
filePath = filePath.replace(/\.tsx$/, ".jsx");
|
|
44
|
+
filePath = filePath.replace(/\.ts$/, ".js");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
filePath,
|
|
49
|
+
content,
|
|
50
|
+
relativePath: path.relative(cwd, filePath),
|
|
51
|
+
name: `${registryId}:${id}`,
|
|
52
|
+
};
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
await Promise.all(
|
|
57
|
+
transformedSnippets.map(async ({ filePath, content }) => {
|
|
58
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
59
|
+
await fs.writeFile(filePath, content);
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const snippetResults = transformedSnippets.map(({ name, relativePath }) => ({
|
|
64
|
+
name,
|
|
65
|
+
path: relativePath,
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
registryResult.push(...snippetResults);
|
|
69
|
+
|
|
70
|
+
p.log.success(
|
|
71
|
+
`${highlight(`${registryId}:${id}`)} 관련 스니펫 다운로드 완료: ${highlight(snippetResults.map((r) => r.path).join(", "))}`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "vitest";
|
|
2
|
-
import { addRelativeRegistries } from "../utils/add-relative-registries";
|
|
3
|
-
import type { RegistryLib, RegistryUI } from "@/src/schema";
|
|
4
|
-
|
|
5
|
-
const libConfig: RegistryLib = [
|
|
6
|
-
{
|
|
7
|
-
name: "a",
|
|
8
|
-
files: ["a.tsx"],
|
|
9
|
-
},
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
const uiConfig: RegistryUI = [
|
|
13
|
-
{
|
|
14
|
-
name: "a",
|
|
15
|
-
files: ["a.tsx"],
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
name: "b",
|
|
19
|
-
innerDependencies: ["ui:a"],
|
|
20
|
-
files: ["b.tsx"],
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "c",
|
|
24
|
-
innerDependencies: ["ui:b"],
|
|
25
|
-
files: ["c.tsx"],
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: "d",
|
|
29
|
-
innerDependencies: ["ui:a", "ui:b"],
|
|
30
|
-
files: ["d.tsx"],
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: "e",
|
|
34
|
-
innerDependencies: ["ui:d"],
|
|
35
|
-
files: ["e.tsx"],
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: "f",
|
|
39
|
-
innerDependencies: ["lib:a"],
|
|
40
|
-
files: ["f.tsx"],
|
|
41
|
-
},
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
describe("addRelativeRegistries", () => {
|
|
45
|
-
test("4 deps test", () => {
|
|
46
|
-
const userSelects = ["e"];
|
|
47
|
-
const result = addRelativeRegistries({
|
|
48
|
-
userSelects,
|
|
49
|
-
uiRegistryIndex: uiConfig,
|
|
50
|
-
libRegistryIndex: [],
|
|
51
|
-
});
|
|
52
|
-
expect(result).toEqual(
|
|
53
|
-
expect.arrayContaining([
|
|
54
|
-
{
|
|
55
|
-
type: "ui",
|
|
56
|
-
name: "e",
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
type: "ui",
|
|
60
|
-
name: "d",
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
type: "ui",
|
|
64
|
-
name: "a",
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
type: "ui",
|
|
68
|
-
name: "b",
|
|
69
|
-
},
|
|
70
|
-
]),
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("3 deps test", () => {
|
|
75
|
-
const userSelects = ["d"];
|
|
76
|
-
const result = addRelativeRegistries({
|
|
77
|
-
userSelects,
|
|
78
|
-
uiRegistryIndex: uiConfig,
|
|
79
|
-
libRegistryIndex: [],
|
|
80
|
-
});
|
|
81
|
-
expect(result).toEqual(
|
|
82
|
-
expect.arrayContaining([
|
|
83
|
-
{
|
|
84
|
-
type: "ui",
|
|
85
|
-
name: "d",
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
type: "ui",
|
|
89
|
-
name: "a",
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
type: "ui",
|
|
93
|
-
name: "b",
|
|
94
|
-
},
|
|
95
|
-
]),
|
|
96
|
-
);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("3 deps test", () => {
|
|
100
|
-
const userSelects = ["c"];
|
|
101
|
-
const result = addRelativeRegistries({
|
|
102
|
-
userSelects,
|
|
103
|
-
uiRegistryIndex: uiConfig,
|
|
104
|
-
libRegistryIndex: [],
|
|
105
|
-
});
|
|
106
|
-
expect(result).toEqual(
|
|
107
|
-
expect.arrayContaining([
|
|
108
|
-
{
|
|
109
|
-
type: "ui",
|
|
110
|
-
name: "c",
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
type: "ui",
|
|
114
|
-
name: "b",
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
type: "ui",
|
|
118
|
-
name: "a",
|
|
119
|
-
},
|
|
120
|
-
]),
|
|
121
|
-
);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("2 deps test", () => {
|
|
125
|
-
const userSelects = ["b"];
|
|
126
|
-
const result = addRelativeRegistries({
|
|
127
|
-
userSelects,
|
|
128
|
-
uiRegistryIndex: uiConfig,
|
|
129
|
-
libRegistryIndex: [],
|
|
130
|
-
});
|
|
131
|
-
expect(result).toEqual(
|
|
132
|
-
expect.arrayContaining([
|
|
133
|
-
{
|
|
134
|
-
type: "ui",
|
|
135
|
-
name: "b",
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
type: "ui",
|
|
139
|
-
name: "a",
|
|
140
|
-
},
|
|
141
|
-
]),
|
|
142
|
-
);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("1 deps test", () => {
|
|
146
|
-
const userSelects = ["a"];
|
|
147
|
-
const result = addRelativeRegistries({
|
|
148
|
-
userSelects,
|
|
149
|
-
uiRegistryIndex: uiConfig,
|
|
150
|
-
libRegistryIndex: [],
|
|
151
|
-
});
|
|
152
|
-
expect(result).toEqual(
|
|
153
|
-
expect.arrayContaining([
|
|
154
|
-
{
|
|
155
|
-
type: "ui",
|
|
156
|
-
name: "a",
|
|
157
|
-
},
|
|
158
|
-
]),
|
|
159
|
-
);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test("lib deps test", () => {
|
|
163
|
-
const userSelects = ["f"];
|
|
164
|
-
const result = addRelativeRegistries({
|
|
165
|
-
userSelects,
|
|
166
|
-
uiRegistryIndex: uiConfig,
|
|
167
|
-
libRegistryIndex: libConfig,
|
|
168
|
-
});
|
|
169
|
-
expect(result).toEqual(
|
|
170
|
-
expect.arrayContaining([
|
|
171
|
-
{
|
|
172
|
-
type: "ui",
|
|
173
|
-
name: "f",
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
type: "lib",
|
|
177
|
-
name: "a",
|
|
178
|
-
},
|
|
179
|
-
]),
|
|
180
|
-
);
|
|
181
|
-
});
|
|
182
|
-
});
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { RegistryLibMachineGenerated, RegistryUIMachineGenerated } from "@/src/schema";
|
|
2
|
-
|
|
3
|
-
interface AddRelativeComponentsProps {
|
|
4
|
-
userSelects: string[];
|
|
5
|
-
uiRegistryIndex: RegistryUIMachineGenerated;
|
|
6
|
-
libRegistryIndex: RegistryLibMachineGenerated;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function addRelativeRegistries({
|
|
10
|
-
userSelects,
|
|
11
|
-
uiRegistryIndex,
|
|
12
|
-
libRegistryIndex,
|
|
13
|
-
}: AddRelativeComponentsProps) {
|
|
14
|
-
const selectedComponents: { type: "ui" | "lib"; name: string }[] = [];
|
|
15
|
-
|
|
16
|
-
function addSeedDependencies({
|
|
17
|
-
registryName,
|
|
18
|
-
type,
|
|
19
|
-
}: { registryName: string; type: "ui" | "lib" }) {
|
|
20
|
-
if (selectedComponents.some((c) => c.name === registryName && c.type === type)) return;
|
|
21
|
-
|
|
22
|
-
selectedComponents.push({ type, name: registryName });
|
|
23
|
-
|
|
24
|
-
const registry =
|
|
25
|
-
type === "ui"
|
|
26
|
-
? uiRegistryIndex.find((c) => c.name === registryName)
|
|
27
|
-
: libRegistryIndex.find((c) => c.name === registryName);
|
|
28
|
-
if (!registry) return;
|
|
29
|
-
|
|
30
|
-
if (registry.innerDependencies) {
|
|
31
|
-
for (const dep of registry.innerDependencies) {
|
|
32
|
-
const [depType, depName] = dep.split(":");
|
|
33
|
-
addSeedDependencies({ registryName: depName, type: depType as "ui" | "lib" });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
for (const registryName of userSelects) {
|
|
39
|
-
addSeedDependencies({ registryName, type: "ui" });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return Array.from(selectedComponents);
|
|
43
|
-
}
|