Skip to content

Reconfigure ESLint to work well with prettier#957

Merged
FyreByrd merged 27 commits into
mainfrom
feature/integrate-eslint
May 13, 2026
Merged

Reconfigure ESLint to work well with prettier#957
FyreByrd merged 27 commits into
mainfrom
feature/integrate-eslint

Conversation

@FyreByrd
Copy link
Copy Markdown
Collaborator

@FyreByrd FyreByrd commented May 13, 2026

Rebase of #869 to resolve merge conflicts

PR comment from #869:
Per #864.
This pull-request includes several related changes:

  1. Reconfigure ESLint using a modern configuration and good rules for svelte.
    These rules include svelte and typescript extensions,
    as well as an integration to disable rules that conflict with prettier.
    All configuration is now consolidated in eslint.config.js, eliminating .eslintrc.cjs.
  2. Disable noisy rules. A number of rules are loud/unnecessary.
    I've turned off no-dupe-style-properties, require-each-key, and no-unused-svelte-ignore because they are spurious for our codebase.
    I've also disabled the typescript no-require-imports for the tailwind configuration file, which uses require style imports.
  3. Correct low-level eslint warnings.
    While I haven't fixed some of the more in-depth issues, every trivial issue should be corrected in this series of commits.
    This includes a number of empty objects, missing blocks around case statements, and a hundred or so conversions from var and let to const.
    As best as I can tell no behavior changes were introduced here.
    I am a bit unsure about 8a67ec3, which changes the types on the CatalogData interface to object from the empty-object type. This could cause an issue if empty-object behavior was intended, but that seems unlikely.

As it stands, the linting tests will fail because of seven unresolved errors.
Each of these is more in-depth than I want to do in a short commit, and will receive a separate PR after some discussion about the best way to resolve them.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed font selection styling that was not applying due to an attribute typo.
  • Code Quality & Improvements

    • Enhanced error handling and defensive checks across data validation and conversions.
    • Improved type safety with better TypeScript typing and null/undefined guards.
    • Upgraded linting configuration for stricter code standards and consistency.
    • Updated development dependencies for improved tooling support.

FyreByrd and others added 26 commits May 13, 2026 09:42
Add a proper eslint configuration using extensions for svelte,
typescript, and prettier.

This should ensure eslint does not mess with prettier's settings while
still enabling high-quality linting.

Remove the outdated .eslintrc.cjs in favor of eslint.config.js.
Mainly converting let to const, and var to let.
This change corrects video/index.ts to comply with
`no-case-declarations`.
`getEmbeddedVideoUrl` was using `let` and `const` declarations inside
cases without an appropriate block scope on each case.
As a result of this sloppiness, reused variable names were falling
through suspiciously.

This change adds a block scope to each case, and makes any local
`pattern` and `match` variables const.
Satisfy eslint by breaking up the expect on bkk.chapters in
convert/tests/sab/convertConfigSAB.test.ts to a separate 0-check and undefined-check.
Jest also notes that using toBeDefined() is better than a raw undefined
check.

https://jestjs.io/docs/expect#tobedefined
Tell eslint to ignore the require import rule for tailwind's
configuration.
Require seems to be a standard import mechanism for tailwind:  https://v2.tailwindcss.com/docs/configuration#plugins
Function wasn't being called, lol.
`dab-search-worker.ts`'s `searchDictionary()` did not assign to
`results` after it was set. Declare `results` as `const`.
Instead of calling hasOwnProperty off of an object directly, use
`Object.property` to make the call.
This avoids potential subtle problems, explained in this link:
https://eslint.org/docs/latest/rules/no-prototype-builtins#rule-details
Doesn't like empty blocks, as they may be confusing.
Replace empty object types (`{}`) with `object`.
This prevents random values from being inserted into the interface.

Could be a BREAKING CHANGE!
Other OS's had blocks, not sure why MacOS missed out.
BottomNavigationBar's `handleclick()` used a `let` binding inside a
case. Refactor to add a block around the case.
Sidebar was using a bash-style `and` shortcut instead of a proper `if`.
Fix it.
Don't warn about missing keys in each-blocks.
Remove brackets surrounding literal values.
This rule triggers a warning on statements that eslint does not use, but
the svelte LSP does use.
Several `goto` statements are used without reference to `{base}` because
they use a parameter instead of a literal url.
Silence `eslint` warnings on those in particular.
A link element attempted to set the font-family style attribute, but
misspelled it as "font-famly".

