@yarch/create-admin 0.1.0 → 0.2.1

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 (63) hide show
  1. package/README.md +94 -26
  2. package/bin/create-admin.mjs +115 -20
  3. package/bin/gen-registry-snapshot.mjs +32 -0
  4. package/bin/registry-snapshot.json +15 -0
  5. package/bin/registry.mjs +19 -0
  6. package/package.json +2 -2
  7. package/templates/admin-antd/AGENTS.md +33 -0
  8. package/templates/admin-antd/CLAUDE.md +1 -0
  9. package/templates/admin-antd/GEMINI.md +1 -0
  10. package/templates/admin-antd/archetype.json +48 -7
  11. package/templates/admin-antd/package.json +1 -0
  12. package/templates/admin-arco/AGENTS.md +33 -0
  13. package/templates/admin-arco/CLAUDE.md +1 -0
  14. package/templates/admin-arco/GEMINI.md +1 -0
  15. package/templates/admin-semi/AGENTS.md +33 -0
  16. package/templates/admin-semi/CLAUDE.md +1 -0
  17. package/templates/admin-semi/GEMINI.md +1 -0
  18. package/templates/base-semi/AGENTS.md +30 -0
  19. package/templates/base-semi/CLAUDE.md +1 -0
  20. package/templates/base-semi/GEMINI.md +1 -0
  21. package/templates/base-semi/README.md +35 -0
  22. package/templates/base-semi/archetype.json +13 -0
  23. package/templates/base-semi/conf/nginx.conf.example +19 -0
  24. package/templates/base-semi/index.html +11 -0
  25. package/templates/base-semi/package.json +29 -0
  26. package/templates/base-semi/src/app/micro-apps.config.ts +16 -0
  27. package/templates/base-semi/src/app/router.tsx +44 -0
  28. package/templates/base-semi/src/layouts/basic-layout.tsx +51 -0
  29. package/templates/base-semi/src/main.tsx +22 -0
  30. package/templates/base-semi/src/micro/shared.ts +43 -0
  31. package/templates/base-semi/src/micro/sub-app.tsx +65 -0
  32. package/templates/base-semi/src/pages/dashboard.tsx +16 -0
  33. package/templates/base-semi/src/pages/login.tsx +18 -0
  34. package/templates/base-semi/src/pages/not-found.tsx +13 -0
  35. package/templates/base-semi/src/stores/auth.ts +13 -0
  36. package/templates/base-semi/src/ui/theme.ts +7 -0
  37. package/templates/base-semi/src/vite-env.d.ts +1 -0
  38. package/templates/base-semi/tsconfig.json +16 -0
  39. package/templates/base-semi/vite.config.ts +17 -0
  40. package/templates/sub-semi/AGENTS.md +33 -0
  41. package/templates/sub-semi/CLAUDE.md +1 -0
  42. package/templates/sub-semi/GEMINI.md +1 -0
  43. package/templates/sub-semi/README.md +28 -0
  44. package/templates/sub-semi/archetype.json +13 -0
  45. package/templates/sub-semi/index.html +11 -0
  46. package/templates/sub-semi/package.json +34 -0
  47. package/templates/sub-semi/src/app/root.tsx +23 -0
  48. package/templates/sub-semi/src/app/router.tsx +26 -0
  49. package/templates/sub-semi/src/events.ts +4 -0
  50. package/templates/sub-semi/src/features/invoices/api.ts +19 -0
  51. package/templates/sub-semi/src/features/invoices/hooks/use-invoices.ts +18 -0
  52. package/templates/sub-semi/src/layouts/sub-layout.tsx +12 -0
  53. package/templates/sub-semi/src/main.tsx +28 -0
  54. package/templates/sub-semi/src/pages/invoices.tsx +49 -0
  55. package/templates/sub-semi/src/supply/integrated.ts +48 -0
  56. package/templates/sub-semi/src/supply/shims/contract.ts +25 -0
  57. package/templates/sub-semi/src/supply/standalone.ts +38 -0
  58. package/templates/sub-semi/src/supply/types.ts +18 -0
  59. package/templates/sub-semi/src/types/index.ts +6 -0
  60. package/templates/sub-semi/src/ui/popup.ts +13 -0
  61. package/templates/sub-semi/src/vite-env.d.ts +1 -0
  62. package/templates/sub-semi/tsconfig.json +20 -0
  63. package/templates/sub-semi/vite.config.ts +96 -0
package/README.md CHANGED
@@ -1,42 +1,110 @@
1
- # @yarch/create-admin · yarch web 工程生成器
1
+ # @yarch/create-admin
2
2
 
