Skip to content

fix: resolve Obsidian plugin-review findings (policy, guidelines, lockfile)#219

Merged
chhoumann merged 12 commits into
masterfrom
chhoumann/fix-plugin-review
Jun 23, 2026
Merged

fix: resolve Obsidian plugin-review findings (policy, guidelines, lockfile)#219
chhoumann merged 12 commits into
masterfrom
chhoumann/fix-plugin-review

Conversation

@chhoumann

@chhoumann chhoumann commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary

Resolves the actionable findings from the Obsidian community plugin-review dashboard. Each finding was validated against the current code (the review was captured at 76e830f, before the recent download-streaming and architecture refactors landed), then fixed with the implementation that best fits the codebase — not a mechanical rule-silencing pass. Behavior is preserved; the changes are validated by the full gate suite (lint, typecheck, format, build, 754 unit tests) and by live runtime checks in an isolated Obsidian vault.

To keep these fixes from regressing, the relevant eslint-plugin-obsidianmd guideline rules are wired into the lint config (the project lint previously didn't enforce them — the dashboard ran its own ruleset).

Per-finding breakdown

Policy errors (fixed)

  • Plugin disables/re-enables itself — removed the "Reload PodNotes" command (it called app.plugins.disablePlugin/enablePlugin) and its global.d.ts type declarations.
  • Out-of-date bun lockfile — removed the stale tracked bun.lock; the repo is npm-only (AGENTS.md), so the review's build verification now installs via npm against an in-sync package-lock.json.
  • Build-verification dependency install failed — resolved by the lockfile change above (npm + synced package-lock.json).

Guideline warnings (fixed)

  • Global app object → use the plugin's app reference: feature modules read get(plugin).app; leaf utilities take an explicit Vault/App (e.g. createMediaUrlObjectFromFilePath(vault, …), ensureFolderExists default).
  • Command name repeats plugin name → "Show PodNotes" renamed to "Show player" (the other flagged command, "Reload PodNotes", was removed).
  • Unawaited promises / promise-returned-where-void-expectedvoid-ed fire-and-forget calls; pickFile's callback now accepts void | Promise<void>.
  • Direct style assignmentel.setCssStyles({…}) in the settings tab and download notice.
  • setTimeout/clearTimeout/setInterval/clearIntervalwindow.* for popout-window compatibility.
  • fetch → Obsidian requestUrl (the extension-probe HEAD request).
  • globalThiswindow (media-session lookup).
  • Detach leaves in onunload → removed; the single-instance player view is instead de-duplicated on layout-change, which preserves a leaf the user moved (verified: a cold-restart leaf is kept, a hot-reload duplicate collapses back to one).
  • localStorage → vault-scoped App#loadLocalStorage/saveLocalStorage (with a window.localStorage fallback and legacy-key purge).
  • Manual HTML headingsnew Setting(el).setName(…).setHeading().
  • documentactiveDocument.
  • eslint-plugin-importeslint-plugin-import-x (pinned 4.16.1 for peer compatibility).
  • Duplicated IconName union (×580)IconType and ICON_LIST now derive from one deduplicated source array (all 602 names preserved).

Recommendations

  • Artifact attestations (fixed) — added an actions/attest-build-provenance step (with id-token/attestations write permissions) for main.js + manifest.json in the release workflow.
  • Clipboard access (no change needed) — the only clipboard write is the explicit, user-invoked "Copy universal episode link to clipboard" command, with a graceful fallback when the clipboard API is unavailable. The behavior is already clearly user-initiated; no remediation applies.

Rejected / deferred (with rationale)

  • 'error' type that acts as 'any' (rejected — not reproducible) — does not fire on the current code; the landed refactor already narrowed those catch clauses to unknown.
  • Command IDs containing the plugin id (rejected — out of scope) — the dashboard flagged only command names. Changing command IDs would break users' existing hotkeys and the public API, so IDs are intentionally left unchanged.
  • Dependency vulnerability advisories (deferred — not shipped) — all flagged packages are dev/build tooling (the production tree's only flagged transitive, ws, is tree-shaken out of main.js). Resolving them requires major build-toolchain upgrades that are out of scope for a review-remediation PR and risk destabilizing the toolchain; the shipped artifact is unaffected.

Testing

  • npm run lint, npm run typecheck, npm run format:check, npm run build, npm run test (754 passing) — all green.
  • Live runtime checks in an isolated Obsidian vault: plugin loads, the reload command is gone, "Show player" is correctly named, the view opens (single leaf), a plugin disable/enable cycle leaves no duplicate leaf and logs no errors.
  • npm run docs:build was not run locally (MkDocs isn't installed in this environment); the docs change is a heading rename + section removal in commands.md.

Do not merge — opening for maintainer review.


Open in Devin Review

chhoumann added 10 commits June 23, 2026 23:40
The "Reload PodNotes" command disabled and re-enabled the plugin via
app.plugins.disablePlugin/enablePlugin. Programmatically disabling and
re-enabling a plugin is disallowed by Obsidian developer policy (it can run
newly downloaded code without user awareness), so the command and its
supporting global.d.ts type declarations are removed.

Also rename the "Show PodNotes" command to "Show player" (Obsidian already
shows the plugin name beside the command) and stop leaving the
getUniversalPodcastLink promise unhandled.
The vendored lucide list was duplicated, producing ~580 "union type
constituent is duplicated" warnings. Derive both ICON_LIST (a deduplicated
`as const` array) and the IconType union from a single source of truth so no
name repeats; the full set of 602 icon names is preserved.
Replace the deprecated implicit global `app` with the plugin instance's own
app reference. Feature modules that already depend on the plugin store
(createPodcastNote, createFeedNote, URIHandler) read `get(plugin).app`; the
leaf utilities take the dependency explicitly — createMediaUrlObjectFromFilePath
now receives a Vault, and ensureFolderExists defaults its vault to the plugin
app's. getContextMenuHandler passes its own app through. Tests inject the app
via the plugin store / parameters instead of a global.
Replace manually created HTML heading elements with
Setting().setName().setHeading(), direct element.style writes with
setCssStyles(), and `document` with `activeDocument` for popout-window
compatibility. Settle the fire-and-forget saveSettings()/exportOPML/render
calls with `void`, and widen pickFile's callback to accept an async handler
(no misused promise). Adds jsdom polyfills for setCssStyles and
activeWindow/activeDocument so component tests exercise the same paths.
Switch the extension-probe HEAD request from `fetch` to Obsidian's
`requestUrl` (the sanctioned network primitive), reading the content-type
header case-insensitively and keeping the mp3 fallback. Resolve the vault from
the plugin app reference instead of the global `app`, use window.setTimeout,
and set the download-notice styles via setCssStyles.
onunload no longer detaches the view's leaves: detaching there reset a leaf the
user had moved back to its default location on the next load. To avoid the
duplicate that a hot reload would otherwise leave (the restored leaf plus a
freshly auto-opened one), collapse the single-instance player back to one leaf
on layout-change (a no-op unless more than one exists, so it converges and
preserves the restored leaf on a cold start). Also use window.* timers and
window.navigator instead of globalThis, and void the modal's unmount().
Prefix setTimeout/clearTimeout/setInterval/clearInterval with window. so timers
resolve against the correct (possibly popped-out) window.
Prefer App#loadLocalStorage/App#saveLocalStorage (namespaced per vault by
Obsidian) over the global localStorage, so one vault's feed cache can't leak
into another, with a window.localStorage fallback for non-plugin contexts.
Legacy un-prefixed v1-v4 blobs are purged from the raw localStorage on load.
Add an actions/attest-build-provenance step (with id-token/attestations write
permissions) so main.js and manifest.json carry verifiable build provenance.
…delines

Swap the unmaintained eslint-plugin-import for eslint-plugin-import-x (pinned to
4.16.1 for @typescript-eslint/utils peer compatibility), per the Obsidian
review's module-replacement recommendation. Wire eslint-plugin-obsidianmd's
guideline rules (no global app/fetch/localStorage/globalThis, window timers,
setCssStyles, setHeading, no onunload leaf detach, active document, no plugin
name in command name) into the lint config as a regression guard for the fixes
in this PR. Remove the stale bun.lock so the repo (and the plugin-review build
verification) installs via npm against the in-sync package-lock.json.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploying podnotes with  Cloudflare Pages  Cloudflare Pages

Latest commit: 534ed93
Status: ✅  Deploy successful!
Preview URL: https://ef337203.podnotes.pages.dev
Branch Preview URL: https://chhoumann-fix-plugin-review.podnotes.pages.dev

View logs

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 890f147530

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread package.json
Comment on lines +42 to +43
"eslint-plugin-import-x": "4.16.1",
"eslint-plugin-obsidianmd": "^0.3.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update the npm lockfile for the new ESLint plugins

These new dev dependencies are required by eslint.config.mjs, but package-lock.json was not updated and still has neither the root devDependency entries nor node_modules/eslint-plugin-import-x / node_modules/eslint-plugin-obsidianmd. Any clean lockfile-based install, such as npm ci, will not be able to reproduce this dependency graph and linting will fail because the config imports packages that are absent from the committed lockfile. Please regenerate and commit package-lock.json with these dependency changes.

Useful? React with 👍 / 👎.

The removed "Reload PodNotes" (hrpn) command was the readiness sentinel for
plugin.reload(); without it every E2E reload would poll a command that never
appears and time out (30s). Point it at the always-registered
"podnotes-show-leaf" command instead.
Add coverage for the production paths the review flagged as untested: the
vault-scoped App#load/saveLocalStorage adapter (previously every test fell back
to window.localStorage), and dedupePlayerLeaves' convergence invariant (keep the
first leaf, detach the rest, no-op at <= 1 so a cold-restart leaf is preserved).

@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: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@chhoumann chhoumann merged commit b647b40 into master Jun 23, 2026
2 checks passed
github-actions Bot pushed a commit that referenced this pull request Jun 24, 2026
## [2.17.1](2.17.0...2.17.1) (2026-06-24)

### Bug Fixes

* **download:** stream episode downloads to disk to avoid iOS OOM ([#113](#113)) ([fa0815a](fa0815a))
* resolve Obsidian plugin-review findings ([#219](#219)) ([b647b40](b647b40))
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.17.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant