@axis-ds/cli 1.7.1 → 1.7.2
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 +2 -1
- package/dist/chunk-YYSEFWYD.js +473 -0
- package/dist/index.js +219 -382
- package/dist/monet.d.ts +6 -1
- package/dist/monet.js +5 -3
- package/package.json +1 -1
- package/dist/chunk-LVMDBOXD.js +0 -192
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ axis theme add ontology
|
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
- `axis theme add` 는 `axis.config.json` 의 `globalCss` → `app/globals.css` → `src/app/globals.css` → `styles/globals.css` → `src/styles/globals.css` → 루트 `globals.css` 순으로 찾은 파일의 앞쪽 import 구간 끝(`@tailwind` 등 규칙보다 앞)에 줄을 넣습니다. 1.6.0 부터 브랜드 import 앞에 기본 토큰 CSS 도 넣습니다: Tailwind 3 은 `@axis-ds/tokens/css` · `@axis-ds/tokens/css/shadcn`, Tailwind 4 는 `@axis-ds/tokens/css/tailwind4` 와 `package.json` 에 선언된 `@axis-ds/ui-react` · `@axis-ds/agentic-ui` 의 `tailwind.css` 입니다. 이미 있는 줄은 다시 넣지 않으므로 여러 번 실행해도 같습니다. 규칙 뒤에 있는 기존 `@import` 는 CSS 규칙상 무시될 수 있어 맨 앞으로 옮기라고 알립니다(Next.js 15 는 위치와 관계없이 앞으로 끌어올려 적용함을 측정).
|
|
87
|
-
- `package.json` 에 `@axis-ds/tokens` 가 없으면 `dependencies` 에 `^1.6.0`(1.6.0 부터, 이전에는 `^1.3.0`)을 더하고 설치 명령만 안내합니다. 설치는 실행하지 않습니다. 설치 · 업그레이드 안내 명령은 lockfile 로 고릅니다(`npm install` · `yarn add` · `pnpm add`).
|
|
87
|
+
- `package.json` 에 `@axis-ds/tokens` 가 없으면 `dependencies` 에 `^1.6.0`(1.6.0 부터, 이전에는 `^1.3.0`)을 더하고 설치 명령만 안내합니다. 설치는 실행하지 않습니다. 설치 · 업그레이드 안내 명령은 lockfile 로 고릅니다(`npm install` · `yarn add` · `pnpm add`). lockfile 이 없으면 `package.json` 의 `packageManager`(`npm@…` · `yarn@…` · `pnpm@…`)를 보고, 둘 다 없으면 `pnpm` 입니다(1.7.2 부터 `packageManager` 를 봄).
|
|
88
88
|
- 브랜드 CSS 는 `[data-brand="<brand>"]` 선택자로 적용되므로 루트 레이아웃의 `<html data-brand>` 나 `ThemeProvider` 의 `setBrand` 가 있어야 화면이 바뀝니다. 코드는 고치지 않고, 레이아웃에서 찾지 못하면 두 방법의 예시를 냅니다(`defaultBrand` 는 `@axis-ds/theme` 1.6.2 이하에서 `setBrand(undefined)` 로 끈 상태가 새로고침 뒤 유지되지 않아 예시에 쓰지 않습니다). 마크업 `data-brand` 와 `ThemeProvider` 를 함께 쓰는 레이아웃이면 theme 1.7.0 이상이 필요하다고 한 줄 더 알립니다(1.6.1 부터). Tailwind 3 이면 preset · content 가 빠졌을 때 넣을 줄도 안내합니다(설정 파일은 고치지 않음). preset 안내는 1.6.1 부터 CommonJS 에서 모든 tokens 버전에 되는 `require("@axis-ds/tokens/tailwind").default` 입니다(1.6.0 의 `.default` 없는 형태는 tokens 1.6.2 이하에서 preset 이 빠짐).
|
|
89
89
|
- 넣은 import 줄의 출력은 1.6.1 부터 실제 위치 · 분류를 따릅니다. 컴포넌트 `tailwind.css` 는 「컴포넌트 스캔 경로, 위치 무관」, 기본 토큰 CSS 는 브랜드 줄보다 앞에 들어갔을 때만 「브랜드 앞」이고 뒤에 들어가면 옮기라고 경고합니다.
|
|
90
90
|
- `@axis-ds/tokens` 가 없거나 1.3.0 미만이면 목록 대신 설치 · 업그레이드 안내를 내고 종료 코드 1 로 끝납니다.
|
|
@@ -128,6 +128,7 @@ stdin 이 터미널이 아니면 질문을 띄우지 않습니다(1.7.0 부터).
|
|
|
128
128
|
|
|
129
129
|
- `axis template init -y` 는 질문을 기본값으로 답하고, 비어 있지 않은 디렉터리도 진행합니다(`template apply -y` 와 같은 뜻). 터미널에서도 같습니다.
|
|
130
130
|
- 판정은 stdin 이 터미널인지 하나로 합니다. 그래서 `printf '\n' | axis template init dashboard` 처럼 파이프로 응답을 넣던 스크립트도 이제는 묻지 않고 위 표대로 동작합니다. 값은 `--dir` · `-y` 같은 옵션으로 주세요.
|
|
131
|
+
- `axis template apply` 의 「필요한 의존성」 명령과 `axis template init` 의 다음 단계(`install` · `dev`)는 대상 디렉터리의 lockfile(없으면 `packageManager`)로 매니저를 고르고 범위를 따옴표로 감쌉니다(1.7.2 부터, 1.7.1 까지는 `pnpm` 고정).
|
|
131
132
|
- Windows Git Bash(mintty)처럼 사람이 앞에 있어도 stdin 이 터미널로 잡히지 않는 환경에서는 질문 없이 위 표대로 동작할 수 있습니다(실측 전). 질문이 뜨지 않으면 `winpty npx axis ...` 로 실행하거나, PowerShell · Windows Terminal 에서 실행하거나, `--dir` · `-y` 같은 옵션으로 값을 주세요.
|
|
132
133
|
|
|
133
134
|
## 문서
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ask,
|
|
3
|
+
failNonInteractive,
|
|
4
|
+
isInteractive
|
|
5
|
+
} from "./chunk-HFMMK6R4.js";
|
|
6
|
+
|
|
7
|
+
// src/monet.ts
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import path2 from "path";
|
|
11
|
+
|
|
12
|
+
// ../axis-registry-core/src/project.ts
|
|
13
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "fs";
|
|
14
|
+
import path from "path";
|
|
15
|
+
function resolveBaseDir(baseDirInput) {
|
|
16
|
+
const resolved = path.resolve(baseDirInput);
|
|
17
|
+
try {
|
|
18
|
+
return realpathSync(resolved);
|
|
19
|
+
} catch {
|
|
20
|
+
return resolved;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
var LOCKFILE_COMMANDS = [
|
|
24
|
+
["pnpm-lock.yaml", "pnpm install"],
|
|
25
|
+
["yarn.lock", "yarn install"],
|
|
26
|
+
["package-lock.json", "npm install"]
|
|
27
|
+
];
|
|
28
|
+
function lockfileCommandAt(dir) {
|
|
29
|
+
return LOCKFILE_COMMANDS.find(([file]) => existsSync(path.join(dir, file)))?.[1];
|
|
30
|
+
}
|
|
31
|
+
function isWorkspaceRootDir(dir) {
|
|
32
|
+
if (isFile(path.join(dir, "pnpm-workspace.yaml"))) return true;
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8"));
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (!isPlainObject(parsed)) return false;
|
|
40
|
+
const ws = parsed.workspaces;
|
|
41
|
+
return Array.isArray(ws) || isPlainObject(ws) && Array.isArray(ws.packages);
|
|
42
|
+
}
|
|
43
|
+
function findWorkspaceRoot(baseDirInput) {
|
|
44
|
+
let dir = resolveBaseDir(baseDirInput);
|
|
45
|
+
for (; ; ) {
|
|
46
|
+
if (isWorkspaceRootDir(dir)) return dir;
|
|
47
|
+
if (existsSync(path.join(dir, ".git"))) return void 0;
|
|
48
|
+
const parent = path.dirname(dir);
|
|
49
|
+
if (parent === dir) return void 0;
|
|
50
|
+
dir = parent;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
var PACKAGE_MANAGER_COMMANDS = {
|
|
54
|
+
pnpm: "pnpm install",
|
|
55
|
+
yarn: "yarn install",
|
|
56
|
+
npm: "npm install"
|
|
57
|
+
};
|
|
58
|
+
function packageManagerCommandAt(dir) {
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8"));
|
|
62
|
+
} catch {
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
if (!isPlainObject(parsed) || typeof parsed.packageManager !== "string") return void 0;
|
|
66
|
+
const name = /^(pnpm|yarn|npm)@/.exec(parsed.packageManager.trim())?.[1];
|
|
67
|
+
return name !== void 0 ? PACKAGE_MANAGER_COMMANDS[name] : void 0;
|
|
68
|
+
}
|
|
69
|
+
function detectInstallCommand(baseDirInput) {
|
|
70
|
+
const baseDir = resolveBaseDir(baseDirInput);
|
|
71
|
+
const own = lockfileCommandAt(baseDir);
|
|
72
|
+
if (own !== void 0) return own;
|
|
73
|
+
const found = findWorkspaceRoot(baseDir);
|
|
74
|
+
const root = found !== void 0 && found !== baseDir ? found : void 0;
|
|
75
|
+
if (root !== void 0) {
|
|
76
|
+
const fromRoot = lockfileCommandAt(root);
|
|
77
|
+
if (fromRoot !== void 0) return fromRoot;
|
|
78
|
+
}
|
|
79
|
+
const ownManager = packageManagerCommandAt(baseDir);
|
|
80
|
+
if (ownManager !== void 0) return ownManager;
|
|
81
|
+
if (root !== void 0) {
|
|
82
|
+
const rootManager = packageManagerCommandAt(root);
|
|
83
|
+
if (rootManager !== void 0) return rootManager;
|
|
84
|
+
}
|
|
85
|
+
return "pnpm install";
|
|
86
|
+
}
|
|
87
|
+
function packageManagerOf(command) {
|
|
88
|
+
if (command.startsWith("yarn")) return "yarn";
|
|
89
|
+
if (command.startsWith("npm")) return "npm";
|
|
90
|
+
return "pnpm";
|
|
91
|
+
}
|
|
92
|
+
function hasDependency(pkg, name) {
|
|
93
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
94
|
+
const deps = pkg[field];
|
|
95
|
+
if (typeof deps === "object" && deps !== null && name in deps) {
|
|
96
|
+
return String(deps[name]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return void 0;
|
|
100
|
+
}
|
|
101
|
+
function workspaceRootPackage(baseDirInput) {
|
|
102
|
+
const baseDir = resolveBaseDir(baseDirInput);
|
|
103
|
+
const root = findWorkspaceRoot(baseDir);
|
|
104
|
+
if (root === void 0 || root === baseDir) return void 0;
|
|
105
|
+
try {
|
|
106
|
+
const parsed = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
|
|
107
|
+
return isPlainObject(parsed) ? parsed : void 0;
|
|
108
|
+
} catch {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function declaredDependency(baseDir, pkg, name, rootPkg) {
|
|
113
|
+
if (pkg === void 0) return void 0;
|
|
114
|
+
const own = hasDependency(pkg, name);
|
|
115
|
+
if (own !== void 0) return { range: own, source: "app" };
|
|
116
|
+
const root = rootPkg === void 0 ? workspaceRootPackage(baseDir) : rootPkg ?? void 0;
|
|
117
|
+
const fromRoot = root !== void 0 ? hasDependency(root, name) : void 0;
|
|
118
|
+
return fromRoot !== void 0 ? { range: fromRoot, source: "root" } : void 0;
|
|
119
|
+
}
|
|
120
|
+
function isPlainObject(value) {
|
|
121
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
122
|
+
}
|
|
123
|
+
function addCommandPrefix(installCommand, options = {}) {
|
|
124
|
+
const dev = options.dev ? " -D" : "";
|
|
125
|
+
switch (packageManagerOf(installCommand)) {
|
|
126
|
+
case "yarn":
|
|
127
|
+
return `yarn add${dev}`;
|
|
128
|
+
case "npm":
|
|
129
|
+
return `npm install${dev}`;
|
|
130
|
+
default:
|
|
131
|
+
return `pnpm add${dev}`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function addCommandFor(installCommand, deps, options = {}) {
|
|
135
|
+
const specs = Object.entries(deps).map(([name, range]) => `"${name}@${range}"`).join(" ");
|
|
136
|
+
return `${addCommandPrefix(installCommand, options)} ${specs}`;
|
|
137
|
+
}
|
|
138
|
+
function runScriptCommand(installCommand, script) {
|
|
139
|
+
switch (packageManagerOf(installCommand)) {
|
|
140
|
+
case "yarn":
|
|
141
|
+
return `yarn ${script}`;
|
|
142
|
+
case "npm":
|
|
143
|
+
return `npm run ${script}`;
|
|
144
|
+
default:
|
|
145
|
+
return `pnpm ${script}`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function workspaceRunCommand(installCommand, pkg, script) {
|
|
149
|
+
switch (packageManagerOf(installCommand)) {
|
|
150
|
+
case "yarn":
|
|
151
|
+
return `yarn workspace ${pkg} ${script}`;
|
|
152
|
+
case "npm":
|
|
153
|
+
return `npm run ${script} -w ${pkg}`;
|
|
154
|
+
default:
|
|
155
|
+
return `pnpm --filter ${pkg} ${script}`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
var GLOBAL_CSS_CANDIDATES = [
|
|
159
|
+
"app/globals.css",
|
|
160
|
+
"src/app/globals.css",
|
|
161
|
+
"styles/globals.css",
|
|
162
|
+
"src/styles/globals.css",
|
|
163
|
+
"globals.css"
|
|
164
|
+
];
|
|
165
|
+
var TAILWIND_CONFIG_CANDIDATES = [
|
|
166
|
+
"tailwind.config.ts",
|
|
167
|
+
"tailwind.config.js",
|
|
168
|
+
"tailwind.config.mjs",
|
|
169
|
+
"tailwind.config.cjs"
|
|
170
|
+
];
|
|
171
|
+
function isFile(file) {
|
|
172
|
+
try {
|
|
173
|
+
return statSync(file).isFile();
|
|
174
|
+
} catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function isDirectory(dir) {
|
|
179
|
+
try {
|
|
180
|
+
return statSync(dir).isDirectory();
|
|
181
|
+
} catch {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function findFirstFile(baseDir, candidates) {
|
|
186
|
+
return candidates.find((c) => isFile(path.join(baseDir, c)));
|
|
187
|
+
}
|
|
188
|
+
function detectProjectPaths(baseDirInput) {
|
|
189
|
+
const baseDir = path.resolve(baseDirInput);
|
|
190
|
+
const hasSrc = isDirectory(path.join(baseDir, "src"));
|
|
191
|
+
const css = findFirstFile(baseDir, GLOBAL_CSS_CANDIDATES);
|
|
192
|
+
const tw = findFirstFile(baseDir, TAILWIND_CONFIG_CANDIDATES);
|
|
193
|
+
return {
|
|
194
|
+
componentsDir: hasSrc ? "./src/components/ui" : "./components/ui",
|
|
195
|
+
globalCss: css !== void 0 ? `./${css}` : hasSrc ? "./src/app/globals.css" : "./app/globals.css",
|
|
196
|
+
globalCssSource: css !== void 0 ? "found" : "default",
|
|
197
|
+
tailwindConfig: tw !== void 0 ? `./${tw}` : "./tailwind.config.ts",
|
|
198
|
+
tailwindConfigSource: tw !== void 0 ? "found" : "default"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function projectFileExists(baseDir, relPath) {
|
|
202
|
+
return isFile(path.resolve(baseDir, relPath));
|
|
203
|
+
}
|
|
204
|
+
var ROOT_LAYOUT_FILES = [
|
|
205
|
+
"app/layout.tsx",
|
|
206
|
+
"app/layout.jsx",
|
|
207
|
+
"app/layout.js",
|
|
208
|
+
"src/app/layout.tsx",
|
|
209
|
+
"src/app/layout.jsx",
|
|
210
|
+
"src/app/layout.js",
|
|
211
|
+
"pages/_app.tsx",
|
|
212
|
+
"pages/_app.jsx",
|
|
213
|
+
"pages/_document.tsx",
|
|
214
|
+
"pages/_document.jsx",
|
|
215
|
+
"src/pages/_app.tsx",
|
|
216
|
+
"src/pages/_app.jsx",
|
|
217
|
+
"src/pages/_document.tsx",
|
|
218
|
+
"src/pages/_document.jsx"
|
|
219
|
+
];
|
|
220
|
+
function exactVersionOf(version) {
|
|
221
|
+
if (typeof version !== "string") return void 0;
|
|
222
|
+
const v = version.trim();
|
|
223
|
+
if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
|
|
224
|
+
return v;
|
|
225
|
+
}
|
|
226
|
+
function findInstalledPackage(baseDirInput, name) {
|
|
227
|
+
let dir = resolveBaseDir(baseDirInput);
|
|
228
|
+
for (; ; ) {
|
|
229
|
+
const pkgDir = path.join(dir, "node_modules", ...name.split("/"));
|
|
230
|
+
const file = path.join(pkgDir, "package.json");
|
|
231
|
+
if (isFile(file)) {
|
|
232
|
+
let version;
|
|
233
|
+
try {
|
|
234
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
235
|
+
version = isPlainObject(parsed) ? exactVersionOf(parsed.version) : void 0;
|
|
236
|
+
} catch {
|
|
237
|
+
version = void 0;
|
|
238
|
+
}
|
|
239
|
+
return { dir: pkgDir, version };
|
|
240
|
+
}
|
|
241
|
+
const parent = path.dirname(dir);
|
|
242
|
+
if (parent === dir) return void 0;
|
|
243
|
+
dir = parent;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function isPnpProject(baseDirInput) {
|
|
247
|
+
let dir = resolveBaseDir(baseDirInput);
|
|
248
|
+
for (; ; ) {
|
|
249
|
+
if (isFile(path.join(dir, ".pnp.cjs")) || isFile(path.join(dir, ".pnp.js"))) return true;
|
|
250
|
+
const parent = path.dirname(dir);
|
|
251
|
+
if (parent === dir) return false;
|
|
252
|
+
dir = parent;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function compareExactVersions(a, b) {
|
|
256
|
+
const parse = (v) => {
|
|
257
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?/.exec(v.trim());
|
|
258
|
+
return m ? { core: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] !== void 0 } : void 0;
|
|
259
|
+
};
|
|
260
|
+
const x = parse(a);
|
|
261
|
+
const y = parse(b);
|
|
262
|
+
if (!x || !y) return void 0;
|
|
263
|
+
for (let i = 0; i < 3; i++) if (x.core[i] !== y.core[i]) return x.core[i] - y.core[i];
|
|
264
|
+
return Number(y.pre) - Number(x.pre);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/monet.ts
|
|
268
|
+
var MONET_CATEGORIES = [
|
|
269
|
+
{ id: "hero", name: "Hero Sections", count: 120 },
|
|
270
|
+
{ id: "feature", name: "Feature Sections", count: 191 },
|
|
271
|
+
{ id: "testimonial", name: "Testimonials", count: 47 },
|
|
272
|
+
{ id: "pricing", name: "Pricing Tables", count: 35 },
|
|
273
|
+
{ id: "cta", name: "Call to Action", count: 42 },
|
|
274
|
+
{ id: "stats", name: "Statistics", count: 28 },
|
|
275
|
+
{ id: "footer", name: "Footers", count: 38 },
|
|
276
|
+
{ id: "header", name: "Headers/Navigation", count: 45 },
|
|
277
|
+
{ id: "logo-cloud", name: "Logo Clouds", count: 22 },
|
|
278
|
+
{ id: "team", name: "Team Sections", count: 31 },
|
|
279
|
+
{ id: "faq", name: "FAQ Sections", count: 24 },
|
|
280
|
+
{ id: "contact", name: "Contact Forms", count: 29 },
|
|
281
|
+
{ id: "newsletter", name: "Newsletter", count: 18 },
|
|
282
|
+
{ id: "landing", name: "Full Landing Pages", count: 28 }
|
|
283
|
+
];
|
|
284
|
+
var MonetClient = class {
|
|
285
|
+
apiKey;
|
|
286
|
+
baseUrl = "https://www.monet.design";
|
|
287
|
+
constructor(apiKey) {
|
|
288
|
+
this.apiKey = apiKey || process.env.MONET_API_KEY;
|
|
289
|
+
}
|
|
290
|
+
// 카테고리 목록 출력
|
|
291
|
+
async listCategories() {
|
|
292
|
+
console.log(chalk.blue("\n\u{1F3A8} Monet Design \uCE74\uD14C\uACE0\uB9AC\n"));
|
|
293
|
+
console.log(chalk.gray("https://monet.design - 1000+ React UI \uCEF4\uD3EC\uB10C\uD2B8\n"));
|
|
294
|
+
for (const cat of MONET_CATEGORIES) {
|
|
295
|
+
console.log(
|
|
296
|
+
` ${chalk.cyan(cat.id.padEnd(15))} ${cat.name.padEnd(25)} ${chalk.gray(`(${cat.count}\uAC1C)`)}`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
console.log(chalk.gray("\n\uC0AC\uC6A9\uBC95:"));
|
|
300
|
+
console.log(chalk.gray(" npx @axis-ds/cli monet browse <category> # \uCE74\uD14C\uACE0\uB9AC \uCEF4\uD3EC\uB10C\uD2B8 \uBCF4\uAE30"));
|
|
301
|
+
console.log(chalk.gray(" npx @axis-ds/cli monet search <query> # \uCEF4\uD3EC\uB10C\uD2B8 \uAC80\uC0C9\n"));
|
|
302
|
+
}
|
|
303
|
+
// 컴포넌트 검색 (웹 URL 안내)
|
|
304
|
+
async searchComponents(query) {
|
|
305
|
+
console.log(chalk.blue(`
|
|
306
|
+
\u{1F50D} "${query}" \uAC80\uC0C9 \uC911...
|
|
307
|
+
`));
|
|
308
|
+
const searchUrl = `${this.baseUrl}/c?q=${encodeURIComponent(query)}`;
|
|
309
|
+
console.log(chalk.yellow("Monet\uC740 \uC9C1\uC811 API\uB97C \uC81C\uACF5\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."));
|
|
310
|
+
console.log(chalk.gray("\uB2E4\uC74C \uBC29\uBC95\uC73C\uB85C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uAC00\uC838\uC62C \uC218 \uC788\uC2B5\uB2C8\uB2E4:\n"));
|
|
311
|
+
console.log(chalk.bold("1. \uC6F9\uC5D0\uC11C \uAC80\uC0C9:"));
|
|
312
|
+
console.log(chalk.cyan(` ${searchUrl}
|
|
313
|
+
`));
|
|
314
|
+
console.log(chalk.bold("2. MCP \uC5F0\uB3D9 (Claude Code):"));
|
|
315
|
+
console.log(chalk.gray(" .claude/mcp.json\uC5D0 monet \uC11C\uBC84 \uD65C\uC131\uD654 \uD6C4"));
|
|
316
|
+
console.log(chalk.cyan(' "monet\uC5D0\uC11C hero \uCEF4\uD3EC\uB10C\uD2B8 \uCC3E\uC544\uC918"'));
|
|
317
|
+
console.log(chalk.gray(" \u2192 API \uD0A4 \uD544\uC694: https://monet.design/mcp\n"));
|
|
318
|
+
console.log(chalk.bold("3. \uC218\uB3D9 \uBCF5\uC0AC-\uBD99\uC5EC\uB123\uAE30:"));
|
|
319
|
+
console.log(chalk.gray(" \uC6F9\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8 \uC120\uD0DD \u2192 Copy Code \u2192 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uBD99\uC5EC\uB123\uAE30\n"));
|
|
320
|
+
}
|
|
321
|
+
// 카테고리별 컴포넌트 브라우징
|
|
322
|
+
async browseCategory(category) {
|
|
323
|
+
const cat = MONET_CATEGORIES.find((c) => c.id === category);
|
|
324
|
+
if (!cat) {
|
|
325
|
+
console.log(chalk.red(`
|
|
326
|
+
\u274C '${category}' \uCE74\uD14C\uACE0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
|
327
|
+
`));
|
|
328
|
+
console.log(chalk.gray("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCE74\uD14C\uACE0\uB9AC:"));
|
|
329
|
+
MONET_CATEGORIES.forEach((c) => console.log(chalk.cyan(` ${c.id}`)));
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
console.log(chalk.blue(`
|
|
333
|
+
\u{1F4C2} ${cat.name} (${cat.count}\uAC1C \uCEF4\uD3EC\uB10C\uD2B8)
|
|
334
|
+
`));
|
|
335
|
+
const browseUrl = `${this.baseUrl}/c?category=${category}`;
|
|
336
|
+
console.log(chalk.gray("\uC6F9\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uD655\uC778\uD558\uC138\uC694:"));
|
|
337
|
+
console.log(chalk.cyan(`${browseUrl}
|
|
338
|
+
`));
|
|
339
|
+
console.log(chalk.bold("\uC778\uAE30 \uCEF4\uD3EC\uB10C\uD2B8 \uC608\uC2DC:"));
|
|
340
|
+
const examples = this.getCategoryExamples(category);
|
|
341
|
+
examples.forEach((ex) => {
|
|
342
|
+
console.log(` ${chalk.cyan(ex.id.padEnd(30))} ${chalk.gray(ex.desc)}`);
|
|
343
|
+
});
|
|
344
|
+
console.log(chalk.gray("\n\uCEF4\uD3EC\uB10C\uD2B8 \uAC00\uC838\uC624\uAE30:"));
|
|
345
|
+
console.log(chalk.gray(" 1. \uC6F9\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8 \uC120\uD0DD"));
|
|
346
|
+
console.log(chalk.gray(" 2. Copy Code \uBC84\uD2BC \uD074\uB9AD"));
|
|
347
|
+
console.log(chalk.gray(" 3. npx @axis-ds/cli monet import \uC2E4\uD589\n"));
|
|
348
|
+
}
|
|
349
|
+
// 카테고리별 예시 컴포넌트
|
|
350
|
+
getCategoryExamples(category) {
|
|
351
|
+
const examples = {
|
|
352
|
+
hero: [
|
|
353
|
+
{ id: "hero-with-image", desc: "\uC774\uBBF8\uC9C0 \uBC30\uACBD \uD788\uC5B4\uB85C" },
|
|
354
|
+
{ id: "hero-centered", desc: "\uC911\uC559 \uC815\uB82C \uD788\uC5B4\uB85C" },
|
|
355
|
+
{ id: "hero-split", desc: "\uBD84\uD560 \uB808\uC774\uC544\uC6C3 \uD788\uC5B4\uB85C" }
|
|
356
|
+
],
|
|
357
|
+
feature: [
|
|
358
|
+
{ id: "feature-grid", desc: "\uADF8\uB9AC\uB4DC \uD615\uC2DD \uAE30\uB2A5 \uC18C\uAC1C" },
|
|
359
|
+
{ id: "feature-alternating", desc: "\uAD50\uCC28 \uB808\uC774\uC544\uC6C3 \uAE30\uB2A5" },
|
|
360
|
+
{ id: "feature-icons", desc: "\uC544\uC774\uCF58 \uAE30\uBC18 \uAE30\uB2A5" }
|
|
361
|
+
],
|
|
362
|
+
pricing: [
|
|
363
|
+
{ id: "pricing-three-tiers", desc: "3\uB2E8 \uC694\uAE08\uC81C" },
|
|
364
|
+
{ id: "pricing-comparison", desc: "\uBE44\uAD50 \uD14C\uC774\uBE14 \uC694\uAE08\uC81C" },
|
|
365
|
+
{ id: "pricing-toggle", desc: "\uC6D4/\uC5F0 \uD1A0\uAE00 \uC694\uAE08\uC81C" }
|
|
366
|
+
],
|
|
367
|
+
landing: [
|
|
368
|
+
{ id: "deepcon-ai-landing", desc: "AI SaaS \uB79C\uB529\uD398\uC774\uC9C0" },
|
|
369
|
+
{ id: "saas-landing", desc: "SaaS \uC2A4\uD0C0\uD130 \uB79C\uB529" },
|
|
370
|
+
{ id: "agency-landing", desc: "\uC5D0\uC774\uC804\uC2DC \uB79C\uB529\uD398\uC774\uC9C0" }
|
|
371
|
+
]
|
|
372
|
+
};
|
|
373
|
+
return examples[category] || [{ id: `${category}-basic`, desc: "\uAE30\uBCF8 \uD15C\uD50C\uB9BF" }];
|
|
374
|
+
}
|
|
375
|
+
// 클립보드에서 Monet 코드 가져오기
|
|
376
|
+
/** opts.interactive: 질문을 띄울 수 있는지(WI-0075). 없으면 stdin 이 TTY 인지로 정합니다 */
|
|
377
|
+
async importFromClipboard(opts = {}) {
|
|
378
|
+
console.log(chalk.blue("\n\u{1F4CB} \uD074\uB9BD\uBCF4\uB4DC\uC5D0\uC11C Monet \uCEF4\uD3EC\uB10C\uD2B8 \uAC00\uC838\uC624\uAE30\n"));
|
|
379
|
+
console.log(chalk.yellow("\uC900\uBE44 \uB2E8\uACC4:"));
|
|
380
|
+
console.log(chalk.gray(" 1. monet.design\uC5D0\uC11C \uCEF4\uD3EC\uB10C\uD2B8 \uC120\uD0DD"));
|
|
381
|
+
console.log(chalk.gray(" 2. 'Copy Code' \uBC84\uD2BC\uC73C\uB85C \uCF54\uB4DC \uBCF5\uC0AC"));
|
|
382
|
+
console.log(chalk.gray(" 3. \uC774 \uBA85\uB839\uC5B4 \uC2E4\uD589\n"));
|
|
383
|
+
const interactive = opts.interactive ?? isInteractive();
|
|
384
|
+
if (!interactive) {
|
|
385
|
+
failNonInteractive("\uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uACFC \uBD99\uC5EC \uB123\uC744 \uCF54\uB4DC\uB97C \uBB3C\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", [
|
|
386
|
+
"\uB300\uD654\uD615 \uD130\uBBF8\uB110\uC5D0\uC11C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694: axis monet import"
|
|
387
|
+
]);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const { componentName } = await ask(
|
|
391
|
+
{
|
|
392
|
+
type: "text",
|
|
393
|
+
name: "componentName",
|
|
394
|
+
message: "\uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 (\uC608: hero-section):",
|
|
395
|
+
validate: (v) => v.length > 0 ? true : "\uC774\uB984\uC744 \uC785\uB825\uD558\uC138\uC694"
|
|
396
|
+
},
|
|
397
|
+
interactive
|
|
398
|
+
);
|
|
399
|
+
if (!componentName) return;
|
|
400
|
+
const { componentCode } = await ask(
|
|
401
|
+
{
|
|
402
|
+
type: "text",
|
|
403
|
+
name: "componentCode",
|
|
404
|
+
message: "\uBCF5\uC0AC\uD55C \uCF54\uB4DC\uB97C \uBD99\uC5EC\uB123\uC73C\uC138\uC694 (\uD55C \uC904\uB85C):"
|
|
405
|
+
},
|
|
406
|
+
interactive
|
|
407
|
+
);
|
|
408
|
+
if (!componentCode) {
|
|
409
|
+
console.log(chalk.yellow("\n\uCF54\uB4DC\uAC00 \uC785\uB825\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const configPath = path2.resolve("axis.config.json");
|
|
413
|
+
let componentsDir = "./src/components/monet";
|
|
414
|
+
if (await fs.pathExists(configPath)) {
|
|
415
|
+
const config = await fs.readJSON(configPath);
|
|
416
|
+
componentsDir = config.monetDir || path2.join(path2.dirname(config.componentsDir), "monet");
|
|
417
|
+
}
|
|
418
|
+
const fileName = `${componentName}.tsx`;
|
|
419
|
+
const filePath = path2.join(componentsDir, fileName);
|
|
420
|
+
await fs.ensureDir(componentsDir);
|
|
421
|
+
await fs.writeFile(filePath, componentCode);
|
|
422
|
+
console.log(chalk.green(`
|
|
423
|
+
\u2713 ${filePath} \uC800\uC7A5 \uC644\uB8CC`));
|
|
424
|
+
console.log(chalk.gray("\n\uB2E4\uC74C \uB2E8\uACC4:"));
|
|
425
|
+
for (const line of monetImportNextSteps(process.cwd())) console.log(chalk.gray(` ${line}`));
|
|
426
|
+
console.log("");
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
function monetImportNextSteps(baseDir) {
|
|
430
|
+
return [
|
|
431
|
+
"1. \uD30C\uC77C\uC744 \uC5F4\uC5B4 import \uACBD\uB85C \uC218\uC815",
|
|
432
|
+
`2. \uD544\uC694\uD55C \uC758\uC874\uC131 \uC124\uCE58 (${addCommandPrefix(detectInstallCommand(baseDir))} ...)`,
|
|
433
|
+
"3. \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9"
|
|
434
|
+
];
|
|
435
|
+
}
|
|
436
|
+
function getMonetUrl(path3) {
|
|
437
|
+
return `https://monet.design${path3}`;
|
|
438
|
+
}
|
|
439
|
+
function getMonetMcpConfig() {
|
|
440
|
+
return {
|
|
441
|
+
monet: {
|
|
442
|
+
url: "https://www.monet.design/api/remote/mcp",
|
|
443
|
+
headers: {
|
|
444
|
+
Authorization: "Bearer ${MONET_API_KEY}"
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export {
|
|
451
|
+
resolveBaseDir,
|
|
452
|
+
detectInstallCommand,
|
|
453
|
+
hasDependency,
|
|
454
|
+
workspaceRootPackage,
|
|
455
|
+
declaredDependency,
|
|
456
|
+
isPlainObject,
|
|
457
|
+
addCommandPrefix,
|
|
458
|
+
addCommandFor,
|
|
459
|
+
runScriptCommand,
|
|
460
|
+
workspaceRunCommand,
|
|
461
|
+
GLOBAL_CSS_CANDIDATES,
|
|
462
|
+
detectProjectPaths,
|
|
463
|
+
projectFileExists,
|
|
464
|
+
ROOT_LAYOUT_FILES,
|
|
465
|
+
findInstalledPackage,
|
|
466
|
+
isPnpProject,
|
|
467
|
+
compareExactVersions,
|
|
468
|
+
MONET_CATEGORIES,
|
|
469
|
+
MonetClient,
|
|
470
|
+
monetImportNextSteps,
|
|
471
|
+
getMonetUrl,
|
|
472
|
+
getMonetMcpConfig
|
|
473
|
+
};
|