3
- > W6-W9(2026-09-03 拍板):npm 分发 + 交互问答 + maven-archetype 式全量渲染 + 模板资产化。
4
- > golang 栈 `cmd/yarch-init` 的 web 对偶——模板是**声明式资产**(`{{var}}` 占位 + `archetype.json` 变量声明,不要求自身可安装),工程正确性由 CI「生成后冒烟」保证。
3
+ > 一条命令生成**生成即合规**的中后台工程——契约底座、命名标准、结构纪律全部预置,三档 UI 任选。
4
+ > npm:[@yarch/create-admin](https://www.npmjs.com/package/@yarch/create-admin)(v0.1.0+)
5
5
 
6
- ## 创建工程(一行命令,零 clone)
6
+ ## 优势:比裸脚手架(create-vite)多给什么
7
+
8
+ | 维度 | 裸脚手架 | 本生成器 |
9
+ |---|---|---|
10
+ | 接口层 | 空壳,信封/错误码自己搭 | `@yarch/contract` 预接线:RestResponse 信封解包 · 13 码错误码表 · traceId 透传 · 401 自动跳登录 |
11
+ | 工程名 | 随手起 | 强制 registry 标准(小写短横线 + 禁裸通用词);工程名 = npm 包名 = 服务名,生成即登记提醒 |
12
+ | UI 库 | 自己选、自己集成 | **三档一键选**:Semi / antd / Arco——契约底座与结构不变,只换 UI 壳 |
13
+ | 升级 | 模板拷走即断亲 | 底座是 npm 版本依赖:`pnpm update @yarch/contract @yarch/react` 一行跟进平台修复 |
14
+ | 结构 | 分层靠自觉 | feature-first 分层 + depcruise 同款机检口径(pages 薄入口、禁反向依赖) |
15
+ | 跨栈 | — | 与 java / golang 栈同一套契约:一个 traceId 前后端拉通,错误码语义唯一 |
16
+
17
+ ## 创建:三档 UI,各一条命令
7
18
 
8
19
  ```bash
9
- # 标准路径:从 npm registry 拉取脚手架(三包已发版后即可用)——
10
- npm create @yarch/admin@latest ysaas-console # 交互问答:工程名 → UI 档 → 描述 → 端口 → 代理 → GitLab 分组
20
+ # Semi 档(默认 · 抖音系)
21
+ npm create @yarch/admin@latest ysaas-console
22
+
23
+ # antd 档(蚂蚁系)
11
24
  npm create @yarch/admin@latest ysaas-console -- --ui antd
12
25
 
13
- # 发版前/开发中(本地,等价 golang `go run ./cmd/yarch-init`):
14
- node stacks/web/packages/create/bin/create-admin.mjs ysaas-console --deps file
26
+ # Arco 档(字节系)
27
+ npm create @yarch/admin@latest ysaas-console -- --ui arco
28
+ ```
29
+
30
+ 不传 `--ui` 则交互三选一(回车落默认 Semi);交互同时问描述 / 端口 / API 代理目标 / GitLab 分组,全部有默认值。三档底座与目录结构完全一致,切换档位 = 换个名字重新生成。
31
+
32
+ ## 起跑(两分钟)
33
+
34
+ ```bash
35
+ cd ysaas-console
36
+ pnpm install
37
+ pnpm dev # http://localhost:5173
38
+ pnpm build # tsc --noEmit + vite build
15
39
  ```
16
40
 
17
- 工程名即一切标识(W7 标准化):npm 包名 = registry.md 服务名,生成器强制 `^[a-z][a-z0-9-]{1,31}$` + 禁裸通用词(一-1/一-2,与 golang 侧同款校验)。
41
+ - **接后端**:`vite.config.ts` 已预配 `/api/v1` 反向代理,改 target 即指向你的服务;
42
+ - **升级底座**:`pnpm update @yarch/contract @yarch/react`;
43
+ - **登记**:按生成后的控制台提醒,去 yarch 仓 `contract/registry.md` 登记服务名(PR 即登记)。
18
44
 
19
- ## 问答变量(archetype.json 声明,渲染进目录/文件/内容)
45
+ ## 微前端:一条命令生成基座 / 子应用
20
46
 
21
- | 变量 | 问题 | 默认 |
47
+ ```bash
48
+ # 基座(布局/菜单/登录态/路由分发/错误兜底,全局唯一)
49
+ npm create @yarch/admin@latest ysaas-console -- --micro base
50
+
51
+ # 子应用(独立·集成双运行形态,只做域内页面)
52
+ npm create @yarch/admin@latest ysaas-billing -- --micro sub --port 5174
53
+ ```
54
+
55
+ 与单应用档的差异(规约:[micro-frontend.md](../../../contract/web/micro-frontend.md)):
56
+
57
+ - **应用名双重校验**:在服务名规则之上加「服务名-用途」两段式 + `contract/registry.md` 登记表核对(重名/未登记当场拒绝;登记表离线时可用包内快照);
58
+ - **基座**:`micro-apps.config.ts` 登记表声明式装配菜单与路由分发(禁硬编码子应用路由)、登录态唯一持有、共享运行时注册(`src/micro/shared.ts`)、加载失败重试页;
59
+ - **子应用**:`src/supply/` 供给层实现独立·集成双运行(十一-2)——独立模式全量自足可单独 `pnpm dev`,集成构建(`pnpm build:micro` → `dist-micro/`)react 系与 contract 由基座 window 全局提供,产物体积里框架消失;
60
+ - **UI 档**:本批仅 Semi(`--ui` 其余档触发式扩展)。
61
+
62
+ 仓内活案例:[examples/ysaas-console](../examples/ysaas-console)(基座)+ [examples/ysaas-billing](../examples/ysaas-billing)(子应用),事件上行/双模式/e2e 全链路。
63
+
64
+ <details>
65
+ <summary><b>附录:交互问答明细 · flags 全表 · 命名规则 · 原理 · 发布(点开)</b></summary>
66
+
67
+ ### 交互问答(单应用 6 项;微前端形态在最前多一项「工程形态」)
68
+
69
+ | # | 问题 | 默认 |
22
70
  |---|---|---|
23
- | `packageName` | 工程名(= npm 包名 = registry 服务名) | 必填 |
24
- | `appName` | 展示名(index.html 标题) | = packageName |
25
- | `description` | 工程描述 | `<名>:基于 yarch web 脚手架生成的中后台工程` |
26
- | `port` | vite dev 端口 | 5173 |
27
- | `proxyTarget` | `/api/v1` 反向代理目标 | `http://localhost:8080` |
28
- | `yarchContractDep` / `yarchReactDep` | @yarch 底座依赖形态 | `--deps version`(默认,`^0.1.0`,标准形态)· `--deps file`(发版前本地过渡 = golang replace 行对偶) |
71
+ | 0 | 工程形态:1) 单应用(默认)2) 微前端基座 3) 微前端子应用 | 单应用 |
72
+ | 1 | 工程名(= npm 包名 = registry 服务名,须过校验) | 必填 |
73
+ | 2 | UI 档:1) Semi(默认)2) antd 3) Arco | semi |
74
+ | 3 | 工程描述 | `<工程名>:基于 yarch web 脚手架生成的中后台工程` |
75
+ | 4 | dev 端口 | 5173 |
76
+ | 5 | API 代理目标 | `http://localhost:8080` |
77
+ | 6 | GitLab 分组(登记提示用) | 空 |
29
78
 
