From d1c8703b65197e74a69de71793b60556be0e81c6 Mon Sep 17 00:00:00 2001 From: chen Date: Sun, 27 Sep 2026 17:23:00 +0800 Subject: [PATCH 01/92] chore: update workflow --- .github/workflows/alpha.yml | 52 ----------------------------------- .github/workflows/default.yml | 29 +++++++++++++------ .github/workflows/deploy.yml | 9 +++--- src/library/package.json | 2 +- 4 files changed, 26 insertions(+), 66 deletions(-) delete mode 100644 .github/workflows/alpha.yml diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml deleted file mode 100644 index d592da50..00000000 --- a/.github/workflows/alpha.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: CI - -on: - push: - branches: - - alpha -env: - REPOSITORY_PATH: https://${{secrets.ACCESS_TOKEN}}@github.com/ -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: init - run: | - git config --global user.name "${GITHUB_ACTOR}" - git config --global user.email "${GITHUB_ACTOR}@gmail.com" - - name: pull-code - uses: actions/checkout@v7 - - name: install-node - uses: actions/setup-node@v7 - with: - node-version: 24.x - cache: "npm" - - name: install-dependencies - run: | - npm ci --legacy-peer-deps - - name: lint - run: | - npm run lint - - name: hook-code - run: | - npm run sync - - name: test - run: | - npm run test:ci - - name: build - run: | - npm run build - - id: publish - name: publish - uses: JS-DevTools/npm-publish@v3 - if: ${{ github.repository_owner == 'wszgrcy' }} - with: - token: ${{ secrets.NPM_PUBLISH_TOKEN }} - package: ./dist/package.json - tag: alpha - - if: ${{ github.repository_owner == 'wszgrcy' && steps.publish.outputs.type }} - run: | - echo "[${{ steps.publish.outputs.type }}]版本已变更: ${{ steps.publish.outputs.old-version }} => ${{ steps.publish.outputs.version }}" - git tag v${{steps.publish.outputs.version}} - git push origin v${{steps.publish.outputs.version}} diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 5443d011..a5a9fcc6 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -4,8 +4,10 @@ on: push: branches: - master -env: - REPOSITORY_PATH: https://${{secrets.ACCESS_TOKEN}}@github.com/ + - alpha +permissions: + id-token: write + contents: write jobs: build: runs-on: ubuntu-latest @@ -21,7 +23,7 @@ jobs: uses: actions/setup-node@v7 with: node-version: 24.x - cache: "npm" + cache: 'npm' - name: install-dependencies run: | npm ci --legacy-peer-deps @@ -37,15 +39,26 @@ jobs: - name: build run: | npm run build + - name: dist-tag + id: dist-tag + run: | + # master -> latest,其它分支(alpha 等)-> 分支同名 tag + if [ "${GITHUB_REF_NAME}" = "master" ]; then + echo "tag=latest" >> "$GITHUB_OUTPUT" + else + echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" + fi - id: publish name: publish - uses: JS-DevTools/npm-publish@v3 - if: ${{ github.repository_owner == 'wszgrcy' }} + uses: JS-DevTools/npm-publish@v4 + if: ${{ github.repository_owner == 'wszgrcy' }} with: - token: ${{ secrets.NPM_PUBLISH_TOKEN }} package: ./dist/package.json - - if: ${{ github.repository_owner == 'wszgrcy' && steps.publish.outputs.type }} + tag: ${{ steps.dist-tag.outputs.tag }} + provenance: true + access: public + - if: ${{ github.repository_owner == 'wszgrcy' && steps.publish.outputs.type }} run: | - echo "[${{ steps.publish.outputs.type }}]Version changed: ${{ steps.publish.outputs.old-version }} => ${{ steps.publish.outputs.version }}" + echo "[${{ steps.publish.outputs.type }}]Version changed: ${{ steps.publish.outputs.old-version }} => ${{ steps.publish.outputs.version }} (dist-tag: ${{ steps.dist-tag.outputs.tag }})" git tag v${{steps.publish.outputs.version}} git push origin v${{steps.publish.outputs.version}} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7889edae..37c669a6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,9 +4,8 @@ on: push: branches: - master -env: - REPOSITORY_PATH: https://${{secrets.ACCESS_TOKEN}}@github.com/ - # GITHUB_TOKEN: ${{secrets.ACCESS_TOKEN}} +permissions: + contents: write jobs: build: runs-on: ubuntu-latest @@ -21,7 +20,7 @@ jobs: uses: actions/setup-node@v7 with: node-version: 24.x - cache: "npm" + cache: 'npm' - name: install-dependencies run: | npm ci --legacy-peer-deps @@ -45,5 +44,5 @@ jobs: git status git add -A HUSKY=0 git commit -m 'build: 页面构建' - HUSKY=0 git push --force "${REPOSITORY_PATH}${GITHUB_REPOSITORY}.git" gh-pages + HUSKY=0 git push --force origin gh-pages # git pull diff --git a/src/library/package.json b/src/library/package.json index d5f225f3..aac4e5b6 100644 --- a/src/library/package.json +++ b/src/library/package.json @@ -1,6 +1,6 @@ { "name": "angular-miniprogram", - "version": "1.5.2", + "version": "2.0.0", "type": "commonjs", "description": "使用Angular开发小程序", "keywords": [ From 16b61caa2d4eb6f7a5b85c86f9cb0891bb5b5c35 Mon Sep 17 00:00:00 2001 From: chen Date: Mon, 28 Sep 2026 18:19:14 +0800 Subject: [PATCH 02/92] =?UTF-8?q?test:=20karma=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + DEVELOPMENT.md | 460 +++- package-lock.json | 2446 +++++++++++++---- package.json | 4 +- script/wechat-karma.cjs | 953 +++++++ src/builder/control-flow.spec.ts | 41 +- src/builder/karma/client/karma.ts | 67 +- src/builder/karma/client/main.ts | 171 ++ src/builder/karma/client/platform/wx/index.ts | 4 +- src/builder/karma/jasmine-define.ts | 5 +- src/builder/karma/schema.json | 16 + src/builder/karma/vite/derived-tsconfig.ts | 51 +- src/builder/karma/vite/index.ts | 126 +- src/builder/karma/vite/karma-framework.ts | 45 +- .../parse-node/component-context.ts | 29 +- src/builder/vite/index.ts | 14 +- .../plugins/mini-program-assets.plugin.ts | 75 +- .../plugins/require-context-shim.plugin.ts | 152 + src/builder/vite/schema.json | 69 +- src/library/ng-package.json | 9 +- src/library/package.json | 9 +- .../component-template-hook.factory.ts | 48 +- .../platform/default/lview-layout.spec.ts | 8 +- src/library/platform/default/lview-layout.ts | 33 + .../default/lview-to-node-list.spec.ts | 104 +- .../default/path-based-setdata.spec.ts | 43 +- test/hello-world-app/angular.json | 163 +- test/hello-world-app/karma.conf.js | 73 +- test/hello-world-app/src/app.json | 17 +- test/hello-world-app/src/project.config.json | 2 +- .../life-time/life-time.component.ts | 2 +- .../life-time/life-time.module.ts | 9 - .../ng-content/ng-content.component.ts | 2 +- .../ng-content/ng-content.module.ts | 9 - .../spec-component/ng-for/ng-for.component.ts | 4 +- .../spec-component/ng-for/ng-for.module.ts | 10 - .../spec-component/ng-if/ng-if.component.ts | 4 +- .../src/spec-component/ng-if/ng-if.module.ts | 10 - .../ng-switch/ng-switch.component.ts | 4 +- .../ng-switch/ng-switch.module.ts | 10 - .../ng-template-outlet.component.ts | 4 +- .../ng-template-outlet.module.ts | 10 - .../self-template/self-template.component.ts | 4 +- .../self-template/self-template.module.ts | 10 - .../signal-io/signal-io.component.ts | 2 +- .../signal-io/signal-io.module.ts | 8 - .../style-class/style-class.component.ts | 2 +- .../style-class/style-class.module.ts | 9 - .../tag-view-convert.component.ts | 2 +- .../tag-view-convert.module.ts | 9 - .../control-flow-spec.entry.ts | 5 +- .../control-flow.component.ts | 43 +- .../control-flow-spec/control-flow.module.ts | 7 - .../control-flow-spec/control-flow.spec.ts | 2 +- .../src/spec/empty/empty.component.ts | 2 +- .../src/spec/empty/empty.entry.ts | 6 +- .../src/spec/empty/empty.module.ts | 9 - .../src/spec/http-spec/http-spec.entry.ts | 5 +- .../src/spec/http-spec/http.component.ts | 41 +- .../src/spec/http-spec/http.module.ts | 9 - .../src/spec/http-spec/http.spec.ts | 31 +- .../life-time-spec/life-time-spec.entry.ts | 5 +- .../life-time-spec/life-time.component.ts | 7 +- .../spec/life-time-spec/life-time.module.ts | 10 - .../src/spec/life-time-spec/life-time.spec.ts | 2 +- .../ng-content-spec/ng-content-spec.entry.ts | 6 +- .../ng-content-spec/ng-content.component.ts | 9 +- .../spec/ng-content-spec/ng-content.module.ts | 9 - .../spec/ng-content-spec/ng-content.spec.ts | 2 +- .../src/spec/ng-for-spec/ng-for-spec.entry.ts | 5 +- .../src/spec/ng-for-spec/ng-for.component.ts | 9 +- .../src/spec/ng-for-spec/ng-for.module.ts | 10 - .../src/spec/ng-for-spec/ng-for.spec.ts | 2 +- .../src/spec/ng-if-spec/ng-if-spec.entry.ts | 5 +- .../src/spec/ng-if-spec/ng-if.component.ts | 9 +- .../src/spec/ng-if-spec/ng-if.module.ts | 10 - .../src/spec/ng-if-spec/ng-if.spec.ts | 2 +- .../ng-library-import-spec.entry.ts | 5 +- .../ng-library-import.component.ts | 10 +- .../ng-library-import.module.ts | 9 - .../ng-library-import.spec.ts | 2 +- .../ng-switch-spec/ng-switch-spec.entry.ts | 5 +- .../ng-switch-spec/ng-switch.component.ts | 9 +- .../spec/ng-switch-spec/ng-switch.module.ts | 10 - .../src/spec/ng-switch-spec/ng-switch.spec.ts | 2 +- .../ng-template-outlet-spec.entry.ts | 5 +- .../ng-template-outlet.component.ts | 9 +- .../ng-template-outlet.module.ts | 10 - .../ng-template-outlet.spec.ts | 2 +- .../self-template-spec.entry.ts | 5 +- .../self-template.component.ts | 11 +- .../self-template.module.ts | 10 - .../self-template-spec/self-template.spec.ts | 2 +- .../signal-io-spec/signal-io-spec.entry.ts | 5 +- .../signal-io-spec/signal-io.component.ts | 9 +- .../spec/signal-io-spec/signal-io.module.ts | 9 - .../src/spec/signal-io-spec/signal-io.spec.ts | 2 +- .../style-class-spec.component.ts | 9 +- .../style-class-spec.entry.ts | 6 +- .../style-class-spec.module.ts | 10 - .../style-class-spec/style-class-spec.spec.ts | 2 +- .../tag-view-convert-spec.entry.ts | 6 +- .../tag-view-convert.component.ts | 9 +- .../tag-view-convert.module.ts | 10 - .../tag-view-convert.spec.ts | 2 +- .../src/spec/util/fixture-server.ts | 32 + test/hello-world-app/src/spec/util/index.ts | 1 + .../src/spec/util/open-component.ts | 30 +- .../src/spec/util/page-info.ts | 29 +- test/hello-world-app/src/test.ts | 7 + 110 files changed, 4570 insertions(+), 1342 deletions(-) create mode 100644 script/wechat-karma.cjs create mode 100644 src/builder/vite/plugins/require-context-shim.plugin.ts delete mode 100644 test/hello-world-app/src/spec-component/life-time/life-time.module.ts delete mode 100644 test/hello-world-app/src/spec-component/ng-content/ng-content.module.ts delete mode 100644 test/hello-world-app/src/spec-component/ng-for/ng-for.module.ts delete mode 100644 test/hello-world-app/src/spec-component/ng-if/ng-if.module.ts delete mode 100644 test/hello-world-app/src/spec-component/ng-switch/ng-switch.module.ts delete mode 100644 test/hello-world-app/src/spec-component/ng-template-outlet/ng-template-outlet.module.ts delete mode 100644 test/hello-world-app/src/spec-component/self-template/self-template.module.ts delete mode 100644 test/hello-world-app/src/spec-component/signal-io/signal-io.module.ts delete mode 100644 test/hello-world-app/src/spec-component/style-class/style-class.module.ts delete mode 100644 test/hello-world-app/src/spec-component/tag-view-convert/tag-view-convert.module.ts delete mode 100644 test/hello-world-app/src/spec/control-flow-spec/control-flow.module.ts delete mode 100644 test/hello-world-app/src/spec/empty/empty.module.ts delete mode 100644 test/hello-world-app/src/spec/http-spec/http.module.ts delete mode 100644 test/hello-world-app/src/spec/life-time-spec/life-time.module.ts delete mode 100644 test/hello-world-app/src/spec/ng-content-spec/ng-content.module.ts delete mode 100644 test/hello-world-app/src/spec/ng-for-spec/ng-for.module.ts delete mode 100644 test/hello-world-app/src/spec/ng-if-spec/ng-if.module.ts delete mode 100644 test/hello-world-app/src/spec/ng-library-import-spec/ng-library-import.module.ts delete mode 100644 test/hello-world-app/src/spec/ng-switch-spec/ng-switch.module.ts delete mode 100644 test/hello-world-app/src/spec/ng-template-outlet-spec/ng-template-outlet.module.ts delete mode 100644 test/hello-world-app/src/spec/self-template-spec/self-template.module.ts delete mode 100644 test/hello-world-app/src/spec/signal-io-spec/signal-io.module.ts delete mode 100644 test/hello-world-app/src/spec/style-class-spec/style-class-spec.module.ts delete mode 100644 test/hello-world-app/src/spec/tag-view-convert-spec/tag-view-convert.module.ts create mode 100644 test/hello-world-app/src/spec/util/fixture-server.ts diff --git a/.gitignore b/.gitignore index b7e8f52d..4be874a0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,8 @@ deploy/api-doc __q __w __decl + +# karma builder 派生出的临时 tsconfig(derived-tsconfig.ts 生成), +# 里面带 typeRoots 的**机器绝对路径**,提交上去既泄本地路径、 +# 又在别的机器上直接不对。模板仓已有同名规则,库仓补齐。 +**/.tsconfig.generated-*.json diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b409c6c0..4c371b42 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -19,6 +19,259 @@ npm run lint # eslint --max-warnings 0 npm run sync # 手动从 angular/angular@17.3.1 同步源码(需要网络) ``` +> **上面都是 Node 侧用例**。还有一层「在真·微信开发者工具里跑」的 +> 小程序 karma 用例,见下面 [微信真机 karma 测试](#微信真机-karma-测试)。 +> 那层需要开发者工具 + 真实 AppID + 手动登录,不能纯命令行无人值守。 + +--- + +## 微信真机 karma 测试 + +### 两层测试的分工 + +| | Node jasmine(`npm test`) | 小程序 karma(`npm run test:wechat`) | +| -------------- | ------------------------------ | ------------------------------------- | +| 跑在哪 | Node 进程,`wx` 用 Proxy 桩 | 真·微信开发者工具里的小程序运行时 | +| 覆盖 | 编译器、纯函数、可 mock 的逻辑 | 渲染、生命周期、`wx.*` 真实行为 | +| 需要开发者工具 | 否 | **是** | +| 需要真实 AppID | 否 | **是**(游客模式不行) | +| 需要手动登录 | 否 | **是**(见下) | +| 速度 | 全量约 4 分钟 | 13 个 spec 约 17 秒 | + +两层不能互相替代。典型例子:内建控制流 `@if` 的 `nodeList` 填充 bug, +Node 侧合成 lView 测不出来,只有真机跑才暴露。 + +### 前置条件(四条,缺一不可) + +**1. 微信开发者工具已安装,且服务端口已开启** + +IDE → 设置 → 安全设置 → **服务端口:开**。 +没开会直接报: + +``` +工具的服务端口已关闭。要使用命令行调用,请手动打开工具 -> 设置 -> 安全设置,将服务端口开启。 +``` + +**2. 必须手动启动 IDE 并登录 —— CLI 拉不起来登录态** + +这是最容易踩的一条。CLI **能**拉起 IDE 进程(`--port` 会让它启动并监听), +但**那个实例是登出状态**,且等多久都不会恢复: + +``` +# 已登录状态下 cli quit,再让 CLI 从零拉起: +cli --port 40710 islogin → {"login":false} +等待 40s 再查 → {"login":false} + +# 且 profile 是同一个(--debug 实测): +userDirPath C:\Users\\AppData\Local\微信开发者工具\User Data\\Default +``` + +同 profile、同机器,CLI 拉起的实例就是 `login:false`。 + +**所以正确顺序是:你手动打开 IDE → 扫码登录 → 再跑测试。** + +登录没上的表现很坑,**不会报错**: + +``` +cli auto → ✔ auto ← 假成功 +# 然后小程序永远不连 karma,脚本干等到超时 +``` + +**3. AppID 用游客的就行** + +> ⚠️ **两次纠正**。早先记的两条都是错的: +> +> 1. 「游客模式网络被掐」—— 错,当时把「未登录」归因到了 appid 上 +> 2. 「游客模式 `cli auto` 不可靠」—— 也错,当时把「会话互斥」归因到了 appid 上 +> +> **游客 appid 可以跑测试,实测 13/13 SUCCESS 且可复现。** + +网络硬证据(带唯一标记的服务器,验证**内容真的回来了**,不是只看状态码): + +``` +loopback >> status=200 marker回传=true + body={"marker":"FX-TOURIST-OK-9911","echo":"/hello","host":"127.0.0.1:9901"} +LAN >> status=200 marker回传=true + body={"marker":"FX-TOURIST-OK-9911","echo":"/lan","host":"192.168.31.198:9901"} +``` + +标记串、echo 路径、host 头全部原样返回 —— 真实往返,不是缓存也不是假应答。 + +**之前反复失败的真正原因:会话互斥。** + +DevTools 的自动化会话**同一时刻只能有一个**。上一轮跑完脚本只杀了 +node/karma,**项目窗口还开在 IDE 里**;新一轮 `cli auto` 去抢会话, +旧连接被强制关掉,karma 那边刚连上就断: + +``` +Connected on socket +WARN [小程序]: Disconnected (0 times) reconnect failed before timeout of 2000ms (transport close) +Executed 0 of null +``` + +干净 A/B(同代码、同机器、同游客 appid): + +| 前置动作 | 结果 | +| ------------------- | -------------------------------------------- | +| 先 `cli close` 再跑 | ✅ `Executed 13 of null SUCCESS`(连复两次) | +| 不 close 直接跑 | ❌ `transport close` → `Executed 0` | + +**脚本已修**:跑之前自动 `cli close --project <产物>` 并等 8 秒, +不用手动干预。修后连跑两次均全自动 PASS。 + +**唯一真正需要真实 AppID 的场景**:`cli open`。游客 appid 走 `open` +会报 `code: 10 不存在此 AppID`。但测试链路走的是 `auto`,不是 `open`, +所以碰不到这个限制。 + +**4. `urlCheck: false`(就是 IDE 里那个「不校验合法域名」勾选)** + +IDE → 详情 → 本地设置 → +**「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」** + +这个勾选对应 `project.config.json` 里的 `setting.urlCheck`(**反逻辑**: +勾选 = `false`): + +```json +// test/hello-world-app/src/project.config.json +{ "setting": { "urlCheck": false } } +``` + +**游客模式下这个是必需的,不是可选的。** 原因: + +> 域名白名单是挂在 AppID 上的。**游客模式没有 AppID → 没有任何白名单 +> 上下文 → 所有域名都不合法**。不关掉校验,连 `127.0.0.1` 都过不了。 + +所以「游客 + 本地开发」的正确组合是: + +| 项 | 值 | +| -------------- | ----------------------------------------------------- | +| AppID | `touristappid` | +| 不校验合法域名 | **勾选**(`urlCheck: false`) | +| 登录 | **需要**(未登录时 `wx.request` 直接 `request:fail`) | + +注意最后一行:**游客模式免的是 AppID,不免登录。** 开发者工具本身 +仍然要扫码登录,否则请求根本发不出去。 + +这个设置没开的典型报错: + +``` +request:fail url not in domain list +``` + +而登录没上的报错长得不一样(没有 `url not in domain list`): + +``` +request:fail status: undefined ← 请求根本没发出 +``` + +两个报错能用来快速区分是「域名校验没关」还是「没登录」。 + +### 怎么跑 + +```bash +# 1. 手动打开微信开发者工具并扫码登录 +# 2. 跑(游客 appid 直接可用,不用 --appid) +npm run test:wechat +# 或显式: +node script/wechat-karma.cjs \ + --project ./test/hello-world-app \ + --dist ./test/hello-world-app/dist/karma/app +``` + +仓库里 `src/project.config.json` 提交的就是 `touristappid`,**直接就能跑 +测试**,不用换真实 AppID。想换成自己的也可以,用 `--appid` 注入,不进版本库。 + +脚本做的事:**登录态预检** → **自动 close 残留项目窗口** → 起 karma +→ 等 server ready → `cli auto` → 轮询日志里的 `Executed X of Y` +→ 杀进程 → 按结果 exit 0/1(可直接进 CI)。 + +**两个预检为什么重要**(都是踩过坑换来的): + +- **登录态**:未登录时 `cli auto` 会假成功,不预检就得干等 180s + 超时且看不出原因。现在几秒内直接告诉你: + +``` +[wechat-karma] 失败: 开发者工具未登录。 +CLI 拉起的 IDE 实例是登出状态(实测同 profile 也不带登录态,等待也不会恢复), +必须手动打开微信开发者工具并扫码登录后再跑。 +EXIT=1 +``` + +- **残留窗口**:不先 close 上一轮,会话互斥会导致 `transport close`, + 表现是 `Executed 0`。脚本现在自动 close + 等 8 秒。 + +IDE 服务端口与 `.ide` 记录不一致时,用 `--ide-port <端口>` 直接指定, +不用去改文件。 + +### 端口机制(`.ide` 文件) + +CLI **不直接问 IDE 端口**,而是读一个状态文件: + +``` +%LOCALAPPDATA%\微信开发者工具\User Data\\Default\ + ├── .ide ← IDE 服务端口 + ├── .ide-status ← 服务端口开关("On" / "Off") + └── .cli ← CLI 自己的端口 +``` + +IDE 每次启动**随机挑端口**,而 `.ide` 只在 IDE 自己的启动流程里写。 +用 `taskkill /F` 强杀、或直接双击 exe 启动,都会让 `.ide` 与实际端口脱节: + +``` +.ide = 40710(陈旧) +实际监听 = 41994 +→ CLI 读 40710 → ECONNREFUSED → 判定「IDE 没启动」→ 去拉新实例 +→ 但已有实例占着 → 40710 永远开不出来 → wait IDE port timeout +``` + +**`--port` 的关键限制**(实测确认): + +> `--port` 只在「CLI 亲自拉起 IDE」那一次生效。 +> 要连**已经在跑**的 IDE,CLI 仍然只认 `.ide` 文件。 + +所以 IDE 已在跑但端口对不上时,直接把真实端口写回去最快: + +```bash +echo 41994 > "$LOCALAPPDATA/微信开发者工具/User Data//Default/.ide" +``` + +### 故障速查表 + +| 现象 | 原因 | 解法 | +| ------------------------------------------------- | -------------------------------------- | ----------------------------------------------------- | +| `不存在此 AppID (code 10)` | 用了 `touristappid` 走 `cli open` | 换真实 AppID | +| `需要重新登录 (code 10)` | IDE 登录态丢了 | 手动登录 IDE | +| `✔ auto` 但无测试结果,最后超时 | 登录态为 `false`(假成功) | `islogin` 预检,登录后重跑 | +| `wait IDE port timeout` | `.ide` 与实际端口不一致 | 写回真实端口,或干净退出后 `--port` 重拉 | +| `工具的服务端口已关闭` | IDE 安全设置里服务端口没开 | 设置 → 安全设置 → 服务端口 开 | +| `Connected on socket` 后 `no message in 30000 ms` | 上一轮 DevTools 实例还在,把新会话挤掉 | 脚本已自动 `cli close`;手动跑就先 close 旧项目等几秒 | +| `Disconnected ... transport close` → `Executed 0` | 同上,**会话互斥**(不是 appid 问题) | 同上 | +| `Executed N of null` | karma adapter 的 `total` 竞态(已知) | 脚本已按日志静默判定,不影响结果 | + +### 已验证的网络矩阵 + +``` +目标 真实 AppID 游客 appid +loopback http://127.0.0.1:9901 ✅ ✅(标记串回传验证) +LAN http://192.168.31.198:9901 ✅ ✅(标记串回传验证) +external https://registry.npmjs.org ✅ ✅ +``` + +**局域网可通**:手机连同 WiFi 就能打本机 dev server,真机联调不用改代码 +(把 karma 的 `clientHost` 指到本机局域网 IP 即可)。 + +游客与真实 AppID 在网络上**没有区别**,两者都需要:已登录 + `urlCheck:false`。 + +### 为什么 http spec 打本地服务而不是外部 API + +原来打的是 `https://api.realworld.io/api/articles`,该域名已返 +**HTTP 530**(Cloudflare 源站不在,宿主机 `curl` 同样 530),测试会 +长期红且与代码无关。现在由 `karma.conf.js` 起一个本地 fixture 服务, +请求仍是真的 `wx.request → 127.0.0.1`,**适配层链路一字不变**, +只是响应可控、可重复。见 `src/spec/util/fixture-server.ts`。 + +--- + ## 🔴 发布产物形态:包必须是 `type: commonjs` `src/library/package.json` 里显式写了 `"type": "commonjs"`,**不要删**。 @@ -399,19 +652,19 @@ Options<{}, {}, {}> -> Options<{}, {}, {}, []> ### 21 → 22 -| 项目 | 说明 | -| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 版本 | `@angular/*` 22.1.7 / `@angular-devkit/*` 22.1.8 / ng-packagr 22.1.1 / TS 6.0.3 / webpack 5.109.2 | -| **TS 6.0:strict 默认开启** | 空 tsconfig 也会开 `noImplicitAny`。本仓库 `tsconfig.base.json` 已显式 `strict: false`,但 fixture 的没写,直接继承新默认值,冒出成堆 TS7006 / TS7008 / TS2564。显式补 `strict: false`(单独设置的 `strictNullChecks` 不受影响) | -| TS 6.0:废弃项变硬错误 | `baseUrl` / `moduleResolution=node10` / `downlevelIteration` / `target=ES5` 全部报错,加 `"ignoreDeprecations": "6.0"` | -| TS 6.0:根 tsconfig | 根 `tsconfig.json` 是 solution-style(只有 references),但 `code-recycle` 跑 sync 时 ts-node 会拿它直接用。空 `compilerOptions` 让 TS 6 用默认 `target=ES5` 并因缺 `rootDir` 报 TS5107 / TS5011,补上 `target` / `module` / `rootDir` | -| TS 6.0:@types 不再自动全量注入 | karma client 的 tsconfig 显式声明 `typeRoots` 与 `types`(`jasmine` 命名空间、`node` 的 `Console`) | -| `createNgModuleRef` 移除 | 改用 `createNgModule`(签名一致) | -| `ComponentFactoryResolver` 整体移除 | `NgModuleRef.componentFactoryResolver` 也没了。废弃的 `pageStartup(module, component)` 路径改为用模块 injector 当 `environmentInjector` 走 `createComponent` | -| `@content` 新块 | 内容查询块,依赖运行时 content query 观察投影内容并重渲染。小程序 slot / self 模板是静态的,对不上,按 `@defer` 先例显式抛错 | +| 项目 | 说明 | +| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 版本 | `@angular/*` 22.1.7 / `@angular-devkit/*` 22.1.8 / ng-packagr 22.1.1 / TS 6.0.3 / webpack 5.109.2 | +| **TS 6.0:strict 默认开启** | 空 tsconfig 也会开 `noImplicitAny`。本仓库 `tsconfig.base.json` 已显式 `strict: false`,但 fixture 的没写,直接继承新默认值,冒出成堆 TS7006 / TS7008 / TS2564。显式补 `strict: false`(单独设置的 `strictNullChecks` 不受影响) | +| TS 6.0:废弃项变硬错误 | `baseUrl` / `moduleResolution=node10` / `downlevelIteration` / `target=ES5` 全部报错,加 `"ignoreDeprecations": "6.0"` | +| TS 6.0:根 tsconfig | 根 `tsconfig.json` 是 solution-style(只有 references),但 `code-recycle` 跑 sync 时 ts-node 会拿它直接用。空 `compilerOptions` 让 TS 6 用默认 `target=ES5` 并因缺 `rootDir` 报 TS5107 / TS5011,补上 `target` / `module` / `rootDir` | +| TS 6.0:@types 不再自动全量注入 | karma client 的 tsconfig 显式声明 `typeRoots` 与 `types`(`jasmine` 命名空间、`node` 的 `Console`) | +| `createNgModuleRef` 移除 | 改用 `createNgModule`(签名一致) | +| `ComponentFactoryResolver` 整体移除 | `NgModuleRef.componentFactoryResolver` 也没了。废弃的 `pageStartup(module, component)` 路径改为用模块 injector 当 `environmentInjector` 走 `createComponent` | +| `@content` 新块 | 内容查询块,依赖运行时 content query 观察投影内容并重渲染。小程序 slot / self 模板是静态的,对不上,按 `@defer` 先例显式抛错 | | **ICU 消息**(`{x, plural/select}`) | 编译成 `ɵɵpipe` + `I18nSelect` 动态切换子模板。**实测该节点会真的出现在 `parseTemplate` 结果里**,而 `visitIcu` 曾是空实现 → 整段内容静默消失 + 后续节点槽位错位且不报错。现显式抛错。注:这**不是「做不到」**——本 fork 已有的 `__templateName`(`