Skip to content

feat: align recommended ruleset with scanners and improve configuration#164

Open
saberzero1 wants to merge 2 commits into
obsidianmd:masterfrom
saberzero1:properly-scope-rules-and-use-directory-scanner-ruleset
Open

feat: align recommended ruleset with scanners and improve configuration#164
saberzero1 wants to merge 2 commits into
obsidianmd:masterfrom
saberzero1:properly-scope-rules-and-use-directory-scanner-ruleset

Conversation

@saberzero1

@saberzero1 saberzero1 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Unifies #86 and #158 into a single changeset. The recommended config now matches the community plugin scanner's ruleset and properly scopes type-checked rules to TypeScript files only.

Supersedes #86 and #158.

Problem

Two issues existed independently:

  1. Type scoping (fix: properly scope rules to file types #86): The recommended config applied all rules uniformly. Rules that call getParserServices (like no-plugin-as-component, no-view-references-in-plugin, prefer-file-manager-trash-file, prefer-instanceof, no-unsupported-api) would crash or produce misleading errors when run on plain JavaScript files that aren't parsed by @typescript-eslint/parser.
  2. Scanner mismatch (feat: add review ruleset #158): Developers using recommended locally got different results than during review. The scanner applies toWarns(), re-escalates ~14 rules back to error, adds rules not in recommended (no-unsanitized/*, @typescript-eslint/no-unsafe-*, eslint-comments/*), and disables rules like validate-manifest and ui/sentence-case. There was no way to replicate what the scanner checks without reverse-engineering its config chain.

Changes

Type-scoped rule split (lib/index.ts)

  • Split recommendedPluginRulesConfig into recommendedPluginRulesConfigBase (JS + TS) and recommendedPluginRulesConfigTypeChecked (TS only)
  • JS files receive only base rules; TS files receive both base and type-checked rules
  • Uses separate file-scoped config entries rather than decomposing tseslint internals

Scanner-matching recommended config (lib/index.ts)

  • recommended now matches the community plugin scanner's portal-release config
  • Security rules at error: no-eval, no-implied-eval, no-unsanitized/method, no-unsanitized/property, no-forbidden-elements, regex-lookbehind
  • Portal re-escalations at error: settings-tab/*, detach-leaves, no-plugin-as-component, no-sample-code, platform, no-static-styles-assignment, no-view-references-in-plugin, no-unsupported-api, sample-names
  • Remaining obsidianmd rules downgraded to warn (matching scanner behavior)
  • no-nodejs-modules always warn (scanner doesn't check manifest.isDesktopOnly)
  • Disabled: validate-manifest, validate-license, ui/sentence-case*, no-undef, no-console, import/no-unresolved, @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-base-to-string
  • @typescript-eslint/no-unsafe-* family at warn
  • no-new-func off, replaced by rule-custom-message wrapper combining no-new-func and no-console custom messages
  • eslint-comments/no-unlimited-disable, require-description, disable-enable-pair, no-restricted-disable at error
  • linterOptions.reportUnusedDisableDirectives and reportUnusedInlineConfigs at error
  • JSON file overrides disabling type-aware rules
  • depend/ban-dependencies at warn for source files

New dependency

  • Added @eslint-community/eslint-plugin-eslint-comments as a regular dependency with type shim in lib/shim.d.ts

import.meta.dirname fix

  • Added findPackageJson() helper that walks up directories to find eslint-plugin-obsidianmd's package.json, with import.meta.dirname fallback for Node 18 compatibility

Documentation

  • Updated README to document that recommended matches the scanner
  • Added scanner file ignore patterns for full equivalence
  • Regenerated rule docs via eslint-doc-generator

Tests

  • Added tests/recommendedConfig.test.ts with structural smoke tests verifying the config is well-formed, all registered rules are referenced, type-checked rules are properly scoped, and required plugins are registered

Review feedback addressed

Feedback Resolution
prefer-active-window-timers doesn't exist Fixed to prefer-window-timers
no-global-this dropped unintentionally Restored
prefer-active-doc changed from warn to off unintentionally Restored to warn
React/Svelte stub configs unnecessary Dropped
Fragile IIFE decomposing tseslint internals Replaced with file-scoped config entries
packageJson extraction silently removes from users Kept in flatRecommendedConfig

Scanner compatibility

Verified against the community plugin scanner. The scanner consumes obsidianmd.configs.recommended and applies its own overrides at each config level. Since all overrides are explicit per-rule, they take precedence regardless of what recommended sets. No changes required in the scanner.

What changes for consumers

The recommended config now produces results matching the community plugin scanner instead of stricter standalone rules. Rules that were previously error may now be warn or off to match scanner behavior. Consumers who want stricter checking can override individual rules in their own config.

@saberzero1 saberzero1 marked this pull request as ready for review June 11, 2026 15:26

@tgrosinger tgrosinger left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This pull request seems to be doing a lot of different things all lumped into one commit, making it very hard to understand what is changing and why.

I think we need to break this up into more targeted changes that can be effectively reviewed and reasoned about.

Comment thread lib/index.ts
const manifest = getManifest();
const packageJson = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, "../../package.json"), "utf8")) as PackageJson;

function findPackageJson(startDir: string): string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I encountered Claude trying to add this too and it makes me a little uncomfortable. Let's at the very least not walk higher than the first .git directory sibling we can find.

Comment thread lib/index.ts
};

// Combined rules for TypeScript files
const recommendedPluginRulesConfig: RulesConfig = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a reason for shuffling these around? It feels like the end result is equivalent. We already had a way of only running rules that require type information on TS files, we were just combining them in the rules array below instead of creating a separate RulesConfig.

Comment thread lib/index.ts
// Rules that require TypeScript type information (TypeScript-only).
// These must only run on files parsed by @typescript-eslint/parser.
const recommendedPluginRulesConfigTypeChecked: RulesConfig = {
"obsidianmd/no-plugin-as-component": "error",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please shoot for min-diff when possible. If this array had been left in the same location as the removed recommendedTypedRulesConfig it would be much easier to see which of the internals were actually changed.

Comment thread lib/index.ts
]);

// Scanner's restricted imports (same packages, warn severity, scanner's moment phrasing)
const scannerRestrictedImportsOptions = [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this being duplicated? This information is already defined in ruleOptions.ts.

Comment thread lib/index.ts
"eslint-comments": eslintComments,
obsidianmd: plugin,
},
rules: {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why are all of these being defined here?

Taking one just as an example no-forbidden-elements is already defined as an error in lib/index.ts.

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