Skip to content

fix(english/mvlempyr): return contract-conformant novel items - #2508

Merged
rajarsheechatterjee merged 1 commit into
lnreader:masterfrom
ceifeirocv:fix/mvlempyr-novelitem-contract
Sep 8, 2026
Merged

rajarsheechatterjee merged 1 commit into
lnreader:masterfrom
ceifeirocv:fix/mvlempyr-novelitem-contract

Conversation

@ceifeirocv

Copy link
Copy Markdown
Contributor

Problem

Opening any MVLEMPYR novel from browse or search crashes the novel screen on LNReader v2.1.0 (the current Latest release) with:

TypeError: undefined is not a function
  at anonymous
  at renderWithHooks
  at updateSimpleMemoComponent
  at updateMemoComponent

Refs #2437 (same versions: app 2.1.0, plugin 1.0.12).

Root cause

popularNovels and searchNovels return the raw cached objects from getAllNovels() straight through paginate(), so the internal ExtraNovelData fields ride along on what is typed as a Plugin.NovelItem:

{ "name": "...", "path": "...", "cover": "...",
  "genres": ["Horror", "Mature"], "tags": [...], "avgReview": 5, "created": 1745549652956 }

NovelItem is {name, path, cover?}, and genres is only valid on SourceNovel, where it is documented as a comma-separated string. parseNovel returns it correctly as a string; the browse/search list did not.

Before app v2.1.2, NovelGenres called genres.split(/,\s*/) inside a useMemo without validating the type. A novel opened from browse renders that header from the list item — with the array — before parseNovel resolves. Array.prototype.split does not exist, so calling it throws undefined is not a function, which matches the reported stack exactly.

App-side this was guarded in LNReader eb12bdf ("Prevent Novel Genre Rendering Crashes", shipped in v2.1.2), but v2.1.2 and v2.1.3 are both pre-releases — everyone on stable v2.1.0 still hits it. Plugins update independently of the app, so fixing it here reaches those users without an app update.

Fix

Map both return sites down to {name, path, cover}. Filtering and sorting still run on the full objects beforehand, and getAllNovels()'s cache is untouched, so the filter/order behaviour is unchanged.

Version bumped 1.0.121.0.13 (patch — compatibility fix).

Verification

  • npm run check:plugin -- plugins/english/mvlempyr.ts → 4/4 PASS (popularNovels 20 novels, searchNovels 6 results, parseNovel 1432 chapters, parseChapter 9044 chars)
  • npx eslint clean, npx tsc --noEmit --project tsconfig.production.json clean
  • Asserted against the live site that popularNovels and searchNovels now return exactly {cover, name, path}, and that parseNovel().genres is still a string ("Horror,Mature,Mystery,Psychological,Supernatural")

Checklist

  • Update version code if an existing plugin was modified
  • Test changes in Plugin Playground or the app
  • Reference related issues in the PR body (e.g. Closes #xyz)
  • Commit messages follow type(scope): description

Note

Per AGENTS.md, disclosing that this change was prepared with AI assistance so reviewers can weight their review accordingly.

popularNovels and searchNovels returned the raw cached objects from
getAllNovels(), leaking the internal ExtraNovelData fields. That put
genres on a NovelItem as a string[], while the contract types it as a
comma-separated string on SourceNovel only.

Older app versions called genres.split() unguarded when rendering a
novel opened from browse, so the array crashed the novel screen with
"undefined is not a function". Map both return sites down to
{name, path, cover}; filtering and sorting still run on the full
objects beforehand.

Refs lnreader#2437
@rajarsheechatterjee
rajarsheechatterjee merged commit 88dfd94 into lnreader:master Sep 8, 2026
3 checks passed
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Sep 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants