mirror of
https://github.com/wiseguru/ReWrite-Voice-Notes.git
synced 2026-07-22 07:49:19 +00:00
Lint: mirror the Obsidian review bot to catch what slipped past 1.2.0
Local npm run lint now enforces the three classes the community-review bot caught only at submission: - type-checked @typescript-eslint rules (no-deprecated, no-unsafe-*, no-unnecessary-type-assertion) scoped to src/ - no-unsupported-api (Obsidian API newer than manifest minAppVersion), cherry-picked from a 0.4.1 alias since the pinned 0.1.9 base lacks it - noInlineConfig, so an eslint-disable can no longer silence a rule Base plugin stays pinned at 0.1.9 (0.4.x's sentence-case diverges from the bot); only the single no-unsupported-api rule is taken from the alias. Docs: CLAUDE.md, RELEASING.md, release-checklist SKILL.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f94856eae3
commit
437aa5e050
6 changed files with 162 additions and 13 deletions
|
|
@ -19,7 +19,7 @@ npm run review # advisory local code review of the working-tree diff
|
|||
npm run review:docs # advisory local doc-consistency review
|
||||
```
|
||||
|
||||
- `build`/`lint`/`test` are release blockers if they fail.
|
||||
- `build`/`lint`/`test` are release blockers if they fail. `npm run lint` is configured to mirror the Obsidian community-review bot for the classes that previously slipped through to submission: the type-checked `@typescript-eslint` rules (`no-deprecated` / `no-unsafe-*` / `no-unnecessary-type-assertion`), `no-unsupported-api` (an Obsidian API newer than `manifest.json`'s `minAppVersion`), and `noInlineConfig` (an `eslint-disable` can no longer silence a rule). If lint fails on one of these, it is doing its job; do not work around it with a disable comment. See RELEASING.md's guideline-conflict checklist.
|
||||
- The two `review` runs are **advisory** (always exit 0). Read the reports in `docs/claude-scratch/local-review-<mode>-report.md` and surface anything real. They will not block, and they are imperfect (local, quantized model), so treat findings as a first-pass filter, not a gate.
|
||||
- **If a review report says "(no findings returned)" or looks empty/garbled**, it is almost always a model/tooling issue, not a clean bill of health — see `docs/DEV_TOOLING.md` Gotchas (reasoning models need thinking disabled; the context must fit the prompt; a huge diff truncates). Fix the tooling or note it; do not read empty output as "no problems."
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ Two things make `src/` runnable under Vitest even though it targets the Obsidian
|
|||
- Bundler: [esbuild.config.mjs](esbuild.config.mjs). `obsidian`, `electron`, all `@codemirror/*`, all `@lezer/*`, and Node built-ins are marked `external`. Never import other runtime deps without bundling them in.
|
||||
- Release artifacts are `main.js`, `manifest.json`, and `styles.css` at the repo root. Do not commit the generated `main.js`.
|
||||
- TypeScript config ([tsconfig.json](tsconfig.json)) is strict: `noImplicitAny`, `strictNullChecks`, `noImplicitReturns`, `noUncheckedIndexedAccess`, `useUnknownInCatchVariables`, `baseUrl: "src"`. Target ES6, module ESNext, lib DOM + ES5/6/7 only. No Node lib, so don't reach for Node APIs in plugin code.
|
||||
- ESLint ([eslint.config.mts](eslint.config.mts)) layers `eslint-plugin-obsidianmd`'s recommended rules on top of `typescript-eslint`. These rules encode Obsidian-specific correctness checks; respect them rather than disabling.
|
||||
- ESLint ([eslint.config.mts](eslint.config.mts)) layers `eslint-plugin-obsidianmd`'s recommended rules on top of `typescript-eslint`. These rules encode Obsidian-specific correctness checks; respect them rather than disabling. The config is deliberately tuned to **mirror the Obsidian community-review bot** for the classes that once slipped through to submission (see [docs/RELEASING.md](docs/RELEASING.md)): it enables the type-checked `@typescript-eslint` rules (`no-deprecated`, `no-unsafe-*`, `no-unnecessary-type-assertion`) scoped to `src/`; cherry-picks `no-unsupported-api` from a `0.4.1` alias of the plugin (`obsidianmd-latest`, since the pinned `0.1.9` base predates that rule) so an Obsidian API newer than `manifest.json`'s `minAppVersion` fails lint locally; and sets `noInlineConfig` so an `eslint-disable` comment can never silence a rule (the bot forbids disables). The base plugin stays pinned at `0.1.9` because `0.4.x`'s recommended `ui/sentence-case` is over-aggressive and diverges from what the bot enforces; only the single `no-unsupported-api` rule is taken from the alias. To reach an API outside the typed/deprecated surface, use a local structural type-alias (e.g. `ScriptProcessorNodeLike` in [src/realtime/pcm.ts](src/realtime/pcm.ts)), never a disable comment.
|
||||
|
||||
## Source layout
|
||||
|
||||
|
|
|
|||
|
|
@ -48,9 +48,11 @@ The tag name must equal `manifest.json`'s `version` exactly. `.npmrc` already pi
|
|||
|
||||
These are the recurring findings; clear them before tagging. Most are also why the items above exist.
|
||||
|
||||
**Local lint now mirrors the bot for the classes that previously slipped through** (the 1.2.0 submission failed on three the local lint did not catch). [eslint.config.mts](../eslint.config.mts) enables: the type-checked `@typescript-eslint` rules (`no-deprecated`, the `no-unsafe-*` family, `no-unnecessary-type-assertion`); `no-unsupported-api` (cherry-picked from a `0.4.1` alias of `eslint-plugin-obsidianmd` since our pinned `0.1.9` base lacks it) so a direct Obsidian API newer than `minAppVersion` is caught locally; and `noInlineConfig` so an `eslint-disable` can never silence a rule. So `npm run lint` failing on these now is the point. When the review bot adds a new rule class we do not catch, add it here the same way (prefer cherry-picking one rule from the alias over adopting `0.4.x`'s recommended config wholesale, whose `ui/sentence-case` is over-aggressive and diverges from the bot).
|
||||
|
||||
- **Plugin `id`**: lowercase letters and hyphens only, must not end in `plugin`, must not contain `obsidian`. Locked once published; do not change it. (Ours is `rewrite-voice-notes`.)
|
||||
- **No newer-than-minAppVersion APIs**: see the `minAppVersion` rule above.
|
||||
- **No `eslint-disable` directives.** The bot rejects disabling its rules. If a string trips `ui/sentence-case` (e.g. a random example), pass it through a variable instead of a string literal; the rule only inspects literals.
|
||||
- **No newer-than-minAppVersion APIs**: see the `minAppVersion` rule above. Enforced locally by `obsidianmd-latest/no-unsupported-api`.
|
||||
- **No `eslint-disable` directives.** The bot rejects disabling its rules, and `noInlineConfig` makes them inert locally (an attempt to suppress an error just leaves the error). Reach APIs outside the typed/deprecated surface through local type-aliases instead (see [src/realtime/pcm.ts](../src/realtime/pcm.ts)'s `ScriptProcessorNodeLike`). If a string trips `ui/sentence-case` (e.g. a random example), pass it through a variable instead of a string literal; the rule only inspects literals.
|
||||
- **Popout-window safety**: use `activeDocument` / `activeWindow` instead of `document` / `window`-as-globalThis where a popout could differ; use `window.setTimeout` / `window.clearTimeout` (not bare `setTimeout`); avoid `globalThis` (use `window`). For paired `addEventListener` / `removeEventListener`, capture one document reference so removal targets the same object.
|
||||
- **No `!important` in [styles.css](../styles.css).** Raise specificity, use CSS variables, or toggle via Obsidian's `el.toggle()` / `hide()` / `show()` (which set inline display) instead.
|
||||
- **Manifest `description`**: action-focused, <= 250 chars, ends with a period, no emoji.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import tseslint from 'typescript-eslint';
|
||||
import obsidianmd from "eslint-plugin-obsidianmd";
|
||||
// The Obsidian community-review bot runs a newer eslint-plugin-obsidianmd than our
|
||||
// pinned 0.1.9 base, whose `no-unsupported-api` rule (flags a direct Obsidian API call
|
||||
// newer than manifest `minAppVersion`) is what caught `FileManager.trashFile` at
|
||||
// submission. We keep 0.1.9 as the base config (its `ui/sentence-case` is the one the bot
|
||||
// actually enforces; 0.4.x's is over-aggressive and produces wrong fixes), and cherry-pick
|
||||
// ONLY that one rule from a 0.4.1 alias so the minAppVersion check runs locally too.
|
||||
import obsidianmdLatest from "obsidianmd-latest";
|
||||
import globals from "globals";
|
||||
import { globalIgnores } from "eslint/config";
|
||||
|
||||
|
|
@ -55,6 +62,15 @@ const sentenceCaseOptions = {
|
|||
|
||||
export default tseslint.config(
|
||||
{
|
||||
// The community-review bot rejects `eslint-disable` directives (it flags both the
|
||||
// undescribed comment and disabling a rule at all). `noInlineConfig` makes every inline
|
||||
// eslint directive inert, so a rule (ours or the bot's) cannot be silenced from a
|
||||
// comment: an attempt to suppress a real error just leaves the error in place and fails
|
||||
// `npm run lint`. Reach APIs outside the typed/deprecated surface through local
|
||||
// type-aliases instead (see src/realtime/pcm.ts), never a disable comment.
|
||||
linterOptions: {
|
||||
noInlineConfig: true,
|
||||
},
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
|
|
@ -101,6 +117,18 @@ export default tseslint.config(
|
|||
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
// Cherry-picked from the 0.4.1 alias (see the import comment): the single rule that
|
||||
// flags a direct Obsidian API newer than manifest `minAppVersion`. Registering the
|
||||
// plugin only makes its rules available; nothing else from 0.4.1 runs unless enabled
|
||||
// here, so its divergent sentence-case rule stays off. This is the check that would
|
||||
// have caught `trashFile` locally before the 1.2.0 submission.
|
||||
files: ['src/**/*.ts'],
|
||||
plugins: { 'obsidianmd-latest': obsidianmdLatest },
|
||||
rules: {
|
||||
'obsidianmd-latest/no-unsupported-api': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
// test/ runs under Node via Vitest, not inside Obsidian, so the Node-builtin ban that
|
||||
// keeps src/ portable to the Electron/mobile plugin runtime doesn't apply here.
|
||||
|
|
|
|||
136
package-lock.json
generated
136
package-lock.json
generated
|
|
@ -22,6 +22,7 @@
|
|||
"eslint-plugin-obsidianmd": "0.1.9",
|
||||
"globals": "14.0.0",
|
||||
"jiti": "2.6.1",
|
||||
"obsidianmd-latest": "npm:eslint-plugin-obsidianmd@0.4.1",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "8.35.1",
|
||||
|
|
@ -478,6 +479,34 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-plugin-eslint-comments": {
|
||||
"version": "4.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.7.2.tgz",
|
||||
"integrity": "sha512-LF03qURSwEWm2dz5wtdDCzNk+7Opl0X7q6I3undsaIuNsEiNvRV3BCtqu14Q/6Pzg1tBj44LcxpW2EpSLZStZw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"ignore": "^7.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-plugin-eslint-comments/node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
|
||||
|
|
@ -626,7 +655,6 @@
|
|||
"integrity": "sha512-rvR/EZtvUG3p9uqrSmcDJPYSH7atmWr0RnFWN6m917MAPx82+zQgPUmDu0whPFG6XTyM0vB/hR6c1Q63OaYtCQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint/core": "^0.17.0",
|
||||
"@eslint/plugin-kit": "^0.4.1",
|
||||
|
|
@ -705,7 +733,6 @@
|
|||
"integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
|
|
@ -1188,7 +1215,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz",
|
||||
"integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
|
|
@ -2882,12 +2908,20 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-no-unsanitized": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.5.tgz",
|
||||
"integrity": "sha512-MSB4hXPVFQrI8weqzs6gzl7reP2k/qSjtCoL2vUMSDejIIq9YL1ZKvq5/ORBXab/PvfBBrWO2jWviYpL+4Ghfg==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"eslint": "^9 || ^10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-obsidianmd": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-obsidianmd/-/eslint-plugin-obsidianmd-0.1.9.tgz",
|
||||
"integrity": "sha512-/gyo5vky3Y7re4BtT/8MQbHU5Wes4o6VRqas3YmXE7aTCnMsdV0kfzV1GDXJN9Hrsc9UQPoeKUMiapKL0aGE4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/eslint-plugin-sdl": "^1.1.0",
|
||||
"@types/eslint": "8.56.2",
|
||||
|
|
@ -2930,7 +2964,6 @@
|
|||
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.8.7.tgz",
|
||||
"integrity": "sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/codemirror": "5.60.8",
|
||||
"moment": "2.29.4"
|
||||
|
|
@ -4560,10 +4593,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/obsidian": {
|
||||
"version": "1.10.3",
|
||||
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.3.tgz",
|
||||
"integrity": "sha512-VP+ZSxNMG7y6Z+sU9WqLvJAskCfkFrTz2kFHWmmzis+C+4+ELjk/sazwcTHrHXNZlgCeo8YOlM6SOrAFCynNew==",
|
||||
"license": "MIT",
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz",
|
||||
"integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==",
|
||||
"dependencies": {
|
||||
"@types/codemirror": "5.60.8",
|
||||
"moment": "2.29.4"
|
||||
|
|
@ -4573,6 +4605,92 @@
|
|||
"@codemirror/view": "6.38.6"
|
||||
}
|
||||
},
|
||||
"node_modules/obsidianmd-latest": {
|
||||
"name": "eslint-plugin-obsidianmd",
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-obsidianmd/-/eslint-plugin-obsidianmd-0.4.1.tgz",
|
||||
"integrity": "sha512-Nv3593kVsFOS8kir/HWaJ5CR3xcyiPWEPyXst5Pib8mxRYYuLYPpJS2ALMoZXHulHyiGwoYW8AaxvLRc4rhrRg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "^4.7.2",
|
||||
"@eslint/config-helpers": "^0.4.2",
|
||||
"@eslint/js": "^9.30.1",
|
||||
"@eslint/json": "0.14.0",
|
||||
"@microsoft/eslint-plugin-sdl": "^1.1.0",
|
||||
"@types/eslint": "9.6.1",
|
||||
"@types/node": "20.12.12",
|
||||
"@typescript-eslint/types": "^8.33.1",
|
||||
"@typescript-eslint/utils": "^8.33.1",
|
||||
"eslint": ">=9.19.0",
|
||||
"eslint-plugin-depend": "1.3.1",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-json-schema-validator": "5.1.0",
|
||||
"eslint-plugin-no-unsanitized": "^4.1.5",
|
||||
"eslint-plugin-security": "2.1.1",
|
||||
"globals": "14.0.0",
|
||||
"obsidian": "1.12.3",
|
||||
"semver": "^7.7.4",
|
||||
"typescript": "5.4.5",
|
||||
"typescript-eslint": "^8.35.1"
|
||||
},
|
||||
"bin": {
|
||||
"eslint-plugin-obsidian": "dist/lib/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@eslint/js": "^9.30.1",
|
||||
"@eslint/json": "0.14.0",
|
||||
"eslint": ">=9.19.0",
|
||||
"obsidian": "1.8.7",
|
||||
"typescript-eslint": "^8.35.1"
|
||||
}
|
||||
},
|
||||
"node_modules/obsidianmd-latest/node_modules/@types/eslint": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/obsidianmd-latest/node_modules/@types/node": {
|
||||
"version": "20.12.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
|
||||
"integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/obsidianmd-latest/node_modules/semver": {
|
||||
"version": "7.8.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
|
||||
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/obsidianmd-latest/node_modules/typescript": {
|
||||
"version": "5.4.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
"eslint-plugin-obsidianmd": "0.1.9",
|
||||
"globals": "14.0.0",
|
||||
"jiti": "2.6.1",
|
||||
"obsidianmd-latest": "npm:eslint-plugin-obsidianmd@0.4.1",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "8.35.1",
|
||||
|
|
|
|||
Loading…
Reference in a new issue