create-secra 0.1.4 → 0.1.7

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.
Files changed (64) hide show
  1. package/README.md +6 -0
  2. package/README.zh-CN.md +6 -0
  3. package/antd-adapter-template/apps/core/index.html +13 -0
  4. package/antd-adapter-template/apps/core/package.json +18 -0
  5. package/antd-adapter-template/apps/core/public/favicon.ico +1 -0
  6. package/antd-adapter-template/apps/core/public/favicon.svg +1 -0
  7. package/antd-adapter-template/apps/core/public/logo.svg +1 -0
  8. package/antd-adapter-template/apps/core/src/api/auth.ts +49 -0
  9. package/antd-adapter-template/apps/core/src/assets/react.svg +1 -0
  10. package/antd-adapter-template/apps/core/src/components/AntdGlobalProvider.tsx +87 -0
  11. package/antd-adapter-template/apps/core/src/components/AntdRootLayout.tsx +10 -0
  12. package/antd-adapter-template/apps/core/src/components/layout.tsx +387 -0
  13. package/antd-adapter-template/apps/core/src/guards/auth-route-guard.ts +45 -0
  14. package/antd-adapter-template/apps/core/src/main.tsx +65 -0
  15. package/antd-adapter-template/apps/core/src/pages/auth/components/account-login-fields.tsx +60 -0
  16. package/antd-adapter-template/apps/core/src/pages/auth/components/phone-login-fields.tsx +60 -0
  17. package/antd-adapter-template/apps/core/src/pages/auth/login.tsx +169 -0
  18. package/antd-adapter-template/apps/core/src/pages/index.tsx +156 -0
  19. package/antd-adapter-template/apps/core/src/router.ts +42 -0
  20. package/antd-adapter-template/apps/core/src/shims/use-sync-external-store-shim.ts +3 -0
  21. package/antd-adapter-template/apps/core/src/theme/theme.css +48 -0
  22. package/antd-adapter-template/apps/core/src/types/crypto-js.d.ts +5 -0
  23. package/antd-adapter-template/apps/core/src/utils/index.ts +12 -0
  24. package/antd-adapter-template/apps/core/src/utils/md5.ts +6 -0
  25. package/antd-adapter-template/apps/core/tsconfig.app.json +11 -0
  26. package/antd-adapter-template/apps/core/tsconfig.json +13 -0
  27. package/antd-adapter-template/apps/core/tsconfig.node.json +7 -0
  28. package/antd-adapter-template/apps/core/vite.config.ts +118 -0
  29. package/antd-adapter-template/eslint.config.js +23 -0
  30. package/antd-adapter-template/package.json +63 -0
  31. package/antd-adapter-template/packages/sdk/.swcrc +18 -0
  32. package/antd-adapter-template/packages/sdk/package.json +52 -0
  33. package/antd-adapter-template/packages/sdk/src/build/index.ts +28 -0
  34. package/antd-adapter-template/packages/sdk/src/build/plugins/auto-import.ts +46 -0
  35. package/antd-adapter-template/packages/sdk/src/build/plugins/bundle-analyzer.ts +33 -0
  36. package/antd-adapter-template/packages/sdk/src/build/plugins/remove-console.ts +23 -0
  37. package/antd-adapter-template/packages/sdk/src/build/plugins/unocss.ts +202 -0
  38. package/antd-adapter-template/packages/sdk/src/build/plugins/unplugin-icon.ts +43 -0
  39. package/antd-adapter-template/packages/sdk/src/components/i18n-switch-dropdown.tsx +139 -0
  40. package/antd-adapter-template/packages/sdk/src/components/index.ts +2 -0
  41. package/antd-adapter-template/packages/sdk/src/components/theme-switch-dropdown.tsx +131 -0
  42. package/antd-adapter-template/packages/sdk/src/hooks/auth/core.ts +101 -0
  43. package/antd-adapter-template/packages/sdk/src/hooks/auth/index.ts +139 -0
  44. package/antd-adapter-template/packages/sdk/src/hooks/auth/with-auth.tsx +41 -0
  45. package/antd-adapter-template/packages/sdk/src/hooks/index.ts +1 -0
  46. package/antd-adapter-template/packages/sdk/src/i18n/index.ts +150 -0
  47. package/antd-adapter-template/packages/sdk/src/index.ts +11 -0
  48. package/antd-adapter-template/packages/sdk/src/request/index.ts +436 -0
  49. package/antd-adapter-template/packages/sdk/src/storage/README.md +30 -0
  50. package/antd-adapter-template/packages/sdk/src/storage/index.ts +57 -0
  51. package/antd-adapter-template/packages/sdk/src/styles/reset.css +111 -0
  52. package/antd-adapter-template/packages/sdk/src/theme/index.ts +466 -0
  53. package/antd-adapter-template/packages/sdk/tsconfig.json +16 -0
  54. package/antd-adapter-template/pnpm-workspace.yaml +3 -0
  55. package/antd-adapter-template/tsconfig.app.json +29 -0
  56. package/antd-adapter-template/tsconfig.json +7 -0
  57. package/antd-adapter-template/tsconfig.node.json +27 -0
  58. package/antd-adapter-template/turbo.json +17 -0
  59. package/bin/index.mjs +165 -33
  60. package/package.json +6 -2
  61. package/template/apps/core/src/main.tsx +11 -18
  62. package/template/apps/core/src/router.ts +5 -1
  63. package/template/package.json +1 -1
  64. package/template/packages/sdk/src/build/plugins/unocss.ts +3 -0
