@liujitcn/kratos-uni-app-cli 0.0.61 → 0.0.64

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.
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { scaffoldKratosApp } from '../src/index.mjs'
3
+ import { cliMessage } from '../src/messages.mjs'
3
4
 
4
5
  const args = process.argv.slice(2)
5
6
  if (args[0] !== 'create' || !args[1]) {
6
- console.error('用法: kratos-uni-app create <目录> [--module <名称>] [--with <包名>]')
7
+ console.error(cliMessage('usage'))
7
8
  process.exitCode = 1
8
9
  } else {
9
10
  const modules = []
@@ -13,7 +14,7 @@ if (args[0] !== 'create' || !args[1]) {
13
14
  if (args[index] === '--module' && args[index + 1]) modules.push(args[++index])
14
15
  else if (args[index] === '--with' && args[index + 1]) packages.push(args[++index])
15
16
  else if (args[index] === '--kratos-project') kratosProject = true
16
- else throw new Error(`未知参数:${args[index]}`)
17
+ else throw new Error(cliMessage('unknown_argument', { argument: args[index] }))
17
18
  }
18
19
  scaffoldKratosApp(args[1], { modules, packages, kratosProject })
19
20
  }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "type": "git",
8
8
  "url": "git@github.com:liujitcn/kratos-admin.git"
9
9
  },
10
- "version": "0.0.61",
10
+ "version": "0.0.64",
11
11
  "type": "module",
