@yarch/create-admin 0.1.0 → 0.2.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.
Files changed (51) hide show
  1. package/README.md +94 -26
  2. package/bin/create-admin.mjs +112 -20
  3. package/bin/gen-registry-snapshot.mjs +32 -0
  4. package/bin/registry-snapshot.json +13 -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-arco/AGENTS.md +33 -0
  9. package/templates/admin-semi/AGENTS.md +33 -0
  10. package/templates/base-semi/AGENTS.md +30 -0
  11. package/templates/base-semi/README.md +35 -0
  12. package/templates/base-semi/archetype.json +13 -0
  13. package/templates/base-semi/conf/nginx.conf.example +19 -0
  14. package/templates/base-semi/index.html +11 -0
  15. package/templates/base-semi/package.json +29 -0
  16. package/templates/base-semi/src/app/micro-apps.config.ts +16 -0
  17. package/templates/base-semi/src/app/router.tsx +44 -0
  18. package/templates/base-semi/src/layouts/basic-layout.tsx +51 -0
  19. package/templates/base-semi/src/main.tsx +22 -0
  20. package/templates/base-semi/src/micro/shared.ts +43 -0
  21. package/templates/base-semi/src/micro/sub-app.tsx +65 -0
  22. package/templates/base-semi/src/pages/dashboard.tsx +16 -0
  23. package/templates/base-semi/src/pages/login.tsx +18 -0
  24. package/templates/base-semi/src/pages/not-found.tsx +13 -0
  25. package/templates/base-semi/src/stores/auth.ts +13 -0
  26. package/templates/base-semi/src/ui/theme.ts +7 -0
  27. package/templates/base-semi/src/vite-env.d.ts +1 -0
  28. package/templates/base-semi/tsconfig.json +16 -0
  29. package/templates/base-semi/vite.config.ts +17 -0
  30. package/templates/sub-semi/AGENTS.md +33 -0
  31. package/templates/sub-semi/README.md +28 -0
  32. package/templates/sub-semi/archetype.json +13 -0
  33. package/templates/sub-semi/index.html +11 -0
  34. package/templates/sub-semi/package.json +34 -0
  35. package/templates/sub-semi/src/app/root.tsx +23 -0
  36. package/templates/sub-semi/src/app/router.tsx +26 -0
  37. package/templates/sub-semi/src/events.ts +4 -0
  38. package/templates/sub-semi/src/features/invoices/api.ts +19 -0
  39. package/templates/sub-semi/src/features/invoices/hooks/use-invoices.ts +18 -0
  40. package/templates/sub-semi/src/layouts/sub-layout.tsx +12 -0
  41. package/templates/sub-semi/src/main.tsx +28 -0
  42. package/templates/sub-semi/src/pages/invoices.tsx +49 -0
  43. package/templates/sub-semi/src/supply/integrated.ts +48 -0
  44. package/templates/sub-semi/src/supply/shims/contract.ts +25 -0
  45. package/templates/sub-semi/src/supply/standalone.ts +38 -0
  46. package/templates/sub-semi/src/supply/types.ts +18 -0
  47. package/templates/sub-semi/src/types/index.ts +6 -0
  48. package/templates/sub-semi/src/ui/popup.ts +13 -0
  49. package/templates/sub-semi/src/vite-env.d.ts +1 -0
  50. package/templates/sub-semi/tsconfig.json +20 -0
  51. 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,13 @@ 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.2.0";
28
36
 
29
37
  const servicePattern = /^[a-z][a-z0-9-]{1,31}$/;
30
38
  // 禁裸通用词(registry.md 一-1/一-2 口径摘录,与 golang yarch-init 同款;完整表以 contract/registry.md 为准)。
@@ -40,11 +48,13 @@ const PLACEHOLDER = /\{\{\s*([a-zA-Z][a-zA-Z0-9]*)\s*\}\}/g;
40
48
 
41
49
  function usage() {
42
50
  return [
43
- "用法:create-admin <工程名|输出目录> [--name <名>] [--ui semi|antd|arco] [--desc <描述>]",
44
- " [--port <端口>] [--proxy <目标>] [--group <GitLab分组>] [--deps file|version]",
45
- " [--src <模板根>] [--out <目录>] [--yes]",
51
+ "用法:create-admin <工程名|输出目录> [--name <名>] [--ui semi|antd|arco] [--micro base|sub]",
52
+ " [--registry <registry.md 路径>] [--desc <描述>] [--port <端口>] [--proxy <目标>]",
53
+ " [--group <GitLab分组>] [--deps file|version] [--src <模板根>] [--out <目录>] [--yes]",
54
+ " --micro 微前端模板档(W10):base=基座 / sub=子应用;缺省=单应用 admin(不受微前端规约约束)",
55
+ " --registry 微前端名称核对用的 contract/registry.md 路径(默认用包内快照 bin/registry-snapshot.json)",
46
56
  " --yes 非交互:缺省项全走默认值(CI 用)",
47
- " --deps @yarch 底座依赖形态:version(默认,^0.1.0,需 @yarch 已发 npm)| file(发版前本地过渡)",
57
+ " --deps @yarch 底座依赖形态:version(默认,^0.2.0,需 @yarch 已发 npm)| file(发版前本地过渡)",
48
58
  ].join("\n");
49
59
  }