package/bin/index.mjs CHANGED
@@ -1,13 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { spawn } from "node:child_process";
4
3
  import { cpSync, existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { emitKeypressEvents } from "node:readline";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import path from "node:path";
7
7
  import process from "node:process";
8
8
 
9
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
10
  const DEFAULT_TEMPLATE = path.resolve(__dirname, "../template");
11
+ const ANT_DESIGN_ADAPTER_TEMPLATE = path.resolve(__dirname, "../antd-adapter-template");
12
+ const TEMPLATE_OPTIONS = [
13
+ {
14
+ key: "1",
15
+ label: "最小可用版本",
16
+ templatePath: DEFAULT_TEMPLATE,
17
+ templateName: "template",
18
+ },
19
+ {
20
+ key: "2",
21
+ label: "antd UI 适配器版本",
22
+ templatePath: ANT_DESIGN_ADAPTER_TEMPLATE,
23
+ templateName: "antd-adapter-template",
24
+ },
25
+ ];
11
26
  const EXCLUDED_DIRS = new Set(["node_modules", "dist", ".turbo", ".git"]);
12
27
  const targetArg = process.argv[2];
13
28
  const targetDir = targetArg && targetArg.trim() ? targetArg.trim() : "secra-app";
@@ -42,41 +57,163 @@ function updatePackageName(cwd, name) {
42
57
  }
43
58
  }
44
59
 