12
12
  "files": [
13
13
  "assets",
package/src/index.mjs CHANGED
@@ -8,17 +8,18 @@ import {
8
8
  writeFileSync,
9
9
  } from 'node:fs'
10
10
  import { basename, resolve } from 'node:path'
11
+ import { cliMessage, resolveCliLocale } from './messages.mjs'
11
12
 
12
13
  const cliPackage = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
13
14
  const publicPackageVersion = cliPackage.version
14
15
  if (typeof publicPackageVersion !== 'string' || !publicPackageVersion) {
15
- throw new Error('CLI package.json 缺少有效版本')
16
+ throw new Error(cliMessage('package_version_missing'))
16
17
  }
17
18
 
18
19
  /** 创建独立 Kratos uni-app workspace。 */
19
20
  export function scaffoldKratosApp(targetPath, options = {}) {
20
21
  const target = resolve(targetPath)
21
- if (existsSync(target)) throw new Error(`目标目录已存在:${target}`)
22
+ if (existsSync(target)) throw new Error(cliMessage('target_exists', { target }))
22
23
  const projectName = basename(target)
23
24
  const modules = [...new Set(options.modules ?? [])]
24
25
  const packages = [...new Set(options.packages ?? [])]
@@ -41,39 +42,7 @@ export function scaffoldKratosApp(targetPath, options = {}) {
41
42
  write(
42
43
  target,
43
44
  'README.md',
44
- `# ${projectName}
45
-
46
- \`${projectName}\` 是由 \`@liujitcn/kratos-uni-app-cli\` 创建的独立 pnpm workspace,支持 H5 和微信小程序。
47
-
48
- ## 目录结构
49
-
50
- \`\`\`text
51
- ${projectName}
52
- ├── apps/uni-app # 私有 uni-app 宿主
53
- ├── packages/modules # workspace 内本地业务模块
54
- ├── package.json # 公共命令和开发依赖
55
- ├── pnpm-workspace.yaml # workspace 包范围
56
- └── tsconfig.json # 共享 TypeScript 配置
57
- \`\`\`
58
-
59
- 宿主只负责入口、manifest、模块清单和启动;底座能力由 \`@liujitcn/kratos-uni-app-core\` 提供,默认业务页面由 \`@liujitcn/kratos-uni-app-system\` 提供。本地模块通过各自公开入口接入,不跨包相对引用源码。
60
-
61
- ## 开发
62
-
63
- \`\`\`bash
64
- pnpm install
65
- pnpm dev:h5
66
- pnpm dev:mp-weixin
67
- pnpm build:h5
68
- pnpm build:mp-weixin
69
- \`\`\`
70
-
71
- H5 通过局域网 IP 访问时,先在仓库根目录运行 bash scripts/generate-dev-cert.sh 192.168.1.100 生成证书,再在 .env.development-h5.local 中设置 VITE_APP_HTTPS=true;后端使用 HTTPS 时同时设置 VITE_APP_API_URL=https://localhost:7001。
72
-
73
- 模块装配入口是 \`apps/uni-app/src/module-manifest.ts\`。新增、删除或调整模块时同步维护该清单,并在模块自己的 README 中记录页面和接口职责。
74
-
75
- 生产构建前请在 \`.env.production\` 中填写实际 API 和静态资源地址;该文件默认留空,不应使用本机地址。
76
- `,
45
+ renderLocalizedReadme('workspace.md', { __PROJECT_NAME__: projectName }),
77
46
  )
78
47
  const dependencies = {
79
48
  '@liujitcn/kratos-uni-app-core': `^${publicPackageVersion}`,
@@ -119,41 +88,7 @@ H5 通过局域网 IP 访问时,先在仓库根目录运行 bash scripts/gener
119
88
  write(
120
89
  target,
121
90
  'apps/uni-app/README.md',
122
- `# @liujitcn/kratos-uni-app
123
-
124
- \`apps/uni-app\` 是 \`${projectName}\` 的私有 uni-app 宿主,负责组合模块并提供 H5、微信小程序的构建入口,不承载可复用业务实现。
125
-
126
- ## 目录结构
127
-
128
- \`\`\`text
129
- apps/uni-app
130
- ├── src
131
- │ ├── pages/bootstrap # 固定启动页面
132
- │ ├── App.vue # 应用根组件和全局样式入口
133
- │ ├── main.ts # Vue 与 Kratos uni-app 启动入口
134
- │ ├── manifest.json # uni-app 平台配置
135
- │ ├── module-manifest.ts # 唯一模块清单
136
- │ └── pages.json # 固定 bootstrap 路由
137
- ├── index.html # H5 HTML 入口
138
- ├── package.json # 宿主命令和运行依赖
139
- └── vite.config.ts # 模块页面装配与 uni-app 插件
140
- \`\`\`
141
-
142
- ## 模块装配
143
-
144
- \`src/module-manifest.ts\` 默认注册 core 和 system,并按声明顺序加载本地模块与已发布模块。业务页面、API 和状态应放在所属模块,宿主仅维护组合关系和平台配置。
145
-
146
- ## 命令
147
-
148
- 在 workspace 根目录执行:
149
-
150
- \`\`\`bash
151
- pnpm dev:h5
152
- pnpm dev:mp-weixin
153
- pnpm build:h5
154
- pnpm build:mp-weixin
155
- \`\`\`
156
- `,
91
+ renderLocalizedReadme('host.md', { __PROJECT_NAME__: projectName }),
157
92
  )
158
93
  const imports = [
159
94
  "import { coreModule } from '@liujitcn/kratos-uni-app-core/module'",
@@ -232,6 +167,7 @@ import {
232
167
  defineConfig,
233
168
  kratosApp,
234
169
  loadEnv,
170
+ viteMessage,
235
171
  type ConfigEnv,
236
172
  type UserConfig,
237
173
  } from '@liujitcn/kratos-uni-app-core/vite'
@@ -245,7 +181,7 @@ function resolveHttpsOptions(env, root) {
245
181
  const keyPath = resolve(root, env.VITE_APP_HTTPS_KEY || '../../certs/dev-key.pem')
246
182
  const certPath = resolve(root, env.VITE_APP_HTTPS_CERT || '../../certs/dev-cert.pem')
247
183
  if (!existsSync(keyPath) || !existsSync(certPath)) {
248
- throw new Error('VITE_APP_HTTPS 已开启,但未找到证书文件,请先在仓库根目录运行 scripts/generate-dev-cert.sh;期望路径:' + keyPath + ' 和 ' + certPath)
184
+ throw new Error(viteMessage('https_certificate_missing', { keyPath, certPath }))
249
185
  }
250
186
  return { key: readFileSync(keyPath), cert: readFileSync(certPath) }
251
187
  }
@@ -340,29 +276,10 @@ export default defineConfig(({ mode }) => {
340
276
  write(
341
277
  target,
342
278
  `packages/modules/${name}/README.md`,
343
- `# @local/${name}
344
-
345
- \`@local/${name}\` 是 \`${projectName}\` workspace 内的本地业务模块,通过 \`@liujitcn/kratos-uni-app-core\` 的公开接口接入宿主。
346
-
347
- ## 目录结构
348
-
349
- \`\`\`text
350
- packages/modules/${name}
351
- ├── src
352
- │ ├── views # 模块页面;页面私有组件放在就近 components
353
- │ ├── index.d.mts # 模块入口类型
354
- │ └── index.mjs # 页面、视图键和图标注册入口
355
- ├── package.json # 模块名称、依赖和 exports
356
- └── README.md # 模块职责与维护说明
357
- \`\`\`
358
-
359
- ## 开发约束
360
-
361
- - 页面放在 \`src/views\`,并在 \`src/index.mjs\` 的模块定义中登记页面和稳定视图键。
362
- - 请求、认证、导航和状态能力只通过 \`@liujitcn/kratos-uni-app-core\` 的公开 exports 使用。
363
- - 页面替换使用稳定视图键,接口不能直接下发任意组件路径。
364
- - 修改模块后从 workspace 根目录运行对应 H5 或微信小程序命令验证装配结果。
365
- `,
279
+ renderLocalizedReadme('module.md', {
280
+ __PROJECT_NAME__: projectName,
281
+ __MODULE_NAME__: name,
282
+ }),
366
283
  )
367
284
  write(
368
285
  target,
@@ -398,7 +315,7 @@ function writeEnvironmentFiles(target) {
398
315
  write(
399
316
  target,
400
317
  '.env.development-h5',
401
- 'VITE_APP_PORT=5004\nVITE_APP_HTTPS=false\n# VITE_APP_HTTPS_KEY=../../certs/dev-key.pem\n# VITE_APP_HTTPS_CERT=../../certs/dev-cert.pem\n# 后端使用 HTTPS 时,在 .env.development-h5.local 覆盖 VITE_APP_API_URL=https://localhost:7001\nVITE_APP_BASE_PATH=/\nVITE_APP_BASE_API=/api\nVITE_APP_API_URL=http://localhost:7001\nVITE_APP_STATIC_API=\nVITE_APP_STATIC_URL=http://localhost:7001\n',
318
+ `VITE_APP_PORT=5004\nVITE_APP_HTTPS=false\n# VITE_APP_HTTPS_KEY=../../certs/dev-key.pem\n# VITE_APP_HTTPS_CERT=../../certs/dev-cert.pem\n# ${cliMessage('https_environment_hint')}\nVITE_APP_BASE_PATH=/\nVITE_APP_BASE_API=/api\nVITE_APP_API_URL=http://localhost:7001\nVITE_APP_STATIC_API=\nVITE_APP_STATIC_URL=http://localhost:7001\n`,
402
319
  )
403
320
  write(
404
321
  target,
@@ -414,11 +331,12 @@ function writeEnvironmentFiles(target) {
414
331
 
415
332
  /** 校验本地业务模块名称。 */
416
333
  function validateModuleName(name) {
417
- if (!/^[a-z][a-z0-9-]*$/.test(name)) throw new Error(`模块名无效:${name}`)
334
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) throw new Error(cliMessage('module_name_invalid', { name }))
418
335
  }
419
336
 
420
337
  function validatePackageName(name) {
421
- if (!/^(?:@[a-z0-9-]+\/)?[a-z0-9-]+$/.test(name)) throw new Error(`包名无效:${name}`)
338
+ if (!/^(?:@[a-z0-9-]+\/)?[a-z0-9-]+$/.test(name))
339
+ throw new Error(cliMessage('package_name_invalid', { name }))
422
340
  }
423
341
 
424
342
  function write(root, file, content) {
@@ -449,6 +367,18 @@ function renderWorkspaceTemplates(source, target, tokens) {
449
367
  }
450
368
  }
451
369
 
370
+ /** 按 CLI 语言加载并替换 workspace README 模板。 */
371
+ function renderLocalizedReadme(name, tokens) {
372
+ const template = readFileSync(
373
+ new URL(`../templates/readmes/${resolveCliLocale()}/${name}`, import.meta.url),
374
+ 'utf8',
375
+ )
376
+ return Object.entries(tokens).reduce(
377
+ (content, [key, value]) => content.replaceAll(key, () => value),
378
+ template,
379
+ )
380
+ }
381
+
452
382
  /** 创建业务模块语言源文件及 API、RPC、测试目录,注册产物由同步命令生成。 */
453
383
  function writeModuleLocales(target, name) {
454
384
  for (const directory of ['src/api', 'src/rpc', 'test']) {
@@ -0,0 +1,38 @@
1
+ const messages = {
2
+ 'zh-CN': {
3
+ package_version_missing: 'CLI package.json 缺少有效版本',
4
+ target_exists: '目标目录已存在:{target}',
5
+ usage: '用法: kratos-uni-app create <目录> [--module <名称>] [--with <包名>]',
6
+ unknown_argument: '未知参数:{argument}',
7
+ module_name_invalid: '模块名无效:{name}',
8
+ package_name_invalid: '包名无效:{name}',
9
+ https_environment_hint:
10
+ '后端使用 HTTPS 时,在 .env.development-h5.local 中设置 VITE_APP_API_URL=https://localhost:7001。',
11
+ },
12
+ 'en-US': {
13
+ package_version_missing: 'CLI package.json is missing a valid version',
14
+ target_exists: 'The target directory already exists: {target}',
15
+ usage: 'Usage: kratos-uni-app create <directory> [--module <name>] [--with <package>]',
16
+ unknown_argument: 'Unknown argument: {argument}',
17
+ module_name_invalid: 'Invalid module name: {name}',
18
+ package_name_invalid: 'Invalid package name: {name}',
19
+ https_environment_hint:
20
+ 'When the backend uses HTTPS, set VITE_APP_API_URL=https://localhost:7001 in .env.development-h5.local.',
21
+ },
22
+ }
23
+
24
+ /** 解析脚手架文案所用的语言区域。 */
25
+ export function resolveCliLocale() {
26
+ return (process.env.KRATOS_ADMIN_LOCALE ?? process.env.LC_ALL ?? process.env.LANG ?? '')
27
+ .toLowerCase()
28
+ .startsWith('zh')
29
+ ? 'zh-CN'
30
+ : 'en-US'
31
+ }
32
+
33
+ /** 根据环境变量选择 uni-app CLI 文案语言。 */
34
+ export function cliMessage(key, params = {}) {
35
+ const locale = resolveCliLocale()
36
+ const template = messages[locale][key] ?? messages['en-US'][key] ?? key
37
+ return template.replace(/\{([A-Za-z0-9_]+)\}/g, (_, name) => params[name] ?? `{${name}}`)
38
+ }
@@ -0,0 +1,34 @@
1
+ # @liujitcn/kratos-uni-app
2
+
3
+ `apps/uni-app` is the private uni-app host for `__PROJECT_NAME__`. It assembles modules and provides H5 and WeChat Mini Program build entry points; reusable business implementations belong in modules.
4
+
5
+ ## Directory Structure
6
+
7
+ ```text
8
+ apps/uni-app
9
+ ├── src
10
+ │ ├── pages/bootstrap # Fixed startup page
11
+ │ ├── App.vue # Root component and global styles
12
+ │ ├── main.ts # Vue and Kratos uni-app startup
13
+ │ ├── manifest.json # uni-app platform configuration
14
+ │ ├── module-manifest.ts # Single module manifest
15
+ │ └── pages.json # Fixed bootstrap route
16
+ ├── index.html # H5 HTML entry
17
+ ├── package.json # Host commands and runtime dependencies
18
+ └── vite.config.ts # Module assembly and uni-app plugins
19
+ ```
20
+
21
+ ## Module Assembly
22
+
23
+ `src/module-manifest.ts` registers core and system by default, then loads local and published modules in declaration order. Keep business pages, APIs, and state in their owning modules; the host only maintains composition and platform configuration.
24
+
25
+ ## Commands
26
+
27
+ Run from the workspace root:
28
+
29
+ ```bash
30
+ pnpm dev:h5
31
+ pnpm dev:mp-weixin
32
+ pnpm build:h5
33
+ pnpm build:mp-weixin
34
+ ```
@@ -0,0 +1,22 @@
1
+ # @local/__MODULE_NAME__
2
+
3
+ `@local/__MODULE_NAME__` is a local business module in the `__PROJECT_NAME__` workspace. It integrates with the host through the public APIs of `@liujitcn/kratos-uni-app-core`.
4
+
5
+ ## Directory Structure
6
+
7
+ ```text
8
+ packages/modules/__MODULE_NAME__
9
+ ├── src
10
+ │ ├── views # Module pages; keep page-specific components nearby
11
+ │ ├── index.d.mts # Module entry types
12
+ │ └── index.mjs # Page, view key, and icon registration
13
+ ├── package.json # Module name, dependencies, and exports
14
+ └── README.md # Module responsibilities and maintenance notes
15
+ ```
16
+
17
+ ## Development Rules
18
+
19
+ - Put pages in `src/views` and register pages and stable view keys in `src/index.mjs`.
20
+ - Use only the public exports of `@liujitcn/kratos-uni-app-core` for requests, authentication, navigation, and state.
21
+ - Replace pages through stable view keys; APIs must not return arbitrary component paths.
22
+ - After changing the module, run the relevant H5 or WeChat Mini Program command from the workspace root to verify assembly.
@@ -0,0 +1,32 @@
1
+ # __PROJECT_NAME__
2
+
3
+ `__PROJECT_NAME__` is an independent pnpm workspace created by `@liujitcn/kratos-uni-app-cli`. It supports H5 and WeChat Mini Programs.
4
+
5
+ ## Directory Structure
6
+
7
+ ```text
8
+ __PROJECT_NAME__
9
+ ├── apps/uni-app # Private uni-app host
10
+ ├── packages/modules # Local workspace business modules
11
+ ├── package.json # Shared commands and development dependencies
12
+ ├── pnpm-workspace.yaml # Workspace package patterns
13
+ └── tsconfig.json # Shared TypeScript configuration
14
+ ```
15
+
16
+ The host contains only the entry point, manifest, module list, and startup wiring. Core capabilities are provided by `@liujitcn/kratos-uni-app-core`; default business pages are provided by `@liujitcn/kratos-uni-app-system`. Local modules integrate through their public package entry points and must not use cross-package relative imports.
17
+
18
+ ## Development
19
+
20
+ ```bash
21
+ pnpm install
22
+ pnpm dev:h5
23
+ pnpm dev:mp-weixin
24
+ pnpm build:h5
25
+ pnpm build:mp-weixin
26
+ ```
27
+
28
+ To access H5 over a LAN IP, run `bash scripts/generate-dev-cert.sh 192.168.1.100` from the repository root, then set `VITE_APP_HTTPS=true` in `.env.development-h5.local`. If the backend uses HTTPS, also set `VITE_APP_API_URL=https://localhost:7001`.
29
+
30
+ The module assembly entry is `apps/uni-app/src/module-manifest.ts`. Keep it in sync when adding, removing, or changing modules, and document page and API responsibilities in each module README.
31
+
32
+ Before a production build, set the real API and static asset URLs in `.env.production`. That file is intentionally blank by default and should not contain localhost addresses.
@@ -0,0 +1,34 @@
1
+ # @liujitcn/kratos-uni-app
2
+
3
+ `apps/uni-app` 是 `__PROJECT_NAME__` 的私有 uni-app 宿主,负责组合模块并提供 H5、微信小程序的构建入口,不承载可复用业务实现。
4
+
5
+ ## 目录结构
6
+
7
+ ```text
8
+ apps/uni-app
9
+ ├── src
10
+ │ ├── pages/bootstrap # 固定启动页面
11
+ │ ├── App.vue # 应用根组件和全局样式入口
12
+ │ ├── main.ts # Vue 与 Kratos uni-app 启动入口
13
+ │ ├── manifest.json # uni-app 平台配置
14
+ │ ├── module-manifest.ts # 唯一模块清单
15
+ │ └── pages.json # 固定 bootstrap 路由
16
+ ├── index.html # H5 HTML 入口
17
+ ├── package.json # 宿主命令和运行依赖
18
+ └── vite.config.ts # 模块页面装配与 uni-app 插件
19
+ ```
20
+
21
+ ## 模块装配
22
+
23
+ `src/module-manifest.ts` 默认注册 core 和 system,并按声明顺序加载本地模块与已发布模块。业务页面、API 和状态应放在所属模块,宿主仅维护组合关系和平台配置。
24
+
25
+ ## 命令
26
+
27
+ 在 workspace 根目录执行:
28
+
29
+ ```bash
30
+ pnpm dev:h5
31
+ pnpm dev:mp-weixin
32
+ pnpm build:h5
33
+ pnpm build:mp-weixin
34
+ ```
@@ -0,0 +1,22 @@
1
+ # @local/__MODULE_NAME__
2
+
3
+ `@local/__MODULE_NAME__` 是 `__PROJECT_NAME__` workspace 内的本地业务模块,通过 `@liujitcn/kratos-uni-app-core` 的公开接口接入宿主。
4
+
5
+ ## 目录结构
6
+
7
+ ```text
8
+ packages/modules/__MODULE_NAME__
9
+ ├── src
10
+ │ ├── views # 模块页面;页面私有组件放在就近 components
11
+ │ ├── index.d.mts # 模块入口类型
12
+ │ └── index.mjs # 页面、视图键和图标注册入口
13
+ ├── package.json # 模块名称、依赖和 exports
14
+ └── README.md # 模块职责与维护说明
15
+ ```
16
+
17
+ ## 开发约束
18
+
19
+ - 页面放在 `src/views`,并在 `src/index.mjs` 的模块定义中登记页面和稳定视图键。
20
+ - 请求、认证、导航和状态能力只通过 `@liujitcn/kratos-uni-app-core` 的公开 exports 使用。
21
+ - 页面替换使用稳定视图键,接口不能直接下发任意组件路径。
22
+ - 修改模块后从 workspace 根目录运行对应 H5 或微信小程序命令验证装配结果。
@@ -0,0 +1,32 @@
1
+ # __PROJECT_NAME__
2
+
3
+ `__PROJECT_NAME__` 是由 `@liujitcn/kratos-uni-app-cli` 创建的独立 pnpm workspace,支持 H5 和微信小程序。
4
+
5
+ ## 目录结构
6
+
7
+ ```text
8
+ __PROJECT_NAME__
9
+ ├── apps/uni-app # 私有 uni-app 宿主
10
+ ├── packages/modules # workspace 内本地业务模块
11
+ ├── package.json # 公共命令和开发依赖
12
+ ├── pnpm-workspace.yaml # workspace 包范围
13
+ └── tsconfig.json # 共享 TypeScript 配置
14
+ ```
15
+
16
+ 宿主只负责入口、manifest、模块清单和启动;底座能力由 `@liujitcn/kratos-uni-app-core` 提供,默认业务页面由 `@liujitcn/kratos-uni-app-system` 提供。本地模块通过各自公开入口接入,不跨包相对引用源码。
17
+
18
+ ## 开发
19
+
20
+ ```bash
21
+ pnpm install
22
+ pnpm dev:h5
23
+ pnpm dev:mp-weixin
24
+ pnpm build:h5
25
+ pnpm build:mp-weixin
26
+ ```
27
+
28
+ H5 通过局域网 IP 访问时,先在仓库根目录运行 `bash scripts/generate-dev-cert.sh 192.168.1.100` 生成证书,再在 `.env.development-h5.local` 中设置 `VITE_APP_HTTPS=true`;后端使用 HTTPS 时同时设置 `VITE_APP_API_URL=https://localhost:7001`。
29
+
30
+ 模块装配入口是 `apps/uni-app/src/module-manifest.ts`。新增、删除或调整模块时同步维护该清单,并在模块自己的 README 中记录页面和接口职责。
31
+
32
+ 生产构建前请在 `.env.production` 中填写实际 API 和静态资源地址;该文件默认留空,不应使用本机地址。
@@ -2,6 +2,7 @@ import { readdir, readFile, stat } from 'node:fs/promises'
2
2
  import { dirname, extname, relative, resolve, sep } from 'node:path'
3
3
  import { fileURLToPath } from 'node:url'
4
4
  import ts from 'typescript'
5
+ import { workspaceMessage } from './locale-messages.mjs'
5
6
 
6
7
  const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
7
8
  const coreRoot = resolve(appRoot, 'packages/core')
@@ -30,7 +31,7 @@ for (const packageFile of packageFiles) {
30
31
  ? resolve(packageRoot, prefix)
31
32
  : resolve(packageRoot, target)
32
33
  if (!(await pathExists(checkPath))) {
33
- throw new Error(`${metadata.name} 导出目标不存在:${target}`)
34
+ throw new Error(workspaceMessage('export_target_missing', { name: metadata.name, target }))
34
35
  }
35
36
  }
36
37
  }
@@ -40,8 +41,8 @@ const distinctPublicVersions = new Set(publicPackageVersions.values())
40
41
  if (distinctPublicVersions.size > 1 || [...distinctPublicVersions].some((version) => !version)) {
41
42
  const versions = [...publicPackageVersions]
42
43
  .map(([name, version]) => `${name}@${version}`)
43
- .join('、')
44
- throw new Error(`uni-app 公开包版本必须保持一致:${versions}`)
44
+ .join(workspaceMessage('list_separator'))
45
+ throw new Error(workspaceMessage('public_version_mismatch', { versions }))
45
46
  }
46
47
 
47
48
  for (const sourceFile of allFiles.filter((file) => sourceExtensions.has(extname(file)))) {
@@ -52,13 +53,16 @@ for (const sourceFile of allFiles.filter((file) => sourceExtensions.has(extname(
52
53
  const line = source.slice(0, imported.pos).split('\n').length
53
54
  if ((specifier === '@' || specifier.startsWith('@/')) && !isWithin(sourceFile, coreRoot)) {
54
55
  violations.push(
55
- `${format(sourceFile, line)}: 非 core 代码不得使用 core 私有别名 ${specifier}`,
56
+ workspaceMessage('core_alias_forbidden', { location: format(sourceFile, line), specifier }),
56
57
  )
57
58
  continue
58
59
  }
59
60
  if (specifier.startsWith('@system/') && !isWithin(sourceFile, systemRoot)) {
60
61
  violations.push(
61
- `${format(sourceFile, line)}: 非 system 代码不得使用 system 私有别名 ${specifier}`,
62
+ workspaceMessage('system_alias_forbidden', {
63
+ location: format(sourceFile, line),
64
+ specifier,
65
+ }),
62
66
  )
63
67
  continue
64
68
  }
@@ -66,7 +70,12 @@ for (const sourceFile of allFiles.filter((file) => sourceExtensions.has(extname(
66
70
  const sourcePackage = findPackageByPath(sourceFile)
67
71
  const targetPackage = findPackageByPath(resolve(dirname(sourceFile), specifier))
68
72
  if (sourcePackage && targetPackage && sourcePackage !== targetPackage) {
69
- violations.push(`${format(sourceFile, line)}: 禁止通过相对路径跨包引用 ${specifier}`)
73
+ violations.push(
74
+ workspaceMessage('cross_package_relative_forbidden', {
75
+ location: format(sourceFile, line),
76
+ specifier,
77
+ }),
78
+ )
70
79
  }
71
80
  continue
72
81
  }
@@ -80,7 +89,11 @@ for (const sourceFile of allFiles.filter((file) => sourceExtensions.has(extname(
80
89
  .exportEntries.some(([pattern]) => matchExportPattern(pattern, subpath))
81
90
  if (!exported) {
82
91
  violations.push(
83
- `${format(sourceFile, line)}: ${specifier} 未在 ${packageName} exports 中公开`,
92
+ workspaceMessage('export_not_public', {
93
+ location: format(sourceFile, line),
94
+ specifier,
95
+ packageName,
96
+ }),
84
97
  )
85
98
  }
86
99
  }
@@ -88,11 +101,17 @@ for (const sourceFile of allFiles.filter((file) => sourceExtensions.has(extname(
88
101
 
89
102
  if (violations.length) {
90
103
  console.error(
91
- `uni-app package exports 检查失败:\n${violations.map((item) => ` ${item}`).join('\n')}`,
104
+ workspaceMessage('exports_check_failed', {
105
+ violations: violations.map((item) => ` ${item}`).join('\n'),
106
+ }),
92
107
  )
93
108
  process.exit(1)
94
109
  }
95
- console.log(`uni-app package exports 检查通过(${[...packages.keys()].join('、')})`)
110
+ console.log(
111
+ workspaceMessage('exports_check_passed', {
112
+ packages: [...packages.keys()].join(workspaceMessage('list_separator')),
113
+ }),
114
+ )
96
115
 
97
116
  async function collectFiles(directory) {
98
117
  const files = []
@@ -0,0 +1,52 @@
1
+ const messages = {
2
+ 'zh-CN': {
3
+ missing_default_locale: '{module} 缺少 zh-CN 语言包',
4
+ locale_set_mismatch: '{module} 语言集合不一致',
5
+ locale_code_invalid: '语言代码无效:{locale}',
6
+ key_set_mismatch: '{module}/{locale} 语言键集合不一致',
7
+ bundle_outdated: '语言注册产物过期:{file},请执行 pnpm i18n:sync',
8
+ export_target_missing: '{name} 导出目标不存在:{target}',
9
+ public_version_mismatch: 'uni-app 公开包版本必须保持一致:{versions}',
10
+ core_alias_forbidden: '{location}: 非 core 代码不得使用 core 私有别名 {specifier}',
11
+ system_alias_forbidden: '{location}: 非 system 代码不得使用 system 私有别名 {specifier}',
12
+ cross_package_relative_forbidden: '{location}: 禁止通过相对路径跨包引用 {specifier}',
13
+ export_not_public: '{location}: {specifier} 未在 {packageName} exports 中公开',
14
+ core_dir_unexpected: '{packageName}: core 包目录异常',
15
+ system_dir_unexpected: '{packageName}: system 包目录异常',
16
+ exports_check_failed: 'uni-app package exports 检查失败:\n{violations}',
17
+ exports_check_passed: 'uni-app package exports 检查通过({packages})',
18
+ list_separator: '、',
19
+ },
20
+ 'en-US': {
21
+ missing_default_locale: '{module} is missing the zh-CN locale bundle',
22
+ locale_set_mismatch: '{module} has a different locale set',
23
+ locale_code_invalid: 'Invalid locale code: {locale}',
24
+ key_set_mismatch: 'Locale message keys differ for {module}/{locale}',
25
+ bundle_outdated: 'Locale bundle is out of date: {file}. Run pnpm i18n:sync.',
26
+ export_target_missing: '{name} export target does not exist: {target}',
27
+ public_version_mismatch: 'uni-app public package versions must match: {versions}',
28
+ core_alias_forbidden:
29
+ '{location}: Core private aliases cannot be used outside core: {specifier}',
30
+ system_alias_forbidden:
31
+ '{location}: System private aliases cannot be used outside system: {specifier}',
32
+ cross_package_relative_forbidden:
33
+ '{location}: Cross-package relative import is forbidden: {specifier}',
34
+ export_not_public: '{location}: {specifier} is not exposed by {packageName} exports',
35
+ core_dir_unexpected: '{packageName}: unexpected core package directory',
36
+ system_dir_unexpected: '{packageName}: unexpected system package directory',
37
+ exports_check_failed: 'uni-app package exports check failed:\n{violations}',
38
+ exports_check_passed: 'uni-app package exports check passed ({packages})',
39
+ list_separator: ', ',
40
+ },
41
+ }
42
+
43
+ /** 根据工作区语言偏好渲染脚手架诊断文案。 */
44
+ export function workspaceMessage(key, params = {}) {
45
+ const locale = (process.env.KRATOS_ADMIN_LOCALE ?? process.env.LC_ALL ?? process.env.LANG ?? '')
46
+ .toLowerCase()
47
+ .startsWith('zh')
48
+ ? 'zh-CN'
49
+ : 'en-US'
50
+ const template = messages[locale][key] ?? messages['en-US'][key] ?? key
51
+ return template.replace(/\{([A-Za-z0-9_]+)\}/g, (_, name) => params[name] ?? `{${name}}`)
52
+ }
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
2
2
  import { dirname, resolve } from 'node:path'
3
3
  import { fileURLToPath } from 'node:url'
4
+ import { workspaceMessage } from './locale-messages.mjs'
4
5
 
5
6
  const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
6
7
  const modulesRoot = resolve(root, 'packages/modules')
@@ -15,21 +16,23 @@ for (const entry of existsSync(modulesRoot)
15
16
  .filter((file) => file.endsWith('.json'))
16
17
  .map((file) => file.slice(0, -5))
17
18
  .sort((a, b) => (a === 'zh-CN' ? -1 : b === 'zh-CN' ? 1 : a.localeCompare(b)))
18
- if (!locales.includes('zh-CN')) throw new Error(`${entry.name} 缺少 zh-CN 语言包`)
19
+ if (!locales.includes('zh-CN')) {
20
+ throw new Error(workspaceMessage('missing_default_locale', { module: entry.name }))
21
+ }
19
22
  if (expectedLocales && locales.join(',') !== expectedLocales)
20
- throw new Error(`${entry.name} 语言集合不一致`)
23
+ throw new Error(workspaceMessage('locale_set_mismatch', { module: entry.name }))
21
24
  expectedLocales = locales.join(',')
22
25
  const messages = Object.fromEntries(
23
26
  locales.map((locale) => {
24
27
  if (!/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(locale))
25
- throw new Error(`语言代码无效:${locale}`)
28
+ throw new Error(workspaceMessage('locale_code_invalid', { locale }))
26
29
  return [locale, JSON.parse(readFileSync(resolve(directory, `${locale}.json`), 'utf8'))]
27
30
  }),
28
31
  )
29
32
  const keys = Object.keys(messages['zh-CN']).sort().join('\0')
30
33
  for (const locale of locales) {
31
34
  if (Object.keys(messages[locale]).sort().join('\0') !== keys)
32
- throw new Error(`${entry.name}/${locale} 语言键集合不一致`)
35
+ throw new Error(workspaceMessage('key_set_mismatch', { module: entry.name, locale }))
33
36
  }
34
37
  // 模块在 Vite 配置阶段由 Node 直接加载,使用无需 TS/JSON loader 的原生 ESM。
35
38
  const content = [
@@ -42,5 +45,5 @@ for (const entry of existsSync(modulesRoot)
42
45
  const file = resolve(directory, 'generated.mjs')
43
46
  if (write) writeFileSync(file, content)
44
47
  else if (!existsSync(file) || readFileSync(file, 'utf8') !== content)
45
- throw new Error(`语言注册产物过期:${file},请执行 pnpm i18n:sync`)
48
+ throw new Error(workspaceMessage('bundle_outdated', { file }))
46
49
  }