chore: add obsidian compatibility lint baseline

This commit is contained in:
nejimakibird 2026-06-06 08:14:54 +09:00
parent 6f28126222
commit 17b37c91e2
6 changed files with 4780 additions and 3 deletions

3
.gitignore vendored
View file

@ -24,3 +24,6 @@ pnpm-debug.log*
# working directory
_archives/
scripts/
!scripts/
scripts/*
!scripts/check-css-compat.mjs

View file

@ -0,0 +1,15 @@
# Obsidian lint baseline 0.1.12
This baseline introduces local lint visibility for Obsidian community plugin compatibility review concerns. The goal for 0.1.12 is to surface warning groups before release, not to fix every existing warning in this task.
## Warning groups
- Popout compatibility: replace direct `document` access with Obsidian active document/window APIs where appropriate, prefer window-scoped timers for `requestAnimationFrame`, and avoid `instanceof HTMLElement` checks that can fail across popout windows.
- Unsafe TypeScript values: review `any` usage and type-aware unsafe assignment, calls, member access, arguments, and returns reported by `@typescript-eslint`.
- `require()` style import: replace CommonJS-style imports with ES module imports where practical.
- Filesystem, vault enumeration, and clipboard behavior: review warnings around broad vault iteration, filesystem-style access patterns, and clipboard or DOM interactions against Obsidian plugin review expectations.
- CSS compatibility: `scripts/check-css-compat.mjs` guards active CSS against `display: contents`; the current codebase passes this check.
## Follow-up
Treat these warning groups as cleanup candidates for focused implementation tickets. Do not change model syntax, renderer behavior, or release version files as part of the lint baseline itself.

50
eslint.config.mjs Normal file
View file

@ -0,0 +1,50 @@
import { defineConfig } from "eslint/config";
import obsidianmd from "eslint-plugin-obsidianmd";
import tseslint from "typescript-eslint";
const obsidianWarningRules = Object.fromEntries(
Object.keys(obsidianmd.rules).map((ruleName) => [`obsidianmd/${ruleName}`, "warn"]),
);
function withWarningSeverity(ruleConfig) {
if (Array.isArray(ruleConfig)) {
return ["warn", ...ruleConfig.slice(1)];
}
return "warn";
}
const typescriptWarningRules = Object.fromEntries(
tseslint.configs.recommendedTypeChecked
.flatMap((config) => Object.entries(config.rules ?? {}))
.filter(([ruleName]) => ruleName.startsWith("@typescript-eslint/"))
.map(([ruleName, ruleConfig]) => [ruleName, withWarningSeverity(ruleConfig)]),
);
export default defineConfig([
{
ignores: ["dist/**", "build/**", "node_modules/**", "main.js"],
},
...obsidianmd.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
{
files: ["src/**/*.ts"],
languageOptions: {
parserOptions: {
project: "./tsconfig.json",
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
...obsidianWarningRules,
...typescriptWarningRules,
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unsafe-argument": "warn",
"@typescript-eslint/no-unsafe-assignment": "warn",
"@typescript-eslint/no-unsafe-call": "warn",
"@typescript-eslint/no-unsafe-member-access": "warn",
"@typescript-eslint/no-unsafe-return": "warn",
"@typescript-eslint/no-require-imports": "warn",
},
},
]);

4654
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,10 @@
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production"
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"lint": "npm run lint:obsidian && npm run lint:css-compat",
"lint:obsidian": "eslint \"src/**/*.ts\"",
"lint:css-compat": "node scripts/check-css-compat.mjs"
},
"keywords": [
"obsidian",
@ -16,10 +19,14 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/node": "^20.17.24",
"esbuild": "^0.25.2",
"eslint": "^10.4.1",
"eslint-plugin-obsidianmd": "^0.3.0",
"obsidian": "^1.8.10",
"tslib": "^2.8.1",
"typescript": "^5.8.3"
"typescript": "^5.8.3",
"typescript-eslint": "^8.60.1"
}
}

View file

@ -0,0 +1,50 @@
import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
const root = process.cwd();
const ignoredDirectories = new Set(["node_modules", "dist", "build"]);
const displayContentsPattern = /\bdisplay\s*:\s*contents\b/i;
const findings = [];
async function scanDirectory(directory) {
const entries = await readdir(directory, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
if (!ignoredDirectories.has(entry.name)) {
await scanDirectory(path.join(directory, entry.name));
}
continue;
}
if (!entry.isFile() || !entry.name.endsWith(".css")) {
continue;
}
const filePath = path.join(directory, entry.name);
const source = await readFile(filePath, "utf8");
const lines = source.split(/\r?\n/);
lines.forEach((line, index) => {
if (displayContentsPattern.test(line)) {
findings.push({
filePath: path.relative(root, filePath),
line: index + 1,
text: line.trim(),
});
}
});
}
}
await scanDirectory(root);
if (findings.length > 0) {
console.error("CSS compatibility check failed: avoid `display: contents` in active CSS.");
for (const finding of findings) {
console.error(`${finding.filePath}:${finding.line}: ${finding.text}`);
}
process.exitCode = 1;
} else {
console.log("CSS compatibility check passed: no active `display: contents` usage found.");
}