Skip to content

Run knip in CI and delete the dead code it found - #185

Merged
szdziedzic merged 3 commits into
szdziedzic-claude/root-scriptsfrom
szdziedzic-claude/knip-in-ci
Sep 22, 2026
Merged

szdziedzic merged 3 commits into
szdziedzic-claude/root-scriptsfrom
szdziedzic-claude/knip-in-ci

Conversation

@szdziedzic

@szdziedzic szdziedzic commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #184, which adds the check script this PR extends. Retarget to expo once #184 lands.

What

  • knip runs in CI. A Knip job joins Lint and Typecheck in .eas/workflows/checks.yml, and bun run check now runs it too.
  • Config tuned so the findings are real. Test files and child fixtures are entry points, so an export that only a test imports counts as used. ignoreExportsUsedInFile is on, which also replaces the one @lintignore tag. node-swift is declared as a Swift-only dependency: Package.swift consumes it, no JS imports it. Redundant entry patterns that knip infers from package.json are dropped.
  • Dead code deleted. Everything knip still reported after the tuning, except two functions kept on request:
    • middleware-utils.ts: requestHost, textResponse, noStoreJsonResponse, sseResponse, readTextBody, jsonResponse, and the SseSink type. Nothing imported them. exec-ws.ts uses its own requestHost from exec-ws-utils.ts.
    • deviceFrames.tsx: the five legacy DEVICE_* constants and screenInsets, plus its re-export in simulator/index.ts.
    • grid.ts gridPreviewHref, presentation.ts PRESENTATION_CORNER_STORAGE_KEY, simulator-resize.ts SIMULATOR_RESIZE_LAYOUT_TRANSITION, trails.ts modeLabel.
  • Kept with a @public tag: setCapabilityEnabled in launch-manager.ts and its only callee capabilityDefinition in capabilities.ts. Open PRs refactor(camera): manage a device-wide feed without app resets #148, feat(clipboard): copy and paste between the browser and the simulator #102, and feat(capture): add the capture runtime and reboot lifecycle #53 import setCapabilityEnabled. The tag names them, and the first one to land makes both live; remove the tag then.

Why

Agents copy what they see. Unreachable exports are patterns an agent will extend. Running knip on every PR stops new dead code from landing, and this PR clears the backlog so the job starts green.

Evidence

On this branch, after bun run build:

$ knip
(no output, exit 0)

$ oxlint
Found 0 warnings and 0 errors.

$ tsc --noEmit
(exit 0)

$ bun run test
 1212 pass
 97 skip
 0 fail

Before the tuning, knip on expo reported 188 unused exports and 43 unused types, most of them exports used only by tests or only inside their own file.

🤖 Generated with Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

@szdziedzic
szdziedzic force-pushed the szdziedzic-claude/knip-in-ci branch from bcb1992 to 0d58eee Compare September 22, 2026 21:14

@gwdp gwdp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here, reviewing on behalf of @gwdp.

The knip tuning is much better than what we tried locally (we only narrowed --include). bun run check on this branch: lint, typecheck and knip all clean.

One heads-up before the deletions land: setCapabilityEnabled and capabilityDefinition are unused on expo, but three open PRs import setCapabilityEnabled from launch-manager: #148 (camera), #102 (clipboard) and #53 (capture). Deleting them here means each of those has to add them back. Could we keep those two for now, maybe with a knip ignore that names those PRs, and let the first one to land make them live?

I checked the rest of the deleted symbols against open PRs too. #173 uses the one-argument requestHost from exec-ws-utils.ts, which stays, and #25 and #26 define their own copies. So nothing else is affected.

@szdziedzic
szdziedzic force-pushed the szdziedzic-claude/knip-in-ci branch 2 times, most recently from ee31b49 to 4c7d063 Compare September 22, 2026 21:48
@szdziedzic

Copy link
Copy Markdown
Collaborator Author

Kept both, as asked. setCapabilityEnabled and its only callee capabilityDefinition are back with a @public tag whose comment names #148, #102, and #53, so knip stays clean and the first of those to land makes them live. Verified the three PRs do reference setCapabilityEnabled in their diffs. knip, lint, typecheck, and the two related suites are green on 4c7d063. PR body updated to match.