Correct spelling.
Add the `curly` rule to enforce use of braces in if-statements.
Then run `eslint . --fix` to correct all violations, and `prettier
--write .` to reformat.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Warning

Rate limit exceeded

@FyreByrd has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 42 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 13110c0c-1372-4438-a3fe-782ce70d3b4d

📥 Commits

Reviewing files that changed from the base of the PR and between 56830fb and 659779f.

📒 Files selected for processing (1)
  • eslint.config.js
📝 Walkthrough

Walkthrough

ESLint configuration migrated to flat config with TypeScript/Svelte/Prettier support; package dependencies updated for Svelte 5 compatibility. Svelte store exports converted to $state() API. Extensive code formatting: single-line conditionals reformatted to multi-line blocks, let declarations changed to const. ESLint suppressions added for raw HTML and navigation patterns; one font-family style attribute typo fixed.

Changes

ESLint Configuration & Tooling Migration

Layer / File(s) Summary
Flat config adoption and dependency updates
eslint.config.js, .eslintrc.cjs, package.json
ESLint migrated from CommonJS config (.eslintrc.cjs, removed) to flat config format (eslint.config.js using ts.config(...)), composing TypeScript ESLint, Svelte, and Prettier presets. Svelte-specific overrides enable parser options (projectService, extraFileExtensions, svelteConfig) and adjust rule sets. Dev dependencies updated: @typescript-eslint/eslint-plugin, @typescript-eslint/parser, eslint-plugin-svelte, @sveltejs/vite-plugin-svelte, and pinned svelte to ^5.55.5; typescript-eslint added at ^8.36.0.

Svelte 5 Store Reactivity API

Layer / File(s) Summary
Store exports migrated to $state() API
src/lib/data/stores/lexicon.svelte.ts
Six exported Svelte store declarations converted from export let ... to export const ... = $state(...): vernacularLanguageId, displayNames, vernacularWords, reversals, selectedWord, wordIDs. Preserves type shapes and initialization; updates store export semantics for Svelte 5.
Store selector formatting
src/lib/data/stores/scripture.js, src/lib/data/stores/store-types.js, src/lib/data/stores/theme.js, src/routes/contents/[id]/+page.svelte
Derived store selectors refactored to multi-line conditional formatting. hasOwnProperty calls replaced with Object.prototype.hasOwnProperty.call(...) for safety. referenceTexts cache changed from Map() to SvelteMap() in contents page.

Control Flow Formatting & Code Quality

Layer / File(s) Summary
Single-line to multi-line conditional restructuring
convert/convert*.ts, src/lib/components/*.svelte, src/lib/data/*.ts, src/lib/scripts/*.js, src/routes/**/*.svelte
Approximately 50+ files: single-line if statements (e.g., if (cond) return; or if (cond) statement;) converted to explicit multi-line blocks with braces. Early-return guards, callback invocations, and error handlers reformatted for readability. Behavioral logic unchanged; improves ESLint compliance and code clarity.
Variable declaration safety
convert/*.ts, src/lib/**/*.ts, src/lib/**/*.js
Loop variables, local bindings, and temporary values changed from let to const where assignments do not occur post-initialization (e.g., for (const item of array), const db = await open(), destructuring assignments). Type signatures and return values unchanged.

ESLint Directive & Lint Rule Management

Layer / File(s) Summary
Raw HTML & navigation suppressions
src/lib/components/*.svelte, src/routes/**/*.svelte, src/lib/navigate/index.ts, src/lib/search-worker/data/repositories/pk-search-repository-impl.ts
ESLint eslint-disable-next-line comments added for svelte/no-at-html-tags (around {@html ...} usages), svelte/no-navigation-without-base (around goto(...) calls), svelte/no-store-async (async derived store), and svelte/no-dom-manipulating. Suppresses known linting conflicts without altering runtime behavior. Removed one @typescript-eslint/no-unused-vars directive from src/app.d.ts.
Converter module logging & defensive checks
convert/convert*.ts, convert/fileUtils.ts, convert/index.ts
Verbose logging wrapped in explicit if (verbose) { console.log(...) } blocks (instead of single-line forms). Error handlers expanded (e.g., if (err) { throw err; } blocks). Defensive null/undefined checks added: parseBookCollections skips entries lacking audio/f nodes; parseAudioSources skips missing filename; parseFeatures rethrows non-ReferenceError exceptions.

