create-bubbles 0.1.27 → 0.1.28
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/dist/index.js +4 -4
- package/package.json +10 -2
- package/template-pnpm-vp-monorepo/.changeset/README.md +8 -0
- package/template-pnpm-vp-monorepo/.changeset/config.json +11 -0
- package/template-pnpm-vp-monorepo/.gitattributes +5 -0
- package/template-pnpm-vp-monorepo/.github/workflows/ci.yml +33 -0
- package/template-pnpm-vp-monorepo/.github/workflows/docs.yml +55 -0
- package/template-pnpm-vp-monorepo/.github/workflows/release.yml +54 -0
- package/template-pnpm-vp-monorepo/.vite-hooks/commit-msg +1 -0
- package/template-pnpm-vp-monorepo/.vite-hooks/pre-commit +1 -0
- package/template-pnpm-vp-monorepo/.vite-hooks/run.mjs +17 -0
- package/template-pnpm-vp-monorepo/.vscode/settings.json +23 -0
- package/{LICENSE → template-pnpm-vp-monorepo/LICENSE} +2 -2
- package/template-pnpm-vp-monorepo/README.md +93 -0
- package/template-pnpm-vp-monorepo/_gitignore +43 -0
- package/template-pnpm-vp-monorepo/apps/docs/.vitepress/config.mts +69 -0
- package/template-pnpm-vp-monorepo/apps/docs/guide/getting-started.md +70 -0
- package/template-pnpm-vp-monorepo/apps/docs/guide/release.md +73 -0
- package/template-pnpm-vp-monorepo/apps/docs/guide/workspace.md +84 -0
- package/template-pnpm-vp-monorepo/apps/docs/index.md +22 -0
- package/template-pnpm-vp-monorepo/apps/docs/package.json +23 -0
- package/template-pnpm-vp-monorepo/apps/docs/tsconfig.json +7 -0
- package/template-pnpm-vp-monorepo/apps/docs/vite.config.ts +12 -0
- package/template-pnpm-vp-monorepo/apps/playground/index.html +14 -0
- package/template-pnpm-vp-monorepo/apps/playground/package.json +24 -0
- package/template-pnpm-vp-monorepo/apps/playground/src/App.vue +214 -0
- package/template-pnpm-vp-monorepo/apps/playground/src/env.d.ts +6 -0
- package/template-pnpm-vp-monorepo/apps/playground/src/main.ts +6 -0
- package/template-pnpm-vp-monorepo/apps/playground/src/style.css +728 -0
- package/template-pnpm-vp-monorepo/apps/playground/tsconfig.json +10 -0
- package/template-pnpm-vp-monorepo/apps/playground/vite.config.ts +29 -0
- package/template-pnpm-vp-monorepo/commitlint.config.mjs +3 -0
- package/template-pnpm-vp-monorepo/eslint.config.mjs +22 -0
- package/template-pnpm-vp-monorepo/mise.toml +3 -0
- package/template-pnpm-vp-monorepo/package.json +40 -0
- package/template-pnpm-vp-monorepo/packages/utils/LICENSE +21 -0
- package/template-pnpm-vp-monorepo/packages/utils/README.md +26 -0
- package/template-pnpm-vp-monorepo/packages/utils/_gitignore +4 -0
- package/template-pnpm-vp-monorepo/packages/utils/package.json +42 -0
- package/template-pnpm-vp-monorepo/packages/utils/scripts/verify-package.mjs +127 -0
- package/template-pnpm-vp-monorepo/packages/utils/src/index.ts +12 -0
- package/template-pnpm-vp-monorepo/packages/utils/tests/consumer/commonjs.cts +20 -0
- package/template-pnpm-vp-monorepo/packages/utils/tests/consumer/esm.mts +20 -0
- package/template-pnpm-vp-monorepo/packages/utils/tests/consumer/tsconfig.json +10 -0
- package/template-pnpm-vp-monorepo/packages/utils/tests/index.test.ts +30 -0
- package/template-pnpm-vp-monorepo/packages/utils/tsconfig.json +4 -0
- package/template-pnpm-vp-monorepo/packages/utils/vite.config.ts +20 -0
- package/template-pnpm-vp-monorepo/pnpm-workspace.yaml +27 -0
- package/template-pnpm-vp-monorepo/tsconfig/base.json +22 -0
- package/template-pnpm-vp-monorepo/tsconfig/node.json +8 -0
- package/template-pnpm-vp-monorepo/tsconfig/vue.json +7 -0
- package/template-pnpm-vp-monorepo/tsconfig.json +4 -0
- package/template-pnpm-vp-monorepo/vite.config.ts +38 -0
- package/template-vp-monorepo-react-nestjs/.spaces/01./344/274/201/344/270/232/347/272/247/351/241/271/347/233/256/347/272/247//346/235/203/351/231/220/344/270/216/350/217/234/345/215/225//346/212/200/346/234/257/345/245/221/347/272/246.md +5 -3
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//344/272/244/344/273/230/350/256/260/345/275/225.md +9 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//345/217/221/345/270/203/350/257/204/344/274/260.md +93 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//345/267/245/345/205/267//350/277/220/350/241/214/346/227/266/345/217/221/345/270/203/346/243/200/346/237/245.mjs +340 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/346/212/245/345/221/212.md +28 -6
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214/comment-clean/catalog/en_US.json +4 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214/comment-clean/i18n.config.mjs +9 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214/comment-clean/report.json +16 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214/comment-clean/source/app.ts +2 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214/empty-scan/catalog/en_US.json +4 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214/empty-scan/i18n.config.mjs +9 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214/empty-scan/report.json +19 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214/output.json +84 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214//345/275/223/345/211/215/345/244/215/351/252/214.json +86 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214//346/243/200/346/237/245/347/273/223/346/236/234.json +30 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256/cli-/346/211/253/346/217/217/350/276/271/347/225/214//346/263/250/351/207/212/345/233/236/345/275/222.json +40 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256//345/214/205/345/217/221/345/270/203/bubblesjs-i18n-core-0.0.0.tgz +0 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256//345/214/205/345/217/221/345/270/203/bubblesjs-i18n-react-0.0.0.tgz +0 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256//345/214/205/345/217/221/345/270/203/bubblesjs-i18n-vue-0.0.1.tgz +0 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256//345/214/205/345/217/221/345/270/203//344/272/247/347/211/251/345/244/215/351/252/214.json +419 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256//345/214/205/345/217/221/345/270/203//346/243/200/346/237/245/347/273/223/346/236/234.json +339 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//346/265/213/350/257/225/350/257/201/346/215/256//350/277/220/350/241/214/346/227/266/345/217/221/345/270/203/346/243/200/346/237/245.json +131 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//350/256/276/350/256/241/346/226/271/346/241/210.md +19 -0
- package/template-vp-monorepo-react-nestjs/.spaces/04./347/225/214/351/235/242/344/270/216/344/270/273/351/242/230//345/233/275/351/231/205/345/214/226//351/234/200/346/261/202/346/226/207/346/241/243.md +21 -3
- package/template-vp-monorepo-react-nestjs/.spaces/06./350/256/244/350/257/201/344/270/216/344/274/232/350/257/235//344/274/232/350/257/235/347/256/241/347/220/206//346/212/200/346/234/257/345/245/221/347/272/246.md +5 -2
- package/template-vp-monorepo-react-nestjs/.spaces/06./350/256/244/350/257/201/344/270/216/344/274/232/350/257/235//344/274/232/350/257/235/347/256/241/347/220/206//351/234/200/346/261/202/346/226/207/346/241/243.md +4 -3
- package/template-vp-monorepo-react-nestjs/.spaces/06./350/256/244/350/257/201/344/270/216/344/274/232/350/257/235//344/277/256/346/224/271/345/257/206/347/240/201/PRD.md +2 -1
- package/template-vp-monorepo-react-nestjs/.spaces/06./350/256/244/350/257/201/344/270/216/344/274/232/350/257/235//344/277/256/346/224/271/345/257/206/347/240/201//346/212/200/346/234/257/345/245/221/347/272/246.md +3 -2
- package/template-vp-monorepo-react-nestjs/.spaces/06./350/256/244/350/257/201/344/270/216/344/274/232/350/257/235//344/277/256/346/224/271/345/257/206/347/240/201//346/265/213/350/257/225/346/212/245/345/221/212.md +15 -12
- package/template-vp-monorepo-react-nestjs/.spaces/06./350/256/244/350/257/201/344/270/216/344/274/232/350/257/235//344/277/256/346/224/271/345/257/206/347/240/201//350/201/224/350/260/203/350/256/260/345/275/225.md +6 -5
- package/template-vp-monorepo-react-nestjs/.spaces/06./350/256/244/350/257/201/344/270/216/344/274/232/350/257/235//344/277/256/346/224/271/345/257/206/347/240/201//350/256/276/350/256/241/346/226/271/346/241/210.md +2 -1
- package/template-vp-monorepo-react-nestjs/.spaces/06./350/256/244/350/257/201/344/270/216/344/274/232/350/257/235//344/277/256/346/224/271/345/257/206/347/240/201//351/234/200/346/261/202/346/226/207/346/241/243.md +4 -3
- package/template-vp-monorepo-react-nestjs/.spaces/README.md +3 -2
- package/template-vp-monorepo-react-nestjs/apps/web/test/{password-actions.spec.ts → password/password-actions.spec.ts} +3 -3
- package/template-vp-monorepo-react-nestjs/apps/web/test/{password-reset-action.spec.ts → password/password-reset-action.spec.ts} +4 -2
- package/template-vp-monorepo-react-nestjs/apps/web/test/{workspaces-password-entry.spec.tsx → password/workspaces-password-entry.spec.tsx} +7 -7
- package/template-vp-monorepo-react-nestjs/packages/i18n/cli/README.md +2 -0
- package/template-vp-monorepo-react-nestjs/packages/i18n/cli/src/scanner.test.ts +88 -0
- package/template-vp-monorepo-react-nestjs/packages/i18n/cli/src/scanner.ts +34 -11
- package/template-vp-monorepo-react-nestjs/packages/i18n/cli/test/command.test.ts +36 -0
- package/template-vp-monorepo-react-nestjs/packages/i18n/react/src/use-store.ts +15 -5
- package/template-vp-monorepo-react-nestjs/tsconfig.lib.json +1 -1
- package/template-vp-react/package.json +5 -5
- package/template-vp-vue-eslint-vapor/.vscode/settings.json +53 -0
- package/template-vp-vue-eslint-vapor/package.json +1 -0
- package/template-vp-vue-eslint-vapor/pnpm-workspace.yaml +0 -24
- package/template-vp-vue-eslint-vapor/src/main.ts +2 -3
- package/template-vp-vue-eslint-vapor/vite.config.ts +26 -31
- package/template-vp-react/.vscode/settings.json +0 -18
- /package/{template-vp-vue-eslint-vapor → template-pnpm-vp-monorepo}/.vscode/extensions.json +0 -0
- /package/template-vp-monorepo-react-nestjs/apps/server/test/auth/{password-operations.spec.ts → password/password-operations.spec.ts} +0 -0
- /package/template-vp-monorepo-react-nestjs/apps/server/test/auth/{password.integration.spec.ts → password/password.integration.spec.ts} +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# i18n 独立包发布评估
|
|
2
|
+
|
|
3
|
+
> 状态:验证记录。日期:2026-10-09。结论:CLI 注释提取与 React selector 缓存已验证通过,当前版本仍不宜直接发布。
|
|
4
|
+
|
|
5
|
+
## 评估范围
|
|
6
|
+
|
|
7
|
+
依据 [需求文档](./需求文档.md),重新检查 `@bubblesjs/i18n-core`、`@bubblesjs/i18n-react`、`@bubblesjs/i18n-vue` 和 `@bubblesjs/i18n-cli` 的独立 npm 发布准备情况。本轮对用户修改的 React selector 缓存执行真实渲染验证,重跑构建、CLI 测试、UI locale 测试、类型/格式检查及本地打包。未修改业务实现、包配置或执行 npm 发布。
|
|
8
|
+
|
|
9
|
+
## 结论与包边界
|
|
10
|
+
|
|
11
|
+
方案可以作为轻量国际化库发布,定位是字符串 key、扁平 JSON 词条、占位符插值、异步语言加载、React/Vue 订阅和词条维护 CLI。核心与适配层没有业务应用依赖,React/Vue 已声明框架 peerDependencies,构建也将框架排除出 bundle。
|
|
12
|
+
|
|
13
|
+
建议保持现有四包,版本协调发布:core 提供运行时,react/vue 提供按需框架适配,cli 作为开发依赖。若需要统一入口,可后续增加聚合包;单一包不是此次独立发布的前提。应用内 antd、Pro Components、Day.js 和 `<html lang>` 的映射继续作为接入示例。
|
|
14
|
+
|
|
15
|
+
## 技术发布阻塞
|
|
16
|
+
|
|
17
|
+
| 问题 | 当前证据 | 整改要求 |
|
|
18
|
+
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
19
|
+
| 四包禁止发布且公开入口为源码 | 四个 `package.json` 均 `private: true`;`exports.types/import` 指向 `src/*.ts` 或 `*.tsx`,CLI `bin` 也指向 TS 源码 | 调整发布元数据,入口引用构建后的 JS 与声明文件,CLI `bin` 引用 `dist/cli.mjs` |
|
|
20
|
+
| 构建产物未进入实际 tarball | 四包构建成功,但 core/react/vue 的 dry-run 清单和实际 tarball 均完全没有 `dist`,仅有源码和开发配置 | 用 `files` 白名单明确包含 dist、README 和许可证;增加打包前构建步骤 |
|
|
21
|
+
| CLI 在 Windows 当前检出状态不能打包 | `pnpm --dir packages/i18n/cli pack --dry-run --json` 返回 `ERR_PNPM_BIN_CRLF`,shebang 为 CRLF | 将 bin 指向构建后的可执行入口,并确保发布入口使用 LF,验证权限与命令可执行性 |
|
|
22
|
+
| core 独立检查不通过 | `vp check --no-fmt packages/i18n` 在 `core/src/i18n.ts:41,74` 报 `TS2584: Cannot find name 'console'`;lib tsconfig 只包含 ES2020 且 types 为空 | 校准核心包声明环境或日志实现,独立 lint/type check 必须通过 |
|
|
23
|
+
|
|
24
|
+
相关配置:[core](../../../packages/i18n/core/package.json)、[React](../../../packages/i18n/react/package.json)、[Vue](../../../packages/i18n/vue/package.json)、[CLI](../../../packages/i18n/cli/package.json)。运行时包产物为 `dist/index.js`、`dist/index.d.ts`;CLI 库入口为 `dist/index.mjs`、`dist/index.d.mts`,不能将 CLI 的声明入口误写为 `.d.ts`。
|
|
25
|
+
|
|
26
|
+
工作区源码入口能被 Vite 消费,不代表普通安装环境可用。以 `--preserve-symlinks` 模拟 node_modules 路径的 Node 导入,core 返回 `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`,React 返回 `.tsx` 的 `ERR_UNKNOWN_FILE_EXTENSION`。
|
|
27
|
+
|
|
28
|
+
`workspace:*` 与 `catalog:` 本身不是缺陷:本轮实际检查 core/react/vue tarball,pnpm 已把适配包的 core 依赖转换为 `0.0.0`,并把 catalog 开发依赖转换为真实版本范围。发布应使用能够执行这种转换的打包流程;CLI 因 CRLF 阻塞,本次没有生成其实际 tarball。
|
|
29
|
+
|
|
30
|
+
## 建议发布前修复的行为缺陷
|
|
31
|
+
|
|
32
|
+
| 优先级 | 问题和触发条件 | 定位与建议 |
|
|
33
|
+
| ------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| P1 | 异步语言切换按完成顺序提交。先请求 en_US,再请求 zh_CN,zh_CN 先完成、en_US 后完成,最终语言错误地变回 en_US | [core/i18n.ts](../../../packages/i18n/core/src/i18n.ts) 第 36–47 行;增加请求序号,只提交最后一次请求的结果 |
|
|
35
|
+
| P2 | 缺失词条读取 Object 原型链:`tr('toString')` 返回函数,`tr('constructor', { a: 1 })` 抛错;缺失 `{toString}` 插值会输出原生函数文本 | [core/i18n.ts](../../../packages/i18n/core/src/i18n.ts) 第 34 行和 [format.ts](../../../packages/i18n/core/src/format.ts) 第 8 行;查词条与插值参数时只允许自有属性 |
|
|
36
|
+
| P2 | CLI include 匹配零源文件时,`check` 仍输出 `Check passed.` 并返回 0,不能可靠发现扫描范围配置错误 | [command.ts](../../../packages/i18n/cli/src/command.ts) 第 116、189–199 行;默认拒绝零源文件检查,允许空项目时应使用明确配置 |
|
|
37
|
+
|
|
38
|
+
初次 CLI 删除问题通过 `--dry-run` 复现,没有真正删除词条;两个场景的语言包 SHA256 前后完全相同。运行时复现的预期结果是确认当前缺陷,不能将其记为产品测试通过。
|
|
39
|
+
|
|
40
|
+
### 已修复:React 对象 selector 快照不稳定
|
|
41
|
+
|
|
42
|
+
[use-store.ts](../../../packages/i18n/react/src/use-store.ts) 第 13–24 行使用独立闭包缓存,同一 state 引用复用 selector 结果。本轮真实 React DOM 检查具名与内联对象 selector,均渲染 2 次,状态更新后 DOM 从 en_US 变为 zh_CN,无警告或异常,已撤销无限更新缺陷。另行只读验证 StrictMode 下父级重渲染、state 更新及 store 替换,结果也正常。当前 React 版本为 19.3.0;未执行完整 React 18/19 版本矩阵。缓存仍不提供跨 state 的可选相等比较,这是优化范围;SSR 仍见下述限制。
|
|
43
|
+
|
|
44
|
+
### 已修复:CLI 调用边界注释漏提取
|
|
45
|
+
|
|
46
|
+
`tr(/*说明*/ '保留')` 等静态调用现在可以正常提取。[scanner.ts](../../../packages/i18n/cli/src/scanner.ts) 在调用名后、左括号后和字符串后跳过空白、行注释与块注释;字符串正文和动态表达式限制保持原有规则。新增 16 个扫描用例及 1 个实际 `sync --clean` 用例,本轮重新执行 CLI 共 71 个测试通过。相同 dry-run 场景识别两个词条,`deleted` 为空、`unchangedCount` 为 2,语言包 SHA256 不变。当前证据见 [当前复验](./测试证据/cli-扫描边界/当前复验.json);首次修复证据见 [注释回归](./测试证据/cli-扫描边界/注释回归.json)。
|
|
47
|
+
|
|
48
|
+
## 首发支持范围与其他建议
|
|
49
|
+
|
|
50
|
+
- React SSR 当前不可用:`useSyncExternalStore` 缺少 `getServerSnapshot`,`renderToString` 报 `Missing getServerSnapshot`。如首发承诺 SSR,需要修复并定义服务端快照;仅支持浏览器渲染时应在 README 明确。
|
|
51
|
+
- Vue Provider 在 setup 中只注入一次 `props.store`,替换 prop 后后代仍使用旧 store。应支持替换,或明确实例初始化后保持不变的契约。
|
|
52
|
+
- core 公开导出的 `shallowEqualObject({ first: undefined }, { second: undefined })` 错误返回 true。建议检查键是否同时存在,或缩小公共工具的导出范围。
|
|
53
|
+
- 发布包目前缺少许可证、仓库与发布说明;core/react/vue 缺少 README。补充各包安装、最小接入、支持环境、语言加载失败语义和 API 文档。版本 `0.0.0` 合法,但建议确定一致的预发布版本与依赖范围。
|
|
54
|
+
- 当前仅输出 ESM 可以作为首发范围,不必为了发布强制增加 CJS。复数、ICU、命名空间等能力属于扩展选择,不是此次技术发布阻塞。
|
|
55
|
+
- CLI 文本扫描会提取注释或普通字符串中的完整 `tr(...)`,README 已声明这一边界。Excel 同步文档属于尚未实施的规划,不能作为已交付功能宣传。
|
|
56
|
+
|
|
57
|
+
## 实际验证
|
|
58
|
+
|
|
59
|
+
环境:Windows,Node `v24.21.0`,pnpm `12.10.1`,全局 vp `1.1.0`,本地 vite-plus `0.3.1`。
|
|
60
|
+
|
|
61
|
+
| 执行项 | 结果 |
|
|
62
|
+
| --------------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
63
|
+
| `vp install --frozen-lockfile` | 2026-10-08 历史记录:成功、锁文件未变化;本轮未重新安装 |
|
|
64
|
+
| `vp run --filter '@bubblesjs/i18n-*' build` | 四包成功;React 因源码变化重新构建,其他三包命中构建缓存 |
|
|
65
|
+
| CLI `vp test run` 与 `vp run --filter @bubblesjs/i18n-cli test` | 6 个文件、71 个测试通过;缓存结果后另执行包内测试确认 |
|
|
66
|
+
| React 修改文件 `vp check --no-fmt` | lint 和类型检查通过;启用格式检查时未通过 |
|
|
67
|
+
| CLI 修改文件的 `vp check` | 2026-10-08 历史记录:4 文件格式与 3 TS 文件 lint/type 通过 |
|
|
68
|
+
| `vp run --filter web test --run test/i18n-locale.spec.ts` | 1 个文件、2 个 UI locale 映射测试通过 |
|
|
69
|
+
| `vp check packages/i18n` | 失败,36 个文件存在格式问题,包含 React 修改文件 |
|
|
70
|
+
| `vp check --no-fmt packages/i18n` | 失败,core 两处 console 的 TS2584 错误 |
|
|
71
|
+
| core/react/vue dry-run 与实际 tarball 检查 | 三包均缺少 dist;源码入口、private 保留,pnpm 依赖协议转换正常 |
|
|
72
|
+
| CLI `pnpm pack --dry-run --json` | 失败,ERR_PNPM_BIN_CRLF |
|
|
73
|
+
| `node packages/i18n/cli/dist/cli.mjs --help` | 成功,构建产物可以由 Node 启动 |
|
|
74
|
+
| 运行时与 CLI 最小场景 | React 对象 selector 与注释提取通过;其他上述问题仍可复现,见证据 |
|
|
75
|
+
|
|
76
|
+
现有测试通过只证明其覆盖范围内的行为;core、react、vue 三包目前没有独立测试文件,应用两个映射测试没有覆盖加载竞态和真实框架适配;本轮真实适配验证由归档工具执行。当前包内容已阻塞消费,尚未完成正常安装后的类型解析和框架接入验收。本次未执行远端发布,未开展声明的最低 Node 版本、框架版本矩阵或浏览器端完整接入测试。
|
|
77
|
+
|
|
78
|
+
证据与复现入口:
|
|
79
|
+
|
|
80
|
+
- [构建、测试、打包与入口检查结果](./测试证据/包发布/检查结果.json)。
|
|
81
|
+
- [本轮实际包产物复验](./测试证据/包发布/产物复验.json)。
|
|
82
|
+
- [实际 React tarball](./测试证据/包发布/bubblesjs-i18n-react-0.0.0.tgz),仅供核验当前内容,不是可发布产物。
|
|
83
|
+
- [CLI 扫描边界历史基线](./测试证据/cli-扫描边界/output.json),保存修复前的执行结果;相邻目录保存两组配置、源码、JSON 与报告。调用边界注释的当前验证见 [注释回归](./测试证据/cli-扫描边界/注释回归.json),零源文件 check 问题仍未修复。
|
|
84
|
+
- [运行时发布检查工具](./工具/运行时发布检查.mjs)与[运行时检查结果](./测试证据/运行时发布检查.json)。
|
|
85
|
+
- 既有应用集成证据见 [测试报告](./测试报告.md)。
|
|
86
|
+
|
|
87
|
+
## 达到可发布状态的验收
|
|
88
|
+
|
|
89
|
+
1. 四包通过独立构建、类型和格式检查,公开入口与 tarball 内文件一致;CLI 在声明的 Node 环境下可执行。
|
|
90
|
+
2. 修复 P1 行为缺陷及词条查找契约错误,补充对应回归测试。
|
|
91
|
+
3. 明确 SSR、Vue store 替换和 ESM 的支持范围,完成 README、许可证、版本与发布元数据。
|
|
92
|
+
4. 用真实 tarball 在隔离消费者工程中验证安装、类型解析、core 翻译、React/Vue 订阅和 CLI sync/check;不依赖本仓库的源码别名或开发脚本。
|
|
93
|
+
5. 依赖协议正确转换、core 版本可被适配包解析后,再进行协调的预发布。
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import { dirname, resolve } from 'node:path'
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
6
|
+
|
|
7
|
+
const toolDirectory = dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
const repositoryRoot = resolve(toolDirectory, '../../../..')
|
|
9
|
+
const evidencePath = resolve(toolDirectory, '../测试证据/运行时发布检查.json')
|
|
10
|
+
const appRequire = createRequire(resolve(repositoryRoot, 'apps/web/package.json'))
|
|
11
|
+
const vueRequire = createRequire(resolve(repositoryRoot, 'packages/i18n/vue/package.json'))
|
|
12
|
+
const distPaths = {
|
|
13
|
+
core: resolve(repositoryRoot, 'packages/i18n/core/dist/index.js'),
|
|
14
|
+
react: resolve(repositoryRoot, 'packages/i18n/react/dist/index.js'),
|
|
15
|
+
vue: resolve(repositoryRoot, 'packages/i18n/vue/dist/index.js'),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** 将异常转换为可归档的信息,避免写入无关调用栈。 */
|
|
19
|
+
function errorDetails(error) {
|
|
20
|
+
return {
|
|
21
|
+
name: error instanceof Error ? error.name : typeof error,
|
|
22
|
+
message: error instanceof Error ? error.message : String(error),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 使用已构建的公开入口复现语言加载乱序,不依赖计时器或网络。 */
|
|
27
|
+
async function checkLanguageSwitchRace(core) {
|
|
28
|
+
const pending = new Map()
|
|
29
|
+
const store = core.createI18n({
|
|
30
|
+
locale: 'initial',
|
|
31
|
+
message: { greeting: 'Initial' },
|
|
32
|
+
/** 保存每次加载的完成方法,由检查工具明确控制完成顺序。 */
|
|
33
|
+
loaderMessage: (locale) => new Promise((complete) => pending.set(locale, complete)),
|
|
34
|
+
})
|
|
35
|
+
const first = store.getState().loadLocale('slow')
|
|
36
|
+
const second = store.getState().loadLocale('latest')
|
|
37
|
+
pending.get('latest')({ greeting: 'Latest' })
|
|
38
|
+
await second
|
|
39
|
+
pending.get('slow')({ greeting: 'Stale' })
|
|
40
|
+
await first
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
requestOrder: ['slow', 'latest'],
|
|
44
|
+
completionOrder: ['latest', 'slow'],
|
|
45
|
+
expectedLocale: 'latest',
|
|
46
|
+
actualLocale: store.getState().locale,
|
|
47
|
+
actualTranslation: store.getState().tr('greeting'),
|
|
48
|
+
problemReproduced: store.getState().locale !== 'latest',
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 确认一个实例的语言切换不会改变另一个实例的语言或词条。 */
|
|
53
|
+
async function checkInstanceIsolation(core) {
|
|
54
|
+
const first = core.createI18n({
|
|
55
|
+
locale: 'zh_CN',
|
|
56
|
+
message: { greeting: '你好' },
|
|
57
|
+
loaderMessage: async () => ({ greeting: 'Hello' }),
|
|
58
|
+
})
|
|
59
|
+
const second = core.createI18n({ locale: 'zh_CN', message: { greeting: '独立实例' } })
|
|
60
|
+
await first.getState().loadLocale('en_US')
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
first: { locale: first.getState().locale, translation: first.getState().tr('greeting') },
|
|
64
|
+
second: { locale: second.getState().locale, translation: second.getState().tr('greeting') },
|
|
65
|
+
passed: second.getState().locale === 'zh_CN' && second.getState().tr('greeting') === '独立实例',
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 检查缺失词条是否误读对象原型,以及携带插值参数时是否抛出异常。 */
|
|
70
|
+
function checkPrototypeMessageKeys(core) {
|
|
71
|
+
const store = core.createI18n({ message: {} })
|
|
72
|
+
const values = ['constructor', 'toString'].map((key) => {
|
|
73
|
+
const result = store.getState().tr(key)
|
|
74
|
+
return {
|
|
75
|
+
key,
|
|
76
|
+
expected: key,
|
|
77
|
+
actualType: typeof result,
|
|
78
|
+
returnedString: typeof result === 'string',
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
let interpolationError = null
|
|
82
|
+
try {
|
|
83
|
+
store.getState().tr('constructor', { count: 1 })
|
|
84
|
+
} catch (error) {
|
|
85
|
+
interpolationError = errorDetails(error)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
values,
|
|
90
|
+
interpolationError,
|
|
91
|
+
problemReproduced:
|
|
92
|
+
values.some((result) => !result.returnedString) || interpolationError !== null,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** 检查未提供的插值参数是否仍按契约保留原占位符。 */
|
|
97
|
+
function checkPrototypeInterpolationKey(core) {
|
|
98
|
+
const expected = 'Value: {toString}'
|
|
99
|
+
const actual = core.formatMessage(expected, {})
|
|
100
|
+
return { expected, actual, problemReproduced: actual !== expected }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 检查公开浅比较方法能否区分不同键且值均为 undefined 的对象。 */
|
|
104
|
+
function checkShallowObjectKeys(core) {
|
|
105
|
+
const actual = core.shallowEqualObject({ first: undefined }, { second: undefined })
|
|
106
|
+
return {
|
|
107
|
+
inputDescription: '{ first: undefined } 与 { second: undefined }',
|
|
108
|
+
expected: false,
|
|
109
|
+
actual,
|
|
110
|
+
problemReproduced: actual !== false,
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** 使用真实 React 服务端渲染器检查国际化订阅是否提供服务器快照。 */
|
|
115
|
+
function checkReactSSR(core, reactAdapter, React, renderToString) {
|
|
116
|
+
const store = core.createI18n({ locale: 'en_US' })
|
|
117
|
+
/** 在服务端组件中读取完整国际化状态。 */
|
|
118
|
+
function Example() {
|
|
119
|
+
const state = reactAdapter.useI18nStore(store)
|
|
120
|
+
return React.createElement('span', null, state.locale)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
return {
|
|
125
|
+
expected: '完成服务端渲染',
|
|
126
|
+
html: renderToString(React.createElement(Example)),
|
|
127
|
+
problemReproduced: false,
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return { expected: '完成服务端渲染', error: errorDetails(error), problemReproduced: true }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** 检查具名或内联对象 selector 的快照稳定性,以及状态更新后的实际 DOM。 */
|
|
135
|
+
async function checkReactObjectSelector({
|
|
136
|
+
core,
|
|
137
|
+
reactAdapter,
|
|
138
|
+
React,
|
|
139
|
+
createRoot,
|
|
140
|
+
inlineSelector = false,
|
|
141
|
+
}) {
|
|
142
|
+
const store = core.createI18n({ locale: 'en_US' })
|
|
143
|
+
const host = document.createElement('div')
|
|
144
|
+
document.body.appendChild(host)
|
|
145
|
+
const root = createRoot(host)
|
|
146
|
+
const warnings = []
|
|
147
|
+
const originalConsoleError = console.error
|
|
148
|
+
let renderCount = 0
|
|
149
|
+
let observedError = null
|
|
150
|
+
let before = null
|
|
151
|
+
let after = null
|
|
152
|
+
|
|
153
|
+
/** 返回新的状态片段对象,复现公开 selector 类型允许的常见用法。 */
|
|
154
|
+
function selectLocale(state) {
|
|
155
|
+
return { locale: state.locale }
|
|
156
|
+
}
|
|
157
|
+
/** 订阅对象 selector,并统计初始渲染和状态变化后的渲染次数。 */
|
|
158
|
+
function Example() {
|
|
159
|
+
renderCount += 1
|
|
160
|
+
const result = reactAdapter.useI18nStore(
|
|
161
|
+
store,
|
|
162
|
+
inlineSelector ? (state) => ({ locale: state.locale }) : selectLocale,
|
|
163
|
+
)
|
|
164
|
+
return React.createElement('span', null, result.locale)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.error = (...items) => warnings.push(String(items[0]))
|
|
168
|
+
try {
|
|
169
|
+
await React.act(() => root.render(React.createElement(Example)))
|
|
170
|
+
before = host.textContent
|
|
171
|
+
await React.act(() => store.setState((state) => ({ ...state, locale: 'zh_CN' })))
|
|
172
|
+
after = host.textContent
|
|
173
|
+
} catch (error) {
|
|
174
|
+
observedError = errorDetails(error)
|
|
175
|
+
} finally {
|
|
176
|
+
try {
|
|
177
|
+
await React.act(() => root.unmount())
|
|
178
|
+
} finally {
|
|
179
|
+
console.error = originalConsoleError
|
|
180
|
+
host.remove()
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
expected: '稳定渲染,状态更新后 DOM 从 en_US 变为 zh_CN',
|
|
186
|
+
selectorKind: inlineSelector ? 'inline' : 'named',
|
|
187
|
+
stateUpdates: 1,
|
|
188
|
+
before,
|
|
189
|
+
after,
|
|
190
|
+
renderCount,
|
|
191
|
+
warnings,
|
|
192
|
+
error: observedError,
|
|
193
|
+
problemReproduced: observedError !== null || before !== 'en_US' || after !== 'zh_CN',
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** 使用真实 Vue 渲染器检查 Provider 的 store prop 更换是否传递给后代。 */
|
|
198
|
+
async function checkVueStoreReplacement(core, vueAdapter, Vue) {
|
|
199
|
+
const first = core.createI18n({ locale: 'first' })
|
|
200
|
+
const second = core.createI18n({ locale: 'second' })
|
|
201
|
+
const active = Vue.shallowRef(first)
|
|
202
|
+
const Consumer = Vue.defineComponent({
|
|
203
|
+
/** 在默认插槽后代中消费国际化语言。 */
|
|
204
|
+
setup() {
|
|
205
|
+
const { locale } = vueAdapter.useI18n()
|
|
206
|
+
return () => Vue.h('span', locale.value)
|
|
207
|
+
},
|
|
208
|
+
})
|
|
209
|
+
const app = Vue.createApp({
|
|
210
|
+
/** 保留 Provider 实例,更新它接收的 store 属性。 */
|
|
211
|
+
setup() {
|
|
212
|
+
return () =>
|
|
213
|
+
Vue.h(vueAdapter.I18nProvider, { store: active.value }, { default: () => Vue.h(Consumer) })
|
|
214
|
+
},
|
|
215
|
+
})
|
|
216
|
+
const host = document.createElement('div')
|
|
217
|
+
document.body.appendChild(host)
|
|
218
|
+
try {
|
|
219
|
+
app.mount(host)
|
|
220
|
+
const before = host.textContent
|
|
221
|
+
active.value = second
|
|
222
|
+
await Vue.nextTick()
|
|
223
|
+
const after = host.textContent
|
|
224
|
+
return {
|
|
225
|
+
before,
|
|
226
|
+
expectedAfter: 'second',
|
|
227
|
+
actualAfter: after,
|
|
228
|
+
problemReproduced: after !== 'second',
|
|
229
|
+
}
|
|
230
|
+
} finally {
|
|
231
|
+
app.unmount()
|
|
232
|
+
host.remove()
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** 单独记录工具执行异常,避免将环境失败错误地标成产品缺陷。 */
|
|
237
|
+
async function captureCheck(check) {
|
|
238
|
+
try {
|
|
239
|
+
return await check()
|
|
240
|
+
} catch (error) {
|
|
241
|
+
return { toolError: errorDetails(error) }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** 读取现有构建产物执行隔离检查,仅写入同功能目录的 JSON 证据。 */
|
|
246
|
+
async function main() {
|
|
247
|
+
const { Window } = appRequire('happy-dom')
|
|
248
|
+
const window = new Window()
|
|
249
|
+
const globalKeys = [
|
|
250
|
+
'window',
|
|
251
|
+
'document',
|
|
252
|
+
'navigator',
|
|
253
|
+
'Element',
|
|
254
|
+
'HTMLElement',
|
|
255
|
+
'SVGElement',
|
|
256
|
+
'Node',
|
|
257
|
+
'IS_REACT_ACT_ENVIRONMENT',
|
|
258
|
+
]
|
|
259
|
+
const originalDescriptors = new Map(
|
|
260
|
+
globalKeys.map((key) => [key, Object.getOwnPropertyDescriptor(globalThis, key)]),
|
|
261
|
+
)
|
|
262
|
+
for (const key of globalKeys) {
|
|
263
|
+
const value =
|
|
264
|
+
key === 'window' ? window : key === 'IS_REACT_ACT_ENVIRONMENT' ? true : window[key]
|
|
265
|
+
Object.defineProperty(globalThis, key, { value, writable: true, configurable: true })
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const core = await import(pathToFileURL(distPaths.core).href)
|
|
270
|
+
const reactAdapter = await import(pathToFileURL(distPaths.react).href)
|
|
271
|
+
const vueAdapter = await import(pathToFileURL(distPaths.vue).href)
|
|
272
|
+
const React = appRequire('react')
|
|
273
|
+
const { renderToString } = appRequire('react-dom/server')
|
|
274
|
+
const { createRoot } = appRequire('react-dom/client')
|
|
275
|
+
const Vue = vueRequire('vue')
|
|
276
|
+
const artifacts = {}
|
|
277
|
+
for (const [name, file] of Object.entries(distPaths)) {
|
|
278
|
+
artifacts[name] = {
|
|
279
|
+
path: file,
|
|
280
|
+
sha256: createHash('sha256')
|
|
281
|
+
.update(await readFile(file))
|
|
282
|
+
.digest('hex'),
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const checks = {
|
|
286
|
+
languageSwitchRace: await captureCheck(() => checkLanguageSwitchRace(core)),
|
|
287
|
+
instanceIsolation: await captureCheck(() => checkInstanceIsolation(core)),
|
|
288
|
+
prototypeMessageKeys: await captureCheck(() => checkPrototypeMessageKeys(core)),
|
|
289
|
+
prototypeInterpolationKey: await captureCheck(() => checkPrototypeInterpolationKey(core)),
|
|
290
|
+
shallowObjectKeys: await captureCheck(() => checkShallowObjectKeys(core)),
|
|
291
|
+
reactSSR: await captureCheck(() => checkReactSSR(core, reactAdapter, React, renderToString)),
|
|
292
|
+
reactObjectSelector: await captureCheck(() =>
|
|
293
|
+
checkReactObjectSelector({ core, reactAdapter, React, createRoot }),
|
|
294
|
+
),
|
|
295
|
+
reactInlineObjectSelector: await captureCheck(() =>
|
|
296
|
+
checkReactObjectSelector({ core, reactAdapter, React, createRoot, inlineSelector: true }),
|
|
297
|
+
),
|
|
298
|
+
vueProviderStoreReplacement: await captureCheck(() =>
|
|
299
|
+
checkVueStoreReplacement(core, vueAdapter, Vue),
|
|
300
|
+
),
|
|
301
|
+
}
|
|
302
|
+
const report = {
|
|
303
|
+
assessmentDate: '2026-10-09',
|
|
304
|
+
executedAtUtc: new Date().toISOString(),
|
|
305
|
+
command: 'node .spaces/04.界面与主题/国际化/工具/运行时发布检查.mjs',
|
|
306
|
+
method:
|
|
307
|
+
'直接导入当前 dist;使用真实 React SSR、React DOM、Vue 渲染器与 happy-dom;未安装依赖、未构建、未修改业务代码。',
|
|
308
|
+
environment: {
|
|
309
|
+
node: process.version,
|
|
310
|
+
platform: process.platform,
|
|
311
|
+
react: appRequire('react/package.json').version,
|
|
312
|
+
reactDom: appRequire('react-dom/package.json').version,
|
|
313
|
+
vue: vueRequire('vue/package.json').version,
|
|
314
|
+
dependencyResolution: {
|
|
315
|
+
appRequireBase: resolve(repositoryRoot, 'apps/web/package.json'),
|
|
316
|
+
vueRequireBase: resolve(repositoryRoot, 'packages/i18n/vue/package.json'),
|
|
317
|
+
happyDom: appRequire.resolve('happy-dom'),
|
|
318
|
+
react: appRequire.resolve('react'),
|
|
319
|
+
reactDomServer: appRequire.resolve('react-dom/server'),
|
|
320
|
+
reactDomClient: appRequire.resolve('react-dom/client'),
|
|
321
|
+
vue: vueRequire.resolve('vue'),
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
artifacts,
|
|
325
|
+
checks,
|
|
326
|
+
}
|
|
327
|
+
await mkdir(dirname(evidencePath), { recursive: true })
|
|
328
|
+
await writeFile(evidencePath, `${JSON.stringify(report, null, 2)}\n`, 'utf8')
|
|
329
|
+
console.log(JSON.stringify({ evidencePath, checks }, null, 2))
|
|
330
|
+
if (Object.values(checks).some((check) => check.toolError)) process.exitCode = 1
|
|
331
|
+
} finally {
|
|
332
|
+
await window.happyDOM.close()
|
|
333
|
+
for (const [key, descriptor] of originalDescriptors) {
|
|
334
|
+
if (descriptor) Object.defineProperty(globalThis, key, descriptor)
|
|
335
|
+
else delete globalThis[key]
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
await main()
|
|
@@ -9,11 +9,33 @@
|
|
|
9
9
|
|
|
10
10
|
## 验证结果(2026-09-19)
|
|
11
11
|
|
|
12
|
-
| 命令
|
|
13
|
-
|
|
|
14
|
-
| `pnpm --filter web build`
|
|
15
|
-
| `pnpm --filter web test --run` | 通过,17 个测试文件、112 个测试
|
|
16
|
-
| `pnpm i18n:check`
|
|
17
|
-
| `git diff --check`
|
|
12
|
+
| 命令 | 结果 |
|
|
13
|
+
| ------------------------------ | ------------------------------------- |
|
|
14
|
+
| `pnpm --filter web build` | 通过,TypeScript 与 Vite 构建成功 |
|
|
15
|
+
| `pnpm --filter web test --run` | 通过,17 个测试文件、112 个测试 |
|
|
16
|
+
| `pnpm i18n:check` | 通过,1254 个词条无新增、废弃或未使用 |
|
|
17
|
+
| `git diff --check` | 通过 |
|
|
18
18
|
|
|
19
19
|
本次未改动后端接口、权限契约或 UI 截图;浏览器视觉回归仍沿用主题验证说明中的既有流程。
|
|
20
|
+
|
|
21
|
+
## 独立包发布复验(2026-10-09)
|
|
22
|
+
|
|
23
|
+
状态:验证记录。当前四包构建成功,CLI 6 文件、71 个测试与应用 UI locale 映射 2 个测试通过。真实 React DOM 中具名和内联对象 selector 各渲染 2 次,DOM 从 en_US 更新为 zh_CN,均无警告或异常;此前无限更新缺陷已修复。
|
|
24
|
+
|
|
25
|
+
当前版本仍不宜直接发布:三个运行时包实际 tarball 均无 dist,四包 private 和源码入口尚未调整,CLI 打包仍报 ERR_PNPM_BIN_CRLF;core 两处 console 的 TS2584 仍存在,整个 i18n 目录有 36 个文件格式未通过,React 修改文件本身 lint/type 通过但格式未通过。语言加载竞态、词条/插值原型链查找、SSR、Vue store 替换及浅比较错误仍可复现。完整证据、问题定位及支持范围说明见 [发布评估](./发布评估.md)。
|
|
26
|
+
|
|
27
|
+
2026-10-08 初次测试数量为 54 个;上述 71 个是本轮重新执行结果。CLI 注释 dry-run 再次识别 2 个 key、deleted 为空、unchangedCount 为 2;零源文件 check 仍误报成功,语言包 SHA256 均未变化。当前 CLI 证据见 [当前复验](./测试证据/cli-扫描边界/当前复验.json)。
|
|
28
|
+
|
|
29
|
+
## CLI 调用边界注释回归(2026-10-08)
|
|
30
|
+
|
|
31
|
+
状态:验证记录。已修复调用名、左括号和静态字符串之后出现注释时的漏提取,保持原始文本扫描约定。
|
|
32
|
+
|
|
33
|
+
| 执行项 | 结果 |
|
|
34
|
+
| -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
|
|
35
|
+
| `vp run --filter @bubblesjs/i18n-cli test` | 6 个文件、71 个测试通过;新增 16 个扫描用例和 1 个命令用例 |
|
|
36
|
+
| `vp run --filter @bubblesjs/i18n-cli build` | 通过,生成 CLI JS 与声明产物 |
|
|
37
|
+
| 在 CLI 目录执行 `vp check src/scanner.ts src/scanner.test.ts test/command.test.ts README.md` | 4 个修改文件格式通过;3 个 TS 文件 lint、类型检查通过 |
|
|
38
|
+
| 使用新 dist 对既有 comment-clean 场景执行 `sync --clean --dry-run` | 两个词条均识别;deleted 为空,unchangedCount 为 2,语言包 SHA256 不变 |
|
|
39
|
+
| 临时目录实际执行 `sync --clean` | 正常与带注释调用的既有英文译文原样保留,只删除废弃词条 |
|
|
40
|
+
|
|
41
|
+
扫描回归覆盖三个调用边界、连续混合注释、LF/CR/CRLF/U+2028/U+2029、源码行列位置、字符串内注释符号、注释内完整调用、动态表达式和未闭合块注释。执行输出见 [检查结果](./测试证据/cli-扫描边界/检查结果.json);构建产物场景见 [注释回归](./测试证据/cli-扫描边界/注释回归.json)。[原始执行结果](./测试证据/cli-扫描边界/output.json) 是修复前历史基线,零源文件 check 和其他发布问题本次未修复;此次修改文件检查通过不代表整个 i18n 目录的原有检查问题已解决。
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"command": "check",
|
|
3
|
+
"clean": false,
|
|
4
|
+
"dryRun": true,
|
|
5
|
+
"files": [
|
|
6
|
+
{
|
|
7
|
+
"project": "sample",
|
|
8
|
+
"path": "catalog/en_US.json",
|
|
9
|
+
"locale": "en_US",
|
|
10
|
+
"added": [],
|
|
11
|
+
"unused": [
|
|
12
|
+
"保留",
|
|
13
|
+
"正常"
|
|
14
|
+
],
|
|
15
|
+
"deleted": [],
|
|
16
|
+
"unchangedCount": 0
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"status": "历史基线",
|
|
3
|
+
"verifiedAt": "2026-10-08T03:55:06.777Z",
|
|
4
|
+
"environment": {
|
|
5
|
+
"node": "v24.21.0",
|
|
6
|
+
"platform": "win32",
|
|
7
|
+
"cwd": "C:\\malei\\stu\\bubbles\\packages\\create-bubbles\\template-vp-monorepo-react-nestjs",
|
|
8
|
+
"cli": "C:\\malei\\stu\\bubbles\\packages\\create-bubbles\\template-vp-monorepo-react-nestjs\\packages\\i18n\\cli\\dist\\cli.mjs"
|
|
9
|
+
},
|
|
10
|
+
"scenarios": [
|
|
11
|
+
{
|
|
12
|
+
"name": "comment-clean",
|
|
13
|
+
"command": [
|
|
14
|
+
"C:\\Users\\Administrator\\.vite-plus\\js_runtime\\node\\24.21.0\\node.exe",
|
|
15
|
+
"C:\\malei\\stu\\bubbles\\packages\\create-bubbles\\template-vp-monorepo-react-nestjs\\packages\\i18n\\cli\\dist\\cli.mjs",
|
|
16
|
+
"sync",
|
|
17
|
+
"--clean",
|
|
18
|
+
"--dry-run",
|
|
19
|
+
"--config",
|
|
20
|
+
"C:\\malei\\stu\\bubbles\\packages\\create-bubbles\\template-vp-monorepo-react-nestjs\\.spaces\\04.界面与主题\\国际化\\测试证据\\cli-扫描边界\\comment-clean\\i18n.config.mjs"
|
|
21
|
+
],
|
|
22
|
+
"exitCode": 0,
|
|
23
|
+
"stdout": "bubbles-i18n sync dry-run\nConfig: .spaces/04.界面与主题/国际化/测试证据/cli-扫描边界/comment-clean/i18n.config.mjs\nProject sample: 1 source files, 1 keys.\n en_US catalog/en_US.json: added 0, unused 0, deleted 1, unchanged 1.\nTotal: added 0, unused 0, deleted 1, unchanged 1.\nReport: report.json\n",
|
|
24
|
+
"stderr": "",
|
|
25
|
+
"report": {
|
|
26
|
+
"command": "sync",
|
|
27
|
+
"clean": true,
|
|
28
|
+
"dryRun": true,
|
|
29
|
+
"files": [
|
|
30
|
+
{
|
|
31
|
+
"project": "sample",
|
|
32
|
+
"path": "catalog/en_US.json",
|
|
33
|
+
"locale": "en_US",
|
|
34
|
+
"added": [],
|
|
35
|
+
"unused": [],
|
|
36
|
+
"deleted": ["保留"],
|
|
37
|
+
"unchangedCount": 1
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"catalogUnchanged": true,
|
|
42
|
+
"catalogSha256Before": "a47f0302d7f0345dfbc94a15baf9c80c7fa23ea7f6e6d2c67d6f6c26d86daf58",
|
|
43
|
+
"catalogSha256After": "a47f0302d7f0345dfbc94a15baf9c80c7fa23ea7f6e6d2c67d6f6c26d86daf58"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "empty-scan",
|
|
47
|
+
"command": [
|
|
48
|
+
"C:\\Users\\Administrator\\.vite-plus\\js_runtime\\node\\24.21.0\\node.exe",
|
|
49
|
+
"C:\\malei\\stu\\bubbles\\packages\\create-bubbles\\template-vp-monorepo-react-nestjs\\packages\\i18n\\cli\\dist\\cli.mjs",
|
|
50
|
+
"check",
|
|
51
|
+
"--config",
|
|
52
|
+
"C:\\malei\\stu\\bubbles\\packages\\create-bubbles\\template-vp-monorepo-react-nestjs\\.spaces\\04.界面与主题\\国际化\\测试证据\\cli-扫描边界\\empty-scan\\i18n.config.mjs"
|
|
53
|
+
],
|
|
54
|
+
"exitCode": 0,
|
|
55
|
+
"stdout": "bubbles-i18n check\nConfig: .spaces/04.界面与主题/国际化/测试证据/cli-扫描边界/empty-scan/i18n.config.mjs\nProject sample: 0 source files, 0 keys.\n en_US catalog/en_US.json: added 0, unused 2, deleted 0, unchanged 0.\nTotal: added 0, unused 2, deleted 0, unchanged 0.\nReport: report.json\nCheck passed.\n",
|
|
56
|
+
"stderr": "",
|
|
57
|
+
"report": {
|
|
58
|
+
"command": "check",
|
|
59
|
+
"clean": false,
|
|
60
|
+
"dryRun": true,
|
|
61
|
+
"files": [
|
|
62
|
+
{
|
|
63
|
+
"project": "sample",
|
|
64
|
+
"path": "catalog/en_US.json",
|
|
65
|
+
"locale": "en_US",
|
|
66
|
+
"added": [],
|
|
67
|
+
"unused": ["保留", "正常"],
|
|
68
|
+
"deleted": [],
|
|
69
|
+
"unchangedCount": 0
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"catalogUnchanged": true,
|
|
74
|
+
"catalogSha256Before": "a47f0302d7f0345dfbc94a15baf9c80c7fa23ea7f6e6d2c67d6f6c26d86daf58",
|
|
75
|
+
"catalogSha256After": "a47f0302d7f0345dfbc94a15baf9c80c7fa23ea7f6e6d2c67d6f6c26d86daf58"
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"observations": {
|
|
79
|
+
"commentCallMarkedDeleted": true,
|
|
80
|
+
"emptyScanCheckPassed": true,
|
|
81
|
+
"allCatalogsUnchanged": true
|
|
82
|
+
},
|
|
83
|
+
"note": "调用边界注释修复前的执行结果;修复后的 comment-clean 结果见注释回归.json。empty-scan 问题本次未修复。"
|
|
84
|
+
}
|