50
60
 
@@ -77,6 +87,43 @@ function validateName(name) {
77
87
  return null;
78
88
  }
79
89
 
90
+ // —— 微前端应用名校验(micro-frontend.md 二-1/二-4,十三表「创建期」机检)——
91
+
92
+ // 快照来源:--registry 指向的 registry.md(CI/内网用最新表)优先,否则用随包分发的快照。
93
+ function loadRegistry(registryArg) {
94
+ if (registryArg) {
95
+ return parseRegistryMd(readFileSync(resolve(registryArg), "utf8"));
96
+ }
97
+ try {
98
+ const snapshot = JSON.parse(readFileSync(join(PKG_ROOT, "bin", "registry-snapshot.json"), "utf8"));
99
+ return {
100
+ serviceNames: new Set(snapshot.serviceNames),
101
+ appNames: new Set(snapshot.appNames),
102
+ };
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
107
+
108
+ function validateMicroName(name, registry) {
109
+ const baseErr = validateName(name);
110
+ if (baseErr) return baseErr;
111
+ if (!registry) {
112
+ return "无法核对 registry.md(缺 bin/registry-snapshot.json 且未传 --registry <path>)——微前端应用名必须核对登记表(二-1)";
113
+ }
114
+ if (!name.includes("-")) {
115
+ return "微前端应用名须由「服务名-用途/域」两段及以上构成(micro-frontend.md 二-1,正例 ysaas-billing)";
116
+ }
117
+ const service = name.split("-")[0];
118
+ if (!registry.serviceNames.has(service)) {
119
+ return `首段 ${service} 不在 registry.md 二节已登记服务名表(二-1);若快照过期,用 --registry 指向最新 contract/registry.md`;
120
+ }
121
+ if (registry.appNames.has(name)) {
122
+ return `应用名 ${name} 已在 registry.md 四节登记(二-4:跨项目不得重名)`;
123
+ }
124
+ return null;
125
+ }
126
+
80
127
  async function askText(rl, question, { fallback = "", validate = null, optional = false } = {}) {
81
128
  for (;;) {
82
129
  const hint = fallback ? `(默认 ${fallback})` : optional ? "(可空)" : "";
@@ -144,12 +191,23 @@ async function main() {
144
191
  const args = parseArgs(process.argv.slice(2));
145
192
  const nameArg = args.name || (args._.length > 0 && !args._[0].startsWith("/") ? args._[0] : null);
146
193
 
194
+ let micro = args.micro;
195
+ if (micro !== undefined && !(micro in MICRO_KINDS)) {
196
+ fatal(`--micro 须为 ${Object.keys(MICRO_KINDS).join("|")},当前:${micro}`);
197
+ }
147
198
  const ui = args.ui ?? "semi";
148
199
  if (!(ui in UI_TIERS)) fatal(`--ui 须为 ${Object.keys(UI_TIERS).join("|")},当前:${ui}`);
200
+ if (micro && ui !== "semi") {
201
+ fatal("微前端模板档本批仅 semi(W1 默认档);其余 UI 档触发式扩展(PLAN.md W10)");
202
+ }
149
203
 
150
204
  const interactive = !args.yes && process.stdin.isTTY;
151
205
  if (!nameArg && !interactive) fatal(`工程名必填(或用 --yes 走交互外模式):\n${usage()}`);
152
206
 
207
+ // 微前端形态才需要登记表核对(二-1 首段=已登记服务名;单应用仍走一-1/一-2 正则+禁裸词)。
208
+ const needsRegistry = micro !== undefined || interactive; // 交互形态问答后可能选 micro
209
+ const registry = needsRegistry ? loadRegistry(args.registry) : null;
210
+
153
211
  const rl = interactive ? readline.createInterface({ input: process.stdin, output: process.stdout }) : null;
154
212
  try {
155
213
  let name = nameArg;
@@ -160,17 +218,34 @@ async function main() {
160
218
 
161
219
  if (interactive) {
162
220
  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));
221
+ if (micro === undefined) {
222
+ const kindIdx = await askSelect(
223
+ rl,
224
+ "工程形态(micro-frontend.md 一:单应用不受微前端规约约束;基座全局唯一、子应用双运行形态)",
225
+ [
226
+ "单应用 admin(默认,npm create @yarch/admin 常规形态)",
227
+ "微前端基座 base(--micro base)",
228
+ "微前端子应用 sub(--micro sub)",
229
+ ],
230
+ 0,
231
+ );
232
+ micro = kindIdx === 0 ? undefined : kindIdx === 1 ? "base" : "sub";
233
+ }
234
+ const validator = micro ? (v) => validateMicroName(v, registry) : validateName;
235
+ const nameLabel = micro
236
+ ? "应用名(= 路由前缀 = storage/事件前缀,二-2 一名三用;首段=已登记服务名)"
237
+ : "工程名(= npm 包名 = registry 服务名)";
238
+ if (!name) name = await askText(rl, nameLabel, { validate: validator });
239
+ else if (validator(name)) fatal(validator(name));
165
240
  if (!description) {
166
241
  description = await askText(rl, "工程描述", {
167
- fallback: `${name}:基于 yarch web 脚手架生成的中后台工程`,
242
+ fallback: `${name}:基于 yarch web 脚手架生成的${micro ? (micro === "base" ? "微前端基座" : "微前端子应用") : "中后台"}工程`,
168
243
  validate: (v) => (v.includes('"') || v.includes("\\") ? "不得包含双引号与反斜杠(要写进 package.json)" : null),
169
244
  });
170
245
  }
171
246
  if (!port) {
172
247
  port = await askText(rl, "dev 端口", {
173
- fallback: "5173",
248
+ fallback: micro === "sub" ? "5174" : "5173",
174
249
  validate: (v) => (/^\d{4,5}$/.test(v) && Number(v) >= 1024 && Number(v) <= 65535 ? null : "1024~65535 的数字端口号"),
175
250
  });
176
251
  }
@@ -184,10 +259,10 @@ async function main() {
184
259
  }
185
260
 
186
261
  if (!name) fatal(`工程名缺失。\n${usage()}`);
187
- const nameErr = validateName(name);
262
+ const nameErr = micro ? validateMicroName(name, registry) : validateName(name);
188
263
  if (nameErr) fatal(`工程名 ${JSON.stringify(name)} ${nameErr}`);
189
- description ??= `${name}:基于 yarch web 脚手架生成的中后台工程`;
190
- port ??= "5173";
264
+ description ??= `${name}:基于 yarch web 脚手架生成的${micro ? (micro === "base" ? "微前端基座" : "微前端子应用") : "中后台"}工程`;
265
+ port ??= micro === "sub" ? "5174" : "5173";
191
266
  proxyTarget ??= "http://localhost:8080";
192
267
  group ??= "";
193
268
  if (!/^\d{4,5}$/.test(port) || Number(port) < 1024 || Number(port) > 65535) {
@@ -197,14 +272,22 @@ async function main() {
197
272
  fatal(`代理目标 ${proxyTarget} 无效:须为 http(s):// 开头`);
198
273
  }
199
274
 
200
- // @yarch 底座依赖形态(W8):version = ^0.1.0(标准形态,@yarch/contract、@yarch/react 已发 npm);
275
+ // @yarch 底座依赖形态(W8):version = 版本依赖(标准形态,@yarch/contract、@yarch/react 已发 npm);
201
276
  // file: 绝对路径 = golang replace 行对偶(发版前本地过渡,yarch 源码变更后重跑 pnpm install 刷新)。
202
277
  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")}`;
278
+ const yarchContractDep = depsMode === "version" ? YARCH_VERSION : `file:${resolve(PKG_ROOT, "../contract")}`;
279
+ const yarchReactDep = depsMode === "version" ? YARCH_VERSION : `file:${resolve(PKG_ROOT, "../react")}`;
206
280
 
207
- const vars = { packageName: name, appName: name, description, port, proxyTarget, yarchContractDep, yarchReactDep };
281
+ const vars = {
282
+ packageName: name,
283
+ appName: name,
284
+ service: name.split("-")[0],
285
+ description,
286
+ port,
287
+ proxyTarget,
288
+ yarchContractDep,
289
+ yarchReactDep,
290
+ };
208
291
 
209
292
  const outDir = resolve(args.out || (args._.length > 0 ? args._[args._.length - 1] : name));
210
293
  let existing;
@@ -215,7 +298,9 @@ async function main() {
215
298
  }
216
299
  if (existing && existing.length > 0) fatal(`输出目录 ${outDir} 非空`);
217
300
 
218
- const templateDir = resolve(args.src || join(PKG_ROOT, "templates", `admin-${ui}`));
301
+ const templateDir = resolve(
302
+ args.src || join(PKG_ROOT, "templates", micro ? `${micro}-semi` : `admin-${ui}`),
303
+ );
219
304
  try {
220
305
  readFileSync(join(templateDir, "archetype.json"));
221
306
  } catch {
@@ -230,12 +315,19 @@ async function main() {
230
315
 
231
316
  const tierDesc = JSON.parse(readFileSync(join(templateDir, "archetype.json"), "utf8")).description;
232
317
  const groupHint = group ? `(属主建议:${group}/${name})` : "";
233
- console.log(`✅ 已生成 ${outDir}(${n} 个文件,admin-${ui} 档)—— ${tierDesc}
318
+ const tierLabel = micro ? `${micro}-semi 档` : `admin-${ui} 档`;
319
+ const registryHint = micro
320
+ ? `2. 应用名已置为 ${JSON.stringify(name)}——去 yarch 仓 contract/registry.md 四节前端应用名登记表登记${groupHint}
321
+ 3. ${micro === "base"
322
+ ? "基座:micro-apps.config.ts 是子应用入口登记表(十二-2,git 纳管);子应用接入只改此表"
323
+ : "子应用:在基座仓 micro-apps.config.ts 登记入口(十二-2);pnpm dev 独立运行 / pnpm dev:micro 被基座加载(十一)"}`
324
+ : `2. 服务名已置为 ${JSON.stringify(name)}——去 yarch 仓 contract/registry.md 登记表登记${groupHint}`;
325
+ console.log(`✅ 已生成 ${outDir}(${n} 个文件,${tierLabel})—— ${tierDesc}
234
326
 
235
327
  下一步:
236
328
  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 形态重新生成或手动替换)"}
329
+ ${registryHint}
330
+ ${micro ? "4" : "3"}. @yarch 底座为 ${depsMode === "version" ? `${YARCH_VERSION} 版本依赖(升级 = pnpm update @yarch/contract @yarch/react)` : "file: 本地依赖(发版前过渡;正式发版后改用默认 version 形态重新生成或手动替换)"}
239
331
  `);
240
332
  } finally {
241
333
  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,13 @@
1
+ {
2
+ "source": "contract/registry.md",
3
+ "generatedAt": "2026-09-16T13:10:34.107Z",
4
+ "serviceNames": [
5
+ "yagent",
6
+ "ybookreading",
7
+ "ysaas"
8
+ ],
9
+ "appNames": [
10
+ "ysaas-billing",
11
+ "ysaas-console"
12
+ ]
13
+ }
@@ -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.0",
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,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,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,30 @@
1
+ # AGENTS.md — {{packageName}}(微前端基座)工程守则
2
+
3
+ > 本工程由 yarch 脚手架生成(`--micro base`),基于跨栈统一契约(yarch 仓 `contract/`)与微前端规约 `contract/web/micro-frontend.md`。
4
+ > 任何 AI 编码代理改码前先读本文件;条文与直觉冲突时以本文件为准,红线改动直接拒绝——机检与 CR 均按此执行。
5
+
6
+ ## 基座五权(只归基座,条文 micro-frontend.md 一-1)
7
+
8
+ 登录态唯一持有(十-1)· 导航端口唯一注册(十-2)· 菜单与路由分发(三-4)· 主题下发(六-1)· 错误兜底(四-5)。子应用索要任何一项 = 架构违规。
9
+
10
+ ## 契约红线(违反 = 机检失败 / CR 必拒)
11
+
12
+ | 红线 | 规则 | 出处 |
13
+ |---|---|---|
14
+ | manifest 装配 | 子应用接入/下线**只改 `src/app/micro-apps.config.ts` 登记表**(entry/version/routePrefix/menu);禁在任何组件里硬编码子应用路由或菜单 | 十二-2 / 三-4 |
15
+ | 登记先行 | 新子应用应用名须先在 yarch 仓 `contract/registry.md` 四节登记(两段式、首段=已登记服务名、全局不重名),再入本工程登记表 | registry.md 四 |
16
+ | 共享运行时 | `src/micro/shared.ts` 注册 `__YARCH_REACT__ / __YARCH_CONTRACT__ / __YARCH_SUPPLY__` 后才能启动载器;新增共享成员须同步子应用供给层口径 | 八-1 / 八-2 |
17
+ | 事件订阅宿主 | 子应用事件(`{应用名}:{动词-名词}`)的订阅挂**常驻布局壳**(`layouts/basic-layout.tsx`),不挂会随路由卸载的页面;订阅与退订成对 | 九-1 / 七-4 |
18
+ | 统一信封 / 错误码 / traceId | 同单应用档:判错只看 `code`,错误码只用常量,报障必附 traceId | contract/api/* |
19
+ | 底座升级 | `@yarch/contract`·`@yarch/react`·`@micro-zoe/micro-app` 一律 `pnpm update`;禁 patch / 内联拷贝 | — |
20
+
21
+ ## 验证(改完必跑)
22
+
23
+ ```bash
24
+ pnpm build # tsc --noEmit + vite build
25
+ pnpm build:micro 同款子应用联调:入口 URL 经 VITE_SUB_*_URL 注入,禁改死代码
26
+ ```
27
+
28
+ ## 完成定义(DoD)
29
+
30
+ tsc 零错 · build 通过 · 新子应用接入只动了登记表 · 基座在子应用全部下线后仍可独立运行(三-1)。
@@ -0,0 +1,35 @@
1
+ # {{appName}} · 微前端基座
2
+
3
+ > {{description}}。规约依据:[contract/web/micro-frontend.md](https://github.com/ydonghao/yarch/blob/main/contract/web/micro-frontend.md) v1.0;
4
+ > 载器 micro-app(contract/README.md 关键架构决策登记表默认档,观察哨:停更切 qiankun)。
5
+
6
+ ## 五权归基座(三-2/十-1/十-2)
7
+
8
+ 登录态与 token、菜单/路由分发、主题与 UI 档定义、全局错误兜底、布局外壳——全部在本工程;
9
+ 子应用经 `__YARCH_SUPPLY__`(props 下行,五-5)与共享 contract 单例(八-1)取用,禁子应用自建。
10
+
11
+ ## 开发
12
+
13
+ ```bash
14
+ pnpm install && pnpm dev # http://localhost:{{port}}
15
+ ```
16
+
17
+ ## 子应用接入三步(十二-2:入口变更只改 manifest)
18
+
19
+ 1. 生成子应用:`npm create @yarch/admin ysaas-billing -- --micro sub --registry <contract/registry.md>`
20
+ 2. 在 `src/app/micro-apps.config.ts` 登记入口/路由前缀/菜单元数据(跨环境入口用 `.env.local` 覆盖,十二-7)
21
+ 3. 子应用侧起 `pnpm dev:micro`(集成模式 dev server,本基座即可加载)
22
+
23
+ ## 双模式冒烟(十一-3,CI 口径)
24
+
25
+ - 子应用独立:`pnpm build`(自足产物)
26
+ - 子应用集成:`pnpm build:micro`(框架/contract 经 window 全局 shim,产物体积消失——八-1/八-2 机检对象)
27
+
28
+ ## 部署(十二-3/十二-4)
29
+
30
+ 构建产物内容寻址(hash 文件名);nginx 一应用一 root + `try_files` 兜底——样例见 `conf/nginx.conf.example`。
31
+
32
+ ## 登记提醒
33
+
34
+ 应用名 `{{packageName}}`(首段服务名 `{{service}}`)须在 yarch 仓 `contract/registry.md` 四节前端应用名登记表登记(二-1/二-4);
35
+ storage key / 事件名 / 路由前缀均以应用名为前缀(二-2 一名三用)。
@@ -0,0 +1,13 @@
1
+ {
2
+ "description": "yarch 微前端基座模板 · Semi 档:micro-app 载器(默认档)+ manifest 声明式装配 + 登录态/导航/主题/错误兜底五权归基座",
3
+ "variables": [
4
+ { "name": "packageName", "desc": "应用名(= npm 包名 = 路由前缀 = storage/事件前缀,micro-frontend.md 二-1/二-2)", "required": true, "default": "" },
5
+ { "name": "appName", "desc": "展示名(index.html 标题)", "required": false, "default": "= packageName" },
6
+ { "name": "service", "desc": "首段服务名(registry.md 已登记,二-1)", "required": false, "default": "" },
7
+ { "name": "description", "desc": "工程描述(package.json)", "required": false, "default": "" },
8
+ { "name": "port", "desc": "vite dev 端口", "required": false, "default": "5173" },
9
+ { "name": "proxyTarget", "desc": "/api/v1 反向代理目标", "required": false, "default": "http://localhost:8080" },
10
+ { "name": "yarchContractDep", "desc": "@yarch/contract 依赖(发版前 file: 本地路径,发版后 ^x.y.z)", "required": false, "default": "" },
11
+ { "name": "yarchReactDep", "desc": "@yarch/react 依赖(同上)", "required": false, "default": "" }
12
+ ]
13
+ }