45
- function detectPackageManager() {
60
+ function detectPackageManager(projectRoot) {
46
61
  const userAgent = process.env.npm_config_user_agent || "";
47
62
  if (userAgent.startsWith("pnpm")) return "pnpm";
48
63
  if (userAgent.startsWith("yarn")) return "yarn";
49
64
  if (userAgent.startsWith("bun")) return "bun";
65
+
66
+ // Fallback to the project's packageManager field (e.g. "pnpm@9.x").
67
+ if (projectRoot) {
68
+ const packageJsonPath = path.join(projectRoot, "package.json");
69
+ if (existsSync(packageJsonPath)) {
70
+ try {
71
+ const content = readFileSync(packageJsonPath, "utf8");
72
+ const json = JSON.parse(content);
73
+ const packageManager = String(json.packageManager || "").trim();
74
+ if (packageManager.startsWith("pnpm")) return "pnpm";
75
+ if (packageManager.startsWith("yarn")) return "yarn";
76
+ if (packageManager.startsWith("bun")) return "bun";
77
+ if (packageManager.startsWith("npm")) return "npm";
78
+ } catch {
79
+ // Ignore parsing failures and fall through to npm.
80
+ }
81
+ }
82
+ }
83
+
50
84
  return "npm";
51
85
  }
52
86
 
53
- function runInstall(cwd, manager) {
54
- const args =
55
- manager === "npm"
56
- ? ["install"]
57
- : manager === "yarn"
58
- ? []
59
- : manager === "bun"
60
- ? ["install"]
61
- : ["install"];
62
-
63
- return new Promise((resolve, reject) => {
64
- const child = spawn(manager, args, {
65
- cwd,
66
- stdio: "inherit",
67
- shell: process.platform === "win32",
68
- });
69
- child.on("close", (code) => {
70
- if (code === 0) resolve();
71
- else reject(new Error(`Dependency install failed with code ${code}`));
72
- });
73
- child.on("error", reject);
87
+ async function promptTemplateSelection() {
88
+ const renderMenu = (selectedIndex) => {
89
+ const lines = ["[create-secra] 请选择初始化版本(↑/↓ 切换,Enter 确认):"];
90
+ for (let i = 0; i < TEMPLATE_OPTIONS.length; i += 1) {
91
+ const option = TEMPLATE_OPTIONS[i];
92
+ const prefix = i === selectedIndex ? "❯" : " ";
93
+ lines.push(` ${prefix} ${option.key}. ${option.label}`);
94
+ }
95
+ process.stdout.write(`${lines.join("\n")}\n`);
96
+ };
97
+
98
+ return new Promise((resolve) => {
99
+ let selectedIndex = 0;
100
+ let renderedLines = 0;
101
+ const stdin = process.stdin;
102
+ const stdout = process.stdout;
103
+ const shouldRestoreRaw = stdin.isTTY && !stdin.isRaw;
104
+
105
+ const clearRenderedMenu = () => {
106
+ if (renderedLines > 0) {
107
+ stdout.write(`\u001b[${renderedLines}A`);
108
+ stdout.write("\u001b[J");
109
+ }
110
+ };
111
+
112
+ const draw = () => {
113
+ clearRenderedMenu();
114
+ renderMenu(selectedIndex);
115
+ renderedLines = TEMPLATE_OPTIONS.length + 1;
116
+ };
117
+
118
+ const cleanup = () => {
119
+ stdin.removeListener("keypress", onKeypress);
120
+ if (stdin.isTTY && shouldRestoreRaw) {
121
+ stdin.setRawMode(false);
122
+ }
123
+ stdin.pause();
124
+ };
125
+
126
+ const finish = () => {
127
+ cleanup();
128
+ clearRenderedMenu();
129
+ const selected = TEMPLATE_OPTIONS[selectedIndex] ?? TEMPLATE_OPTIONS[0];
130
+ stdout.write(`[create-secra] 请选择初始化版本:${selected.label}\n`);
131
+ resolve(selected);
132
+ };
133
+
134
+ const onKeypress = (_str, key) => {
135
+ if (key?.name === "up") {
136
+ selectedIndex = (selectedIndex - 1 + TEMPLATE_OPTIONS.length) % TEMPLATE_OPTIONS.length;
137
+ draw();
138
+ return;
139
+ }
140
+
141
+ if (key?.name === "down") {
142
+ selectedIndex = (selectedIndex + 1) % TEMPLATE_OPTIONS.length;
143
+ draw();
144
+ return;
145
+ }
146
+
147
+ if (key?.name === "return") {
148
+ finish();
149
+ return;
150
+ }
151
+
152
+ if (key?.ctrl && key?.name === "c") {
153
+ cleanup();
154
+ process.exit(1);
155
+ }
156
+ };
157
+
158
+ emitKeypressEvents(stdin);
159
+ stdin.setRawMode(true);
160
+ stdin.resume();
161
+ stdin.on("keypress", onKeypress);
162
+ draw();
74
163
  });
75
164
  }
76
165
 
166
+ async function resolveTemplate() {
167
+ const templateName = process.env.SECRA_TEMPLATE_NAME || "";
168
+ const templateFromEnv = process.env.SECRA_TEMPLATE;
169
+
170
+ if (templateFromEnv) {
171
+ return {
172
+ template: templateFromEnv,
173
+ selectedName: templateName || "custom",
174
+ selectedLabel: templateName || "自定义模板",
175
+ };
176
+ }
177
+
178
+ if (templateName) {
179
+ const matchedByName = TEMPLATE_OPTIONS.find((option) => option.templateName === templateName);
180
+ if (matchedByName) {
181
+ return {
182
+ template: matchedByName.templatePath,
183
+ selectedName: matchedByName.templateName,
184
+ selectedLabel: matchedByName.label,
185
+ };
186
+ }
187
+ }
188
+
189
+ const matchedByArg = TEMPLATE_OPTIONS.find((option) => option.key === process.argv[3]);
190
+ if (matchedByArg) {
191
+ return {
192
+ template: matchedByArg.templatePath,
193
+ selectedName: matchedByArg.templateName,
194
+ selectedLabel: matchedByArg.label,
195
+ };
196
+ }
197
+
198
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
199
+ const fallback = TEMPLATE_OPTIONS[0];
200
+ return {
201
+ template: fallback.templatePath,
202
+ selectedName: fallback.templateName,
203
+ selectedLabel: fallback.label,
204
+ };
205
+ }
206
+
207
+ const selected = await promptTemplateSelection();
208
+ return {
209
+ template: selected.templatePath,
210
+ selectedName: selected.templateName,
211
+ selectedLabel: selected.label,
212
+ };
213
+ }
214
+
77
215
  async function main() {
78
- const template = process.env.SECRA_TEMPLATE || DEFAULT_TEMPLATE;
79
- const manager = detectPackageManager();
216
+ const { template, selectedLabel } = await resolveTemplate();
80
217
 
81
218
  if (existsSync(targetPath)) {
82
219
  const files = readdirSync(targetPath);
@@ -89,6 +226,7 @@ async function main() {
89
226
  fail(`Template does not exist: ${template}`);
90
227
  }
91
228
 
229
+ console.log(`[create-secra] 已选择:${selectedLabel}`);
92
230
  console.log(`[create-secra] Scaffolding from local template: ${template} ...`);
93
231
  try {
94
232
  cpSync(template, targetPath, {
@@ -101,17 +239,11 @@ async function main() {
101
239
  }
102
240
 
103
241
  updatePackageName(targetPath, path.basename(targetPath));
104
-
105
- console.log(`[create-secra] Installing dependencies with ${manager} ...`);
106
- try {
107
- await runInstall(targetPath, manager);
108
- } catch (error) {
109
- fail(error instanceof Error ? error.message : String(error));
110
- }
242
+ const manager = detectPackageManager(targetPath);
111
243
 
112
244
  console.log(`[create-secra] Done. Next steps:`);
113
245
  console.log(` cd ${targetDir}`);
114
- console.log(` pnpm install`);
246
+ console.log(` ${manager === "yarn" ? "yarn" : `${manager} install`}`);
115
247
  console.log(` pnpm -r --filter "./packages/*" build`);
116
248
  console.log(` pnpm dev`);
117
249
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-secra",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "description": "Create a Secra project from the official template.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,12 +8,16 @@
8
8
  },
9
9
  "files": [
10
10
  "bin",
11
- "template"
11
+ "template",
12
+ "antd-adapter-template"
12
13
  ],
13
14
  "keywords": [
14
15
  "create",
15
16
  "secra",
16
17
  "scaffold"
17
18
  ],
19
+ "scripts": {
20
+ "prepublishOnly": "npm version patch"
21
+ },
18
22
  "license": "MIT"
19
23
  }
@@ -1,21 +1,14 @@
1
- import "@vlian/sdk/reset.css";
1
+ import '@vlian/sdk/reset.css';
2
2
  import { startApp } from '@vlian/framework/core';
3
- import { LogLevel } from '@vlian/framework/utils';
4
3
  import { getRoutes } from './router';
5
- void startApp({
6
- showSplashScreen: 'auto', // 自动判断是否显示启动页
7
- loggerLevel: LogLevel.DEBUG,
8
- configStrategy: {
9
- //加载关键配置 这些配置必须在应用渲染前加载完成
10
- loadCriticalConfig: async () => {
11
- return {}
12
- },
13
- },
14
- lifecycle: {
15
- },
16
- router: {
17
- enabled: true,
18
- routes: getRoutes,
19
- mode: 'browser',
20
- }
4
+
5
+ startApp({
6
+ router: {
7
+ enabled: true,
8
+ routes: getRoutes,
9
+ mode: 'browser',
10
+ },
11
+ }).catch((error) => {
12
+ // eslint-disable-next-line no-console
13
+ console.error('启动失败:', error);
21
14
  });
@@ -1,11 +1,15 @@
1
1
  import type { RouteConfig } from "@vlian/framework/core"
2
2
 
3
+ const HomePage = lazy(() => import("./pages/index"));
4
+
3
5
  export const getRoutes = async (): Promise<RouteConfig[]> => {
4
6
  return [
5
7
  {
6
8
  path: "/",
7
9
  name: "home",
8
- page: () => import('./pages/index.tsx'),
10
+ page: async () => ({
11
+ default: HomePage,
12
+ }),
9
13
  handle: {
10
14
  title: "首页",
11
15
  order: 1,
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@ant-design/icons": "^6.1.0",
15
- "@vlian/framework": "^1.2.1",
15
+ "@vlian/framework": "^1.2.16",
16
16
  "@vlian/infrastructure": "^0.9.11",
17
17
  "@vlian/sdk": "workspace:*",
18
18
  "ahooks": "^3.8.5",
@@ -24,6 +24,9 @@ export function setupUnocss(
24
24
  unocssConfig?: Partial<UserConfig>
25
25
  ): ReturnType<typeof unocss> {
26
26
  return unocss({
27
+ // Disable auto-loading `uno.config.*` from cwd to avoid noisy
28
+ // "Config file not found" logs in template apps without standalone config files.
29
+ configFile: false,
27
30
  presets: [
28
31
  // 基础预设 - 包含 Tailwind CSS 兼容的工具类
29
32
  presetUno(),