@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.
- package/README.md +94 -26
- package/bin/create-admin.mjs +115 -20
- package/bin/gen-registry-snapshot.mjs +32 -0
- package/bin/registry-snapshot.json +15 -0
- package/bin/registry.mjs +19 -0
- package/package.json +2 -2
- package/templates/admin-antd/AGENTS.md +33 -0
- package/templates/admin-antd/CLAUDE.md +1 -0
- package/templates/admin-antd/GEMINI.md +1 -0
- package/templates/admin-antd/archetype.json +48 -7
- package/templates/admin-antd/package.json +1 -0
- package/templates/admin-arco/AGENTS.md +33 -0
- package/templates/admin-arco/CLAUDE.md +1 -0
- package/templates/admin-arco/GEMINI.md +1 -0
- package/templates/admin-semi/AGENTS.md +33 -0
- package/templates/admin-semi/CLAUDE.md +1 -0
- package/templates/admin-semi/GEMINI.md +1 -0
- package/templates/base-semi/AGENTS.md +30 -0
- package/templates/base-semi/CLAUDE.md +1 -0
- package/templates/base-semi/GEMINI.md +1 -0
- package/templates/base-semi/README.md +35 -0
- package/templates/base-semi/archetype.json +13 -0
- package/templates/base-semi/conf/nginx.conf.example +19 -0
- package/templates/base-semi/index.html +11 -0
- package/templates/base-semi/package.json +29 -0
- package/templates/base-semi/src/app/micro-apps.config.ts +16 -0
- package/templates/base-semi/src/app/router.tsx +44 -0
- package/templates/base-semi/src/layouts/basic-layout.tsx +51 -0
- package/templates/base-semi/src/main.tsx +22 -0
- package/templates/base-semi/src/micro/shared.ts +43 -0
- package/templates/base-semi/src/micro/sub-app.tsx +65 -0
- package/templates/base-semi/src/pages/dashboard.tsx +16 -0
- package/templates/base-semi/src/pages/login.tsx +18 -0
- package/templates/base-semi/src/pages/not-found.tsx +13 -0
- package/templates/base-semi/src/stores/auth.ts +13 -0
- package/templates/base-semi/src/ui/theme.ts +7 -0
- package/templates/base-semi/src/vite-env.d.ts +1 -0
- package/templates/base-semi/tsconfig.json +16 -0
- package/templates/base-semi/vite.config.ts +17 -0
- package/templates/sub-semi/AGENTS.md +33 -0
- package/templates/sub-semi/CLAUDE.md +1 -0
- package/templates/sub-semi/GEMINI.md +1 -0
- package/templates/sub-semi/README.md +28 -0
- package/templates/sub-semi/archetype.json +13 -0
- package/templates/sub-semi/index.html +11 -0
- package/templates/sub-semi/package.json +34 -0
- package/templates/sub-semi/src/app/root.tsx +23 -0
- package/templates/sub-semi/src/app/router.tsx +26 -0
- package/templates/sub-semi/src/events.ts +4 -0
- package/templates/sub-semi/src/features/invoices/api.ts +19 -0
- package/templates/sub-semi/src/features/invoices/hooks/use-invoices.ts +18 -0
- package/templates/sub-semi/src/layouts/sub-layout.tsx +12 -0
- package/templates/sub-semi/src/main.tsx +28 -0
- package/templates/sub-semi/src/pages/invoices.tsx +49 -0
- package/templates/sub-semi/src/supply/integrated.ts +48 -0
- package/templates/sub-semi/src/supply/shims/contract.ts +25 -0
- package/templates/sub-semi/src/supply/standalone.ts +38 -0
- package/templates/sub-semi/src/supply/types.ts +18 -0
- package/templates/sub-semi/src/types/index.ts +6 -0
- package/templates/sub-semi/src/ui/popup.ts +13 -0
- package/templates/sub-semi/src/vite-env.d.ts +1 -0
- package/templates/sub-semi/tsconfig.json +20 -0
- package/templates/sub-semi/vite.config.ts +96 -0
package/README.md
CHANGED
|
@@ -1,42 +1,110 @@
|
|
|
1
|
-
# @yarch/create-admin
|
|
1
|
+
# @yarch/create-admin
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
3
|
+
> 一条命令生成**生成即合规**的中后台工程——契约底座、命名标准、结构纪律全部预置,三档 UI 任选。
|
|
4
|
+
> npm:[@yarch/create-admin](https://www.npmjs.com/package/@yarch/create-admin)(v0.1.0+)
|
|
5
5
|
|
|
6
|
-
##
|
|
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
|
-
#
|
|
10
|
-
npm create @yarch/admin@latest ysaas-console
|
|
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
|
-
#
|
|
14
|
-
|
|
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
|
-
|
|
41
|
+
- **接后端**:`vite.config.ts` 已预配 `/api/v1` 反向代理,改 target 即指向你的服务;
|
|
42
|
+
- **升级底座**:`pnpm update @yarch/contract @yarch/react`;
|
|
43
|
+
- **登记**:按生成后的控制台提醒,去 yarch 仓 `contract/registry.md` 登记服务名(PR 即登记)。
|
|
18
44
|
|
|
19
|
-
##
|
|
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
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
### 原理(一段话)
|
|
33
104
|
|
|
34
|
-
|
|
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
|
-
|
|
107
|
+
### 发布(维护者)
|
|
41
108
|
|
|
42
|
-
推 tag `stacks/web/vX.Y.Z`
|
|
109
|
+
推 tag `stacks/web/vX.Y.Z` → [web-publish.yml](../../../.github/workflows/web-publish.yml):断言 + 机检 → contract → react → create 顺序发布(幂等)。
|
|
110
|
+
</details>
|
package/bin/create-admin.mjs
CHANGED
|
@@ -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] [--
|
|
44
|
-
" [--
|
|
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.
|
|
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 (
|
|
164
|
-
|
|
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 =
|
|
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
|
-
|
|
205
|
-
const
|
|
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 = {
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
238
|
-
3. @yarch 底座为 ${depsMode === "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} 个`);
|
package/bin/registry.mjs
ADDED
|
@@ -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
|
|
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/
|
|
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
|
-
{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
{
|
|
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
|
}
|
|
@@ -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 层。
|