Skip to content

feat(admin): add ESLint, Prettier, and Stylelint for admin SPA#625

Open
waynesun09 wants to merge 7 commits into
mainfrom
add-frontend-linting
Open

feat(admin): add ESLint, Prettier, and Stylelint for admin SPA#625
waynesun09 wants to merge 7 commits into
mainfrom
add-frontend-linting

Conversation

@waynesun09
Copy link
Copy Markdown
Contributor

Summary

  • Adds ESLint 9 (flat config) with eslint-plugin-svelte, typescript-eslint, and Svelte 5 component quality rules
  • Adds Prettier with prettier-plugin-svelte for consistent formatting
  • Adds Stylelint with stylelint-config-html/svelte for CSS linting in scoped styles
  • Configures component size enforcement: 150-line whole-file limit + per-block limits (script=100, template=80, style=120) at warn level
  • Formats existing codebase with Prettier in a separate commit for clean git blame
  • Adds 6 npm scripts: lint, lint:fix, format, format:check, stylelint, stylelint:fix

Key rules enabled

Rule Level Purpose
svelte/max-lines-per-block warn Prevents monolithic script/template/style blocks
max-lines (150) warn Whole-file component size cap from DESIGN.md
svelte/block-lang error Enforces lang="ts" on scripts
svelte/no-at-html-tags error XSS prevention
svelte/require-each-key error Prevents rendering bugs in {#each}
@typescript-eslint/no-unused-vars error Dead code detection

Phased rollout

  • This PR (Phase 1): All size rules at warn, baseline disables on existing violations
  • Phase 2: Add lint steps to CI workflow + pre-commit hooks (follow-up PR)
  • Phase 3: After OrgList decomposition, upgrade to error and enable color-no-hex

Test plan

  • npm run format:check passes (0 issues)
  • npm run lint passes (0 errors, 0 warnings)
  • npm run stylelint passes (0 issues)
  • npm run check passes (svelte-check: 213 files, 0 errors)
  • Verify CI picks up existing npm run check and npm test steps
  • Verify no regressions in admin SPA functionality

@fullsend-ai-review
Copy link
Copy Markdown

fullsend-ai-review Bot commented May 3, 2026

Review: #625

Head SHA: 9b15e3b
Timestamp: 2026-05-06T00:00:00Z
Outcome: approve

Summary

This PR introduces ESLint 9, Prettier, and Stylelint tooling for the admin SPA with a well-structured configuration. The vast majority of changes are automated Prettier reformats (whitespace, line-length adjustments) with no behavioral impact. A small number of substantive changes — removal of the unused scanComplete state variable, renaming an unused destructured appSlug to _appSlug, adding { cause: e } to two error constructors, adding {#each} keys, and modernizing deprecated CSS properties — are all correct and improve code quality. No security, correctness, or intent-alignment concerns were found.

Findings

Info

  • [style/conventions] web/admin/src/lib/auth/turnstile.tseslint-disable-next-line preserve-caught-error references a rule that does not exist in the ESLint config. The comment is a no-op but serves as a TODO marker. Consider using a plain // TODO: comment instead to avoid confusion about whether a real rule is being suppressed.

  • [correctness] web/admin/src/routes/OrgList.svelte — Two {#each} blocks use array index as key ({#each items as line, i (i)}). This satisfies svelte/require-each-key but provides no reordering benefit over keyless iteration. Acceptable here since missingInstallRequirements and helpBullets are static arrays, but worth noting if they ever become dynamic.

  • [correctness] web/admin/src/lib/layers/githubClient.ts:37, web/admin/src/lib/layers/orgConfigParse.ts:78 — Two error throws gained { cause: e }, preserving the error chain. This is a positive behavioral change beyond pure formatting.

  • [correctness] web/admin/src/routes/OrgList.svelte — Removal of the scanComplete state variable is correct; the variable was only ever written to and never read anywhere in the codebase.

  • [style/conventions] web/admin/src/routes/OrgList.svelte — CSS modernizations (word-break: break-word to overflow-wrap: break-word, clip: rect(...) to clip-path: inset(50%), rgba() to modern rgb() syntax) are all correct upgrades from deprecated properties.

Footer

Outcome: approve
This review applies to SHA 9b15e3b2b4e0127ec87ab37fe7b995683e01c6dc. Any push to the PR head clears this review and requires a new evaluation.

Previous run

Review: #625

Head SHA: 5661747
Timestamp: 2026-05-06T00:00:00Z
Outcome: approve

Summary

This PR adds ESLint 9 (flat config), Prettier, and Stylelint tooling for the admin SPA, along with Husky + lint-staged for pre-commit hooks. The vast majority of the diff is Prettier auto-formatting of existing code (line-wrapping changes, blank lines between CSS rules). The few behavioral changes — adding { cause: e } to two error constructors, adding {#each} keys for svelte/require-each-key, renaming unused variables with _ prefix, and modernizing CSS (clip-path, overflow-wrap, rgb() modern syntax) — are all correct and low-risk. The ESLint config enables good security defaults (svelte/no-at-html-tags: error for XSS prevention, svelte/block-lang to enforce TypeScript). No correctness, security, or injection concerns found.

Findings

Medium

  • [Correctness] web/admin/src/routes/OrgList.sveltescanComplete (renamed _scanComplete) is a $state variable that is written in 7 places but never read anywhere in the template or script logic. This is dead state. The PR correctly silences the lint warning by prefixing with _, but the underlying question is whether this state was intended to drive UI (e.g. a loading indicator) and was accidentally omitted, or is genuinely unnecessary. Consider removing it entirely or wiring it up if it was meant to be used.
    Remediation: Audit whether scanComplete should gate any UI behavior; if not, remove the variable and all its assignments entirely rather than underscore-prefixing.

Low

  • [Correctness] web/admin/src/lib/orgs/fetchOrgs.ts:154 — Similar pattern: appSlug renamed to _appSlug inside the progress callback to suppress unused-var. The final appSlug is correctly captured from the completed fetch result later, so the intermediate one is indeed unused. This is correct but worth a comment explaining why the destructured value is intentionally discarded.

Info

  • [Style] web/admin/src/lib/auth/turnstile.ts:90 — The eslint-disable-next-line preserve-caught-error comment references a rule (preserve-caught-error) that does not appear in the ESLint config in this PR. If this rule doesn't exist, the disable comment is a no-op. This is harmless but may indicate a planned custom rule or a copy-paste artifact. Verify that this rule is actually active.

  • [Style] CSS modernizations (rgba()rgb(), clipclip-path, word-breakoverflow-wrap) are all correct and improve standards compliance. No browser compatibility concerns for the target audience (modern browsers).

  • [Correctness] The two { cause: e } additions in githubClient.ts:37 and orgConfigParse.ts:78 are good improvements that preserve error chains for debugging.

Footer

Outcome: approve
This review applies to SHA 5661747c811403409c02fe1e39e5bec9c33b5f4f. Any push to the PR head clears this review and requires a new evaluation.

Previous run (2)

Review: #625

Head SHA: 613f465
Timestamp: 2026-05-03T00:00:00Z
Outcome: request-changes

Summary

The linting and formatting tooling is well-structured and the Prettier/ESLint/Stylelint configuration is correct. However, introducing Husky v9 into a repo that already uses the Python pre-commit framework creates a hook-path conflict that silently disables security-critical pre-commit hooks (gitleaks, detect-private-key, bandit) on commit. This must be resolved before merge.

Findings

High

  • [Correctness / Platform Security] .husky/pre-commit + package.json ("prepare": "husky") — Husky v9 sets core.hooksPath=.husky/ on npm install, which redirects git from .git/hooks/ to .husky/. The repo already has a .pre-commit-config.yaml with security-relevant hooks (gitleaks, detect-private-key, bandit, actionlint, go-vet, ADR linters). After Husky installs, those hooks no longer run automatically on commit — only npx lint-staged runs. This silently degrades the repo's secret-detection and code-quality guardrails.
    Remediation: Either (a) chain both systems in .husky/pre-commit (e.g., pre-commit run --hook-stage pre-commit && npx lint-staged), (b) add frontend lint-staged as a local hook in .pre-commit-config.yaml and remove Husky entirely, or (c) document the interaction and ensure CI compensates. Option (b) is simplest since the repo already standardises on pre-commit.

Medium

  • [Correctness] web/admin/src/lib/auth/turnstile.ts:90 — The eslint-disable-next-line preserve-caught-error comment references a rule (preserve-caught-error) that does not exist in any of the installed ESLint plugins (@eslint/js, typescript-eslint, eslint-plugin-svelte). ESLint 10 reports unused disable directives at warn level by default, so this should surface as a warning — yet the PR claims 0 warnings. Verify the rule name is correct or remove the disable comment to avoid confusion.
    Remediation: Replace with a valid rule name (e.g., a custom rule planned for Phase 2) or remove the disable comment and add a plain // TODO: instead.

Info

  • [Style] eslint.config.js — The config is placed at the repo root but only lints web/admin/src/. This works correctly due to the ignores block and the files globs, but a comment at the top noting the intended scope (admin SPA only) would help future contributors understand why it lives at the root rather than under web/admin/.

  • [Correctness] web/admin/src/routes/Home.svelte:39word-break: break-word was changed to overflow-wrap: break-word. This is a correct CSS standards improvement (word-break: break-word is a deprecated alias). No action needed; noting for completeness.

Footer

Outcome: request-changes
This review applies to SHA 613f465a10ef7ea8758400ea13f393c2ffaee39b. Any push to the PR head clears this review and requires a new evaluation.

Previous run (3)

Review: #625

Head SHA: 1b9d8c4
Timestamp: 2026-05-03T00:00:00Z
Outcome: approve

Summary

This PR adds ESLint 9, Prettier, and Stylelint tooling for the admin SPA with sensible defaults — including XSS prevention (svelte/no-at-html-tags: error), component size enforcement, and TypeScript enforcement for Svelte script blocks. The source file changes are almost entirely Prettier reformatting with no behavioral changes. The one functional CSS fix (word-break: break-wordoverflow-wrap: break-word in Home.svelte) is correct — the former is a non-standard value. The ESLint configuration is well-structured with proper global ignores, TypeScript parser delegation for Svelte files, and a phased rollout approach (warn-level size rules now, error later). No security, correctness, or intent concerns.

Findings

Medium

(none)

Low

  • [style] web/admin/src/lib/auth/turnstile.ts:90 — The eslint-disable-next-line preserve-caught-error directive references a rule that does not appear in the ESLint configuration or any of the configured plugin presets (@eslint/js, typescript-eslint, eslint-plugin-svelte). This is likely a no-op unused disable directive. If the rule is intended for a future plugin, consider adding a TODO linking to the issue; otherwise, remove the directive or use the correct rule name.

Info

  • [style] PR description states "Phase 2: Add lint steps to CI workflow + pre-commit hooks" but this PR already adds a husky pre-commit hook (npx lint-staged). The phased rollout description may need updating to reflect that pre-commit hooks ship in Phase 1.
  • [correctness] The CSS change from word-break: break-word to overflow-wrap: break-word in Home.svelte is a correct Stylelint-driven fix — break-word is not a valid value for word-break per the CSS spec.

Footer

Outcome: approve
This review applies to SHA 1b9d8c4b47fccdbbefa20ea572785f9fb717eba5. Any push to the PR head clears this review and requires a new evaluation.

Previous run (4)

Review: #625

Head SHA: 616fcb2
Timestamp: 2026-05-03T00:00:00Z
Outcome: approve

Summary

This PR cleanly adds ESLint 9, Prettier, and Stylelint infrastructure to the admin SPA with well-chosen rules and a sensible phased rollout plan. All source file changes are purely Prettier reformatting with no logic modifications — verified by diff inspection. The one substantive CSS change (word-break: break-word to overflow-wrap: break-word) is a standards-compliance improvement. Security-relevant ESLint rules (no-at-html-tags, block-lang, require-each-key) are appropriately set to error, while size/style rules are set to warn for the initial phase. All new dependencies are devDependencies and do not affect production bundles.

Findings

Info

  • [style/conventions] web/admin/src/lib/auth/turnstile.ts:90 — The eslint-disable-next-line preserve-caught-error references a rule that is not visibly configured in eslint.config.js. If no plugin provides this rule, ESLint may report an unused directive warning (depending on reportUnusedDisableDirectives setting). Verify the rule exists or remove the disable comment.

Footer

Outcome: approve
This review applies to SHA 616fcb2806e90fd9fbbd38ea5a76d3a109791982. Any push to the PR head clears this review and requires a new evaluation.

Previous run (5)

Review: #625

Head SHA: 9d2fbb0
Timestamp: 2026-05-03T00:00:00Z
Outcome: comment-only

Summary

This PR adds ESLint 9 (flat config), Prettier, and Stylelint to the admin SPA, along with Prettier-reformatted source files. The changes are well-structured: linting config is sensible (XSS prevention via no-at-html-tags, dead-code detection, component size limits at warn level), and the code modifications are almost entirely mechanical formatting with one minor CSS improvement. Two low-severity observations are noted below but neither blocks merge.

Findings

Medium / Low / Info

  • [correctness] web/admin/src/lib/auth/turnstile.ts:~90 — The eslint-disable-next-line preserve-caught-error comment references a rule name (preserve-caught-error) that does not exist in ESLint core, typescript-eslint, or eslint-plugin-svelte. If reportUnusedDisableDirectives is enabled (or becomes the default in a future ESLint version), this will produce a warning. Consider removing the directive or replacing it with the correct rule name if this was intended to suppress a custom rule not yet configured.
    Severity: low
    Remediation: Verify the intended rule name or remove the disable comment and address the caught-error pattern directly.

  • [style] web/admin/src/routes/Home.svelte — The change from word-break: break-word to overflow-wrap: break-word is a good fix — word-break: break-word is non-standard. Just noting this is a behavioral change (not purely formatting), which is fine.
    Severity: info

Footer

Outcome: comment-only
This review applies to SHA 9d2fbb0dd6fc8746e29447d8f1c95892e303d7ec. Any push to the PR head clears this review and requires a new evaluation.

Copy link
Copy Markdown

@fullsend-ai-review fullsend-ai-review Bot left a comment

Choose a reason for hiding this comment

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

See the review comment for full details.

Copy link
Copy Markdown

@fullsend-ai-review fullsend-ai-review Bot left a comment

Choose a reason for hiding this comment

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

See the review comment for full details.

Copy link
Copy Markdown

@fullsend-ai-review fullsend-ai-review Bot left a comment

Choose a reason for hiding this comment

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

See the review comment for full details.

Copy link
Copy Markdown

@fullsend-ai-review fullsend-ai-review Bot left a comment

Choose a reason for hiding this comment

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

See the review comment for full details.

Copy link
Copy Markdown
Contributor

@ifireball ifireball left a comment

Choose a reason for hiding this comment

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

I'd rather do this after we get #617 merged, to limit the amount of code I have to rebase (it would be huge anyway...)

waynesun09 added 6 commits May 5, 2026 20:50
Add frontend linting scaffolding for the admin SPA (Svelte 5 + Vite 6 + TypeScript):
- ESLint flat config with typescript-eslint, eslint-plugin-svelte, and Prettier compat
- Prettier with prettier-plugin-svelte for consistent code formatting
- Stylelint with stylelint-config-standard and stylelint-config-html/svelte
- npm scripts: lint, lint:fix, format, format:check, stylelint, stylelint:fix

Signed-off-by: Wayne Sun <gsun@redhat.com>
Apply Prettier formatting and Stylelint fixes to all existing admin SPA files.
Add baseline lint disables for pre-existing violations:
- App.svelte: max-lines (TODO: extract components)
- turnstile.ts: preserve-caught-error (TODO: attach cause)
- Home.svelte: replace deprecated word-break with overflow-wrap

Signed-off-by: Wayne Sun <gsun@redhat.com>
- Move ignores block to first position in ESLint flat config array
  per ESLint 9 convention (prevents accidental non-global ignore if
  a files key is later added to the same object)
- Spread ts.configs.recommended (it's an array of 3 config objects)
  instead of relying on defineConfig to flatten nested arrays
- Remove no-op color-no-hex: null from stylelint config (rule does
  not exist in stylelint-config-standard)

Signed-off-by: Wayne Sun <gsun@redhat.com>
Run ESLint, Prettier, and Stylelint on staged files before each
commit. Follows the same pattern as openkaiden/kaiden: Husky v9
with lint-staged, scoped to web/admin/src files only.

Signed-off-by: Wayne Sun <gsun@redhat.com>
- Split lint-staged: Svelte files now get ESLint + Stylelint + Prettier
  (previously only ESLint + Prettier, missing scoped style linting)
- Mark .husky/pre-commit as executable (100755) for hygiene

Signed-off-by: Wayne Sun <gsun@redhat.com>
- Attach cause to re-thrown errors in githubClient and orgConfigParse
- Prefix unused appSlug and scanComplete vars with underscore
- Add each-block keys in OrgList popover lists
- Suppress require-yield in test generators that intentionally throw
- Replace deprecated clip with clip-path in sr-only
- Replace deprecated word-break: break-word with overflow-wrap

Signed-off-by: Wayne Sun <gsun@redhat.com>
@waynesun09 waynesun09 force-pushed the add-frontend-linting branch from 613f465 to 5661747 Compare May 6, 2026 00:56
Copy link
Copy Markdown

@fullsend-ai-review fullsend-ai-review Bot left a comment

Choose a reason for hiding this comment

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

See the review comment for full details.

The variable was written in 7 locations but never read, creating
unnecessary reactive tracking overhead in Svelte 5.

Signed-off-by: Wayne Sun <gsun@redhat.com>
Copy link
Copy Markdown

@fullsend-ai-review fullsend-ai-review Bot left a comment

Choose a reason for hiding this comment

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

See the review comment for full details.

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