Skip to content

chore(ci): add dead-code and unused-export detection to CI (closes #297) - #324

Open
Cyber-Mitch wants to merge 1 commit into
SmartDropLabs:mainfrom
Cyber-Mitch:feat/297-dead-code-detection-ci
Open

chore(ci): add dead-code and unused-export detection to CI (closes #297)#324
Cyber-Mitch wants to merge 1 commit into
SmartDropLabs:mainfrom
Cyber-Mitch:feat/297-dead-code-detection-ci

Conversation

@Cyber-Mitch

Copy link
Copy Markdown

closes #297

No CI step detected unused exports or dead code, so it accumulated silently over time. Adds knip as a dead-code/unused-export gate, wired into the existing PR-gate workflow.

Tool selection — knip, not the two the issue also named

  • ts-prune — rejected. Effectively unmaintained upstream, and has no Next.js App Router awareness: every page.tsx/layout.tsx/route.ts default export, plus metadata/generateStaticParams, would need hand-maintained entry config it barely supports. On a 104-file App Router codebase that's a false-positive storm, not a usable gate.
  • eslint-plugin-unused-imports — rejected as the answer to this specific issue. It only finds unused imports/variables within a file — it does not detect cross-file unused exports, which is literally the issue title. ESLint here already reports unused vars; this plugin wouldn't add the capability actually being asked for.
  • knip — chosen. Built-in Next.js plugin correctly resolves App Router's implicit entry points — verified empirically, not assumed: 0 page/layout/route false positives across all 104 .ts/.tsx files. Reads the repo's one path alias (@/* → ./src/*) from tsconfig.json automatically, with zero manual config. Single-package repo means none of knip's monorepo configuration curve applies. Covers unused exports, unused exported types, and unused dependencies in one pass — actively maintained (v6.33.0).

Scope discipline — a detection gate, not a cleanup PR

First zero-config run: 36 findings (2 unused deps, 1 unused devDep, 24 unused exports, 9 unused exported types, 0 unused files). That's too many to verify-and-delete safely inside a "wire in the detector" PR — several are genuinely ambiguous (framework re-export idioms, a config gap producing a false positive, and an 11-symbol block in src/lib/error-handler.ts that looks like a complete module built ahead of use, which needs real investigation before anyone decides it's safe to remove).

Decision: scoped-ignore baseline. All 36 pre-existing findings are individually grandfathered in knip.ts with per-file comments naming each symbol, so future contributors know exactly why they're allowlisted rather than wondering. Zero code deleted or modified — this PR is the gate, not the cleanup.

The one real config gap was fixed properly, not ignored around

e2e/mocks/freighter.ts initially flagged as an unreferenced file — but that's a config gap, not a real dead-code finding: this repo has two Playwright configs (root playwright.config.ts for ./tests, e2e/playwright.config.ts for ./e2e), and knip's Playwright plugin only auto-detects one. Fixed by pointing the plugin at both config files, rather than ignoring the false positive — this keeps future dead code under e2e/ and tests/ genuinely catchable, instead of permanently blind to that directory.

Proof the gate actually works — a canary, not just a clean run

A clean pnpm run deadcode exit-0 against the current tree doesn't prove the gate would catch anything new — it could just as easily mean the tool is misconfigured to find nothing. Verified instead: added a stray export const __x = 42 in a new src/lib/ file → pnpm run deadcode correctly failed (Unused files (1), exit 1). Removed after confirming. The gate demonstrably fails the build on a genuine new finding.

Changed files

.github/workflows/e2e.yml | 3 + (new step, after "Dependency vulnerability audit")
knip.ts | 92 + (new config: Next.js + Vitest + dual-Playwright plugins, plus the commented 36-item allowlist)
package.json | 2 + (deadcode script, knip devDependency)
pnpm-lock.yaml | 551 +- (knip + transitive deps only — no existing package's pinned version changed or removed)

CI step (.github/workflows/e2e.yml), placed as a cheap static check before the expensive build/Lighthouse/Playwright steps:

- name: Dead code and unused exports
  run: pnpm run deadcode

Verification

Check Result
pnpm run typecheck
pnpm run lint ✅ (pre-existing warnings only)
pnpm run build
pnpm run audit:high
pnpm run bundle:budget ✅ (723.0/780.0 KiB)
pnpm run deadcode ✅ clean; canary-verified to fail on genuine new findings

"How to test" — satisfied

  • package.json now has a dead-code detection tool configured — knip@^6.33.0, deadcode script, knip.ts config
  • CI has a step for unused export detection — .github/workflows/e2e.yml → "Dead code and unused exports"

Follow-up cleanup — a precise checklist, not a vague pointer

Every one of the 36 baseline findings, individually, so whoever picks this up doesn't need to re-run knip and re-derive the list:

Unused dependencies (3)

  • @chakra-ui/next-js — not imported anywhere in src/
  • @stellar/stellar-base — transitively provided by @stellar/stellar-sdk; no direct import
  • autoprefixer (devDep) — Tailwind v3 leftover; Tailwind v4 (@tailwindcss/postcss) autoprefixes internally

src/lib/error-handler.ts — highest-value investigation (11 findings): RPCError, ContractError, ValidationError, ErrorLogger, ErrorHandler, withErrorHandler, useErrorHandler, retryWithBackoff, enum ErrorType, interface AppError. Looks like a complete error-handling module built ahead of use — needs a dynamic-import / planned-feature check before anyone removes it.

Everything else, by file:

  • src/lib/soroban.ts (5) — buildUnlockAssetsTransaction, parseSimulationError, LeaderboardRow, BoostConfig, ContractCallOptions
  • src/lib/backend.ts (3) — getAirdrop, listAirdropRecipients, Recipient
  • src/hooks/useSorobanQuery.ts (3) — useLockAssets, useOptimisticUpdate, useTransactionStates
  • src/i18n/routing.ts (5) — Link, redirect, usePathname, useRouter, Locale — next-intl idiom; either wire up consumers or drop the re-exports
  • src/hooks/useSorobanEvents.ts (1) — classifyGetEventsError
  • src/lib/feeBumpGuard.ts (1) — SPONSORABLE_FUNCTIONS
  • src/types/farm.ts (1) — toStroops
  • src/config/index.ts (1) — StellarNetworkPreset
  • src/hooks/useToast.tsx (1) — NotificationType
  • src/components/ErrorBoundary/ErrorBoundary.tsx (1) — ErrorBoundarySection
  • e2e/mocks/freighter.ts (1) — injectFreighterMock: trivial fix, just drop the export keyword (only used within its own file)

Also unrelated, worth its own small issue: 15 pre-existing vitest failures (matchMedia is not a function, a jsdom/Chakra environment gap) — vitest isn't run in CI today, so this has been invisible. Needs a window.matchMedia polyfill in the vitest setup.

@netlify

netlify Bot commented Aug 29, 2026

Copy link
Copy Markdown

Deploy Preview for spiffy-melomakarona-eb1e8a ready!

Name Link
🔨 Latest commit 97be8c5
🔍 Latest deploy log https://app.netlify.com/projects/spiffy-melomakarona-eb1e8a/deploys/6a936fad65fa650008b55bc6
😎 Deploy Preview https://deploy-preview-324--spiffy-melomakarona-eb1e8a.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@drips-wave

drips-wave Bot commented Aug 29, 2026

Copy link
Copy Markdown

@Cyber-Mitch Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@netlify

netlify Bot commented Aug 29, 2026

Copy link
Copy Markdown

Deploy Preview for smart-drop ready!

Name Link
🔨 Latest commit 97be8c5
🔍 Latest deploy log https://app.netlify.com/projects/smart-drop/deploys/6a936fad7fa1450008443f5b
😎 Deploy Preview https://deploy-preview-324--smart-drop.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Cyber-Mitch

Copy link
Copy Markdown
Author

@ritaifeoluwa @prodbycorne Please Review

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.

No CI step to check for unused exports or dead code

1 participant