30
- CI 非交互:`--yes` + 全量 flags;渲染后扫描残留 `{{var}}`,有即报错(防模板与变量集漂移)。
79
+ ### 命名规则(contract/registry.md 一-1/一-2,与 golang 生成器同款)
80
+
81
+ - 格式 `^[a-z][a-z0-9-]{1,31}$`:小写字母/数字/短横线,字母开头,2~32 字符——大写、下划线、中文一律当场拒绝;
82
+ - 禁裸通用词:`api app service server backend web admin main common system demo user gateway` 不得单独成名(加前缀消歧,如 `ysaas-admin`)。
83
+
84
+ ### 非交互 flags(脚本 / CI)
85
+
86
+ `npm create @yarch/admin@latest <名> -- --ui antd --yes`(npm 传 flag 须 `--` 分隔;pnpm 直接跟)。
87
+ 维护者本地直跑:`node stacks/web/packages/create/bin/create-admin.mjs <名> --ui antd --yes --deps file`。
88
+
89
+ | flag | 取值 | 说明 |
90
+ |---|---|---|
91
+ | `--name` | 工程名 | 也可用第一个位置参数 |
92
+ | `--ui` | `semi` \| `antd` \| `arco` | 非法值报错 |
93
+ | `--micro` | `base` \| `sub` | 微前端模板档;缺省=单应用 admin(不受微前端规约约束) |
94
+ | `--registry` | 路径 | 微前端应用名核对用的 registry.md(缺省用包内快照) |
95
+ | `--desc` | 文本 | 不得含双引号/反斜杠 |
96
+ | `--port` | 1024~65535 | |
97
+ | `--proxy` | `http(s)://…` | API 代理目标 |
98
+ | `--group` | 文本 | GitLab 分组 |
99
+ | `--deps` | `version`(默认)\| `file` | `file` = 指向 yarch 本仓源码(发版前联调) |
100
+ | `--src` / `--out` | 目录 | 模板根 / 输出目录(默认 `./<工程名>`,须空) |
101
+ | `--yes` | | 非交互,缺省全走默认 |
31
102
 
32
- ## 三档模板(templates/,W1 决策)
103
+ ### 原理(一段话)
33
104
 
34
- | | UI |
35
- |---|---|
36
- | `admin-semi`(默认) | Semi Design(抖音系) |
37
- | `admin-antd` | antd(蚂蚁系) |
38
- | `admin-arco` | Arco Design(字节系) |
105
+ maven-archetype / cookiecutter 模式:三档模板是声明式资产(`{{var}}` 占位 + `archetype.json` 变量声明),生成器做问答 → 变量集 → 目录/文件/内容全量替换 + 残留占位符扫描;模板自身不要求可安装,工程正确性由 CI「生成后冒烟」(生成 → install → tsc → build)保证。
39
106
 
40
- ## 发布(W6)
107
+ ### 发布(维护者)
41
108
 
42
- 推 tag `stacks/web/vX.Y.Z` 触发 [web-publish.yml](../../../.github/workflows/web-publish.yml):发版前跑契约断言 + 依赖机检,随后按 contract → react → create 顺序发布三包(幂等:registry 已有同版本则跳过;需仓库 Secret `NPM_TOKEN`)——**golang「tag → module proxy」的 npm 对偶**。发布前自检:`pnpm --filter <包名> pack --dry-run`。
109
+ 推 tag `stacks/web/vX.Y.Z` [web-publish.yml](../../../.github/workflows/web-publish.yml):断言 + 机检 contract → react → create 顺序发布(幂等)。
110
+ </details>
@@ -18,6 +18,7 @@ import { readFileSync, writeFileSync, readdirSync, mkdirSync } from "node:fs";
18
18
  import { fileURLToPath } from "node:url";
19
19
  import { dirname, join, resolve } from "node:path";
20
20
  import readline from "node:readline/promises";
21
+ import { parseRegistryMd } from "./registry.mjs";
21
22
 
22
23
  const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