szdziedzic and others added 3 commits September 23, 2026 00:03
knip was configured but never ran in CI, so unused exports piled up.
Tune the config so test files count as entry points, exports used only
in their own file are not reported, and the Swift-only node-swift
dependency is declared. Then delete what remains: five unused response
helpers in middleware-utils, an unused capability toggle and its only
caller, legacy device-frame constants, and three unused client helpers.
Add a Knip job to the EAS Checks workflow and to `bun run check`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
gwdp pointed out that #148, #102, and #53 import setCapabilityEnabled.
Restore it and its only callee with a @public tag that names those PRs,
so knip stays clean and the first PR to land makes them live.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@szdziedzic
szdziedzic force-pushed the szdziedzic-claude/knip-in-ci branch from 4c7d063 to bd10536 Compare September 22, 2026 22:03

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

⚠️ 1 issue in files not directly in the diff

⚠️ External server is killed by tests

With SERVE_SIM_STATE_DIR set, test:e2e runs destructive suites against that directory. type-command-sim invokes --kill for the pinned device. The user-managed server is terminated despite the leave-alone contract.

Devin Review

@gwdp gwdp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude here, reviewing on behalf of @gwdp.

Approving. Checked bd10536 locally: bun run check (lint, typecheck, knip) is clean, and after a build bun run test gives 1213 pass, 96 skip, 0 fail. setCapabilityEnabled and capabilityDefinition are unchanged apart from the @public tag, so #148, #102 and #53 keep their import, and nothing still references the deleted helpers. It also merges cleanly with #187 on top of #184. Thanks for keeping those two around.

@szdziedzic
szdziedzic merged commit df029c8 into szdziedzic-claude/root-scripts Sep 22, 2026
5 checks passed
@gwdp

gwdp commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Claude here, on behalf of @gwdp.

Heads-up: this merged into szdziedzic-claude/root-scripts, not expo. #184 had already landed on expo as a squash (24fa52d), so the merge commit (df029c8) only exists on that branch. On expo right now there is no Knip job in checks.yml, the deleted helpers in middleware-utils.ts are still there, and the @public tags are missing. Could you open it again against expo (a PR from szdziedzic-claude/root-scripts or a cherry-pick of the three commits should do it)?

@szdziedzic
szdziedzic deleted the szdziedzic-claude/knip-in-ci branch September 22, 2026 23:08
szdziedzic added a commit that referenced this pull request Sep 22, 2026
* Run knip in CI and delete the dead code it found

knip was configured but never ran in CI, so unused exports piled up.
Tune the config so test files count as entry points, exports used only
in their own file are not reported, and the Swift-only node-swift
dependency is declared. Then delete what remains: five unused response
helpers in middleware-utils, an unused capability toggle and its only
caller, legacy device-frame constants, and three unused client helpers.
Add a Knip job to the EAS Checks workflow and to `bun run check`.



* Say that check runs knip too



* Keep setCapabilityEnabled and capabilityDefinition for open PRs

gwdp pointed out that #148, #102, and #53 import setCapabilityEnabled.
Restore it and its only callee with a @public tag that names those PRs,
so knip stays clean and the first PR to land makes them live.



---------


(cherry picked from commit df029c8)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
krystofwoldrich added a commit to expo/expo-device-hub that referenced this pull request Sep 25, 2026
> The description of the PR is generated, but I have review the content
and all correctly describes, what has been done in this PR.

