Skip to content

Commit d72a1ea

Browse files
committed
feat(ui-kit): improve CLI with new commands and component management, update dependencies
1 parent 385b6ff commit d72a1ea

File tree

14 files changed

+413
-53
lines changed

14 files changed

+413
-53
lines changed

internal/create-skyroc/package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@
2525
"bin": {
2626
"create-skyroc": "./src/bin.js"
2727
},
28-
"files": [
29-
"dist",
30-
"template-*"
31-
],
28+
"files": ["dist", "template-*"],
3229
"scripts": {
3330
"build": "pnpm typecheck && pnpm build-only",
3431
"build-only": "tsdown",

internal/ui-kit/package.json

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,10 @@
44
"version": "0.0.1",
55
"private": true,
66
"description": "UI Primitives Template",
7-
"publishConfig": {
8-
"registry": "https://registry.npmjs.org/",
9-
"main": "./dist/index.js",
10-
"module": "./dist/index.js",
11-
"types": "./dist/index.d.ts",
12-
"exports": {
13-
".": {
14-
"import": {
15-
"types": "./dist/index.d.ts",
16-
"default": "./dist/index.js"
17-
},
18-
"require": {
19-
"types": "./dist/index.d.ts",
20-
"default": "./dist/index.js"
21-
}
22-
}
23-
}
24-
},
257
"sideEffects": false,
8+
"bin": {
9+
"ui-kit": "./src/bin.js"
10+
},
2611
"exports": {
2712
".": "./src/index.ts"
2813
},
@@ -33,26 +18,19 @@
3318
"scripts": {
3419
"build": "tsdown"
3520
},
36-
"peerDependencies": {
37-
"@types/react": "*",
38-
"@types/react-dom": "*",
39-
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
40-
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
41-
},
42-
"peerDependenciesMeta": {
43-
"@types/react": {
44-
"optional": true
45-
},
46-
"@types/react-dom": {
47-
"optional": true
48-
}
21+
"dependencies": {
22+
"commander": "^14.0.1",
23+
"execa": "^9.6.0",
24+
"fs-extra": "^11.3.2",
25+
"pathe": "^2.0.3",
26+
"picocolors": "^1.1.1",
27+
"shadcn": "3.3.1"
4928
},
5029
"devDependencies": {
51-
"@types/react": "19.1.0",
52-
"@types/react-dom": "19.1.1",
53-
"react": "19.1.0",
54-
"react-dom": "19.1.1",
30+
"@types/fs-extra": "^11.0.4",
31+
"@types/node": "^24.6.2",
5532
"tsdown": "0.12.9",
33+
"tsx": "4.20.3",
5634
"typescript": "5.8.2"
5735
}
5836
}

internal/ui-kit/src/bin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { require } from 'tsx/cjs/api';
2+
3+
require('./index.ts', import.meta.url);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {
2+
applyCssVariablesFlag,
3+
defaultComponentsJson,
4+
readComponentsJson,
5+
writeComponentsJson
6+
} from '../core/componentsJson';
7+
import { log } from '../core/logger';
8+
import { runShadcn } from '../core/shadcn';
9+
10+
/**
11+
* soyui add [components...] —— 包装 shadcn add
12+
* 在调用前确保 components.json 存在,并按 flags 修正 cssVariables。
13+
* 其余未知参数全部透传给 shadcn。
14+
*/
15+
export async function cmdAdd(
16+
cwd: string,
17+
components: string[],
18+
opts: {
19+
// 我们识别的 wrapper 级选项(默认 false = 不改变)
20+
cssVariables?: boolean;
21+
noCssVariables?: boolean;
22+
23+
// 透传给 shadcn 的原生选项(-y/-o/-p/...)以及未知项
24+
passthrough: string[];
25+
}
26+
) {
27+
// 1) 确保 components.json 存在
28+
const json = (await readComponentsJson(cwd)) ?? defaultComponentsJson();
29+
30+
// 2) 应用 wrapper 对 cssVariables 的覆盖(仅显式传入时才应用)
31+
applyCssVariablesFlag(json, { cssVariables: opts.cssVariables, noCssVariables: opts.noCssVariables });
32+
33+
// 3) 写回(保证 shadcn 运行时能通过 components.json 读取到)
34+
await writeComponentsJson(cwd, json);
35+
log.ok('已准备 components.json,开始执行 shadcn add ...');
36+
37+
// 4) 组织最终参数:components + 剩余未知参数(全部透传)
38+
const finalArgs = [...components, ...opts.passthrough];
39+
40+
// 5) 调用 shadcn add
41+
await runShadcn('add', finalArgs, { cwd });
42+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {
2+
applyCssVariablesFlag,
3+
deepMerge,
4+
defaultComponentsJson,
5+
readComponentsJson,
6+
writeComponentsJson
7+
} from '../core/componentsJson';
8+
import { log } from '../core/logger';
9+
import type { ComponentsJson } from '../types';
10+
11+
/**
12+
* soyui init
13+
* - 若不存在 components.json:写入默认模板
14+
* - 若存在:合并(保留用户已有设置),可通过 flags 更新 tailwind.cssVariables / baseColor / style / prefix 等
15+
*/
16+
export async function cmdInit(
17+
cwd: string,
18+
flags: {
19+
// --no-css-variables
20+
baseColor?: string;
21+
cssVariables?: boolean; // --css-variables
22+
noCssVariables?: boolean; // --style new-york|...
23+
prefix?: string; // --tsx / --no-tsx
24+
registry?: string; // --prefix tw-
25+
rsc?: boolean; // --base-color neutral|zinc|slate...
26+
style?: string; // --rsc / --no-rsc 用 commander 处理
27+
tsx?: boolean; // --registry 用于覆盖 @sr
28+
}
29+
) {
30+
const current = (await readComponentsJson(cwd)) ?? defaultComponentsJson();
31+
32+
// 合并覆盖
33+
const overrides: Partial<ComponentsJson> = {};
34+
if (flags.style !== undefined) overrides.style = flags.style;
35+
if (flags.rsc !== undefined) overrides.rsc = flags.rsc;
36+
if (flags.tsx !== undefined) overrides.tsx = flags.tsx;
37+
if (flags.baseColor !== undefined || flags.prefix !== undefined) {
38+
overrides.tailwind = {
39+
...(overrides.tailwind || current.tailwind || {}),
40+
...(flags.baseColor ? { baseColor: flags.baseColor } : {}),
41+
...(flags.prefix !== undefined ? { prefix: flags.prefix } : {})
42+
};
43+
}
44+
if (flags.registry) {
45+
overrides.registries = {
46+
...(current.registries || {}),
47+
'@sr': flags.registry
48+
};
49+
}
50+
51+
const next = deepMerge(current, overrides);
52+
applyCssVariablesFlag(next, { cssVariables: flags.cssVariables, noCssVariables: flags.noCssVariables });
53+
54+
await writeComponentsJson(cwd, next);
55+
log.ok('components.json 已更新/创建');
56+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { pathExists, readJSON, writeJSON } from 'fs-extra';
2+
import { join } from 'pathe';
3+
4+
import type { ComponentsJson } from '../types';
5+
6+
export async function readComponentsJson(cwd: string) {
7+
const file = join(cwd, 'components.json');
8+
if (!(await pathExists(file))) return null;
9+
return (await readJSON(file)) as ComponentsJson;
10+
}
11+
12+
export async function writeComponentsJson(cwd: string, data: ComponentsJson) {
13+
const file = join(cwd, 'components.json');
14+
await writeJSON(file, data, { spaces: 2 });
15+
}
16+
17+
export function deepMerge<T extends object>(a: T, b: Partial<T>): T {
18+
const out: any = Array.isArray(a) ? [...a] : { ...a };
19+
for (const [k, v] of Object.entries(b as any)) {
20+
if (v && typeof v === 'object' && !Array.isArray(v)) {
21+
(out as any)[k] = deepMerge((out as any)[k] ?? {}, v);
22+
} else if (v !== undefined) {
23+
(out as any)[k] = v;
24+
}
25+
}
26+
return out;
27+
}
28+
29+
/** 生成你的默认 components.json(按你的需求) */
30+
export function defaultComponentsJson(): ComponentsJson {
31+
return {
32+
$schema: 'https://ui.shadcn.com/schema.json',
33+
aliases: {
34+
components: '@/components',
35+
hooks: '@/hooks',
36+
lib: '@/lib',
37+
ui: '@/components/ui',
38+
utils: '@/lib/utils'
39+
},
40+
iconLibrary: 'lucide',
41+
// 你的私有/自定义注册表
42+
registries: {
43+
'@sr': 'http://localhost:3001/r/{name}.json'
44+
},
45+
rsc: true,
46+
style: 'new-york',
47+
tailwind: {
48+
baseColor: 'neutral',
49+
config: '',
50+
css: 'app/globals.css',
51+
cssVariables: true,
52+
prefix: ''
53+
},
54+
tsx: true
55+
};
56+
}
57+
58+
/**
59+
* 规范化:根据 wrapper 传入的 --css-variables/--no-css-variables,落到 tailwind.cssVariables
60+
* 注意:wrapper 默认二者都 false(即不改变已有配置),只有显式传入才改。
61+
*/
62+
export function applyCssVariablesFlag(
63+
json: ComponentsJson,
64+
flags: { cssVariables?: boolean; noCssVariables?: boolean }
65+
) {
66+
if (flags.cssVariables) {
67+
json.tailwind ??= {};
68+
json.tailwind.cssVariables = true;
69+
} else if (flags.noCssVariables) {
70+
json.tailwind ??= {};
71+
json.tailwind.cssVariables = false;
72+
}
73+
}

internal/ui-kit/src/core/logger.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import pc from 'picocolors';
2+
3+
export const log = {
4+
err: (msg: string) => console.error(pc.red('err ') + msg),
5+
info: (msg: string) => console.log(pc.cyan('info ') + msg),
6+
ok: (msg: string) => console.log(pc.green('ok ') + msg),
7+
warn: (msg: string) => console.log(pc.yellow('warn ') + msg)
8+
};

internal/ui-kit/src/core/shadcn.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { execSync } from 'node:child_process';
2+
3+
/**
4+
* 调用 pnpm dlx shadcn@latest <subcommand> ...args
5+
* 透明透传所有 shadcn 的原生命令与参数。
6+
*/
7+
export async function runShadcn(subcommand: string, args: string[], opts?: { cwd?: string }) {
8+
const finalArgs = ['dlx', 'shadcn@latest', subcommand, ...args];
9+
10+
// 在 Windows / Unix 都兼容
11+
const child = execSync(`pnpm ${finalArgs.join(' ')}`, {
12+
cwd: opts?.cwd,
13+
stdio: 'inherit'
14+
});
15+
return child;
16+
}

0 commit comments

Comments
 (0)