@liujitcn/kratos-admin-cli 0.0.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 (32) hide show
  1. package/README.md +98 -0
  2. package/dist/index.d.ts +16 -0
  3. package/dist/index.js +247 -0
  4. package/package.json +33 -0
  5. package/templates/business-workspace/README.md +63 -0
  6. package/templates/business-workspace/_gitignore +7 -0
  7. package/templates/business-workspace/apps/admin/.env +6 -0
  8. package/templates/business-workspace/apps/admin/.env.development +7 -0
  9. package/templates/business-workspace/apps/admin/.env.production +8 -0
  10. package/templates/business-workspace/apps/admin/README.md +54 -0
  11. package/templates/business-workspace/apps/admin/index.html +12 -0
  12. package/templates/business-workspace/apps/admin/package.json +13 -0
  13. package/templates/business-workspace/apps/admin/src/main.ts +4 -0
  14. package/templates/business-workspace/apps/admin/src/module-manifest.ts +29 -0
  15. package/templates/business-workspace/apps/admin/src/modules.ts +6 -0
  16. package/templates/business-workspace/apps/admin/src/vite-env.d.ts +1 -0
  17. package/templates/business-workspace/apps/admin/tsconfig.json +5 -0
  18. package/templates/business-workspace/apps/admin/vite.config.ts +7 -0
  19. package/templates/business-workspace/package.json +24 -0
  20. package/templates/business-workspace/packages/modules/__MODULE_NAME__/README.md +68 -0
  21. package/templates/business-workspace/packages/modules/__MODULE_NAME__/package.json +41 -0
  22. package/templates/business-workspace/packages/modules/__MODULE_NAME__/src/api/index.ts +2 -0
  23. package/templates/business-workspace/packages/modules/__MODULE_NAME__/src/index.ts +1 -0
  24. package/templates/business-workspace/packages/modules/__MODULE_NAME__/src/module.ts +10 -0
  25. package/templates/business-workspace/packages/modules/__MODULE_NAME__/src/rpc/README.md +14 -0
  26. package/templates/business-workspace/packages/modules/__MODULE_NAME__/src/views/index/index.vue +15 -0
  27. package/templates/business-workspace/packages/modules/__MODULE_NAME__/tsconfig.json +12 -0
  28. package/templates/business-workspace/packages/modules/__MODULE_NAME__/tsconfig.package.json +12 -0
  29. package/templates/business-workspace/pnpm-workspace.yaml +4 -0
  30. package/templates/business-workspace/scripts/build-package.mjs +21 -0
  31. package/templates/business-workspace/tsconfig.json +22 -0
  32. package/templates/business-workspace/turbo.json +21 -0
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # @liujitcn/kratos-admin-cli
2
+
3
+ 用于创建 kratos-admin 业务项目的公开命令行工具。生成结果是一个可独立安装、开发和构建的 pnpm workspace,其中宿主保持轻量,业务实现位于可独立发布的模块包。
4
+
5
+ ## 目录与文件
6
+
7
+ ```text
8
+ packages/cli
9
+ ├── src
10
+ │ ├── index.ts
11
+ │ └── index.test.ts
12
+ ├── templates/business-workspace
13
+ │ ├── apps/admin
14
+ │ ├── packages/modules/__MODULE_NAME__ # 可重复渲染的 module 模板
15
+ │ ├── scripts/build-package.mjs
16
+ │ ├── package.json
17
+ │ ├── pnpm-workspace.yaml
18
+ │ ├── README.md
19
+ │ ├── tsconfig.json
20
+ │ └── turbo.json
21
+ ├── package.json
22
+ ├── README.md
23
+ └── tsconfig.json
24
+ ```
25
+
26
+ | 路径 | 作用 |
27
+ | ---------------------------------------------------------------- | ------------------------------------------------------------ |
28
+ | `src/index.ts` | 解析 `create` 命令并生成宿主与一个或多个业务 module。 |
29
+ | `src/index.test.ts` | 验证多 module、默认 System、README 和依赖生成行为。 |
30
+ | `templates/business-workspace/` | 完整业务 workspace 模板;目录和文本中的占位符由 CLI 替换。 |
31
+ | `templates/business-workspace/apps/admin/` | 生成项目的薄宿主模板。 |
32
+ | `templates/business-workspace/packages/modules/__MODULE_NAME__/` | 生成项目的可发布业务模块模板。 |
33
+ | `templates/business-workspace/scripts/build-package.mjs` | 生成业务模块发布源码和声明文件。 |
34
+ | `package.json` | 声明 `kratos-admin` 可执行命令、脚本、Node 版本和发布配置。 |
35
+ | `README.md` | CLI 使用方法、模板结构和维护说明。 |
36
+ | `tsconfig.json` | CLI 源码、测试的编译配置和 `dist` 输出规则。 |
37
+
38
+ 模板内部每个含 `package.json` 的目录都有同级 README,生成时目录名、包名、模块变量和文档占位符会一起替换。
39
+
40
+ ## 使用
41
+
42
+ ```bash
43
+ pnpm dlx @liujitcn/kratos-admin-cli create shop-admin --module shop
44
+ pnpm dlx @liujitcn/kratos-admin-cli create shop-admin --module shop,order
45
+ pnpm dlx @liujitcn/kratos-admin-cli create shop-admin --module shop --with audit
46
+
47
+ # 当前仓库开发
48
+ pnpm module:create ../shop-admin --module shop
49
+ pnpm module:create ../shop-admin --module shop,order
50
+ pnpm module:create ../shop-admin --module shop --module order
51
+ pnpm module:create ../shop-admin --module shop,order --with audit
52
+ pnpm --filter @liujitcn/kratos-admin-cli test
53
+ ```
54
+
55
+ CLI 始终先引入 `@liujitcn/kratos-admin-system`。`--module` 可重复使用,也接受逗号分隔名称,并为每个名称创建独立 module 包;`--with` 接收逗号分隔的额外已发布 module 名称,只装配依赖而不生成源码。CLI 拒绝覆盖已有目录;渲染失败时会清理本次创建的不完整目标目录。
56
+
57
+ 生成结果遵守以下约束:
58
+
59
+ - 宿主通过 `src/module-manifest.ts` 维护装配清单,`src/modules.ts` 默认导出当前全部 module。
60
+ - 业务视图按 `<module>/<view>` 注册,后端菜单必须使用模块前缀。
61
+ - 业务模块只依赖 core 的公开 npm Interface,不引用 core 源码路径。
62
+ - API 按 Proto 一级领域组织,RPC 保留完整 Proto 目录层级。
63
+ - 静态页只能通过 `AdminModule.staticViews` 显式替换。
64
+
65
+ ## 模板占位符
66
+
67
+ | 占位符 | 生成内容 |
68
+ | ----------------------- | ---------------------------------------- |
69
+ | `__PROJECT_NAME__` | 项目目录名称,例如 `shop-admin`。 |
70
+ | `__APP_PACKAGE__` | 宿主包名,例如 `@shop/admin-app`。 |
71
+ | `__APP_DEPENDENCIES__` | System、自有 module 和额外 module 依赖。 |
72
+ | `__MODULE_NAME__` | kebab-case 模块名,例如 `shop`。 |
73
+ | `__MODULE_PASCAL__` | PascalCase 模块名,例如 `Shop`。 |
74
+ | `__MODULE_PACKAGE__` | 模块包名,例如 `@shop/admin-module`。 |
75
+ | `__MODULE_IDENTIFIER__` | 模块入口变量,例如 `shopAdminModule`。 |
76
+ | `__CORE_VERSION__` | 当前 core 包版本对应的 semver 范围。 |
77
+ | `__MODULE_FILTERS__` | 全部自有 module 的 workspace 过滤参数。 |
78
+ | `__MODULE_MANIFEST__` | 宿主模块加载器、包名和预构建依赖清单。 |
79
+ | `__MODULE_NAMES__` | 文档中的全部自有 module 名称。 |
80
+ | `__MODULE_PACKAGES__` | 文档中的全部自有 module 包名。 |
81
+ | `__MODULE_PATHS__` | 全部自有 module 的 TypeScript 路径映射。 |
82
+ | `__MODULE_TREE__` | 文档中的自有 module 目录树。 |
83
+ | `__MODULE_TABLE_ROWS__` | 文档中的自有 module 目录说明。 |
84
+
85
+ ## 维护与验证
86
+
87
+ 修改 CLI 或模板后执行:
88
+
89
+ ```bash
90
+ pnpm --filter @liujitcn/kratos-admin-cli type:check
91
+ pnpm --filter @liujitcn/kratos-admin-cli test
92
+ ```
93
+
94
+ 测试会生成临时 workspace,并检查根、宿主、模块和 RPC README 中的占位符均已替换。模板新增包或改变目录职责时,必须同步更新对应目录的 README 和 `src/index.test.ts` 断言。
95
+
96
+ 发布包包含编译后的 `dist/index.js`、类型声明、workspace 模板和本 README。仓库级
97
+ `make -C frontend package-admin` 会先执行测试和构建,再生成
98
+ `@liujitcn/kratos-admin-cli` tarball。
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /** 创建业务 workspace 的参数。 */
3
+ export interface CreateWorkspaceOptions {
4
+ /** 项目目录名称或路径。 */
5
+ projectName: string;
6
+ /** 需要创建的业务模块名称,使用 kebab-case。 */
7
+ moduleNames: string[];
8
+ /** 宿主额外装配的业务模块名称。 */
9
+ additionalModules?: string[];
10
+ /** 生成命令的工作目录。 */
11
+ cwd?: string;
12
+ }
13
+ /** 创建包含宿主和业务模块包的 pnpm workspace。 */
14
+ export declare function createBusinessWorkspace(options: CreateWorkspaceOptions): Promise<string>;
15
+ /** 解析命令行并执行对应命令。 */
16
+ export declare function runCli(args?: string[]): Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,247 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
6
+ const templateRoot = resolve(packageRoot, "templates/business-workspace");
7
+ const gitignoreTemplateName = "_gitignore";
8
+ const kebabNamePattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
9
+ const officialModuleOptimizeDependencies = {
10
+ system: ["swagger-ui-dist/swagger-ui-bundle.js"]
11
+ };
12
+ /** 创建包含宿主和业务模块包的 pnpm workspace。 */
13
+ export async function createBusinessWorkspace(options) {
14
+ const cwd = options.cwd ?? process.cwd();
15
+ const target = resolve(cwd, options.projectName);
16
+ const projectName = basename(target);
17
+ const moduleNames = normalizeModuleNames(options.moduleNames);
18
+ const primaryModuleName = moduleNames[0];
19
+ const additionalModules = normalizeAdditionalModules(options.additionalModules ?? [], moduleNames);
20
+ validateName(projectName, "项目名称");
21
+ if (await pathExists(target))
22
+ throw new Error(`目标目录已存在,拒绝覆盖: ${target}`);
23
+ const packageVersion = await readCorePackageVersion();
24
+ const primaryModuleTokens = createModuleTokens(primaryModuleName, packageVersion);
25
+ const moduleManifestEntries = [
26
+ {
27
+ packageName: "@liujitcn/kratos-admin-system",
28
+ moduleIdentifier: "systemAdminModule",
29
+ optimizeDependencies: officialModuleOptimizeDependencies.system
30
+ },
31
+ ...moduleNames.map(name => ({
32
+ packageName: `@${name}/admin-module`,
33
+ moduleIdentifier: `${toCamelCase(name)}AdminModule`,
34
+ optimizeDependencies: []
35
+ })),
36
+ ...additionalModules.map(name => ({
37
+ packageName: `@liujitcn/kratos-admin-${name}`,
38
+ moduleIdentifier: `${toCamelCase(name)}AdminModule`,
39
+ optimizeDependencies: officialModuleOptimizeDependencies[name] ?? []
40
+ }))
41
+ ];
42
+ const moduleManifest = moduleManifestEntries
43
+ .map(entry => {
44
+ const lines = [
45
+ " {",
46
+ ` packageName: "${entry.packageName}",`,
47
+ ` load: async () => (await import("${entry.packageName}")).${entry.moduleIdentifier}`
48
+ ];
49
+ if (entry.optimizeDependencies.length > 0) {
50
+ lines[2] += ",";
51
+ lines.push(` optimizeDependencies: ${JSON.stringify(entry.optimizeDependencies)}`);
52
+ }
53
+ lines.push(" }");
54
+ return lines.join("\n");
55
+ })
56
+ .join(",\n");
57
+ const modulePackages = moduleNames.map(name => `@${name}/admin-module`);
58
+ const appDependencies = {
59
+ "@liujitcn/kratos-admin-core": `^${packageVersion}`,
60
+ "@liujitcn/kratos-admin-system": `^${packageVersion}`,
61
+ ...Object.fromEntries(modulePackages.map(packageName => [packageName, "workspace:*"])),
62
+ ...Object.fromEntries(additionalModules.map(name => [`@liujitcn/kratos-admin-${name}`, `^${packageVersion}`]))
63
+ };
64
+ const modulePaths = Object.fromEntries(moduleNames.flatMap(name => {
65
+ const packageName = `@${name}/admin-module`;
66
+ const moduleRoot = `packages/modules/${name}`;
67
+ return [
68
+ [packageName, [`${moduleRoot}/src/index.ts`]],
69
+ [`${packageName}/api/*`, [`${moduleRoot}/src/api/*`]],
70
+ [`${packageName}/package.json`, [`${moduleRoot}/package.json`]],
71
+ [`${packageName}/rpc/*`, [`${moduleRoot}/src/rpc/*`]]
72
+ ];
73
+ }));
74
+ const tokens = {
75
+ ...primaryModuleTokens,
76
+ __PROJECT_NAME__: projectName,
77
+ __APP_PACKAGE__: `@${primaryModuleName}/admin-app`,
78
+ __APP_DEPENDENCIES__: formatJsonValue(appDependencies, " "),
79
+ __MODULE_FILTERS__: modulePackages.map(packageName => `--filter=${packageName}`).join(" "),
80
+ __MODULE_MANIFEST__: moduleManifest,
81
+ __MODULE_NAMES__: moduleNames.join("、"),
82
+ __MODULE_PACKAGES__: modulePackages.map(packageName => `\`${packageName}\``).join("、"),
83
+ __MODULE_PATHS__: formatJsonValue(modulePaths, " "),
84
+ __MODULE_TREE__: createModuleTree(moduleNames),
85
+ __MODULE_TABLE_ROWS__: createModuleTableRows(moduleNames)
86
+ };
87
+ await mkdir(target, { recursive: false });
88
+ try {
89
+ await renderDirectory(templateRoot, target, tokens);
90
+ const moduleTemplateRoot = resolve(templateRoot, "packages/modules/__MODULE_NAME__");
91
+ for (const moduleName of moduleNames.slice(1)) {
92
+ await renderDirectory(moduleTemplateRoot, resolve(target, "packages/modules", moduleName), {
93
+ ...createModuleTokens(moduleName, packageVersion),
94
+ __PROJECT_NAME__: projectName
95
+ });
96
+ }
97
+ }
98
+ catch (error) {
99
+ await rm(target, { recursive: true, force: true });
100
+ throw error;
101
+ }
102
+ return target;
103
+ }
104
+ /** 解析命令行并执行对应命令。 */
105
+ export async function runCli(args = process.argv.slice(2)) {
106
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
107
+ printHelp();
108
+ return;
109
+ }
110
+ if (args[0] !== "create")
111
+ throw new Error(`不支持的命令: ${args[0]}`);
112
+ const projectName = args[1];
113
+ const moduleNames = [...readOptions(args, "--module"), ...readOptions(args, "--modules")];
114
+ const withModules = readOptions(args, "--with");
115
+ if (!projectName || moduleNames.length === 0) {
116
+ throw new Error("用法: kratos-admin create <project> --module <module[,module...]>");
117
+ }
118
+ const target = await createBusinessWorkspace({ projectName, moduleNames, additionalModules: withModules });
119
+ process.stdout.write(`已创建业务 workspace: ${target}\n`);
120
+ }
121
+ /** 渲染模板目录中的路径和文本占位符。 */
122
+ async function renderDirectory(source, target, tokens) {
123
+ await mkdir(target, { recursive: true });
124
+ const entries = await readdir(source, { withFileTypes: true });
125
+ for (const entry of entries) {
126
+ const renderedName = entry.name === gitignoreTemplateName ? ".gitignore" : replaceTokens(entry.name, tokens);
127
+ const sourcePath = join(source, entry.name);
128
+ const targetPath = join(target, renderedName);
129
+ if (entry.isDirectory()) {
130
+ await mkdir(targetPath, { recursive: true });
131
+ await renderDirectory(sourcePath, targetPath, tokens);
132
+ continue;
133
+ }
134
+ const content = await readFile(sourcePath, "utf8");
135
+ await writeFile(targetPath, replaceTokens(content, tokens));
136
+ }
137
+ }
138
+ /** 读取 core 版本,作为生成项目默认的公开包版本。 */
139
+ async function readCorePackageVersion() {
140
+ const packageJson = JSON.parse(await readFile(resolve(packageRoot, "../core/package.json"), "utf8"));
141
+ return packageJson.version;
142
+ }
143
+ /** 读取可重复且支持逗号分隔的命令行选项值。 */
144
+ function readOptions(args, option) {
145
+ return args.flatMap((argument, index) => {
146
+ if (argument !== option)
147
+ return [];
148
+ const value = args[index + 1];
149
+ if (!value || value.startsWith("--"))
150
+ throw new Error(`选项 ${option} 缺少值`);
151
+ return value
152
+ .split(",")
153
+ .map(item => item.trim())
154
+ .filter(Boolean);
155
+ });
156
+ }
157
+ /** 校验项目与模块名称。 */
158
+ function validateName(value, label) {
159
+ if (!kebabNamePattern.test(value))
160
+ throw new Error(`${label}必须使用 kebab-case: ${value}`);
161
+ }
162
+ /** 规范化自有模块列表并校验保留名称。 */
163
+ function normalizeModuleNames(moduleNames) {
164
+ const normalized = [...new Set(moduleNames.map(name => name.trim()).filter(Boolean))];
165
+ const primaryModuleName = normalized[0];
166
+ if (!primaryModuleName)
167
+ throw new Error("至少需要一个业务模块名称");
168
+ normalized.forEach(name => validateName(name, "模块名称"));
169
+ const reservedName = normalized.find(name => name === "system" || name === "kratos-admin");
170
+ if (reservedName)
171
+ throw new Error(`自有模块名称不能使用保留名称: ${reservedName}`);
172
+ return [primaryModuleName, ...normalized.slice(1)];
173
+ }
174
+ /** 规范化额外模块列表并去重。 */
175
+ function normalizeAdditionalModules(moduleNames, currentModules) {
176
+ const normalized = [...new Set(moduleNames.map(name => name.trim()).filter(Boolean))];
177
+ normalized.forEach(name => validateName(name, "额外模块名称"));
178
+ return normalized.filter(name => name !== "system" && !currentModules.includes(name));
179
+ }
180
+ /** 创建单个自有模块模板使用的占位符。 */
181
+ function createModuleTokens(moduleName, packageVersion) {
182
+ return {
183
+ __MODULE_NAME__: moduleName,
184
+ __MODULE_PASCAL__: toPascalCase(moduleName),
185
+ __MODULE_PACKAGE__: `@${moduleName}/admin-module`,
186
+ __MODULE_IDENTIFIER__: `${toCamelCase(moduleName)}AdminModule`,
187
+ __CORE_VERSION__: `^${packageVersion}`
188
+ };
189
+ }
190
+ /** 格式化嵌入模板的多行 JSON 值。 */
191
+ function formatJsonValue(value, indentation) {
192
+ const lines = JSON.stringify(value, null, 2).split("\n");
193
+ return [lines[0], ...lines.slice(1).map(line => `${indentation}${line}`)].join("\n");
194
+ }
195
+ /** 创建 README 中的业务模块目录树。 */
196
+ function createModuleTree(moduleNames) {
197
+ return moduleNames.map(name => `├── packages/modules/${name}`).join("\n");
198
+ }
199
+ /** 创建 README 中的业务模块目录说明。 */
200
+ function createModuleTableRows(moduleNames) {
201
+ return moduleNames
202
+ .map(name => `| \`packages/modules/${name}/\` | 可独立发布的 \`@${name}/admin-module\` 业务 module。 |`)
203
+ .join("\n");
204
+ }
205
+ /** 将 kebab-case 转换为 camelCase。 */
206
+ function toCamelCase(value) {
207
+ return value.replace(/-([a-z0-9])/g, (_, character) => character.toUpperCase());
208
+ }
209
+ /** 将 kebab-case 转换为 PascalCase。 */
210
+ function toPascalCase(value) {
211
+ const camelCase = toCamelCase(value);
212
+ return `${camelCase.charAt(0).toUpperCase()}${camelCase.slice(1)}`;
213
+ }
214
+ /** 替换模板文本中的全部占位符。 */
215
+ function replaceTokens(value, tokens) {
216
+ return Object.entries(tokens).reduce((content, [token, replacement]) => content.replaceAll(token, replacement), value);
217
+ }
218
+ /** 判断路径是否存在。 */
219
+ async function pathExists(path) {
220
+ try {
221
+ await stat(path);
222
+ return true;
223
+ }
224
+ catch {
225
+ return false;
226
+ }
227
+ }
228
+ /** 输出 CLI 使用说明。 */
229
+ function printHelp() {
230
+ process.stdout.write([
231
+ "kratos-admin create <project> --module <module[,module...]> [--module <module>] [--with other]",
232
+ "",
233
+ "示例:",
234
+ " kratos-admin create shop-admin --module shop",
235
+ " kratos-admin create shop-admin --module shop,order",
236
+ " kratos-admin create shop-admin --module shop --module order",
237
+ " kratos-admin create shop-admin --module shop,order --with other",
238
+ ""
239
+ ].join("\n"));
240
+ }
241
+ const executedPath = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).href : "";
242
+ if (import.meta.url === executedPath) {
243
+ runCli().catch(error => {
244
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
245
+ process.exitCode = 1;
246
+ });
247
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@liujitcn/kratos-admin-cli",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Create pnpm workspace projects for kratos-admin business modules",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/liujitcn/kratos-admin",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git@github.com:liujitcn/kratos-admin.git"
11
+ },
12
+ "bin": {
13
+ "kratos-admin": "./dist/index.js"
14
+ },
15
+ "engines": {
16
+ "node": "^20.19.0 || >=22.12.0"
17
+ },
18
+ "files": [
19
+ "dist/index.d.ts",
20
+ "dist/index.js",
21
+ "templates",
22
+ "README.md"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.json",
29
+ "build:package": "pnpm build",
30
+ "type:check": "tsc -p tsconfig.json --noEmit",
31
+ "test": "pnpm build && node --test dist/index.test.js"
32
+ }
33
+ }
@@ -0,0 +1,63 @@
1
+ <!-- prettier-ignore -->
2
+ # __PROJECT_NAME__
3
+
4
+ 基于 kratos-admin 的独立业务管理端 workspace。默认包含一个薄宿主、System module 和自有 module(**MODULE_NAMES**);依赖方向为 `apps/admin -> business module -> @liujitcn/kratos-admin-core`。
5
+
6
+ ## 目录与文件
7
+
8
+ ```text
9
+ __PROJECT_NAME__
10
+ ├── apps/admin
11
+ │ ├── src
12
+ │ ├── package.json
13
+ │ └── README.md
14
+ __MODULE_TREE__
15
+ ├── scripts
16
+ │ └── build-package.mjs
17
+ ├── .gitignore
18
+ ├── package.json
19
+ ├── pnpm-workspace.yaml
20
+ ├── README.md
21
+ ├── tsconfig.json
22
+ └── turbo.json
23
+ ```
24
+
25
+ | 路径 | 作用 |
26
+ | ------------- | --------------------------------------------------- |
27
+ | `apps/admin/` | 可运行的薄宿主,只负责启动和选择启用的业务 module。 |
28
+
29
+ **MODULE_TABLE_ROWS**
30
+ | `scripts/build-package.mjs` | 生成自有 module 的发布源码和 TypeScript 声明。 |
31
+ | `.gitignore` | 忽略依赖、缓存和构建产物。 |
32
+ | `package.json` | 声明 workspace 公共命令、工具依赖和运行时版本。 |
33
+ | `pnpm-workspace.yaml` | 声明宿主和业务 module 的 workspace 范围。 |
34
+ | `README.md` | 当前 workspace 的目录、文件和开发说明。 |
35
+ | `tsconfig.json` | TypeScript 公共配置以及全部自有 module 的源码映射。 |
36
+ | `turbo.json` | 定义开发、构建、发布构建和类型检查任务关系。 |
37
+
38
+ 每个含 `package.json` 的目录都有同级 README,可继续向对应文件查看更细的职责说明。
39
+
40
+ ## 开发
41
+
42
+ ```bash
43
+ pnpm install
44
+ pnpm dev
45
+ pnpm type:check
46
+ pnpm build
47
+ pnpm build:package
48
+ pnpm package
49
+ ```
50
+
51
+ | 命令 | 作用 |
52
+ | -------------------- | ------------------------------------- |
53
+ | `pnpm dev` | 启动 `apps/admin` 开发服务器。 |
54
+ | `pnpm type:check` | 检查宿主和全部业务 module 类型。 |
55
+ | `pnpm build` | 构建宿主应用。 |
56
+ | `pnpm build:package` | 生成全部自有 module 的 npm 发布目录。 |
57
+ | `pnpm package` | 构建全部自有 module 并生成 npm 包。 |
58
+
59
+ 业务 API、RPC、页面和业务组件分别放在 `packages/modules/<module>/src`。业务页面对应的后端菜单组件路径必须使用 module 名称前缀,例如 `shop/index/index`。
60
+
61
+ 宿主 module manifest 位于 `apps/admin/src/module-manifest.ts`,默认先加载 `@liujitcn/kratos-admin-system`,再按创建参数顺序加载自有 module。`apps/admin/src/modules.ts` 加载并默认导出当前全部 module;安装其他 module 后,只需更新宿主依赖和 manifest。
62
+
63
+ 跨模块跳转使用 Vue Router;跨模块代码复用只允许引用对方 `package.json#exports` 公开的 Interface,不使用跨目录相对路径。
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ dist
3
+ .turbo
4
+ *.log
5
+ .DS_Store
6
+ .idea
7
+ .vscode
@@ -0,0 +1,6 @@
1
+ VITE_GLOB_APP_TITLE = __MODULE_PASCAL__ Admin
2
+ VITE_PORT = 8848
3
+ VITE_OPEN = true
4
+ VITE_DEVTOOLS = false
5
+ VITE_REPORT = false
6
+ VITE_CODEINSPECTOR = false
@@ -0,0 +1,7 @@
1
+ VITE_USER_NODE_ENV = development
2
+ VITE_PUBLIC_PATH = /
3
+ VITE_ROUTER_MODE = hash
4
+ VITE_DROP_CONSOLE = true
5
+ VITE_PWA = false
6
+ VITE_API_URL = /api
7
+ VITE_PROXY = [["/api","http://localhost:7001"],["/events","http://localhost:7001"]]
@@ -0,0 +1,8 @@
1
+ VITE_USER_NODE_ENV = production
2
+ VITE_PUBLIC_PATH = /admin/
3
+ VITE_ROUTER_MODE = hash
4
+ VITE_BUILD_COMPRESS = none
5
+ VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
6
+ VITE_DROP_CONSOLE = true
7
+ VITE_PWA = true
8
+ VITE_API_URL = /api
@@ -0,0 +1,54 @@
1
+ <!-- prettier-ignore -->
2
+ # __APP_PACKAGE__
3
+
4
+ `__PROJECT_NAME__` 的管理端宿主。该包私有且不实现业务,默认先组合 `@liujitcn/kratos-admin-system`,再组合自有 module **MODULE_PACKAGES** 和所选的其他业务 module。
5
+
6
+ ## 目录与文件
7
+
8
+ ```text
9
+ apps/admin
10
+ ├── src
11
+ │ ├── main.ts
12
+ │ ├── module-manifest.ts
13
+ │ ├── modules.ts
14
+ │ └── vite-env.d.ts
15
+ ├── .env
16
+ ├── .env.development
17
+ ├── .env.production
18
+ ├── index.html
19
+ ├── package.json
20
+ ├── README.md
21
+ ├── tsconfig.json
22
+ └── vite.config.ts
23
+ ```
24
+
25
+ | 路径 | 作用 |
26
+ | ------------------------ | ------------------------------------------------- |
27
+ | `src/main.ts` | 将默认导出的全部 module 交给 core 启动 Vue 应用。 |
28
+ | `src/module-manifest.ts` | 统一声明 module 加载器、包名和预构建依赖。 |
29
+ | `src/modules.ts` | 加载并默认导出当前宿主启用的全部 module。 |
30
+ | `src/vite-env.d.ts` | 引入 Vite 客户端和 core 全局类型。 |
31
+ | `.env` | 所有模式共享的应用标题、端口等环境变量。 |
32
+ | `.env.development` | 开发模式 API 地址和代理配置。 |
33
+ | `.env.production` | 生产模式 API 地址和构建配置。 |
34
+ | `index.html` | Vite HTML 入口和应用挂载节点。 |
35
+ | `package.json` | 声明宿主命令、core、当前模块和额外模块依赖。 |
36
+ | `README.md` | 当前宿主的目录和文件说明。 |
37
+ | `tsconfig.json` | 宿主 TypeScript 检查配置。 |
38
+ | `vite.config.ts` | 组合 core 配置与模块 manifest 派生的构建参数。 |
39
+
40
+ ## 模块组合
41
+
42
+ `src/module-manifest.ts` 是宿主 module 配置的唯一来源,每个 manifest 项同时声明 npm 包名、运行时加载器和可选预构建依赖。`src/modules.ts` 加载并默认导出全部 module,`vite.config.ts` 从 manifest 派生构建扫描参数。新增或删除 module 时,只需修改宿主依赖和这一份 manifest。
43
+
44
+ 模块注册顺序只影响显式 `staticViews` 替换:后注册模块覆盖先注册模块的同一静态视图键。普通业务页面按 `<module>/<view>` 隔离,不允许通过同名页面覆盖其他模块。
45
+
46
+ 模块之间使用 Vue Router 跳转;跨模块复用代码时,只引用模块公开的 npm 子路径,宿主不得引用模块的 `src` 目录。
47
+
48
+ ## 命令
49
+
50
+ ```bash
51
+ pnpm --filter __APP_PACKAGE__ dev
52
+ pnpm --filter __APP_PACKAGE__ type:check
53
+ pnpm --filter __APP_PACKAGE__ build
54
+ ```
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title><%= title %></title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "__APP_PACKAGE__",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --configLoader runner",
8
+ "build": "vue-tsc --noEmit && vite build --configLoader runner --mode production",
9
+ "preview": "vite preview --configLoader runner",
10
+ "type:check": "vue-tsc --noEmit --skipLibCheck"
11
+ },
12
+ "dependencies": __APP_DEPENDENCIES__
13
+ }
@@ -0,0 +1,4 @@
1
+ import { bootstrapAdminApp } from "@liujitcn/kratos-admin-core";
2
+ import adminModules from "./modules";
3
+
4
+ void bootstrapAdminApp({ modules: adminModules });
@@ -0,0 +1,29 @@
1
+ import type { AdminModule } from "@liujitcn/kratos-admin-core";
2
+
3
+ /** 管理端宿主模块清单项。 */
4
+ export interface AdminModuleManifestItem {
5
+ /** 业务模块 npm 包名,供 Vite 扫描模块源码。 */
6
+ packageName: string;
7
+ /** 加载业务模块运行时定义。 */
8
+ load: () => Promise<AdminModule>;
9
+ /** 业务模块需要预构建的依赖。 */
10
+ optimizeDependencies?: string[];
11
+ }
12
+
13
+ /** 当前宿主启用的管理端业务模块清单。 */
14
+ export const adminModuleManifest = [
15
+ __MODULE_MANIFEST__
16
+ ] satisfies AdminModuleManifestItem[];
17
+
18
+ /** 当前宿主需要扫描的业务模块包。 */
19
+ export const adminModulePackages = adminModuleManifest.map(item => item.packageName);
20
+
21
+ /** 当前宿主需要预构建的业务模块依赖。 */
22
+ export const adminModuleOptimizeDependencies = adminModuleManifest.flatMap(item => {
23
+ return item.optimizeDependencies?.map(dependency => `${item.packageName} > ${dependency}`) ?? [];
24
+ });
25
+
26
+ /** 加载当前宿主启用的全部业务模块。 */
27
+ export async function loadAdminModules(): Promise<AdminModule[]> {
28
+ return Promise.all(adminModuleManifest.map(item => item.load()));
29
+ }
@@ -0,0 +1,6 @@
1
+ import { loadAdminModules } from "./module-manifest";
2
+
3
+ /** 当前宿主启用的全部管理端业务模块。 */
4
+ const adminModules = await loadAdminModules();
5
+
6
+ export default adminModules;
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "vite.config.ts"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
@@ -0,0 +1,7 @@
1
+ import { defineAdminViteConfig } from "@liujitcn/kratos-admin-core/vite.config";
2
+ import { adminModuleOptimizeDependencies, adminModulePackages } from "./src/module-manifest";
3
+
4
+ export default defineAdminViteConfig({
5
+ modulePackages: adminModulePackages,
6
+ optimizeDependencies: adminModuleOptimizeDependencies
7
+ });
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "turbo run dev --filter=__APP_PACKAGE__",
7
+ "build": "turbo run build --filter=__APP_PACKAGE__",
8
+ "build:package": "turbo run build:package __MODULE_FILTERS__",
9
+ "type:check": "turbo run type:check",
10
+ "package": "pnpm build:package && pnpm --recursive __MODULE_FILTERS__ pack"
11
+ },
12
+ "devDependencies": {
13
+ "@types/node": "^25.9.1",
14
+ "sass": "^1.100.0",
15
+ "turbo": "^2.5.6",
16
+ "typescript": "6.0.3",
17
+ "vite": "^8.0.14",
18
+ "vue-tsc": "^3.3.2"
19
+ },
20
+ "engines": {
21
+ "node": "^20.19.0 || >=22.12.0"
22
+ },
23
+ "packageManager": "pnpm@10.33.4"
24
+ }
@@ -0,0 +1,68 @@
1
+ <!-- prettier-ignore -->
2
+ # __MODULE_PACKAGE__
3
+
4
+ <!-- prettier-ignore -->
5
+ `__PROJECT_NAME__` 的 __MODULE_PASCAL__ 管理端业务模块。API、RPC、页面和业务组件同包维护,可以发布到 npm 后被不同宿主组合使用。
6
+
7
+ ## 目录与文件
8
+
9
+ ```text
10
+ packages/modules/__MODULE_NAME__
11
+ ├── src
12
+ │ ├── api
13
+ │ │ └── index.ts # 请求公开入口;按需新增 base、system 等领域目录
14
+ │ ├── rpc
15
+ │ │ └── README.md # Proto 生成目录说明
16
+ │ ├── components # 可选的模块内共享组件
17
+ │ ├── views
18
+ │ │ └── index
19
+ │ │ └── index.vue
20
+ │ ├── index.ts
21
+ │ └── module.ts
22
+ ├── package.json
23
+ ├── README.md
24
+ ├── tsconfig.json
25
+ └── tsconfig.package.json
26
+ ```
27
+
28
+ | 路径 | 作用 |
29
+ | --------------------------- | --------------------------------------------------------------- |
30
+ | `src/index.ts` | npm 主入口,导出 `__MODULE_IDENTIFIER__`。 |
31
+ | `src/module.ts` | 收集 `src/views/**/*.vue` 并声明名为 `__MODULE_NAME__` 的模块。 |
32
+ | `src/api/index.ts` | 当前模块请求导出入口;具体文件按 Proto 一级领域组织。 |
33
+ | `src/rpc/README.md` | 当前模块 RPC 生成目录说明,生成类型通过模块包子路径公开。 |
34
+ | `src/views/index/index.vue` | 模板自带的模块首页。 |
35
+ | `package.json` | 声明依赖、模块入口、API/RPC 子路径和 npm 发布配置。 |
36
+ | `README.md` | 当前业务模块的目录、文件和接入说明。 |
37
+ | `tsconfig.json` | 开发态类型检查配置。 |
38
+ | `tsconfig.package.json` | npm 发布声明文件生成配置。 |
39
+
40
+ ## 开发约束
41
+
42
+ - API 放在 `src/api/<proto-domain>`,例如 `src/api/base`、`src/api/system`;RPC 保留 `src/rpc/<proto-domain>/<version>` 等完整生成层级,不扁平化。
43
+ - 页面放在 `src/views`。页面私有组件就近放置,需要模块内多个页面复用时再创建 `src/components`。
44
+ - 底座能力通过 `@liujitcn/kratos-admin-core` 的公开子路径引用,不要依赖 core 源码目录。
45
+ - 跨模块页面跳转使用 Vue Router;跨模块代码复用需要对方先提供 npm 公开导出。
46
+ - 新增页面后,动态菜单组件路径必须使用 `__MODULE_NAME__/` 模块前缀;模板首页对应 `__MODULE_NAME__/index/index`,不兼容 `index/index`。
47
+ - 需要替换 core 静态页面时,使用 core 导出的 `ADMIN_STATIC_VIEWS` 在模块 `staticViews` 中显式映射页面。
48
+
49
+ 模块运行时 Interface 是 `src/index.ts` 导出的 `__MODULE_IDENTIFIER__`。API 和 RPC 通过 `package.json#exports` 公开;页面只通过 `AdminModule.views` 注册,不作为 npm 子路径导出。组件需要真实跨模块复用时,按具体文件增加显式导出,不提供 `components/*` 通配入口。模块内部文件不因位于 `src` 下自动成为公共 Interface。
50
+
51
+ 宿主接入:
52
+
53
+ ```ts
54
+ export const adminModuleManifest = [
55
+ {
56
+ packageName: "__MODULE_PACKAGE__",
57
+ load: async () => (await import("__MODULE_PACKAGE__")).__MODULE_IDENTIFIER__
58
+ }
59
+ ];
60
+ ```
61
+
62
+ ## 命令
63
+
64
+ ```bash
65
+ pnpm --filter __MODULE_PACKAGE__ type:check
66
+ pnpm --filter __MODULE_PACKAGE__ build:package
67
+ pnpm --filter __MODULE_PACKAGE__ pack
68
+ ```
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "__MODULE_PACKAGE__",
3
+ "private": false,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "description": "__MODULE_PASCAL__ admin business module",
7
+ "scripts": {
8
+ "build": "pnpm build:package",
9
+ "build:package": "node ../../../scripts/build-package.mjs .",
10
+ "type:check": "vue-tsc --noEmit --skipLibCheck",
11
+ "prepack": "pnpm build:package"
12
+ },
13
+ "devDependencies": {
14
+ "@liujitcn/kratos-admin-core": "__CORE_VERSION__"
15
+ },
16
+ "peerDependencies": {
17
+ "@liujitcn/kratos-admin-core": "__CORE_VERSION__",
18
+ "vue": "^3.5.35"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/package/declarations/index.d.ts",
23
+ "default": "./dist/package/src/index.ts"
24
+ },
25
+ "./api/*": {
26
+ "types": "./dist/package/declarations/src/api/*.d.ts",
27
+ "default": "./dist/package/src/api/*.ts"
28
+ },
29
+ "./rpc/*": {
30
+ "types": "./dist/package/declarations/src/rpc/*.d.ts",
31
+ "default": "./dist/package/src/rpc/*.ts"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "files": [
36
+ "dist/package"
37
+ ],
38
+ "publishConfig": {
39
+ "access": "public"
40
+ }
41
+ }
@@ -0,0 +1,2 @@
1
+ /** __MODULE_PASCAL__ 模块请求统一从此目录导出。 */
2
+ export {};
@@ -0,0 +1 @@
1
+ export { __MODULE_IDENTIFIER__ } from "./module";
@@ -0,0 +1,10 @@
1
+ import type { Component } from "vue";
2
+ import { defineAdminModule } from "@liujitcn/kratos-admin-core";
3
+
4
+ const viewModules = import.meta.glob<{ default: Component }>("./views/**/*.vue");
5
+
6
+ /** __MODULE_PASCAL__ 管理端业务模块。 */
7
+ export const __MODULE_IDENTIFIER__ = defineAdminModule({
8
+ name: "__MODULE_NAME__",
9
+ views: viewModules
10
+ });
@@ -0,0 +1,14 @@
1
+ # RPC
2
+
3
+ 当前业务模块的 Proto TypeScript 生成目录。目录必须保留 Proto 完整层级,例如:
4
+
5
+ ```text
6
+ src/rpc
7
+ ├── base/v1
8
+ ├── common/v1
9
+ └── system/admin/v1
10
+ ```
11
+
12
+ 只生成当前模块 API 和页面实际依赖的服务及其传递类型。生成文件属于本模块,通过模块包的 `./rpc/*` 子路径公开;不要把业务 RPC 生成到 core,也不要在模块中重复手写等价业务模型。
13
+
14
+ RPC 文件只能由接入项目约定的生成命令更新,禁止手工修改。生成器输出的相对导入保持原样,不为目录美观将 RPC 扁平化。
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <section class="module-page">
3
+ <h2>__MODULE_PASCAL__</h2>
4
+ </section>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ defineOptions({ name: "__MODULE_PASCAL__Index" });
9
+ </script>
10
+
11
+ <style scoped lang="scss">
12
+ .module-page {
13
+ padding: 20px;
14
+ }
15
+ </style>
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "include": [
4
+ "node_modules/@liujitcn/kratos-admin-core/types/generated/auto-imports.d.ts",
5
+ "node_modules/@liujitcn/kratos-admin-core/types/generated/components.d.ts",
6
+ "src/**/*.ts",
7
+ "src/**/*.d.ts",
8
+ "src/**/*.tsx",
9
+ "src/**/*.vue"
10
+ ],
11
+ "exclude": ["node_modules", "dist", "**/*.js"]
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "noEmit": false,
6
+ "declaration": true,
7
+ "emitDeclarationOnly": true,
8
+ "declarationMap": false,
9
+ "outDir": "./dist/declarations",
10
+ "declarationDir": "./dist/declarations"
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ packages:
2
+ - apps/*
3
+ - packages/*
4
+ - packages/modules/*
@@ -0,0 +1,21 @@
1
+ import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { execFileSync } from "node:child_process";
3
+ import { resolve } from "node:path";
4
+
5
+ const packageRoot = resolve(process.cwd(), process.argv[2] ?? ".");
6
+ const declarationRoot = resolve(packageRoot, "dist/declarations");
7
+ const outputRoot = resolve(packageRoot, "dist/package");
8
+ const packageJson = JSON.parse(await readFile(resolve(packageRoot, "package.json"), "utf8"));
9
+
10
+ await rm(outputRoot, { recursive: true, force: true });
11
+ await rm(declarationRoot, { recursive: true, force: true });
12
+ execFileSync("pnpm", ["exec", "vue-tsc", "-p", "tsconfig.package.json"], {
13
+ cwd: packageRoot,
14
+ stdio: "inherit"
15
+ });
16
+ await mkdir(resolve(outputRoot, "src"), { recursive: true });
17
+ await cp(resolve(packageRoot, "src"), resolve(outputRoot, "src"), { recursive: true });
18
+ await cp(declarationRoot, resolve(outputRoot, "declarations"), { recursive: true });
19
+ await writeFile(resolve(outputRoot, "declarations/index.d.ts"), 'export * from "./src/index";\n');
20
+ await rm(declarationRoot, { recursive: true, force: true });
21
+ console.log(`已生成 ${packageJson.name} 发布文件`);
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "types": ["vite/client", "node"],
8
+ "strict": true,
9
+ "noImplicitAny": false,
10
+ "jsx": "preserve",
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "esModuleInterop": true,
14
+ "lib": ["ESNext", "DOM"],
15
+ "skipLibCheck": true,
16
+ "noEmit": true,
17
+ "ignoreDeprecations": "6.0",
18
+ "baseUrl": ".",
19
+ "paths": __MODULE_PATHS__
20
+ },
21
+ "files": []
22
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://turbo.build/schema.json",
3
+ "tasks": {
4
+ "build": {
5
+ "dependsOn": ["^build"],
6
+ "outputs": ["dist/**"]
7
+ },
8
+ "build:package": {
9
+ "dependsOn": ["^build:package"],
10
+ "outputs": ["dist/package/**"]
11
+ },
12
+ "dev": {
13
+ "cache": false,
14
+ "persistent": true
15
+ },
16
+ "type:check": {
17
+ "dependsOn": ["^type:check"],
18
+ "outputs": []
19
+ }
20
+ }
21
+ }