Syncs `packages/serve-sim` with
[expo/serve-sim](https://github.com/expo/serve-sim) `expo` branch, from
`a5f4676` (Release 0.3.0, the submodule pointer before #79) to `c91e75b`
(after Release 0.3.4).

> [!IMPORTANT]
> Merge with **Merge commit**. Each commit is one upstream commit and
links to it. A squash merge loses these links.

## How

Each upstream commit is applied one by one as a subdirectory
cherry-pick:

```sh
git format-patch -1 --stdout <sha> | git am -3 --directory=packages/serve-sim
```

- The original author and date are kept.
- `(#N)` is rewritten to `(expo/serve-sim#N)`, so GitHub does not link
to PRs in this repo.
- Every commit has an `Upstream-Commit:` trailer with the link to the
original commit.
- Commits with a conflict have a `Conflict resolution:` section in the
message.
- The last commit adds a changeset for `expo-device-hub`, the same as
#119.

## Upstream commits (28)

| # | Commit | Upstream |
|---|---|---|
| 1 | fix: keyboard input on iPhone Duo (expo/serve-sim#183) |
[`4adc9ee89`](expo/serve-sim@4adc9ee)
|
| 2 | Release @expo/serve-sim 0.3.1 |
[`700f571bb`](expo/serve-sim@700f571)
|
| 3 | Add root check, test, and test:e2e scripts (expo/serve-sim#184) |
[`24fa52d4a`](expo/serve-sim@24fa52d)
|
| 4 | Encode the filename and import-boundary rules in oxlint
(expo/serve-sim#186) |
[`0dbee895a`](expo/serve-sim@0dbee89)
|
| 5 | Add a PR template and a definition of done for agents
(expo/serve-sim#187) |
[`b1a2fa0e0`](expo/serve-sim@b1a2fa0)
|
| 6 | Run knip in CI and delete the dead code it found
(expo/serve-sim#185) (expo/serve-sim#193) |
[`7f389bd3f`](expo/serve-sim@7f389bd)
|
| 7 | Add REVIEW.md with rules distilled from real review findings
(expo/serve-sim#188) |
[`d3b193835`](expo/serve-sim@d3b1938)
|
| 8 | Create a GitHub release with notes for every published version
(expo/serve-sim#190) |
[`e27bb134e`](expo/serve-sim@e27bb13)
|
| 9 | fix(webrtc): size H.264 to the negotiated level instead of a fixed
cap (expo/serve-sim#167) |
[`5ef198d9c`](expo/serve-sim@5ef198d)
|
| 10 | fix(webrtc): let weak links converge without shrinking the
picture (expo/serve-sim#168) |
[`961f45720`](expo/serve-sim@961f457)
|
| 11 | fix(stream): show the codec and size actually in use in the stats
panel (expo/serve-sim#169) |
[`e6f3d0e2b`](expo/serve-sim@e6f3d0e)
|
| 12 | fix(webrtc): recover when a stream stops decoding mid-session
(expo/serve-sim#170) |
[`8a10cf468`](expo/serve-sim@8a10cf4)
|
| 13 | feat(webrtc): raise the offer's H.264 level to keep native
resolution (expo/serve-sim#171) |
[`daead19b3`](expo/serve-sim@daead19)
|
| 14 | fix(webrtc): retry a rejected offer instead of ending the session
(expo/serve-sim#177) |
[`a43eb8866`](expo/serve-sim@a43eb88)
|
| 15 | feat(logs): serve /logs from a shared per-device ring buffer
(expo/serve-sim#109) |
[`45454283f`](expo/serve-sim@4545428)
|
| 16 | feat(preview): open device logs from a toolbar icon into a bottom
drawer (expo/serve-sim#110) |
[`46ddd0dfe`](expo/serve-sim@46ddd0d)
|
| 17 | feat(crash): collect simulator crash reports and serve them with
a log tail (expo/serve-sim#111) |
[`31698133d`](expo/serve-sim@3169813)
|
| 18 | feat(preview): add a crashes panel to the tools sidebar
(expo/serve-sim#112) |
[`0fa3a29bf`](expo/serve-sim@0fa3a29)
|
| 19 | test(crash): add fixture-backed crash E2E (expo/serve-sim#152) |
[`350d4d1c2`](expo/serve-sim@350d4d1)
|
| 20 | Release @expo/serve-sim 0.3.2 |
[`2fd745bcd`](expo/serve-sim@2fd745b)
|
| 21 | feat(preview): authenticate websockets with a subprotocol
(expo/serve-sim#173) |
[`10f992cbb`](expo/serve-sim@10f992c)
|
| 22 | Release @expo/serve-sim 0.3.3 |
[`b78f57552`](expo/serve-sim@b78f575)
|
| 23 | refactor(preview): remove the metrics cors flag
(expo/serve-sim#174) |
[`bc1518bc8`](expo/serve-sim@bc1518b)
|
| 24 | fix(preview): focus the frame when the stream is clicked
(expo/serve-sim#195) |
[`25a18ef79`](expo/serve-sim@25a18ef)
|
| 25 | chore(lint): turn the four unicorn correctness rules back on
(expo/serve-sim#191) |
[`a91630d27`](expo/serve-sim@a91630d)
|
| 26 | fix(preview): type shifted keys with the hardware keyboard off
(expo/serve-sim#198) |
[`11aea54e6`](expo/serve-sim@11aea54)
|
| 27 | Release @expo/serve-sim 0.3.4 |
[`e18233ff5`](expo/serve-sim@e18233f)
|
| 28 | [serve-sim] Add opt-in user-app log filtering
(expo/serve-sim#196) |
[`c91e75b3c`](expo/serve-sim@c91e75b)
|

## Conflicts and how they were resolved

4 commits had a conflict. All other files in these commits and all other
commits merged cleanly.

#### fix: keyboard input on iPhone Duo (expo/serve-sim#183) —
[4adc9ee89](expo/serve-sim@4adc9ee)

- `src/client/simulator/screen-config-state.ts`: `screenConfigsEqual()`
was
extended on both sides. Hub #134/#137 added the
`supportsPhysicalOrientation`
and `physicalOrientation` comparisons; upstream added
`inputUnavailable`.
  Kept all three comparisons. The other 20 files merged cleanly.

#### Run knip in CI and delete the dead code it found
(expo/serve-sim#185) (expo/serve-sim#193) —
[7f389bd3f](expo/serve-sim@7f389bd)

- `.eas/workflows/checks.yml`: upstream adds a `knip` job to this EAS
workflow, but hub #124 deleted the file when the serve-sim checks moved
to
the monorepo CI. Kept the file deleted, so the knip job is not ported.
The
other 13 files merged cleanly. Running knip in the hub CI is a
follow-up.

#### fix(webrtc): recover when a stream stops decoding mid-session
(expo/serve-sim#170) —
[8a10cf468](expo/serve-sim@8a10cf4)

- `src/client/client.tsx`: import lines only. Upstream added the
`useLadderRestart` and `StreamPanelPeer` imports; hub #134/#137 extended
the
  `duo-pose` and `duo-view` imports (`duoPhysicalPoseChanged`,
`DUO_FACE_DOWN_HELD`, `duoFaceDownFraming`, `DuoFaceDownFraming`). Kept
all
  of them. The rest of the file and the other 20 files merged cleanly.

#### fix(preview): type shifted keys with the hardware keyboard off
(expo/serve-sim#198) —
[11aea54e6](expo/serve-sim@11aea54)

- `src/__tests__/device-session-screen-config.test.ts`: the hard-coded
pass
count of the `device-session-screen-config.child.ts` suite. Base was 24;
hub #134/#136 added 5 tests (29), upstream added 39 tests (63). The
child
suite merged cleanly with both sets, so the count is now 68. Verified
with
`bun test src/__tests__/device-session-screen-config.test.ts`. The other
25
  files merged cleanly.

#### Changes outside `packages/serve-sim` (no conflict)

expo/serve-sim#184 and #195 change `.eas/workflows/sim-test.yml`. Hub
#124 moved that file to the repo root, and `git am -3` followed the
rename, so these changes are in `.eas/workflows/sim-test.yml` (Chrome
for Testing install, `scripts/tart/__tests__/` added to the E2E run).
The test step runs in `packages/serve-sim`, so the paths are correct.

## Follow-ups (later PRs)

- Run serve-sim knip in the hub CI (`.github/workflows/ci.yml`). The
upstream EAS knip job from expo/serve-sim#193 is not ported, because
#124 removed `packages/serve-sim/.eas/workflows/checks.yml`. Note: `bun
run knip` in `packages/serve-sim` fails now with `knip: command not
found`, because the `serve-sim-repo` dev dependencies are not installed
in the monorepo.
- `packages/@expo/hub-client` has its own copy of the serve-sim client
(#119). The upstream client changes in this sync are not ported to it
(for example the WebRTC fixes, expo/serve-sim#195 and #198).

<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/expo/expo-device-hub/pull/146"
target="_blank"><picture><source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-devin-review-dark.svg?v=4"><img
src="https://static.devin.ai/assets/gh-devin-review-light.svg?v=4"
alt="Devin Review"></picture></a>
<!-- devin-review-badge-end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants