Skip to content

Commit 436891e

Browse files
authored
Merge branch 'main' into glary/website-strict-tsconfig
2 parents 11011a6 + 32596ad commit 436891e

36 files changed

Lines changed: 1689 additions & 417 deletions
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
---
2+
name: removing-typescript-suppressions
3+
description: Replaces @ts-expect-error and @ts-ignore directives with minimal type-safe fixes. Use when removing TypeScript compiler suppressions.
4+
---
5+
6+
# Removing TypeScript suppressions
7+
8+
Replace suppressions by repairing the contract that caused the compiler error.
9+
Do not silence the error elsewhere or widen types beyond the real runtime shape.
10+
11+
## Workflow
12+
13+
### 1. Establish the scope
14+
15+
Read `docs/guidance/typescript.md`, `docs/guidance/engineering.md`, and the
16+
guidance for the affected files. Identify the correct comparison ref and list
17+
only suppressions introduced by the change under review:
18+
19+
```bash
20+
git diff --unified=2 <base-ref>...HEAD -- <paths> \
21+
| rg -n '^\+.*@ts-(expect-error|ignore)|^diff --git|^@@'
22+
```
23+
24+
Do not expand the task to old suppressions unless the user asks. Preserve a
25+
suppression used deliberately to test a compiler error when that error is the
26+
behavior under test.
27+
28+
### 2. Read the ownership path
29+
30+
Read each affected file and trace the value to its source type, runtime owner,
31+
and consumers. Check declarations, store types, generated API types, library
32+
return types, test globals, and the relevant `tsconfig` before choosing a fix.
33+
34+
Do not accept the suppression comment as the diagnosis. Verify the failing code
35+
path and runtime shape.
36+
37+
### 3. Expose the real errors
38+
39+
Remove the in-scope suppressions, then run the narrowest typecheck that owns the
40+
files. Use the compiler diagnostics to group failures by root cause instead of
41+
patching each line independently.
42+
43+
Common commands in this repository:
44+
45+
```bash
46+
pnpm typecheck
47+
pnpm typecheck:browser
48+
pnpm typecheck:desktop
49+
pnpm typecheck:scripts
50+
pnpm typecheck:website
51+
```
52+
53+
### 4. Repair the contract
54+
55+
Prefer, in order:
56+
57+
1. correct types at the source
58+
2. control-flow narrowing
59+
3. an existing domain type or generated API type
60+
4. `unknown` at a genuine compatibility boundary, followed by runtime narrowing
61+
62+
Avoid `any`, `as any`, a replacement assertion, a broader optional type, or a
63+
new wrapper that merely hides the mismatch. Keep public API types stable and do
64+
not expose internal store types through public facades.
65+
66+
### 5. Verify behavior and absence
67+
68+
Run the owning typecheck, focused tests, lint, formatting, and a whitespace
69+
check. Use `pnpm exec vitest run` for Vitest, `pnpm test:browser:local` or
70+
`pnpm test:browser` for Playwright, and the owning repository script for other
71+
test types. Confirm that the diff adds no suppression:
72+
73+
```bash
74+
pnpm exec eslint <changed-files>
75+
pnpm exec oxfmt --check <changed-files>
76+
git diff --check <base-ref>...HEAD -- <paths>
77+
git diff --unified=0 <base-ref>...HEAD -- <paths> \
78+
| rg '^\+.*@ts-(expect-error|ignore)'
79+
```
80+
81+
The final `rg` command should return no matches. Run broader checks when the fix
82+
changes a shared type, public contract, store, or cross-package boundary.
83+
84+
## Repair patterns
85+
86+
### Narrow optional browser globals once
87+
88+
Copy an optional global to a local and guard it. This preserves narrowing across
89+
callbacks and asynchronous code.
90+
91+
```typescript
92+
const app = window.app
93+
if (!app) throw new Error('ComfyUI app is not initialized')
94+
95+
await app.api.getNodeDefs()
96+
```
97+
98+
### Use the owner instead of casting a facade
99+
100+
When a public facade intentionally omits internal collections, import the store
101+
or service that owns those collections. Do not widen the facade or cast through
102+
it for one caller.
103+
104+
### Narrow value-or-factory unions
105+
106+
Resolve callbacks before using their values:
107+
108+
```typescript
109+
const label =
110+
typeof command.label === 'function' ? command.label() : command.label
111+
```
112+
113+
Resolve a factory default before passing it to another resolver. Keep untyped
114+
legacy data as `unknown`. Narrow objects before reading properties, functions
115+
before calling them, and returned values before use.
116+
117+
### Read map identity from `Object.entries`
118+
119+
If an object's key is the identifier, do not invent an `id` property on its
120+
values:
121+
122+
```typescript
123+
Object.entries(dialogs).map(([id, dialog]) => ({ id, title: dialog.title }))
124+
```
125+
126+
### Handle nullable factories before dereferencing
127+
128+
Respect library return types such as `LiteGraph.createNode(): LGraphNode | null`:
129+
130+
```typescript
131+
const node = liteGraph.createNode(nodeName, displayName)
132+
if (!node?.widgets?.length) return {}
133+
```
134+
135+
Use optional chaining only when the missing value and the empty value have the
136+
same behavior. Otherwise, use a guard with a useful error.
137+
138+
### Use named payloads across callback boundaries
139+
140+
Heterogeneous array arguments often lose positional types in `page.evaluate`
141+
and similar APIs. Pass an object instead of asserting a tuple:
142+
143+
```typescript
144+
await page.evaluate(
145+
({ nodeName, displayName, inputNames }) => {
146+
// use the independently typed fields
147+
},
148+
{ nodeName, displayName, inputNames }
149+
)
150+
```
151+
152+
### Validate serialization boundaries
153+
154+
Type values produced by code you own before passing them to
155+
`Object.fromEntries`. If JSON or browser data arrives as `unknown`, validate its
156+
full nested shape before assigning a domain type. A result annotation does not
157+
validate data.
158+
159+
### Make test preconditions executable
160+
161+
If a test needs optional output, narrow it with a runtime guard that produces a
162+
clear behavioral failure:
163+
164+
```typescript
165+
if (!serialized.inputs || !serialized.outputs) {
166+
throw new Error('Expected serialized node labels')
167+
}
168+
```
169+
170+
Do not replace the suppression with a non-null assertion.
171+
172+
### Install minimal test globals without claiming full DOM types
173+
174+
When Node tests need a small browser shim, add the property to the host object
175+
instead of assigning `{}` to a full `Window` type:
176+
177+
```typescript
178+
if (typeof window === 'undefined') {
179+
Object.assign(globalThis, { window: {} })
180+
}
181+
```
182+
183+
### Delete documentation-only values
184+
185+
If strict checking reports an unused constant kept only as documentation,
186+
delete it. Put useful context on the value that the code actually checks.
187+
188+
## Guardrails
189+
190+
- Do not hand-declare server response types. Import generated shared types.
191+
- Do not grow `ExtensionManager` or another public API to expose private state.
192+
- Put reusable type guards in leaf modules with runtime-free `import type`
193+
dependencies.
194+
- Do not change runtime behavior while repairing types unless the old code was
195+
demonstrably inconsistent with its runtime contract. Cover such a bug with a
196+
focused test.
197+
- Treat review findings and their proposed fixes as claims. Reproduce the error
198+
against current code, then fix or reject each finding with concrete evidence.