Type Corrections & Minor Logic Fixes

Layer / File(s) Summary
Type & style corrections
src/lib/components/FontList.svelte, src/lib/data/analytics.ts, src/lib/data/catalogData.ts, src/lib/data/audio.ts, src/lib/search/data/test/search-config-repository-impl.test.ts, src/lib/utils/worker-messenger/messenger.test.ts
Fixed typo: style:font-famlystyle:font-family in FontList. Signature updates: logShareContent parameter types changed from boxed String to primitive string. CatalogData interface: sequences retyped to object[], tags/quizzes to object. Test assertions corrected: matcher invocations fixed (.toBeDefined() instead of .toBeDefined). NaN handling in audio formatting returns '0:00'.
Svelte component adaptations
src/lib/components/*.svelte, src/routes/lexicon/+layout.svelte, src/routes/text/+page.svelte
TextAppearanceSelector: _showFontSize and _showThemes changed from reactive ($:) to non-reactive const declarations. +layout.svelte: props destructuring updated to include data field. text/+page.svelte: chevron rotation/color logic refactored for directional display. findVerseElement fallback checks for data-verse attribute existence before range matching.

Sequence Diagram(s)

No sequence diagrams required: changes are primarily configuration updates, code formatting, and type/safety fixes without introducing new multi-component interactions or control flow.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Rationale: Large PR (100+ files, 400+ lines changed) with heterogeneous modifications spanning ESLint infrastructure, Svelte 5 API migration, widespread formatting changes, and type corrections. While most changes are straightforward formatting or declarative updates, the sheer volume and variety—combined with the critical ESLint config rewrite and store API migration—demand careful review of config correctness, store reactivity semantics, and type signature consistency across consumers.

Possibly related PRs

  • sillsdev/appbuilder-pwa#936: Introduces nested content-item rendering components that overlap with main PR's component formatting and adaptations in src/routes/contents/[id]/+page.svelte.
  • sillsdev/appbuilder-pwa#914: Larger Svelte 5/TypeScript rewrite of src/lib/lexicon/components/EntryView.svelte shares code-level overlaps with main PR's guard/formatting tweaks in the same file.
  • sillsdev/appbuilder-pwa#915: Modifies the same src/lib/data/stores/lexicon.svelte.ts exported store declarations; main PR converts stores to $state() API while PR #915 performs broader renaming/logic changes.

Suggested reviewers

  • chrisvire

Poem

🐰 Hop, hop—the configs align!
Flat ESLint paths now shine.
Guards grow multi-line, let becomes const,
Svelte's $state() flows—as it must.
Safe types and suppressions in place,
The rabbit's refactor sets a new pace! 🎉

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/integrate-eslint

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@convert/convertAbout.ts`:
- Around line 14-21: convertAbout currently starts an asynchronous copy via
copyFile with a callback and returns immediately; change convertAbout to be
async and return/await a Promise that resolves when copyFile completes (or
rejects on error) so callers (e.g., ConvertAbout.run) can await completion and
catch errors. Replace the callback-style copyFile(srcFile, dstFile, ...) with a
Promise wrapper (or use fs.promises.copyFile) that rejects on err and resolves
after the verbose log, update convertAbout's signature to async, and ensure any
caller (ConvertAbout.run) awaits the async convertAbout so the step doesn't
succeed before the file copy finishes.

In `@convert/convertBadges.ts`:
- Around line 32-39: The callback-based copyFile call in convertBadges.ts should
be replaced with an awaited promise so the conversion flow waits for the copy
and errors propagate through the async path; replace the copyFile(..., callback)
usage inside the conversion function (where srcFile/dstFile and verbose are in
scope) with an awaited fs.promises.copyFile(srcFile, dstFile) (or the
promisified equivalent) and wrap it in try/catch or let the promise rejection
bubble so you can log the successful copy when verbose and correctly surface
errors instead of throwing inside a callback.

In `@convert/convertBooks.ts`:
- Around line 801-803: Replace the thrown error inside the fs.readFile callback
with rejecting the surrounding Promise so the error propagates into the Promise
chain (and therefore into Promise.all(docs)); specifically, in the fs.readFile
callback shown (currently doing "if (err) { throw err; }") call the Promise
reject function instead (e.g., reject(err)) so functions like the Promise
executor that produce the docs array and the Promise.all(docs) at line 531
receive the rejection rather than causing an uncaught exception.

In `@convert/convertConfig.ts`:
- Around line 592-594: The code currently silently skips malformed XML nodes by
using checks like "if (!audioTag) { continue; }" (and similar checks at the
other referenced spots), which hides source-data defects; instead change to
fail-fast by asserting/throwing when required attributes/nodes are missing:
remove the continue guards and replace them with a clear runtime failure (e.g.,
throw new Error or call a small assert helper) that includes identifying context
(page id, node name, or filename) so conversion stops and the invalid XML is
reported; update the locations around the "audioTag" check and the analogous
audio-file filename checks (and the occurrences around lines 599-601 and
1177-1179) to use these non-null assertions/throws rather than continuing.

In `@convert/convertContents.ts`:
- Around line 74-76: Several guards in convert/convertContents.ts (e.g., checks
like "if (tag === undefined) { return false; }" around the variable "tag" and
similar guards at the listed ranges) silently return benign defaults and should
instead fail fast; replace these silent fallbacks by removing the early-return
and either use non-null assertions (e.g., refer to tag! when accessing required
properties) or throw a clear Error indicating the missing XML element/attribute
(include context such as the expected tag name) so parsing fails loudly; update
all occurrences you noted (lines ~74-76, 97-99, 134-136, 149-151, 164-166,
188-190, 208-210, 238-240, 270-272, 317-319, 331-333, 342-344) to follow this
fail-fast pattern and ensure any downstream code assumes required values are
present.

In `@convert/convertSQLite.ts`:
- Around line 18-25: The callback-based copyFile call (using
copyFile(srcFileWasm, dstFileWasm, function (err) { ... })) creates an async
error-propagation gap; replace it with the promise-based API (import from
fs/promises and await copyFile(srcFileWasm, dstFileWasm)) so the conversion step
runner can deterministically observe success/failure, and either let exceptions
propagate or catch and rethrow/log via the surrounding async function; apply the
same change to the other callback-based copyFile block referenced (the one
around lines 29-36) and preserve the verbose console.log message after the
awaited copy completes.

In `@src/lib/data/scripture.js`:
- Around line 85-87: The catch block that currently reads "catch (e) { // Ignore
errors }" must not swallow errors silently: preserve the non-throwing behavior
but emit a contextual warning and include the error details (e.message or the
error object) so missing/corrupt PKF or fetch failures are visible; update the
catch in the docset loading code (the block catching e) to call your logger
(e.g., processLogger.warn or console.warn) with a clear message like "Failed to
load docset <docsetName> or PKF" and the error, and optionally record a
metric/event for failure reporting.

In `@src/lib/lexicon/components/EntryView.svelte`:
- Around line 294-296: The HTML injection currently renders raw xmlData via
{`@html` xmlData} and createElementString preserves all attributes (including
event handlers like onclick/onerror), so harden by sanitizing the output: either
filter attributes inside createElementString to drop any attributes starting
with "on", any style="expression(...)" or javascript: URLs (href/src), or run
the reconstructed string through a sanitizer such as DOMPurify.sanitize(xmlData)
before it's passed to {`@html`}; update createElementString (and any callers) to
perform attribute filtering if you prefer defensive server-side generation, or
import DOMPurify and sanitize at the point where xmlData is assigned to ensure
no executable attributes reach the {`@html`} injection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a95f9d4f-5ba3-4d08-b991-224aeed689bb

📥 Commits

Reviewing files that changed from the base of the PR and between 62aa25a and 56830fb.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (73)
  • .eslintrc.cjs
  • convert/convertAbout.ts
  • convert/convertBadges.ts
  • convert/convertBooks.ts
  • convert/convertConfig.ts
  • convert/convertContents.ts
  • convert/convertFirebase.ts
  • convert/convertMedia.ts
  • convert/convertReverseIndex.ts
  • convert/convertSQLite.ts
  • convert/convertStyles.ts
  • convert/fileUtils.ts
  • convert/index.ts
  • convert/tests/sab/convertConfigSAB.test.ts
  • convert/tests/sab/convertContentSAB.test.ts
  • eslint.config.js
  • example/index.ts
  • package.json
  • src/app.d.ts
  • src/lib/components/AudioBar.svelte
  • src/lib/components/BottomNavigationBar.svelte
  • src/lib/components/ContentCarousel.svelte
  • src/lib/components/ContentHeading.svelte
  • src/lib/components/ContentSingle.svelte
  • src/lib/components/FontList.svelte
  • src/lib/components/HistoryCard.svelte
  • src/lib/components/HorizontalPanes.svelte
  • src/lib/components/HtmlBookView.svelte
  • src/lib/components/ScriptureViewSofria.svelte
  • src/lib/components/SearchForm.svelte
  • src/lib/components/Sidebar.svelte
  • src/lib/components/StackView.svelte
  • src/lib/components/TabsMenu.svelte
  • src/lib/components/TextAppearanceSelector.svelte
  • src/lib/components/VerticalPanes.svelte
  • src/lib/data/analytics.ts
  • src/lib/data/annotation-share.ts
  • src/lib/data/audio.ts
  • src/lib/data/catalogData.ts
  • src/lib/data/highlights.ts
  • src/lib/data/history.ts
  • src/lib/data/language.ts
  • src/lib/data/mrucache.ts
  • src/lib/data/navigation.test.ts
  • src/lib/data/scripture.js
  • src/lib/data/stores/collection.ts
  • src/lib/data/stores/lexicon.svelte.ts
  • src/lib/data/stores/scripture.js
  • src/lib/data/stores/store-types.js
  • src/lib/data/stores/theme.js
  • src/lib/lexicon/components/EntryView.svelte
  • src/lib/navigate/index.ts
  • src/lib/scripts/query.js
  • src/lib/scripts/render.js
  • src/lib/search-worker/dab-search-worker.ts
  • src/lib/search-worker/data/pk-verse-provider.ts
  • src/lib/search-worker/data/repositories/pk-search-repository-impl.ts
  • src/lib/search-worker/domain/test/search-session-internal.test.ts
  • src/lib/search/data/test/search-config-repository-impl.test.ts
  • src/lib/search/domain/search-query-manager-impl.ts
  • src/lib/utils/worker-messenger/messenger.test.ts
  • src/lib/video/index.ts
  • src/routes/about/+page.svelte
  • src/routes/contents/[id]/+page.svelte
  • src/routes/dev/icons/+page.svelte
  • src/routes/lexicon/+layout.svelte
  • src/routes/lexicon/+layout.ts
  • src/routes/lexicon/+page.svelte
  • src/routes/plans/[id]/+page.svelte
  • src/routes/quiz/[collection]/[id]/+page.svelte
  • src/routes/search/[collection]/[[savedResults]]/+page.ts
  • src/routes/text/+page.svelte
  • src/service-worker.js
💤 Files with no reviewable changes (2)
  • src/app.d.ts
  • .eslintrc.cjs

Comment thread convert/convertAbout.ts
Comment thread convert/convertBadges.ts
Comment thread convert/convertBooks.ts
Comment thread convert/convertConfig.ts
Comment thread convert/convertContents.ts
Comment thread convert/convertSQLite.ts
Comment thread src/lib/data/scripture.js
Comment thread src/lib/lexicon/components/EntryView.svelte
Copy link
Copy Markdown
Collaborator Author

@FyreByrd FyreByrd left a comment

Choose a reason for hiding this comment

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

I'm not seeing the seven remaining errors that are referred to. There aren't really any substantive changes between this and the original PR. This is mostly a rebase to resolve merge conflicts. It won't let me approve it myself, since I am now technically the PR author.

@FyreByrd FyreByrd merged commit 367ad79 into main May 13, 2026
4 checks passed
@FyreByrd FyreByrd deleted the feature/integrate-eslint branch May 13, 2026 15:37
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