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
|
@@ -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
|
|
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
|
|
package/src/create/generator.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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.
|
|
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) {
|