.coderabbit.yaml

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,40 @@ reviews:
4343
2. The PR changes files under `src/` or `packages/` related to the main frontend application but the PR does not change at least one file under `browser_tests/`.
4444
3. The PR description lacks a concrete explanation of why an end-to-end regression test was not added.
4545
46-
Do not fail if the changes are exclusively in `apps/website`, just documentation changes, or changes related to CI processes.
46+
Do not fail if the changes are exclusively in `apps/website` (that package has its own check, "Website end-to-end regression coverage", because its Playwright specs live in `apps/website/e2e/` rather than `browser_tests/`), just documentation changes, or changes related to CI processes.
4747
The goal is to make sure that fixes include End-to-End regression tests. Do not insist on tests when the PR is not fixing a bug.
4848
4949
Pass otherwise.
5050
When failing, mention which bug-fix signal you found and ask the author to either add or update a Playwright regression test under `browser_tests/` or add a concrete explanation in the PR description of why an end-to-end regression test is not practical.
5151
52+
- name: Website end-to-end regression coverage
53+
mode: error
54+
instructions: |
55+
Use only PR metadata already available in the review context:
56+
- the PR title
57+
- commit subjects in this PR
58+
- the files changed in this PR relative to the PR base (equivalent to `base...head`)
59+
- the PR description
60+
- the diff content.
61+
Do not rely on shell commands.
62+
Do not inspect reverse diffs, files changed only on the base branch, or files outside this PR.
63+
If the changed-file list or commit subjects are unavailable, mark the check inconclusive instead of guessing.
64+
65+
This check applies ONLY when the PR changes website runtime files under `apps/website/src/` or `apps/website/public/`. If no such files changed, pass immediately — that includes PRs touching only `apps/website/e2e/`, tooling, config, or CI.
66+
67+
Changes confined to `packages/` are deliberately out of scope here, even though the website consumes those packages: the generic "End-to-end regression coverage for fixes" check already requires a `browser_tests/` regression test for them. Do not demand a second website-specific test for a shared-package change.
68+
69+
Fail if all of the following are true:
70+
1. The diff itself changes observable website runtime behavior — fixes a user-visible bug, adds a page or route, or changes an interactive flow, form, or navigation. A `fix`/`bugfix`/`hotfix` style title or commit subject is only a hint: confirm it against the diff, and never treat the wording alone as qualifying.
71+
2. The PR does not add or update a Playwright assertion under `apps/website/e2e/**/*.spec.ts` that actually exercises the route, flow, or behavior this PR changed. An unrelated assertion elsewhere in the suite does not satisfy this.
72+
3. The PR description lacks a concrete explanation of why an end-to-end regression test was not added.
73+
74+
Do not fail for text/copy-only edits, translation-only changes, static asset swaps, styling-only changes, generated files, dependency metadata, refactors that preserve behavior, or changes confined to tests, tooling, or CI.
75+
A reformatted or whitespace-only edit to an existing spec does not count as adding coverage.
76+
77+
Pass otherwise.
78+
When failing, name the behavior-changing signal you found and ask the author to either add or update a Playwright regression test under `apps/website/e2e/` or record in the PR description why an end-to-end test is not practical.
79+
5280
- name: ADR compliance for entity/litegraph changes
5381
mode: warning
5482
instructions: |
@@ -97,6 +125,29 @@ reviews:
97125
`docs/guidance/vitest.md`, `docs/testing/vitest-patterns.md`, and
98126
`docs/testing/litegraph-testing.md` as required review context for
99127
every changed LiteGraph Vitest test file.
128+
- path: 'apps/website/src/**/*.{ts,vue}'
129+
instructions: |
130+
Changed lines here are measured by the `website-unit` Codecov patch
131+
status, so new behavior needs a colocated Vitest test. Treat
132+
`docs/guidance/vitest.md` as required review context. Flag new
133+
exported logic that no test exercises.
134+
This glob is wider than the gate: `coverage.exclude` in
135+
`apps/website/vitest.config.ts` is the source of truth, and anything
136+
it lists is unmeasured. At time of writing that is `*.test.ts`,
137+
`*.spec.ts`, `*.stories.ts`, `*.d.ts`, `src/test/**`,
138+
`src/content/**`, `src/i18n/**` and `src/content.config.ts`. Do not
139+
cite the `website-unit` gate for changes confined to those.
140+
- path: 'apps/website/src/**/*.astro'
141+
instructions: |
142+
`.astro` files are excluded from coverage because V8 cannot
143+
instrument them. Non-trivial frontmatter logic (data shaping,
144+
branching, formatting) is therefore untestable where it sits — ask
145+
for it to be extracted into a `.ts` module beside the component so it
146+
is covered by the `website-unit` gate. Markup and static content are
147+
fine to leave inline.
148+
Extraction only helps where the destination is instrumented:
149+
`src/content/**` and `src/i18n/**` are excluded from coverage, so do
150+
not ask for logic to be moved into them.
100151
- path: '{browser_tests,apps/website/e2e}/**/*.spec.ts'
101152
instructions: |
102153
Treat `.agents/checks/test-quality.md`, `docs/testing/README.md`,
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Description: Unit tests + coverage reporting for the website (apps/website)
2+
name: 'CI: Website Unit'
3+
4+
on:
5+
push:
6+
branches: [main, master, website/*]
7+
pull_request:
8+
branches-ignore: [wip/*, draft/*, temp/*]
9+
merge_group:
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.ref }}
13+
cancel-in-progress: true
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
changes:
20+
runs-on: ubuntu-latest
21+
outputs:
22+
app-website-changes: ${{ steps.changes.outputs.app-website-changes }}
23+
packages-changes: ${{ steps.changes.outputs.packages-changes }}
24+
steps:
25+
- uses: actions/checkout@v7
26+
- id: changes
27+
uses: ./.github/actions/changes-filter
28+
29+
website-unit:
30+
needs: changes
31+
if: ${{ needs.changes.outputs.app-website-changes == 'true' || needs.changes.outputs.packages-changes == 'true' }}
32+
runs-on: ubuntu-latest
33+
34+
steps:
35+
- uses: actions/checkout@v7
36+
37+
- name: Setup frontend
38+
uses: ./.github/actions/setup-frontend
39+
40+
- name: Run website unit tests with coverage
41+
run: pnpm --filter @comfyorg/website test:coverage
42+
43+
- name: Upload website coverage to Codecov
44+
if: ${{ !cancelled() }}
45+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
46+
with:
47+
working-directory: ./apps/website
48+
files: ./coverage/lcov.info
49+
disable_search: true
50+
flags: website-unit
51+
# Re-root LCOV paths to prevent collisions with root-app coverage.
52+
network_prefix: apps/website/
53+
token: ${{ secrets.CODECOV_TOKEN }}
54+
# Forks have no token, so upload failures cannot block contributors.
55+
fail_ci_if_error: ${{ github.event.pull_request.head.repo.fork != true }}
56+
57+
# Stable branch-protection context when tests are skipped.
58+
website-unit-gate:
59+
needs: [changes, website-unit]
60+
if: ${{ always() }}
61+
runs-on: ubuntu-latest
62+
steps:
63+
- name: Verify website unit tests passed or were not required
64+
env:
65+
CHANGES_RESULT: ${{ needs.changes.result }}
66+
UNIT_RESULT: ${{ needs.website-unit.result }}
67+
WEBSITE_CHANGED: ${{ needs.changes.outputs.app-website-changes }}
68+
PACKAGES_CHANGED: ${{ needs.changes.outputs.packages-changes }}
69+
run: |
70+
if [ "$UNIT_RESULT" = "success" ]; then
71+
echo "Website unit tests passed."
72+
exit 0
73+
fi
74+
# Pass skipped tests only after successful filtering found no relevant changes.
75+
if [ "$UNIT_RESULT" = "skipped" ] &&
76+
[ "$CHANGES_RESULT" = "success" ] &&
77+
[ "$WEBSITE_CHANGED" != "true" ] &&
78+
[ "$PACKAGES_CHANGED" != "true" ]; then
79+
echo "No website or package changes; website unit tests not required."
80+
exit 0
81+
fi
82+
echo "::error title=Website unit tests::changes=$CHANGES_RESULT unit=$UNIT_RESULT website=$WEBSITE_CHANGED packages=$PACKAGES_CHANGED"
83+
exit 1

.github/workflows/pr-cursor-review.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
# SHA-pinned per zizmor `unpinned-uses: hash-pin`. Bump this SHA to pick up
3030
# upstream changes; keep `workflows_ref` matching so prompts/scripts load
3131
# from the same commit as the workflow definition.
32-
uses: Comfy-Org/github-workflows/.github/workflows/cursor-review.yml@732100bee739ef94fe650016b382a78b377c0af7 # github-workflows main (732100b)
32+
uses: Comfy-Org/github-workflows/.github/workflows/cursor-review.yml@41fb8315f0b81c5fa33dd1edc4bb5004609ba51d # github-workflows main (41fb831)
3333
with:
3434
# Overriding diff_excludes replaces the reusable default wholesale, so
3535
# this restates the generated/vendored defaults and adds this repo's heavy
@@ -48,7 +48,7 @@ jobs:
4848
:!**/*-snapshots/**
4949
:!src/workbench/extensions/manager/types/generatedManagerTypes.ts
5050
# Load the prompts/scripts from the same ref as `uses:`.
51-
workflows_ref: 732100bee739ef94fe650016b382a78b377c0af7
51+
workflows_ref: 41fb8315f0b81c5fa33dd1edc4bb5004609ba51d
5252
secrets:
5353
CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
5454
# Optional — enables start/complete Slack DMs to the triggerer.

0 commit comments

Comments
 (0)