sh-ui-cli 0.89.1 → 0.90.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.
@@ -2,6 +2,20 @@
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.90.0",
7
+ "date": "2026-05-14",
8
+ "title": "vite + monorepo + tauri 지원 — Tauri 셸이 apps/{name}/src-tauri/ 안",
9
+ "type": "minor",
10
+ "highlights": [
11
+ "**`--platform vite --structure monorepo --tauri` 동작** — v0.88.0 에서 의도적으로 defer 했던 조합 풀림. Tauri 데스크탑 셸이 모노레포 안의 첫 vite 앱 디렉토리 (`apps/{appName}/src-tauri/`) 에 emit. `frontendDist: \"../dist\"` 관계가 그대로 유지되고, Tauri 의 `pnpm dev` / `pnpm build` beforeCommand 도 app 컨텍스트에서 정상 동작.",
12
+ "**Tauri devUrl 이 app port 와 자동 일치** — 모노레포 첫 app port (default 3000) 가 `tauri.conf.json` 의 `devUrl: http://localhost:{port}` + `vite.config.ts` 의 `server.port` 양쪽에 박힘. standalone (5173) 도 그대로 (placeholder 시스템으로 backward-compat). `{{tauri_dev_url}}` 플레이스홀더 신설, `emitTauri({ devPort })` + `patchViteForTauri({ port })` 옵션 시그니처.",
13
+ "**MCP + CLI 진입점에서 거부 가드 제거** — `mcp.mjs` 와 `generator.js` 의 `tauri + structure === 'monorepo'` throw 삭제. `sh_ui_create_project` 의 description 도 `Vite (standalone/monorepo)` 로 갱신. `tauri + platform !== 'vite'` 가드는 그대로 유지 (next/flutter 는 Tauri 통합 안 함).",
14
+ "**docs `/create` UI 도 풀림** — ProjectOptionsForm 의 `disabled={structure !== 'standalone'}` 제거 + composer 의 `--tauri` 가 monorepo 에서도 emit. `/api/template-content` 가 `apps/{name}/src-tauri/*` 경로를 tauri-shell 템플릿에서 resolve. describeTemplate 의 vite-monorepo 분기가 `tauri=true` 일 때 `apps/{appName}/src-tauri/` 그룹 emit.",
15
+ "**Smoke V12 회귀 가드** — vite + monorepo + tauri scaffold 가 (1) apps/web/src-tauri/ 8 파일 emit, (2) tauri.conf.json devUrl = http://localhost:3000, (3) vite.config.ts port = 3000, (4) Cargo.toml crate name = web/web_lib, (5) 루트엔 src-tauri/ 없음 — 모두 단언. v0.88.0 의 V7d (rejection guard) 는 제거 (조합 풀려 더 이상 거부 안 함)."
16
+ ],
17
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.90.0"
18
+ },
5
19
  {
6
20
  "version": "0.89.1",
7
21
  "date": "2026-05-14",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-ui-cli",
3
- "version": "0.89.1",
3
+ "version": "0.90.0",
4
4
  "description": "sh-ui CLI — 프로젝트 스캐폴드(create) + 컴포넌트 추가(add/list/remove) + IDE-내 AI용 MCP 서버",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -35,7 +35,7 @@ import { CSS_FRAMEWORK_DEFAULT } from '../constants.js';
35
35
  * @property {'tailwind'|'plain'|'css-modules'} [cssFramework]
36
36
  * @property {string} [projectName]
37
37
  * @property {string} [appName] monorepo 첫 앱 이름. 기본 'web'
38
- * @property {boolean} [tauri] platform=vite + structure=standalone 일 때 Tauri 2.x 셸 같이 emit
38
+ * @property {boolean} [tauri] platform=vite (standalone/monorepo 둘 다) 일 때 Tauri 2.x 셸 같이 emit
39
39
  */
40
40
 
41
41
  /**
@@ -140,6 +140,15 @@ export function describeTemplate(opts = {}) {
140
140
  ),
141
141
  ));
142
142
 
143
+ if (tauri) {
144
+ const tauriTpl = TEMPLATE_MANIFEST['tauri-shell'];
145
+ if (!tauriTpl) {
146
+ throw new Error("Template manifest missing entry for 'tauri-shell'.");
147
+ }
148
+ const tauriFiles = tauriTpl.base.map((p) => `apps/${appName}/src-tauri/${p}`);
149
+ groups.push(makeGroup('tauri', `Tauri 셸 (apps/${appName}/src-tauri/)`, tauriFiles));
150
+ }
151
+
143
152
  return finalize(groups);
144
153
  }
145
154
 
@@ -222,12 +222,6 @@ export async function createProject(options = {}) {
222
222
  `--tauri 옵션 제거 또는 --platform vite 사용.`,
223
223
  );
224
224
  }
225
- if (options.structure === 'monorepo') {
226
- throw new Error(
227
- 'platform=vite + structure=monorepo + tauri=true 는 아직 지원 안 함 (v0.89 후속). ' +
228
- 'standalone 으로 시도하거나 tauri 옵션 제거.',
229
- );
230
- }
231
225
  }
232
226
 
233
227
  // arch 결정 — platform 확정 후. 사용자가 --arch 미지정 시:
@@ -352,14 +346,7 @@ export async function createProject(options = {}) {
352
346
  if (projectType === 'standalone') {
353
347
  await generateViteStandalone(targetDir, projectName, theme, cssFramework, arch, themeBase, { tauri: !!options.tauri });
354
348
  } else {
355
- // monorepo + tauri v0.89 후속 명시적 에러
356
- if (options.tauri) {
357
- throw new Error(
358
- 'platform=vite + structure=monorepo + tauri=true 는 아직 지원 안 함 (v0.89 후속). ' +
359
- 'standalone 으로 시도하거나 tauri 옵션 제거.',
360
- );
361
- }
362
- await generateMonorepo(targetDir, projectName, [], { yes: options.yes, theme, css: cssFramework, arch, themeBase, platform: 'vite' });
349
+ await generateMonorepo(targetDir, projectName, [], { yes: options.yes, theme, css: cssFramework, arch, themeBase, platform: 'vite', tauri: options.tauri });
363
350
  }
364
351
 
365
352
  await finalizeProject(targetDir, { dryRun: options.dryRun });
@@ -377,8 +364,11 @@ export async function createProject(options = {}) {
377
364
  console.log(`\n cd ${projectName}`);
378
365
  console.log(' pnpm install');
379
366
  console.log(' pnpm dev\n');
380
- if (options.tauri && projectType === 'standalone') {
367
+ if (options.tauri) {
381
368
  console.log('Tauri 데스크탑 셸:');
369
+ if (projectType === 'monorepo') {
370
+ console.log(' cd apps/web # 또는 첫 앱 이름');
371
+ }
382
372
  console.log(' pnpm tauri dev # Rust 처음 빌드는 5~10분 — 캐시 후 5~10초');
383
373
  console.log(' (Rust 미설치 시 https://rustup.rs/ 참고)\n');
384
374
  }
@@ -798,8 +788,8 @@ async function generateViteStandalone(targetDir, projectName, theme, css, arch,
798
788
 
799
789
  // Tauri 셸 emit (옵션) — vite SPA + native window. standalone 만 v1 지원.
800
790
  if (tauri) {
801
- await emitTauri(targetDir, projectName);
802
- await patchViteForTauri(targetDir);
791
+ await emitTauri(targetDir, projectName, { devPort: 5173 });
792
+ await patchViteForTauri(targetDir, { port: 5173 });
803
793
  }
804
794
  }
805
795
 
@@ -812,7 +802,7 @@ async function generateViteStandalone(targetDir, projectName, theme, css, arch,
812
802
  *
813
803
  * generateViteStandalone 에서 tauri: true 인 경우 호출. monorepo+tauri 는 v0.89 후속.
814
804
  */
815
- async function emitTauri(targetDir, projectName) {
805
+ async function emitTauri(targetDir, projectName, { devPort = 5173 } = {}) {
816
806
  const srcTauriDir = path.join(targetDir, 'src-tauri');
817
807
  await fs.copy(path.join(TEMPLATES_DIR, 'tauri-shell'), srcTauriDir);
818
808
 
@@ -824,6 +814,7 @@ async function emitTauri(targetDir, projectName) {
824
814
 
825
815
  await replaceInAllFiles(srcTauriDir, '{{tauri_crate_name}}', tauriCrateName);
826
816
  await replaceInAllFiles(srcTauriDir, '{{project_name}}', projectName);
817
+ await replaceInAllFiles(srcTauriDir, '{{tauri_dev_url}}', `http://localhost:${devPort}`);
827
818
  }
828
819
 
829
820
  /**
@@ -837,7 +828,7 @@ async function emitTauri(targetDir, projectName) {
837
828
  * 이라 안전. 후속 task 에서 arch-specific vite.config.ts overlay 가 생기면 이 자리에서 머지 전략
838
829
  * 필요 (현재는 단순 overwrite).
839
830
  */
840
- async function patchViteForTauri(targetDir) {
831
+ async function patchViteForTauri(targetDir, { port = 5173 } = {}) {
841
832
  const pkgPath = path.join(targetDir, 'package.json');
842
833
  const pkg = await fs.readJson(pkgPath);
843
834
 
@@ -872,7 +863,7 @@ export default defineConfig({
872
863
  plugins: [react(), tailwindcss(), tsconfigPaths()],
873
864
  clearScreen: false,
874
865
  server: {
875
- port: 5173,
866
+ port: ${port},
876
867
  strictPort: true,
877
868
  host: false,
878
869
  },
@@ -897,7 +888,7 @@ export default defineConfig({
897
888
  }
898
889
  }
899
890
 
900
- async function generateMonorepo(targetDir, projectName, plugins, { yes = false, theme, css, arch, themeBase, platform = 'next' } = {}) {
891
+ async function generateMonorepo(targetDir, projectName, plugins, { yes = false, theme, css, arch, themeBase, platform = 'next', tauri = false } = {}) {
901
892
  await fs.copy(path.join(TEMPLATES_DIR, 'monorepo'), targetDir);
902
893
 
903
894
  // Update root package.json
@@ -929,7 +920,7 @@ async function generateMonorepo(targetDir, projectName, plugins, { yes = false,
929
920
 
930
921
  const appsDir = path.join(targetDir, 'apps', appName);
931
922
  if (platform === 'vite') {
932
- await generateViteApp(appsDir, appName, port, arch, css);
923
+ await generateViteApp(appsDir, appName, port, arch, css, { tauri });
933
924
  } else {
934
925
  await generateApp(appsDir, appName, port, plugins, arch, css);
935
926
  }
@@ -1030,7 +1021,7 @@ async function generateApp(targetDir, appName, port, plugins, arch, css = 'tailw
1030
1021
  }
1031
1022
  }
1032
1023
 
1033
- async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind') {
1024
+ async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind', { tauri = false } = {}) {
1034
1025
  // 베이스 (arch-neutral) + arch 오버레이 — generateApp 과 동일 패턴.
1035
1026
  await fs.copy(path.join(TEMPLATES_DIR, 'vite-app'), targetDir, {
1036
1027
  filter: (src) => !src.includes(`${path.sep}_arch${path.sep}`) && !src.endsWith(`${path.sep}_arch`),
@@ -1089,6 +1080,15 @@ async function generateViteApp(targetDir, appName, port, arch, css = 'tailwind')
1089
1080
  if (await fs.pathExists(uiPkgDir)) {
1090
1081
  await applyCssFrameworkVariant(uiPkgDir, css, { isMonorepo: true, plugins: [], arch, isUiPackage: true });
1091
1082
  }
1083
+
1084
+ // tauri 가 켜져 있으면 이 app 안에 src-tauri/ shell 을 떨어뜨린다 (v0.90.0+).
1085
+ // standalone 과 달리 monorepo 에서는 app 단위로 별도 dev port (default 3000) 가 있고,
1086
+ // tauri 의 devUrl 이 그 port 와 일치해야 한다. frontendDist 는 src-tauri 기준 `../dist`.
1087
+ if (tauri) {
1088
+ const devPort = Number(port) || 3000;
1089
+ await emitTauri(targetDir, appName, { devPort });
1090
+ await patchViteForTauri(targetDir, { port: devPort });
1091
+ }
1092
1092
  }
1093
1093
 
1094
1094
  /**
package/src/mcp.mjs CHANGED
@@ -367,7 +367,7 @@ export async function startMcpServer() {
367
367
  {
368
368
  description:
369
369
  "빈 폴더에 sh-ui 프로젝트 스캐폴드 — Next.js (standalone/monorepo) | Vite (standalone/monorepo) | Flutter. " +
370
- "+ vite + standalone 인 경우 `tauri: true` 로 Tauri 2.x 데스크탑 셸 (`src-tauri/`) 까지 한 번에 emit (Rust toolchain 필요). " +
370
+ "+ vite 인 경우 `tauri: true` 로 Tauri 2.x 데스크탑 셸 (`src-tauri/`) 까지 한 번에 emit (Rust toolchain 필요). " +
371
371
  `FSD 폴더 구조 + 토큰 + sh-ui.config.json 일괄 생성. 사용자가 '새 프로젝트' / '빈 폴더' / '스캐폴드부터' 류 요청을 하면 이 툴 사용 (Bash 로 npx ${cliName} create 직접 호출보다 우선). ` +
372
372
  "**단일 진입점** — theme/plugins/cssFramework/structure 모두 호출 시점에 정해서 한 번에 박는다. 호출 후 sh-ui.config.json/tokens.css 를 손으로 패치하지 말 것 (다음 재스캐폴드 시 유실). " +
373
373
  "산출물: theme 인자가 프리셋이면 sh-ui.config.json 의 theme.base 가 그 이름, base64 면 'custom'. paths.styles · paths.tokens 도 자동 박혀서 sh_ui_add_component 가 사후 패치 없이 동작.",
@@ -397,9 +397,10 @@ export async function startMcpServer() {
397
397
  .describe("기존 디렉토리 덮어쓰기. 기본 false (안전)"),
398
398
  tauri: z.boolean().optional()
399
399
  .describe(
400
- "Tauri 2.x 데스크탑 셸 (`src-tauri/`) 함께 emit. platform=vite + structure=standalone 일 때만 지원. " +
400
+ "Tauri 2.x 데스크탑 셸 (`src-tauri/`) 함께 emit. platform=vite (standalone/monorepo 둘 다) 일 때만 지원. " +
401
+ "monorepo 의 경우 src-tauri/ 는 apps/{appName}/ 안에 emit, devUrl 은 해당 app 의 dev port 와 자동 동기화. " +
401
402
  "Rust toolchain (`cargo`/`rustc`) 가 시스템에 설치되어 있어야 첫 `pnpm tauri dev` 가 동작. " +
402
- "기본 false. monorepo + tauri 는 v0.89 후속.",
403
+ "기본 false.",
403
404
  ),
404
405
  },
405
406
  },
@@ -431,15 +432,6 @@ export async function startMcpServer() {
431
432
  }],
432
433
  };
433
434
  }
434
- if (input.tauri && input.structure === "monorepo") {
435
- return {
436
- isError: true,
437
- content: [{
438
- type: "text",
439
- text: "platform=vite + structure=monorepo + tauri=true 는 아직 지원 안 함 (v0.89 후속). standalone 으로 시도하거나 tauri 옵션 제거.",
440
- }],
441
- };
442
- }
443
435
  const targetParent = resolveCwd(input);
444
436
  const targetDir = resolve(targetParent, input.name);
445
437
  if (existsSync(targetDir) && !input.force) {
@@ -5,7 +5,7 @@
5
5
  "identifier": "app.{{tauri_crate_name}}.dev",
6
6
  "build": {
7
7
  "beforeDevCommand": "pnpm dev",
8
- "devUrl": "http://localhost:5173",
8
+ "devUrl": "{{tauri_dev_url}}",
9
9
  "beforeBuildCommand": "pnpm build",
10
10
  "frontendDist": "../dist"
11
11
  },