sh-ui-cli 0.64.7 → 0.65.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/data/changelog/versions.json +13 -0
- package/package.json +1 -1
- package/src/add.mjs +13 -0
- package/src/create/generator.js +41 -7
- package/templates/monorepo/packages/ui/ui-core/package.json +13 -1
- package/templates/monorepo/packages/ui/ui-core/sh-ui.config.json +15 -0
- package/templates/ui-app-template/package.json +2 -11
- package/templates/ui-app-template/sh-ui.config.json +2 -8
- package/templates/ui-app-template/src/lib/.gitkeep +0 -0
- /package/templates/{ui-app-template → monorepo/packages/ui/ui-core}/src/components/.gitkeep +0 -0
- /package/templates/{ui-app-template → monorepo/packages/ui/ui-core}/src/hooks/.gitkeep +0 -0
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$description": "sh-ui 릴리즈 노트 단일 소스. docs(React)와 showcase(Flutter)가 함께 읽는다. 새 릴리즈마다 맨 앞에 추가.",
|
|
4
4
|
"versions": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.65.0",
|
|
7
|
+
"date": "2026-05-09",
|
|
8
|
+
"title": "monorepo 컴포넌트 단일 SoT — ui-core 도입, ui-app 은 tokens-only",
|
|
9
|
+
"type": "minor",
|
|
10
|
+
"highlights": [
|
|
11
|
+
"**컴포넌트 중복 emit 제거** — monorepo 에서 `sh-ui add <component>` 가 `packages/ui/ui-core` 한 곳에만 컴포넌트/훅을 떨어뜨림. 이전엔 ui-{app} 마다 같은 파일이 복제돼 sync 비용이 컸다.",
|
|
12
|
+
"**역할 분리** — `ui-core` 가 컴포넌트/훅/lib SoT, `ui-{app}` 은 토큰/스타일 전용 (`role: \"tokens-only\"` 마커). `sh-ui add tokens` 만 ui-app 으로 라우팅, 그 외는 모두 ui-core.",
|
|
13
|
+
"**친절한 거부 메시지** — 사용자가 ui-app 에서 직접 `sh-ui add button` 을 실행하면 \"ui-core 에 추가하세요\" 안내 후 종료. 잘못된 위치에 컴포넌트 누적 차단.",
|
|
14
|
+
"**v0.64.x 호환 fallback** — 기존 monorepo 레이아웃(ui-core 부재)에서는 자동으로 ui-apps 직접 라우팅으로 폴백. 마이그레이션 자동 도구는 후속 v0.66.0 (PR-D)."
|
|
15
|
+
],
|
|
16
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.65.0"
|
|
17
|
+
},
|
|
5
18
|
{
|
|
6
19
|
"version": "0.64.7",
|
|
7
20
|
"date": "2026-05-08",
|
package/package.json
CHANGED
package/src/add.mjs
CHANGED
|
@@ -363,6 +363,19 @@ export async function add({
|
|
|
363
363
|
);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
// role: "tokens-only" 패키지 (v0.65+ ui-app) 는 tokens 만 허용.
|
|
367
|
+
// 컴포넌트는 sibling ui-core 패키지로 라우팅되도록 친절한 에러로 안내.
|
|
368
|
+
if (config.role === "tokens-only") {
|
|
369
|
+
const offending = names.filter((n) => n !== "tokens");
|
|
370
|
+
if (offending.length > 0) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`이 패키지는 'tokens-only' role 입니다 — ${offending.map((n) => `'${n}'`).join(', ')} 컴포넌트를 추가할 수 없습니다.\n` +
|
|
373
|
+
`컴포넌트는 sibling ui-core 패키지에 추가하세요 (예: cd ../ui-core && sh-ui add ${offending[0]}).\n` +
|
|
374
|
+
`또는 monorepo 루트에서 \`sh-ui add ${offending[0]}\` 실행 시 자동으로 ui-core 로 라우팅됩니다.`,
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
366
379
|
// 비대화형(non-TTY)이면 prompt 를 못 띄우니 안전하게 keep 으로 강등.
|
|
367
380
|
const effectiveStrategy =
|
|
368
381
|
onConflict === "prompt" && !process.stdin.isTTY ? "keep" : onConflict;
|
package/src/create/generator.js
CHANGED
|
@@ -401,9 +401,45 @@ export async function addComponent(componentName, appName) {
|
|
|
401
401
|
return;
|
|
402
402
|
}
|
|
403
403
|
|
|
404
|
-
// Monorepo: packages/ui/ui-apps
|
|
404
|
+
// Monorepo (v0.65+): tokens 는 packages/ui/ui-apps/ui-{app} 으로, 그 외 컴포넌트/훅은
|
|
405
|
+
// packages/ui/ui-core 단일 SoT 로 라우팅. 컴포넌트 중복 emit 제거가 v0.65 의 핵심.
|
|
406
|
+
// v0.64.x 호환: ui-core/sh-ui.config.json 미존재 시 (구 모노레포 레이아웃) 기존 ui-apps
|
|
407
|
+
// 직접 라우팅 분기로 내려감.
|
|
408
|
+
const uiCoreDir = path.join(cwd, 'packages', 'ui', 'ui-core');
|
|
405
409
|
const uiAppsDir = path.join(cwd, 'packages', 'ui', 'ui-apps');
|
|
406
|
-
|
|
410
|
+
const hasUiCore = await fs.pathExists(path.join(uiCoreDir, 'sh-ui.config.json'));
|
|
411
|
+
const hasUiApps = await fs.pathExists(uiAppsDir);
|
|
412
|
+
|
|
413
|
+
if (!hasUiCore && !hasUiApps) {
|
|
414
|
+
console.log('❌ packages/ui/ui-core 또는 packages/ui/ui-apps/ 디렉토리가 없습니다.');
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (!componentName) {
|
|
419
|
+
componentName = await input({ message: '컴포넌트 이름:' });
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const isTokens = componentName === 'tokens';
|
|
423
|
+
|
|
424
|
+
// ─── 비-tokens (컴포넌트/훅/lib) → ui-core 단일 라우팅 ───
|
|
425
|
+
if (!isTokens && hasUiCore) {
|
|
426
|
+
if (appName) {
|
|
427
|
+
console.log(
|
|
428
|
+
`ℹ️ v0.65+ 에서 컴포넌트는 packages/ui/ui-core 에 단일 emit 됩니다 (--app ${appName} 무시).`,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
console.log(`\n📦 packages/ui/ui-core 에 ${componentName} 추가 중...`);
|
|
432
|
+
try {
|
|
433
|
+
execSync(`npx sh-ui add ${componentName}`, { cwd: uiCoreDir, stdio: 'inherit' });
|
|
434
|
+
console.log(`✅ packages/ui/ui-core 완료`);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.log(`❌ packages/ui/ui-core 실패: ${error.message}`);
|
|
437
|
+
}
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ─── tokens → ui-apps/ui-{app}(s), 또는 v0.64.x 호환 fallback (ui-core 없음) ───
|
|
442
|
+
if (!hasUiApps) {
|
|
407
443
|
console.log('❌ packages/ui/ui-apps/ 디렉토리가 없습니다.');
|
|
408
444
|
return;
|
|
409
445
|
}
|
|
@@ -418,10 +454,6 @@ export async function addComponent(componentName, appName) {
|
|
|
418
454
|
return;
|
|
419
455
|
}
|
|
420
456
|
|
|
421
|
-
if (!componentName) {
|
|
422
|
-
componentName = await input({ message: '컴포넌트 이름:' });
|
|
423
|
-
}
|
|
424
|
-
|
|
425
457
|
let targets;
|
|
426
458
|
if (appName) {
|
|
427
459
|
const pkgName = `ui-${appName}`;
|
|
@@ -431,9 +463,11 @@ export async function addComponent(componentName, appName) {
|
|
|
431
463
|
return;
|
|
432
464
|
}
|
|
433
465
|
targets = [pkgName];
|
|
466
|
+
} else if (uiPackages.length === 1) {
|
|
467
|
+
targets = uiPackages;
|
|
434
468
|
} else {
|
|
435
469
|
const choice = await select({
|
|
436
|
-
message: '어디에 추가할까요?',
|
|
470
|
+
message: isTokens ? '어느 ui 패키지에 토큰을 추가할까요?' : '어디에 추가할까요?',
|
|
437
471
|
choices: [
|
|
438
472
|
{ name: '모든 ui 패키지', value: 'all' },
|
|
439
473
|
...uiPackages.map((name) => ({ name: `packages/ui/ui-apps/${name}`, value: name })),
|
|
@@ -7,17 +7,29 @@
|
|
|
7
7
|
"lint": "eslint . --max-warnings 0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
+
"@base-ui/react": "^1.4.1",
|
|
10
11
|
"class-variance-authority": "^0.7.1",
|
|
11
12
|
"clsx": "^2.1.1",
|
|
12
|
-
"
|
|
13
|
+
"lucide-react": "^0.563.0",
|
|
14
|
+
"next-themes": "^0.4.6",
|
|
15
|
+
"react": "^19.2.4",
|
|
16
|
+
"react-dom": "^19.2.4",
|
|
17
|
+
"react-hook-form": "^7.56.4",
|
|
18
|
+
"sonner": "^2.0.7",
|
|
19
|
+
"tailwind-merge": "^3.5.0",
|
|
20
|
+
"zod": "^4.3.6"
|
|
13
21
|
},
|
|
14
22
|
"devDependencies": {
|
|
23
|
+
"@types/react": "^19.2.10",
|
|
24
|
+
"@types/react-dom": "^19.2.3",
|
|
15
25
|
"@workspace/eslint-config": "workspace:*",
|
|
16
26
|
"@workspace/typescript-config": "workspace:*",
|
|
17
27
|
"eslint": "^9.39.2",
|
|
18
28
|
"typescript": "^5.9.3"
|
|
19
29
|
},
|
|
20
30
|
"exports": {
|
|
31
|
+
"./components/*": "./src/components/*.tsx",
|
|
32
|
+
"./hooks/*": "./src/hooks/*.ts",
|
|
21
33
|
"./lib/*": "./src/lib/*.ts"
|
|
22
34
|
}
|
|
23
35
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"platform": "react",
|
|
3
|
+
"cssFramework": "plain",
|
|
4
|
+
"paths": {
|
|
5
|
+
"components": "src/components",
|
|
6
|
+
"hooks": "src/hooks",
|
|
7
|
+
"utils": "src/lib/utils.ts"
|
|
8
|
+
},
|
|
9
|
+
"aliases": {
|
|
10
|
+
"components": "@workspace/ui-core/components",
|
|
11
|
+
"hooks": "@workspace/ui-core/hooks",
|
|
12
|
+
"utils": "@workspace/ui-core/lib/utils",
|
|
13
|
+
"ui": "@workspace/ui-core/components"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -7,15 +7,9 @@
|
|
|
7
7
|
"lint": "eslint . --max-warnings 0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@base-ui/react": "^1.4.1",
|
|
11
10
|
"@workspace/ui-core": "workspace:*",
|
|
12
|
-
"lucide-react": "^0.563.0",
|
|
13
|
-
"next-themes": "^0.4.6",
|
|
14
11
|
"react": "^19.2.4",
|
|
15
|
-
"react-dom": "^19.2.4"
|
|
16
|
-
"react-hook-form": "^7.56.4",
|
|
17
|
-
"sonner": "^2.0.7",
|
|
18
|
-
"zod": "^4.3.6"
|
|
12
|
+
"react-dom": "^19.2.4"
|
|
19
13
|
},
|
|
20
14
|
"devDependencies": {
|
|
21
15
|
"@tailwindcss/postcss": "^4.1.18",
|
|
@@ -30,9 +24,6 @@
|
|
|
30
24
|
},
|
|
31
25
|
"exports": {
|
|
32
26
|
"./globals.css": "./src/styles/globals.css",
|
|
33
|
-
"./postcss.config": "./postcss.config.mjs"
|
|
34
|
-
"./lib/*": "./src/lib/*.ts",
|
|
35
|
-
"./components/*": "./src/components/*.tsx",
|
|
36
|
-
"./hooks/*": "./src/hooks/*.ts"
|
|
27
|
+
"./postcss.config": "./postcss.config.mjs"
|
|
37
28
|
}
|
|
38
29
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"platform": "react",
|
|
3
3
|
"cssFramework": "plain",
|
|
4
|
+
"role": "tokens-only",
|
|
4
5
|
"theme": {
|
|
5
6
|
"base": "neutral",
|
|
6
7
|
"radius": "md",
|
|
@@ -8,13 +9,6 @@
|
|
|
8
9
|
},
|
|
9
10
|
"paths": {
|
|
10
11
|
"tokens": "src/styles/tokens.css",
|
|
11
|
-
"styles": "src/styles"
|
|
12
|
-
"components": "src/components",
|
|
13
|
-
"utils": "src/lib/utils.ts"
|
|
14
|
-
},
|
|
15
|
-
"aliases": {
|
|
16
|
-
"components": "@workspace/ui-app-name/components",
|
|
17
|
-
"utils": "@workspace/ui-app-name/lib/utils",
|
|
18
|
-
"ui": "@workspace/ui-app-name/components"
|
|
12
|
+
"styles": "src/styles"
|
|
19
13
|
}
|
|
20
14
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|