@seed-design/cli 0.0.0-alpha-20241113031935 → 0.0.0-alpha-20250210081704
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 +3 -3
- package/bin/index.mjs +2 -2
- package/package.json +8 -9
- package/src/commands/add.ts +93 -73
- package/src/commands/init.ts +67 -46
- package/src/constants.ts +2 -0
- package/src/index.ts +1 -3
- package/src/schema.ts +13 -16
- package/src/test/add-relative-registries.test.ts +182 -0
- package/src/utils/add-relative-registries.ts +43 -0
- package/src/utils/color.ts +3 -0
- package/src/utils/get-config.ts +34 -15
- package/src/utils/get-metadata.ts +61 -19
- package/src/utils/get-package-info.ts +2 -1
- package/src/utils/get-package-manager.ts +2 -2
- package/src/utils/install.ts +53 -0
- package/src/commands/check-deprecated-icon-files.ts +0 -256
- package/src/test/add-relative-components.test.ts +0 -62
- package/src/utils/add-relative-components.ts +0 -29
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
}
|
package/src/utils/get-config.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
1
2
|
import { cosmiconfig } from "cosmiconfig";
|
|
3
|
+
import { execa } from "execa";
|
|
4
|
+
import fs from "fs";
|
|
2
5
|
import path from "path";
|
|
3
|
-
import * as p from "@clack/prompts";
|
|
4
6
|
import { z } from "zod";
|
|
5
|
-
import
|
|
7
|
+
import { highlight } from "./color";
|
|
8
|
+
import { getPackageManager } from "./get-package-manager";
|
|
6
9
|
|
|
7
10
|
const MODULE_NAME = "seed-design";
|
|
8
11
|
|
|
@@ -24,8 +27,7 @@ export type RawConfig = z.infer<typeof rawConfigSchema>;
|
|
|
24
27
|
|
|
25
28
|
export const configSchema = rawConfigSchema.extend({
|
|
26
29
|
resolvedUIPaths: z.string(),
|
|
27
|
-
|
|
28
|
-
resolvedUtilPaths: z.string(),
|
|
30
|
+
resolvedLibPaths: z.string(),
|
|
29
31
|
});
|
|
30
32
|
|
|
31
33
|
export async function getConfig(cwd: string) {
|
|
@@ -43,26 +45,43 @@ export type Config = z.infer<typeof configSchema>;
|
|
|
43
45
|
export async function resolveConfigPaths(cwd: string, config: RawConfig) {
|
|
44
46
|
const seedComponentRootPath = path.resolve(cwd, config.path);
|
|
45
47
|
|
|
48
|
+
if (!fs.existsSync(seedComponentRootPath)) {
|
|
49
|
+
fs.mkdirSync(seedComponentRootPath, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const resolvedUIPaths = path.join(seedComponentRootPath, "ui");
|
|
53
|
+
const resolvedLibPaths = path.join(seedComponentRootPath, "lib");
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(resolvedUIPaths)) {
|
|
56
|
+
fs.mkdirSync(resolvedUIPaths, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!fs.existsSync(resolvedLibPaths)) {
|
|
60
|
+
fs.mkdirSync(resolvedLibPaths, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
46
63
|
return configSchema.parse({
|
|
47
64
|
...config,
|
|
48
|
-
resolvedUIPaths
|
|
49
|
-
|
|
50
|
-
resolvedUtilPaths: path.join(seedComponentRootPath, "util"),
|
|
65
|
+
resolvedUIPaths,
|
|
66
|
+
resolvedLibPaths,
|
|
51
67
|
});
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
export async function getRawConfig(cwd: string): Promise<RawConfig | null> {
|
|
55
71
|
try {
|
|
56
72
|
const configResult = await explorer.search(cwd);
|
|
73
|
+
return rawConfigSchema.parse(configResult.config);
|
|
74
|
+
} catch {
|
|
75
|
+
p.log.error("프로젝트 루트 경로에 `seed-design.json` 파일이 없어요.");
|
|
57
76
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
77
|
+
const isConfirm = await p.confirm({ message: "seed-design.json 파일을 생성하시겠어요?" });
|
|
78
|
+
if (isConfirm === true) {
|
|
79
|
+
const packageManager = await getPackageManager(cwd);
|
|
80
|
+
await execa(packageManager, ["seed-design", "init", "--default"], { cwd });
|
|
81
|
+
p.log.message("seed-design.json 파일이 생성됐어요.");
|
|
82
|
+
} else {
|
|
83
|
+
p.outro(highlight("작업이 취소됐어요."));
|
|
84
|
+
process.exit(1);
|
|
61
85
|
}
|
|
62
|
-
|
|
63
|
-
return rawConfigSchema.parse(configResult.config);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.log(error);
|
|
66
|
-
throw new Error(`${cwd} 경로에 seed-design.json 파일을 읽을 수 없습니다.`);
|
|
67
86
|
}
|
|
68
87
|
}
|
|
@@ -1,33 +1,75 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
1
2
|
import { registryUISchema, type RegistryUIMachineGenerated } from "@/src/schema";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
process.env.NODE_ENV === "prod" ? "https://v3.seed-design.io" : "http://localhost:3000";
|
|
5
|
-
|
|
6
|
-
export async function fetchRegistryUIItem(
|
|
4
|
+
export async function fetchRegistryItems(
|
|
7
5
|
fileNames?: string[],
|
|
6
|
+
baseUrl?: string,
|
|
7
|
+
type: "ui" | "lib" = "ui",
|
|
8
8
|
): Promise<RegistryUIMachineGenerated> {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const response = await fetch(`${
|
|
9
|
+
const results = await Promise.all(
|
|
10
|
+
fileNames.map(async (fileName) => {
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(`${baseUrl}/__registry__/${type}/${fileName}.json`);
|
|
13
13
|
return await response.json();
|
|
14
|
-
})
|
|
15
|
-
|
|
14
|
+
} catch (error) {
|
|
15
|
+
const index = await fetch(`${baseUrl}/__registry__/${type}/index.json`).then((res) =>
|
|
16
|
+
res.json(),
|
|
17
|
+
);
|
|
18
|
+
const parsedIndex = registryUISchema.parse(index);
|
|
19
|
+
const availableComponents = parsedIndex.map((component) => component.name);
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
p.log.error(`${type}:${fileName} 컴포넌트는 레지스트리에 존재하지 않아요.`);
|
|
22
|
+
p.log.info(`사용 가능한 컴포넌트: ${availableComponents.join(", ")}`);
|
|
23
|
+
p.log.info(
|
|
24
|
+
JSON.stringify(
|
|
25
|
+
{
|
|
26
|
+
baseUrl,
|
|
27
|
+
error: error.toString(),
|
|
28
|
+
},
|
|
29
|
+
null,
|
|
30
|
+
2,
|
|
31
|
+
),
|
|
32
|
+
);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return results;
|
|
22
39
|
}
|
|
23
40
|
|
|
24
|
-
export async function
|
|
41
|
+
export async function fetchRegistryItem(
|
|
42
|
+
fileName: string,
|
|
43
|
+
baseUrl: string,
|
|
44
|
+
type: "ui" | "lib" = "ui",
|
|
45
|
+
) {
|
|
46
|
+
const [result] = await fetchRegistryItems([fileName], baseUrl, type);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function getRegistryUIIndex(baseUrl?: string) {
|
|
25
51
|
try {
|
|
26
|
-
const [result] = await
|
|
52
|
+
const [result] = await fetchRegistryItems(["index"], baseUrl, "ui");
|
|
27
53
|
|
|
28
54
|
return registryUISchema.parse(result);
|
|
29
55
|
} catch (error) {
|
|
30
|
-
|
|
31
|
-
|
|
56
|
+
p.log.error("레지스트리 인덱스를 가져오는 데 실패했어요.");
|
|
57
|
+
p.log.info(
|
|
58
|
+
JSON.stringify(
|
|
59
|
+
{
|
|
60
|
+
baseUrl,
|
|
61
|
+
error: error.toString(),
|
|
62
|
+
},
|
|
63
|
+
null,
|
|
64
|
+
2,
|
|
65
|
+
),
|
|
66
|
+
);
|
|
67
|
+
process.exit(1);
|
|
32
68
|
}
|
|
33
69
|
}
|
|
70
|
+
|
|
71
|
+
export async function getRegistryLibIndex(baseUrl?: string) {
|
|
72
|
+
const [result] = await fetchRegistryItems(["index"], baseUrl, "lib");
|
|
73
|
+
|
|
74
|
+
return registryUISchema.parse(result);
|
|
75
|
+
}
|
|
@@ -2,12 +2,12 @@ import { detect } from "@antfu/ni";
|
|
|
2
2
|
|
|
3
3
|
export async function getPackageManager(
|
|
4
4
|
targetDir: string,
|
|
5
|
-
): Promise<"yarn" | "pnpm" | "bun" | "npm"> {
|
|
5
|
+
): Promise<"yarn" | "pnpm" | "bun" | "npm" | "deno"> {
|
|
6
6
|
const packageManager = await detect({ programmatic: true, cwd: targetDir });
|
|
7
7
|
|
|
8
8
|
if (packageManager === "yarn@berry") return "yarn";
|
|
9
9
|
if (packageManager === "pnpm@6") return "pnpm";
|
|
10
10
|
if (packageManager === "bun") return "bun";
|
|
11
|
-
|
|
11
|
+
if (packageManager === "deno") return "deno";
|
|
12
12
|
return packageManager ?? "npm";
|
|
13
13
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
import color from "picocolors";
|
|
4
|
+
import { getPackageManager } from "./get-package-manager";
|
|
5
|
+
import { getPackageInfo } from "./get-package-info";
|
|
6
|
+
|
|
7
|
+
interface InstallDependenciesProps {
|
|
8
|
+
cwd: string;
|
|
9
|
+
deps: string[];
|
|
10
|
+
dev?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function installDependencies({ cwd, deps, dev = false }: InstallDependenciesProps) {
|
|
14
|
+
const { start, stop } = p.spinner();
|
|
15
|
+
const packageManager = await getPackageManager(cwd);
|
|
16
|
+
const packageInfo = getPackageInfo();
|
|
17
|
+
|
|
18
|
+
// 이미 설치된 의존성 필터링
|
|
19
|
+
const existingDeps = {
|
|
20
|
+
...packageInfo.dependencies,
|
|
21
|
+
...packageInfo.devDependencies,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const depsToInstall = deps.filter((dep) => !existingDeps[dep]);
|
|
25
|
+
const filteredDeps = deps.filter((dep) => existingDeps[dep]);
|
|
26
|
+
|
|
27
|
+
if (!depsToInstall.length) {
|
|
28
|
+
return {
|
|
29
|
+
installed: new Set(),
|
|
30
|
+
filtered: new Set(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
start(color.gray("의존성 설치중..."));
|
|
35
|
+
|
|
36
|
+
const isDev = dev ? "-D" : null;
|
|
37
|
+
const addCommand = packageManager === "npm" ? "install" : "add";
|
|
38
|
+
const command = [addCommand, isDev, ...depsToInstall].filter(Boolean);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await execa(packageManager, command, { cwd });
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error(`의존성 설치 실패: ${error}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
stop("의존성 설치 완료.");
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
installed: depsToInstall,
|
|
51
|
+
filtered: filteredDeps,
|
|
52
|
+
};
|
|
53
|
+
}
|