23
24
  const UI_TIERS = {
@@ -25,6 +26,14 @@ const UI_TIERS = {
25
26
  antd: "antd(蚂蚁系)",
26
27
  arco: "Arco Design(字节系)",
27
28
  };
29
+ // 微前端模板档(W10):载器 micro-app(contract/README 登记表默认档),UI 档本批固定 semi,其余档触发式。
30
+ const MICRO_KINDS = {
31
+ base: "微前端基座(micro-frontend.md 一-1:全局唯一,承载布局/菜单/登录态/路由分发/错误兜底)",
32
+ sub: "微前端子应用(micro-frontend.md 一-3:独立·集成双运行形态,只做域内页面)",
33
+ };
34
+ // @yarch 底座版本依赖(--deps version 形态);与本仓发版版本同步 bump。
35
+ const YARCH_VERSION = "^0.3.0";
36
+ const YARCH_PRO_REACT_VERSION = "^0.1.0";
28
37
 
29
38
  const servicePattern = /^[a-z][a-z0-9-]{1,31}$/;
30
39
  // 禁裸通用词(registry.md 一-1/一-2 口径摘录,与 golang yarch-init 同款;完整表以 contract/registry.md 为准)。
@@ -40,11 +49,13 @@ const PLACEHOLDER = /\{\{\s*([a-zA-Z][a-zA-Z0-9]*)\s*\}\}/g;
40
49
 
41
50
  function usage() {
42
51
  return [
43
- "用法:create-admin <工程名|输出目录> [--name <名>] [--ui semi|antd|arco] [--desc <描述>]",
44
- " [--port <端口>] [--proxy <目标>] [--group <GitLab分组>] [--deps file|version]",
45
- " [--src <模板根>] [--out <目录>] [--yes]",
52
+ "用法:create-admin <工程名|输出目录> [--name <名>] [--ui semi|antd|arco] [--micro base|sub]",
53
+ " [--registry <registry.md 路径>] [--desc <描述>] [--port <端口>] [--proxy <目标>]",
54
+ " [--group <GitLab分组>] [--deps file|version] [--src <模板根>] [--out <目录>] [--yes]",
55
+ " --micro 微前端模板档(W10):base=基座 / sub=子应用;缺省=单应用 admin(不受微前端规约约束)",
56
+ " --registry 微前端名称核对用的 contract/registry.md 路径(默认用包内快照 bin/registry-snapshot.json)",
46
57
  " --yes 非交互:缺省项全走默认值(CI 用)",
47
- " --deps @yarch 底座依赖形态:version(默认,^0.1.0,需 @yarch 已发 npm)| file(发版前本地过渡)",
58
+ " --deps @yarch 底座依赖形态:version(默认,^0.2.0,需 @yarch 已发 npm)| file(发版前本地过渡)",
48
59
  ].join("\n");
49
60
  }
50
61
 
@@ -77,6 +88,43 @@ function validateName(name) {
77
88
  return null;
78
89
  }
79
90
 
91
+ // —— 微前端应用名校验(micro-frontend.md 二-1/二-4,十三表「创建期」机检)——
92
+
93
+ // 快照来源:--registry 指向的 registry.md(CI/内网用最新表)优先,否则用随包分发的快照。
94
+ function loadRegistry(registryArg) {
95
+ if (registryArg) {
96
+ return parseRegistryMd(readFileSync(resolve(registryArg), "utf8"));
97
+ }
98
+ try {
99
+ const snapshot = JSON.parse(readFileSync(join(PKG_ROOT, "bin", "registry-snapshot.json"), "utf8"));
100
+ return {
101
+ serviceNames: new Set(snapshot.serviceNames),
102
+ appNames: new Set(snapshot.appNames),
103
+ };
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ function validateMicroName(name, registry) {
110
+ const baseErr = validateName(name);
111
+ if (baseErr) return baseErr;
112
+ if (!registry) {
113
+ return "无法核对 registry.md(缺 bin/registry-snapshot.json 且未传 --registry <path>)——微前端应用名必须核对登记表(二-1)";
114
+ }
115
+ if (!name.includes("-")) {
116
+ return "微前端应用名须由「服务名-用途/域」两段及以上构成(micro-frontend.md 二-1,正例 ysaas-billing)";
117
+ }
118
+ const service = name.split("-")[0];
119
+ if (!registry.serviceNames.has(service)) {
120
+ return `首段 ${service} 不在 registry.md 二节已登记服务名表(二-1);若快照过期,用 --registry 指向最新 contract/registry.md`;
121
+ }
122
+ if (registry.appNames.has(name)) {
123
+ return `应用名 ${name} 已在 registry.md 四节登记(二-4:跨项目不得重名)`;
124
+ }
125
+ return null;
126
+ }
127
+
80
128
  async function askText(rl, question, { fallback = "", validate = null, optional = false } = {}) {
81
129
  for (;;) {
82
130
  const hint = fallback ? `(默认 ${fallback})` : optional ? "(可空)" : "";
@@ -144,12 +192,23 @@ async function main() {
144
192
  const args = parseArgs(process.argv.slice(2));
145
193
  const nameArg = args.name || (args._.length > 0 && !args._[0].startsWith("/") ? args._[0] : null);
146
194
 
195
+ let micro = args.micro;
196
+ if (micro !== undefined && !(micro in MICRO_KINDS)) {
197
+ fatal(`--micro 须为 ${Object.keys(MICRO_KINDS).join("|")},当前:${micro}`);
198
+ }
147
199
  const ui = args.ui ?? "semi";
148
200
  if (!(ui in UI_TIERS)) fatal(`--ui 须为 ${Object.keys(UI_TIERS).join("|")},当前:${ui}`);
201
+ if (micro && ui !== "semi") {
202
+ fatal("微前端模板档本批仅 semi(W1 默认档);其余 UI 档触发式扩展(PLAN.md W10)");
203
+ }
149
204
 
150
205
  const interactive = !args.yes && process.stdin.isTTY;
151
206
  if (!nameArg && !interactive) fatal(`工程名必填(或用 --yes 走交互外模式):\n${usage()}`);
152
207
 
208
+ // 微前端形态才需要登记表核对(二-1 首段=已登记服务名;单应用仍走一-1/一-2 正则+禁裸词)。
209
+ const needsRegistry = micro !== undefined || interactive; // 交互形态问答后可能选 micro
210
+ const registry = needsRegistry ? loadRegistry(args.registry) : null;
211
+
153
212
  const rl = interactive ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;
154
213
  try {
155
214
  let name = nameArg;
@@ -160,17 +219,34 @@ async function main() {
160
219
 
161
220
  if (interactive) {
162
221
  console.log("yarch web 工程生成器(@yarch/create-admin)\n");
163
- if (!name) name = await askText(rl, "工程名(= npm 包名 = registry 服务名)", { validate: validateName });
164
- else if (validateName(name)) fatal(validateName(name));
222
+ if (micro === undefined) {
223
+ const kindIdx = await askSelect(
224
+ rl,
225
+ "工程形态(micro-frontend.md 一:单应用不受微前端规约约束;基座全局唯一、子应用双运行形态)",
226
+ [
227
+ "单应用 admin(默认,npm create @yarch/admin 常规形态)",
228
+ "微前端基座 base(--micro base)",
229
+ "微前端子应用 sub(--micro sub)",
230
+ ],
231
+ 0,
232
+ );
233
+ micro = kindIdx === 0 ? undefined : kindIdx === 1 ? "base" : "sub";
234
+ }
235
+ const validator = micro ? (v) => validateMicroName(v, registry) : validateName;
236
+ const nameLabel = micro
237
+ ? "应用名(= 路由前缀 = storage/事件前缀,二-2 一名三用;首段=已登记服务名)"
238
+ : "工程名(= npm 包名 = registry 服务名)";
239
+ if (!name) name = await askText(rl, nameLabel, { validate: validator });
240
+ else if (validator(name)) fatal(validator(name));
165
241
  if (!description) {
166
242
  description = await askText(rl, "工程描述", {
167
- fallback: `${name}:基于 yarch web 脚手架生成的中后台工程`,
243
+ fallback: `${name}:基于 yarch web 脚手架生成的${micro ? (micro === "base" ? "微前端基座" : "微前端子应用") : "中后台"}工程`,
168
244
  validate: (v) => (v.includes('"') || v.includes("\\") ? "不得包含双引号与反斜杠(要写进 package.json)" : null),
169
245
  });
170
246
  }
171
247
  if (!port) {
172
248
  port = await askText(rl, "dev 端口", {
173
- fallback: "5173",
249
+ fallback: micro === "sub" ? "5174" : "5173",
174
250
  validate: (v) => (/^\d{4,5}$/.test(v) && Number(v) >= 1024 && Number(v) <= 65535 ? null : "1024~65535 的数字端口号"),
175
251
  });
176
252
  }
@@ -184,10 +260,10 @@ async function main() {
184
260
  }
185
261
 
186
262
  if (!name) fatal(`工程名缺失。\n${usage()}`);
187
- const nameErr = validateName(name);
263
+ const nameErr = micro ? validateMicroName(name, registry) : validateName(name);
188
264
  if (nameErr) fatal(`工程名 ${JSON.stringify(name)} ${nameErr}`);
189
- description ??= `${name}:基于 yarch web 脚手架生成的中后台工程`;
190
- port ??= "5173";
265
+ description ??= `${name}:基于 yarch web 脚手架生成的${micro ? (micro === "base" ? "微前端基座" : "微前端子应用") : "中后台"}工程`;
266
+ port ??= micro === "sub" ? "5174" : "5173";
191
267
  proxyTarget ??= "http://localhost:8080";
192
268
  group ??= "";
193
269
  if (!/^\d{4,5}$/.test(port) || Number(port) < 1024 || Number(port) > 65535) {
@@ -197,14 +273,24 @@ async function main() {
197
273
  fatal(`代理目标 ${proxyTarget} 无效:须为 http(s):// 开头`);
198
274
  }
199
275
 
200
- // @yarch 底座依赖形态(W8):version = ^0.1.0(标准形态,@yarch/contract、@yarch/react 已发 npm);
276
+ // @yarch 底座依赖形态(W8):version = 版本依赖(标准形态,@yarch/contract、@yarch/react 已发 npm);
201
277
  // file: 绝对路径 = golang replace 行对偶(发版前本地过渡,yarch 源码变更后重跑 pnpm install 刷新)。
202
278
  const depsMode = args.deps === "file" ? "file" : "version";
203
- const yarchContractDep =
204
- depsMode === "version" ? "^0.1.0" : `file:${resolve(PKG_ROOT, "../contract")}`;
205
- const yarchReactDep = depsMode === "version" ? "^0.1.0" : `file:${resolve(PKG_ROOT, "../react")}`;
279
+ const yarchContractDep = depsMode === "version" ? YARCH_VERSION : `file:${resolve(PKG_ROOT, "../contract")}`;
280
+ const yarchReactDep = depsMode === "version" ? YARCH_VERSION : `file:${resolve(PKG_ROOT, "../react")}`;
281
+ const yarchProReactDep = depsMode === "version" ? YARCH_PRO_REACT_VERSION : `file:${resolve(PKG_ROOT, "../pro-react")}`;
206
282
 
207
- const vars = { packageName: name, appName: name, description, port, proxyTarget, yarchContractDep, yarchReactDep };
283
+ const vars = {
284
+ packageName: name,
285
+ appName: name,
286
+ service: name.split("-")[0],
287
+ description,
288
+ port,
289
+ proxyTarget,
290
+ yarchContractDep,
291
+ yarchReactDep,
292
+ yarchProReactDep,
293
+ };
208
294
 
209
295
  const outDir = resolve(args.out || (args._.length > 0 ? args._[args._.length - 1] : name));
210
296
  let existing;
@@ -215,7 +301,9 @@ async function main() {
215
301
  }
216
302
  if (existing && existing.length > 0) fatal(`输出目录 ${outDir} 非空`);
217
303
 
218
- const templateDir = resolve(args.src || join(PKG_ROOT, "templates", `admin-${ui}`));
304
+ const templateDir = resolve(
305
+ args.src || join(PKG_ROOT, "templates", micro ? `${micro}-semi` : `admin-${ui}`),
306
+ );
219
307
  try {
220
308
  readFileSync(join(templateDir, "archetype.json"));
221
309
  } catch {
@@ -230,12 +318,19 @@ async function main() {
230
318
 
231
319
  const tierDesc = JSON.parse(readFileSync(join(templateDir, "archetype.json"), "utf8")).description;
232
320
  const groupHint = group ? `(属主建议:${group}/${name})` : "";
233
- console.log(`✅ 已生成 ${outDir}(${n} 个文件,admin-${ui} 档)—— ${tierDesc}
321
+ const tierLabel = micro ? `${micro}-semi 档` : `admin-${ui} 档`;
322
+ const registryHint = micro
323
+ ? `2. 应用名已置为 ${JSON.stringify(name)}——去 yarch 仓 contract/registry.md 四节前端应用名登记表登记${groupHint}
324
+ 3. ${micro === "base"
325
+ ? "基座:micro-apps.config.ts 是子应用入口登记表(十二-2,git 纳管);子应用接入只改此表"
326
+ : "子应用:在基座仓 micro-apps.config.ts 登记入口(十二-2);pnpm dev 独立运行 / pnpm dev:micro 被基座加载(十一)"}`
327
+ : `2. 服务名已置为 ${JSON.stringify(name)}——去 yarch 仓 contract/registry.md 登记表登记${groupHint}`;
328
+ console.log(`✅ 已生成 ${outDir}(${n} 个文件,${tierLabel})—— ${tierDesc}
234
329
 
235
330
  下一步:
236
331
  1. cd ${outDir} && pnpm install && pnpm dev # http://localhost:${port}
237
- 2. 服务名已置为 ${JSON.stringify(name)}——去 yarch 仓 contract/registry.md 登记表登记${groupHint}
238
- 3. @yarch 底座为 ${depsMode === "version" ? "^0.1.0 版本依赖(升级 = pnpm update @yarch/contract @yarch/react)" : "file: 本地依赖(发版前过渡;正式发版后改用默认 version 形态重新生成或手动替换)"}
332
+ ${registryHint}
333
+ ${micro ? "4" : "3"}. @yarch 底座为 ${depsMode === "version" ? `${YARCH_VERSION} 版本依赖(升级 = pnpm update @yarch/contract @yarch/react)` : "file: 本地依赖(发版前过渡;正式发版后改用默认 version 形态重新生成或手动替换)"}
239
334
  `);
240
335
  } finally {
241
336
  if (rl) rl.close();
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ // 从 contract/registry.md 生成包内快照 bin/registry-snapshot.json(发版前跑;npm 消费方无仓可读,
3
+ // 快照是 --registry 未传时的核对来源)。CI/内网可随时用 --registry 指最新 registry.md 覆盖快照。
4
+ //
5
+ // 用法:node bin/gen-registry-snapshot.mjs [yarch 仓根目录](默认向上定位 ../../../../)
6
+ import { readFileSync, writeFileSync } from "node:fs";
7
+ import { fileURLToPath } from "node:url";
8
+ import { dirname, join, resolve } from "node:path";
9
+ import { parseRegistryMd } from "./registry.mjs";
10
+
11
+ const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
12
+ const repoRoot = resolve(process.argv[2] ?? join(PKG_ROOT, "../../../.."));
13
+ const mdPath = join(repoRoot, "contract", "registry.md");
14
+
15
+ let md;
16
+ try {
17
+ md = readFileSync(mdPath, "utf8");
18
+ } catch {
19
+ console.error(`gen-registry-snapshot: 读不到 ${mdPath}(传 yarch 仓根目录作为参数)`);
20
+ process.exit(1);
21
+ }
22
+
23
+ const { serviceNames, appNames } = parseRegistryMd(md);
24
+ const snapshot = {
25
+ source: "contract/registry.md",
26
+ generatedAt: new Date().toISOString(),
27
+ serviceNames: [...serviceNames].sort(),
28
+ appNames: [...appNames].sort(),
29
+ };
30
+ const out = join(PKG_ROOT, "bin", "registry-snapshot.json");
31
+ writeFileSync(out, `${JSON.stringify(snapshot, null, 2)}\n`);
32
+ console.log(`✅ ${out}:服务名 ${snapshot.serviceNames.length} 个,应用名 ${snapshot.appNames.length} 个`);
@@ -0,0 +1,15 @@
1
+ {
2
+ "source": "contract/registry.md",
3
+ "generatedAt": "2026-09-18T06:28:35.369Z",
4
+ "serviceNames": [
5
+ "yagent",
6
+ "ybookreading",
7
+ "ycomp",
8
+ "ysaas"
9
+ ],
10
+ "appNames": [
11
+ "ycomp-console",
12
+ "ysaas-billing",
13
+ "ysaas-console"
14
+ ]
15
+ }
@@ -0,0 +1,19 @@
1
+ // registry.md 解析(共享模块:create-admin 校验 与 gen-registry-snapshot 快照生成 同源,防漂移)。
2
+ // 解析 contract/registry.md:二节服务名登记表 + 四节前端应用名登记表(首列反引号包裹的行)。
3
+ export function parseRegistryMd(md) {
4
+ const serviceNames = new Set();
5
+ const appNames = new Set();
6
+ let section = "";
7
+ for (const line of md.split("\n")) {
8
+ const heading = line.match(/^##\s+(一|二|三|四|五|六|七|八|九|十)、/);
9
+ if (heading) {
10
+ section = heading[1];
11
+ continue;
12
+ }
13
+ const cell = line.match(/^\|\s*`([a-z][a-z0-9-]{1,31})`\s*\|/);
14
+ if (!cell) continue;
15
+ if (section === "二") serviceNames.add(cell[1]);
16
+ else if (section === "四") appNames.add(cell[1]);
17
+ }
18
+ return { serviceNames, appNames };
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yarch/create-admin",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "yarch web 工程生成器:交互问答 + archetype 全量渲染(maven-archetype/cookiecutter 模式,golang cmd/yarch-init 的 web 对偶)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  "README.md"
13
13
  ],
14
14
  "publishConfig": { "access": "public" },
15
- "repository": { "type": "git", "url": "git+https://github.com/yuandonghao/yarch.git", "directory": "stacks/web/packages/create" },
15
+ "repository": { "type": "git", "url": "git+https://github.com/ydonghao/yarch.git", "directory": "stacks/web/packages/create" },
16
16
  "license": "Apache-2.0",
17
17
  "engines": {
18
18
  "node": ">=20"
@@ -0,0 +1,33 @@
1
+ # AGENTS.md — {{packageName}} 工程守则
2
+
3
+ > 本工程由 yarch 脚手架生成,基于跨栈统一契约(yarch 仓 `contract/`,全部 v1.0 定稿)。
4
+ > 任何 AI 编码代理改码前先读本文件;条文与直觉冲突时以本文件为准,红线改动直接拒绝——机检与 CR 均按此执行。
5
+
6
+ ## 契约红线(违反 = 机检失败 / CR 必拒)
7
+
8
+ | 红线 | 规则 | 出处 |
9
+ |---|---|---|
10
+ | 统一信封 | 响应一律 `{code, message, data, traceId}` 四字段,`code === 0` 即成功;判错只看 `code`,HTTP 状态码不承载业务语义 | contract/api/rest-response.md |
11
+ | 错误码 | 只用 `@yarch/contract` 的 `errorCodes` 常量(1xxx 通用 / 2xxx 认证);新业务码在业务仓登记 3xxx+ 段位;**禁裸数字错误码** | contract/api/error-codes.md |
12
+ | HTTP 封装 | 业务请求只经 `createClient` 产出的客户端(信封解包 / traceId 透传 / 401 跳登录已内置);**禁裸 fetch/axios 直连业务接口** | 参考 `src/features/*/api.ts` 现状 |
13
+ | 分层方向 | `pages/` 只组合 `features/`(禁直调 `features/*/api.ts`,取数经 hooks);`ui/`·`components/` 禁反向依赖 `features/`·`pages/` | yarch depcruise 同款规则 |
14
+ | traceId | 排查链路键即 `traceId`(W3C traceparent);响应头/信封里的 traceId 禁丢弃,报障必附 | contract/api/logging-trace.md |
15
+ | 命名 | 文件/目录 kebab-case;工程名即服务名(小写短横线,禁裸通用词),改名 = registry 变更评审 | contract/registry.md 一 |
16
+ | 底座升级 | `@yarch/contract`·`@yarch/react` 一律 `pnpm update` 跟进版本;**禁 patch / 改 node_modules / 拷贝源码内联** | — |
17
+
18
+ ## 验证(改完必跑,全绿才算完成)
19
+
20
+ ```bash
21
+ pnpm build # tsc --noEmit + vite build——生成工程的机检底线
22
+ ```
23
+
24
+ 平台侧还有契约断言测试与依赖方向机检(depcruise);本工程若引入 CI,建议把两者加进来后再放开依赖方向的约束。
25
+
26
+ ## 登记与升级
27
+
28
+ - 服务名 / 错误码段位 / 前端应用名登记:yarch 仓 `contract/registry.md`(PR 即登记);
29
+ - 底座新版本:`pnpm update @yarch/contract @yarch/react`,只改 version 一行。
30
+
31
+ ## 完成定义(DoD)
32
+
33
+ tsc 零错 · build 通过 · 新增接口全走统一信封与错误码常量 · 新增取数落在 features 层。
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -1,12 +1,53 @@
1
1
  {
2
2
  "description": "yarch 中后台工程模板 · antd 档(蚂蚁系):Vite + React 19 + react-router 7 + @yarch 契约底座",
3
3
  "variables": [
4
- { "name": "packageName", "desc": "工程名(= npm 包名 = registry.md 服务名,一-1 校验)", "required": true, "default": "" },
5
- { "name": "appName", "desc": "展示名(index.html 标题)", "required": false, "default": "= packageName" },
6
- { "name": "description", "desc": "工程描述(package.json)", "required": false, "default": "" },
7
- { "name": "port", "desc": "vite dev 端口", "required": false, "default": "5173" },
8
- { "name": "proxyTarget", "desc": "/api/v1 反向代理目标", "required": false, "default": "http://localhost:8080" },
9
- { "name": "yarchContractDep", "desc": "@yarch/contract 依赖(发版前 file: 本地路径,发版后 ^x.y.z)", "required": false, "default": "" },
10
- { "name": "yarchReactDep", "desc": "@yarch/react 依赖(同上)", "required": false, "default": "" }
4
+ {
5
+ "name": "packageName",
6
+ "desc": "工程名(= npm 包名 = registry.md 服务名,一-1 校验)",
7
+ "required": true,
8
+ "default": ""
9
+ },
10
+ {
11
+ "name": "appName",
12
+ "desc": "展示名(index.html 标题)",
13
+ "required": false,
14
+ "default": "= packageName"
15
+ },
16
+ {
17
+ "name": "description",
18
+ "desc": "工程描述(package.json)",
19
+ "required": false,
20
+ "default": ""
21
+ },
22
+ {
23
+ "name": "port",
24
+ "desc": "vite dev 端口",
25
+ "required": false,
26
+ "default": "5173"
27
+ },
28
+ {
29
+ "name": "proxyTarget",
30
+ "desc": "/api/v1 反向代理目标",
31
+ "required": false,
32
+ "default": "http://localhost:8080"
33
+ },
34
+ {
35
+ "name": "yarchContractDep",
36
+ "desc": "@yarch/contract 依赖(发版前 file: 本地路径,发版后 ^x.y.z)",
37
+ "required": false,
38
+ "default": ""
39
+ },
40
+ {
41
+ "name": "yarchReactDep",
42
+ "desc": "@yarch/react 依赖(同上)",
43
+ "required": false,
44
+ "default": ""
45
+ },
46
+ {
47
+ "name": "yarchProReactDep",
48
+ "desc": "@yarch/pro-react 依赖(CL5 antd 档预装;发版前 file: 本地路径)",
49
+ "required": false,
50
+ "default": ""
51
+ }
11
52
  ]
12
53
  }
@@ -14,6 +14,7 @@
14
14
  "react-router-dom": "^7",
15
15
  "@yarch/contract": "{{yarchContractDep}}",
16
16
  "@yarch/react": "{{yarchReactDep}}",
17
+ "@yarch/pro-react": "{{yarchProReactDep}}",
17
18
  "antd": "^5"
18
19
  },
19
20
  "devDependencies": {
@@ -0,0 +1,33 @@
1
+ # AGENTS.md — {{packageName}} 工程守则
2
+
3
+ > 本工程由 yarch 脚手架生成,基于跨栈统一契约(yarch 仓 `contract/`,全部 v1.0 定稿)。
4
+ > 任何 AI 编码代理改码前先读本文件;条文与直觉冲突时以本文件为准,红线改动直接拒绝——机检与 CR 均按此执行。
5
+
6
+ ## 契约红线(违反 = 机检失败 / CR 必拒)
7
+
8
+ | 红线 | 规则 | 出处 |
9
+ |---|---|---|
10
+ | 统一信封 | 响应一律 `{code, message, data, traceId}` 四字段,`code === 0` 即成功;判错只看 `code`,HTTP 状态码不承载业务语义 | contract/api/rest-response.md |
11
+ | 错误码 | 只用 `@yarch/contract` 的 `errorCodes` 常量(1xxx 通用 / 2xxx 认证);新业务码在业务仓登记 3xxx+ 段位;**禁裸数字错误码** | contract/api/error-codes.md |
12
+ | HTTP 封装 | 业务请求只经 `createClient` 产出的客户端(信封解包 / traceId 透传 / 401 跳登录已内置);**禁裸 fetch/axios 直连业务接口** | 参考 `src/features/*/api.ts` 现状 |
13
+ | 分层方向 | `pages/` 只组合 `features/`(禁直调 `features/*/api.ts`,取数经 hooks);`ui/`·`components/` 禁反向依赖 `features/`·`pages/` | yarch depcruise 同款规则 |
14
+ | traceId | 排查链路键即 `traceId`(W3C traceparent);响应头/信封里的 traceId 禁丢弃,报障必附 | contract/api/logging-trace.md |
15
+ | 命名 | 文件/目录 kebab-case;工程名即服务名(小写短横线,禁裸通用词),改名 = registry 变更评审 | contract/registry.md 一 |
16
+ | 底座升级 | `@yarch/contract`·`@yarch/react` 一律 `pnpm update` 跟进版本;**禁 patch / 改 node_modules / 拷贝源码内联** | — |
17
+
18
+ ## 验证(改完必跑,全绿才算完成)
19
+
20
+ ```bash
21
+ pnpm build # tsc --noEmit + vite build——生成工程的机检底线
22
+ ```
23
+
24
+ 平台侧还有契约断言测试与依赖方向机检(depcruise);本工程若引入 CI,建议把两者加进来后再放开依赖方向的约束。
25
+
26
+ ## 登记与升级
27
+
28
+ - 服务名 / 错误码段位 / 前端应用名登记:yarch 仓 `contract/registry.md`(PR 即登记);
29
+ - 底座新版本:`pnpm update @yarch/contract @yarch/react`,只改 version 一行。
30
+
31
+ ## 完成定义(DoD)
32
+
33
+ tsc 零错 · build 通过 · 新增接口全走统一信封与错误码常量 · 新增取数落在 features 层。
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,33 @@
1
+ # AGENTS.md — {{packageName}} 工程守则
2
+
3
+ > 本工程由 yarch 脚手架生成,基于跨栈统一契约(yarch 仓 `contract/`,全部 v1.0 定稿)。
4
+ > 任何 AI 编码代理改码前先读本文件;条文与直觉冲突时以本文件为准,红线改动直接拒绝——机检与 CR 均按此执行。
5
+
6
+ ## 契约红线(违反 = 机检失败 / CR 必拒)
7
+
8
+ | 红线 | 规则 | 出处 |
9
+ |---|---|---|
10
+ | 统一信封 | 响应一律 `{code, message, data, traceId}` 四字段,`code === 0` 即成功;判错只看 `code`,HTTP 状态码不承载业务语义 | contract/api/rest-response.md |
11
+ | 错误码 | 只用 `@yarch/contract` 的 `errorCodes` 常量(1xxx 通用 / 2xxx 认证);新业务码在业务仓登记 3xxx+ 段位;**禁裸数字错误码** | contract/api/error-codes.md |
12
+ | HTTP 封装 | 业务请求只经 `createClient` 产出的客户端(信封解包 / traceId 透传 / 401 跳登录已内置);**禁裸 fetch/axios 直连业务接口** | 参考 `src/features/*/api.ts` 现状 |
13
+ | 分层方向 | `pages/` 只组合 `features/`(禁直调 `features/*/api.ts`,取数经 hooks);`ui/`·`components/` 禁反向依赖 `features/`·`pages/` | yarch depcruise 同款规则 |
14
+ | traceId | 排查链路键即 `traceId`(W3C traceparent);响应头/信封里的 traceId 禁丢弃,报障必附 | contract/api/logging-trace.md |
15
+ | 命名 | 文件/目录 kebab-case;工程名即服务名(小写短横线,禁裸通用词),改名 = registry 变更评审 | contract/registry.md 一 |
16
+ | 底座升级 | `@yarch/contract`·`@yarch/react` 一律 `pnpm update` 跟进版本;**禁 patch / 改 node_modules / 拷贝源码内联** | — |
17
+
18
+ ## 验证(改完必跑,全绿才算完成)
19
+
20
+ ```bash
21
+ pnpm build # tsc --noEmit + vite build——生成工程的机检底线
22
+ ```
23
+
24
+ 平台侧还有契约断言测试与依赖方向机检(depcruise);本工程若引入 CI,建议把两者加进来后再放开依赖方向的约束。
25
+
26
+ ## 登记与升级
27
+
28
+ - 服务名 / 错误码段位 / 前端应用名登记:yarch 仓 `contract/registry.md`(PR 即登记);
29
+ - 底座新版本:`pnpm update @yarch/contract @yarch/react`,只改 version 一行。
30
+
31
+ ## 完成定义(DoD)
32
+
33
+ tsc 零错 · build 通过 · 新增接口全走统一信封与错误码常量 · 新增取数落在 features 层。