ci(scripts): 用独立 tsconfig.scripts.json 给 scripts/ 补上类型门 (#3494) - #3498
Merged
Conversation
…on (#3494) `scripts/` is not a workspace package, so `pnpm type-check` (turbo, which walks package.json `scripts`) structurally cannot reach it, and check-type-check-coverage.mjs decides coverage per PACKAGE so it could not see the gap either. Every file in `scripts/__tests__/` was therefore compiled by nothing at all - ten pin tests holding ci.yml, docs-links.yml, lint.yml, the changeset guard, the control-byte scanner and the shadcn local patches in place. A pin test the compiler never reads can assert a contract that no longer type-checks and still print green. Measured here: a provably-false type-level assertion appended to ci-cd-pipeline-doc.test.ts left `vitest run` at 13 passed, because type assertions are erased at runtime. - tsconfig.scripts.json: standalone (NOT extending tsconfig.base.json, whose `exclude` lists the test globs and would have made the project vacuous), strict, noEmit, covering `scripts/**/*.ts` by glob. - allowJs:true / checkJs:false, chosen by measurement rather than assumption: allowJs:false left 8 errors needing hand-written .d.mts files (a second source of truth, free to drift); allowJs:true left 5, each a now-false `@ts-expect-error` comment, and gives the pin tests types inferred from the helper itself. Comments updated accordingly. - ci.yml `type-check` job runs `pnpm type-check:scripts` after the install; it needs no workspace build, so it stays in the cheap, fail-fast half. - scripts/__tests__/scripts-type-check.test.ts pins the coverage (every .ts on disk under scripts/ resolves into the program), that the config parses at all, and that CI actually runs it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
yinlianghui
pushed a commit
that referenced
this pull request
Aug 6, 2026
…ype gate objectui#3494 (PR #3498) adds `tsconfig.scripts.json` with `allowJs: true`, whose `include` glob covers this branch's new test file too. Neither PR can see the other, and the two land green individually while the merge is red — no git conflict, so nothing warns. Two separate problems, both found by running #3498's compilerOptions against this branch: - the `@ts-expect-error` above the `../shadcn-sync.js` import becomes TS2578 once the import has an inferred type. Removed (the sibling test files get the same treatment inside #3498). - `fetchRegistry`'s `get` option was the one destructured option without a default, so it is absent from the function's inferred signature and passing it from a `.ts` caller is TS2353. Fixed at the producer by giving it its real default (`https.get`), which also drops the `get ? { get } : undefined` dance at the call site — not by suppressing it at the consumer. Behaviour is unchanged: production still resolves `https.get`, the tests still inject `http.get`. Verified against a local copy of #3498's config — zero errors in either file; the only remaining diagnostics are the five stale directives that #3498 itself removes. Refs objectstack-ai/objectstack#5803 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
yinlianghui
marked this pull request as ready for review
August 6, 2026 13:51
This was referenced Aug 7, 2026
Closed
This was referenced Aug 7, 2026
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
…i#3496) * fix(scripts): never cache a non-2xx or malformed shadcn registry response `fetchUrl` never inspected `res.statusCode`, and a body that failed `JSON.parse` was resolved as a raw string. A 403 from an egress allowlist therefore resolved as if it were a component, and `fetchRegistry` wrote it to disk unconditionally with a 1h TTL — so one blocked run poisoned every `pnpm shadcn:check` for the next hour, reporting "46 cached, 0 fetched" while never retrying. Two independent defences: - transport: a non-2xx (redirects included) and a 2xx that is not JSON now reject, with the status and a sanitised body excerpt in the message. - semantic: only a response passing `isRegistryEntry` (files[0].content) is written to the cache, and the same check runs on READ — an entry poisoned by an older build is dropped on first contact instead of being trusted until its TTL expires, so recovery is immediate. `--update` semantics are unchanged (still never reads the cache, still refuses to write on a failed fetch); the summary line now reports failed and evicted separately so a fully-blocked run cannot read as a success. `main()` runs only when the file is the process entry point, so the offline tests can import the fetch/cache internals. Refs objectstack-ai/objectstack#5803 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt * fix(scripts): make the shadcn-sync test import survive the scripts/ type gate objectui#3494 (PR objectstack-ai#3498) adds `tsconfig.scripts.json` with `allowJs: true`, whose `include` glob covers this branch's new test file too. Neither PR can see the other, and the two land green individually while the merge is red — no git conflict, so nothing warns. Two separate problems, both found by running objectstack-ai#3498's compilerOptions against this branch: - the `@ts-expect-error` above the `../shadcn-sync.js` import becomes TS2578 once the import has an inferred type. Removed (the sibling test files get the same treatment inside objectstack-ai#3498). - `fetchRegistry`'s `get` option was the one destructured option without a default, so it is absent from the function's inferred signature and passing it from a `.ts` caller is TS2353. Fixed at the producer by giving it its real default (`https.get`), which also drops the `get ? { get } : undefined` dance at the call site — not by suppressing it at the consumer. Behaviour is unchanged: production still resolves `https.get`, the tests still inject `http.get`. Verified against a local copy of objectstack-ai#3498's config — zero errors in either file; the only remaining diagnostics are the five stale directives that objectstack-ai#3498 itself removes. Refs objectstack-ai/objectstack#5803 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
…k-ai#3504) (objectstack-ai#3505) 净 main 上 `pnpm type-check:scripts` 红: scripts/__tests__/check-doc-links.test.ts(7,1): error TS2578: Unused '@ts-expect-error' directive. 这是两个各自绿的 PR 合并后产生的语义冲突: - objectstack-ai#3489(449227d)新增该测试时带 `@ts-expect-error`,当时正确 —— 彼时没有任何 tsc 程序 include `scripts/`,该导入确实无类型。 - objectstack-ai#3498(f995a45)引入 `tsconfig.scripts.json`(`allowJs: true`), 首次把 `scripts/**/*.ts` 纳入类型检查。`allowJs` 让 `.mjs` 助手的 类型可被推断,于是所有这类压制注释同时变成 Unused。objectstack-ai#3498 修掉了它 base 上的 5 处同类,而 objectstack-ai#3489 的第 6 处恰好落在其窗口之间。 两 PR 的 CI 各自为绿,合并结果为红 —— 当前所有 PR 的 Type Check 全部误红。 仅删除该行。运行期行为从不依赖该指令(它只影响 tsc,不影响 vitest), `check-doc-links.test.ts` 22 个用例保持全绿。 Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
…dicated tsconfig (objectstack-ai#3550) * ci(root): type-check the four repo-root vitest.setup.* files via a dedicated tsconfig 四个仓库根文件 `vitest.setup.base.ts` / `vitest.setup.dom.tsx` / `vitest.setup.dom-light.tsx` / `vitest.setup.tsx`此前不在任何 tsc program 里 (objectui#3515)。它们位于所有 workspace package 之外,`turbo run type-check` 按 package.json 的 scripts 驱动,结构上够不到;根 `tsconfig.json` 只 include packages/examples/apps;`tsconfig.scripts.json` 只 include `scripts/**`。也没有 任何文件 import 它们——每个使用方都把它们写成 Vitest `setupFiles` 的运行时路径 字符串,所以也没有传递性的类型边。唯一包含它们的根 `tsconfig.node.json` 没有 `include`(默认整个仓库,今天报 21616 个错),且没有任何脚本运行它,不是门禁。 按 objectstack-ai#3494/PR objectstack-ai#3498 处理 `scripts/` 的先例,新增专用 root project `tsconfig.vitest-setup.json` + `pnpm type-check:vitest-setup`,并在 ci.yml 的 type-check job 里接一步。 Fixes objectstack-ai#3515 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt * docs(tsconfig): 更正注释里对 program 内容的描述 原注释说 `paths` 拖进来的东西「全是 .d.ts,被 skipLibCheck 跳过」。实测有且 只有一个例外:`@reduxjs/toolkit` 自带的 `dist/uncheckedindexed.ts`,被某个 依赖声明引用进来,因此是被真正检查的(当前干净)。pin test 的 node_modules 过滤也是为它开的,注释和测试现在说同一件事。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
…t path filtering into the jobs (objectstack-ai#3523 steps 1-2) (objectstack-ai#3722) * ci(step 1): subscribe ci/lint/control-bytes/docs-links to merge_group (objectstack-ai#3523) Step 1 of objectui#3523, on its own commit as the issue's ruling requires: this is a pure addition and changes nothing about which pull requests or pushes run CI. The merge queue is enforced on this repository by a ruleset (objectstack-ai#3243 measured a direct push to `main` returning 405 `Changes must be made through the merge queue`), but not one of the 22 workflows subscribed `merge_group` — repo-wide `event=merge_group` runs stood at total_count = 0. A queue nothing subscribes to can only carry an empty required-check set, so it rebuilt each PR on the current `main` and let it through without validating anything. On 2026-08-07 that was cashed in: objectstack-ai#3503 / objectstack-ai#3510 / objectstack-ai#3516 merged between 02:11Z and 02:15Z with `Type Check` at conclusion=failure, on a `main` poisoned by objectstack-ai#3498, and objectstack-ai#3505 had to hot-fix it. The four workflows named by the issue now carry the trigger. It is spelled `merge_group: types: [checks_requested]`; `checks_requested` is the only activity type GitHub defines for this event today, so this is equivalent to objectstack's bare `merge_group:` and merely refuses to inherit a second type added later. Two `ci.yml` jobs additionally needed to be told the third event exists — both changes are no-ops for `pull_request` and `push`: - `test` moves from `if: github.event_name == 'pull_request'` to `!= 'push'`, so a queue build actually runs the suite instead of skipping every shard. - `docs`'s `should_run` treated anything that is not `push` as a pull request and diffed `github.event.pull_request.*`, which is null on a queue build — an empty revision range, i.e. the site build silently skipped on the last check before `main`. Verified rather than assumed: `concurrency` needs no merge-queue case. On `merge_group` the group expression falls back to `github.ref`, which is the queue's own generation — measured on objectstack, whose 3552 queue runs report head_branch `gh-readonly-queue/main/pr-6594-251e888a…`. That can collide with neither a PR group (a bare number) nor a push group (`refs/heads/main`). Refs objectstack-ai#3523 * ci(step 2): move ci/lint path filtering out of the PR trigger into the jobs (objectstack-ai#3523) Step 2 of objectui#3523, deliberately a separate commit from step 1: step 1 adds runs that did not exist, this one changes which pull requests start CI, and mixing them would make that impossible to review apart. `on.pull_request.paths-ignore` skips the WHOLE workflow when every changed file matches, and GitHub has no per-job path filter. A docs-only or changeset-only PR therefore started neither `ci.yml` nor `lint.yml` — objectstack-ai#3509 measured zero check runs from them. A check that is never *created* does not fail a required-status-check rule, it leaves the pull request pending; inside the merge queue it fails on the ruleset's 60-minute status-check timeout. So none of `Lint`, `Type Check`, `Test (shard N/4)`, `Build & E2E` or `Changeset Fixed Group Check` could be made required while the filter lived on the trigger — which is why the queue's required set was empty to begin with. The filter is not deleted, it moves. `type-check`, `test`, `e2e` (ci.yml) and `lint` (lint.yml) each open with a `Decide whether this change needs a full run` step, and every step after it carries `if: steps.relevant.outputs.should_run == 'true'`. The job always runs and always reports; the paths decide only whether it does any work. That is the shape `ci.yml`'s `docs` job has used since objectstack-ai#3450 — not a new mechanism — and the exclusion lists are byte-for-byte the `paths-ignore` they replace, so which PRs pay for a full run is exactly as before. Two deliberate narrowings, both stated so they can be argued with: - `paths-ignore` stays on the `push` trigger. Branch protection and the merge queue judge pull requests and queue builds, never pushes to `main`, so the push lane gains nothing from losing it and would cost a full CI run on every docs merge. It also gives the ignore list one authored home, which `merge-queue-reporting.test.ts` then pins the in-job copies against. - `changeset-check` is not gated. It is a checkout and one `node` call, so short-circuiting it would cost more in complexity than it saves. The gate fails OPEN: an unresolvable diff runs everything rather than reporting green having built nothing (objectstack#4928's filter contract). Measured both ways against a fixture repository — as shipped an unreachable base sha yields should_run=true; rewritten with the `|| echo ""` spelling `ci.yml`'s older `docs` gate still uses, the same input yields should_run=false, i.e. a silent full skip. That pre-existing `docs` gate is reported separately rather than changed here. `scripts/__tests__/merge-queue-reporting.test.ts` pins both steps; against the pre-change workflows 9 of its 10 assertions go red, each naming its own regression. `lint-workflow.test.ts`'s TypeScript-exclusion tripwire now reads the in-job list as well as the trigger, which is where that list now lives. Refs objectstack-ai#3523 --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3494
前提核验(先于实现)
用
--listFilesOnly扫遍仓库 76 个 tsconfig,确认scripts/__tests__/**不在任何一个 program 里。但 issue 的表述需要收窄一格 —— 「scripts/ 在零 tsconfig 覆盖内」并不完全成立:
scripts/vite-crypto-stub.tsapps/console/tsconfig.node.json(#3305 接进了 console 的type-check)scripts/vite-maplibre-worker.tsscripts/__tests__/*.ts(10 个门禁 pin 测试)也就是说:两个非测试源文件其实有门,10 个 pin 测试一个都没有。issue 的实质结论(门禁 pin 测试自身无类型门)成立,落点不变;这里如实记下差异,避免下一个人按「整个 scripts/ 都没编译」去推断。
错误数:实测 8 条,不是 3 条
#3384 当时测得 3 条。此后
check-control-bytes.test.ts/docs-links-workflow.test.ts/shadcn-local-patches.test.ts等陆续落地,现况:顺带查清了那两条 TS2578 的成因:多行
import { … } from '…mjs'里,TS 把「缺声明」报在说明符所在行(13 / 11 行),而@ts-expect-error挂在import {上一行 —— 这两个指令从来就没生效过,只是没人编译过所以没人知道。allowJs 的取舍:两个方向都真跑了
issue 提示「allowJs/checkJs 会翻转既有
@ts-expect-error的成立性」。对同一批文件实测:allowJs: false.mjs手写.d.mts。第二份事实来源,可以无声漂移 —— 正是check-spec-symbol-derivation.mjs存在的理由。而且any会外溢:3 条 TS2345 并非真缺陷,只是any的连带allowJs: truepatchedComponents()真的是string[],3 条 TS2345 自动消失选
allowJs: true+checkJs: false。后者是刻意的边界:本项目消费 helper 的推断类型,不接管 8 个纯 JS 门禁脚本内部的类型整洁 —— 那是另一件大得多的事。副作用是这条现在更强了:改动门禁 helper 的导出签名,它的 pin 测试会红。
落点
tsconfig.scripts.json(新增) —— 独立,刻意不extendstsconfig.base.json:那是包构建配置,其exclude列了 test glob,继承过来会一个测试文件都编不到,空转通过,正是check-type-check-coverage.mjs5b 段在上一层要抓的形态。vite-*.ts:排除清单是第二件要维护的事,而且 console 哪天不再 import 就会无声掉出所有 program。重叠的代价用对齐 console 那份的选项集付掉(strict / ESNext / bundler,以及不开noImplicitReturns),这样共享文件不会一个项目绿另一个红。//而非/* */:glob 里的双星紧跟斜杠会提前闭合块注释,而解析失败的 tsconfig 不会响亮报错 —— 它退回默认值,tsc -p转头去编译整个仓库。这不是假设,是写这个文件时真踩到的,已由 pin 测试钉住。.github/workflows/ci.yml——type-checkjob 加一步pnpm type-check:scripts,放在 install 之后、Turbo 之前:program 里没有任何@object-ui/*import,不需要^build,便宜且快速失败。package.json—— 加type-check:scripts根脚本。pnpm type-check是turbo run type-check,结构上够不到无 package.json 的目录;没有这个命名脚本,这道门就只能在 CI 上跑,而本地复现不了的门,人会学会忽略它。(与type-check:coverage/check:spec-symbols/check:control-bytes同一惯例。)scripts/__tests__/scripts-type-check.test.ts(新增) —— 断言的是行为不是拼写:配置能否解析、磁盘上每个scripts/**/*.ts是否真落进 program、CI 是否真跑、是否排在 install 之后,以及「不需要 workspace 构建」这个放置前提。content/docs/guide/ci-cd-pipeline.md—— job 表里type-check行逐项列出了它跑什么。不更新就会复刻 ci-cd-pipeline.md 的 ci.yml job 表格漂移:写「Seven jobs」并列了一个不存在的 dev-server job(实际 6 个) #3451 那种「页面少报一道门」的漂移,而钉住这张表的 pin 测试恰好就在本 PR 新覆盖的目录里。验证(方向先声明,再执行)
1. 主方向 —— 预测:改前 RED、改后 GREEN。
2. 反向核验 A —— 把删掉的某个
@ts-expect-error装回去。预测 RED,且原因必须是 TS2578(证明这些指令本就已失效,不是我删掉了一道有效的抑制):3. 反向核验 B(#3181 绊线) —— 往
ci-cd-pipeline-doc.test.ts追加一个必假的类型层断言(Assert< Equal< 1, 2 > >)。这里两个方向都要看,因为它们方向相反:vitest 那一侧的绿才是要点:类型断言在运行时被擦除,跑测试永远看不见它。「pin 测试全绿」+「没有任何 program 读它」= 一个可以钉住已失效契约还照样报绿的测试。
4. pin 测试自身的空转核验 —— 把 include 改窄成
scripts/vite-*.ts。预测 RED 并点名漏掉的文件(而不是空转变绿):5. 回归与门禁
#3496 新增的
scripts/__tests__/shadcn-sync-fetch-cache.test.ts带着这一行:本 PR 之后,该目录下的
.mjs/.jsimport 会由allowJs推断出真类型,于是这个指令变成 TS2578(Unused),新加的 CI 步骤会红。两个 PR 各自都绿,合并后才红 —— git 不会报冲突。后合的一方删掉那一行注释即可(与本 PR 对另外 5 处的处理一致)。#3497 只动
shadcn-check.yml,与本 PR 无交集。无 changeset
纯 CI/工具链改动,不影响任何已发布包;根
package.json是 private,新增的是根脚本而非依赖(先例 #3437)。Generated by Claude Code