From 692fc0f293c6e7f8d02e01cd5d9c369433d9d3b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Jul 2026 16:41:28 +0000 Subject: [PATCH] build(compat): derive the compat typecheck's target version dynamically The 1.11 compatibility gate previously hardcoded the target Obsidian version in two places (an obsidian-1_11 alias devDependency and a static tsconfig.compat.json), so bumping minAppVersion would silently leave the check pinned to the old version. Replace both with scripts/typecheck-compat.mjs, which reads the target from versions.json (the entry for the current manifest version, falling back to manifest.minAppVersion), installs the matching Obsidian typings into a git-ignored cache on demand, and type-checks src against them. The gate now follows minAppVersion automatically with no code changes. Also fold `typecheck:compat` into `npm run build` so it runs wherever the build runs (the husky pre-commit hook and any CI step that builds). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SSikT9ZAfQF8K4SE3DatD3 --- .gitignore | 3 ++ package-lock.json | 17 ---------- package.json | 5 ++- scripts/typecheck-compat.mjs | 62 ++++++++++++++++++++++++++++++++++++ tsconfig.compat.json | 17 ---------- 5 files changed, 67 insertions(+), 37 deletions(-) create mode 100644 scripts/typecheck-compat.mjs delete mode 100644 tsconfig.compat.json diff --git a/.gitignore b/.gitignore index 5e9bb59..c9de6f6 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ data.json # test coverage coverage/ +# generated by scripts/typecheck-compat.mjs +tsconfig.compat.generated.json + # Claude worktrees .claude/ diff --git a/package-lock.json b/package-lock.json index e0764e1..8b7e0cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,6 @@ "husky": "^9.1.7", "jiti": "2.6.1", "jsdom": "^29.1.1", - "obsidian-1_11": "npm:obsidian@^1.11.0", "semantic-release": "^25.0.5", "tslib": "2.4.0", "typescript": "^5.8.3", @@ -9858,22 +9857,6 @@ "@codemirror/view": "6.38.6" } }, - "node_modules/obsidian-1_11": { - "name": "obsidian", - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.11.0.tgz", - "integrity": "sha512-lVqN9AmDWHzhNATi2tDnjqVgI6WUYKeT+lIsAycAyLt4XCC6zRsWzb+tFCiB7Rn3PpttefjoovilhYwvS4Iqxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/codemirror": "5.60.8", - "moment": "2.29.4" - }, - "peerDependencies": { - "@codemirror/state": "6.5.0", - "@codemirror/view": "6.38.6" - } - }, "node_modules/obug": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", diff --git a/package.json b/package.json index 6175a63..1f10292 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ }, "scripts": { "dev": "node esbuild.config.mjs", - "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", - "typecheck:compat": "tsc -p tsconfig.compat.json", + "build": "tsc -noEmit -skipLibCheck && npm run typecheck:compat && node esbuild.config.mjs production", + "typecheck:compat": "node scripts/typecheck-compat.mjs", "version": "node version-bump.mjs && git add manifest.json versions.json", "lint": "eslint .", "test": "vitest run", @@ -41,7 +41,6 @@ "husky": "^9.1.7", "jiti": "2.6.1", "jsdom": "^29.1.1", - "obsidian-1_11": "npm:obsidian@^1.11.0", "semantic-release": "^25.0.5", "tslib": "2.4.0", "typescript": "^5.8.3", diff --git a/scripts/typecheck-compat.mjs b/scripts/typecheck-compat.mjs new file mode 100644 index 0000000..d6821db --- /dev/null +++ b/scripts/typecheck-compat.mjs @@ -0,0 +1,62 @@ +#!/usr/bin/env node +/* + * Type-checks src/ against the exact Obsidian API version this release targets. + * Any use of an API newer than our declared minAppVersion that is NOT + * runtime-guarded becomes a compile error here. + * + * The target version is derived dynamically — never hardcoded. versions.json + * maps each plugin version to its minAppVersion; we use the entry for the + * current manifest version, falling back to manifest.minAppVersion (which is the + * canonical floor, and the reliable source when version-bump.mjs omits a + * versions.json entry because the minAppVersion was unchanged). + * + * The matching Obsidian typings are installed on demand into node_modules/.cache + * (git-ignored) and reused across runs, so bumping minAppVersion automatically + * re-points this check with no code changes. + */ +import { execSync } from 'node:child_process'; +import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join, relative } from 'node:path'; +import process from 'node:process'; + +const root = join(dirname(fileURLToPath(import.meta.url)), '..'); +const readJson = (p) => JSON.parse(readFileSync(join(root, p), 'utf8')); + +const manifest = readJson('manifest.json'); +const versions = readJson('versions.json'); +const target = versions[manifest.version] ?? manifest.minAppVersion; + +if (!target) { + console.error('compat: could not resolve target Obsidian version from versions.json / manifest.json'); + process.exit(1); +} + +// Install the target typings into a per-version cache (reused on later runs). +const cacheDir = join(root, 'node_modules', '.cache', 'obsidian-compat', target); +const dtsBase = join(cacheDir, 'node_modules', 'obsidian', 'obsidian'); // no extension +if (!existsSync(`${dtsBase}.d.ts`)) { + mkdirSync(cacheDir, { recursive: true }); + console.log(`compat: installing obsidian@${target} typings...`); + execSync( + `npm install --prefix "${cacheDir}" --no-save --no-audit --no-fund --loglevel=error obsidian@${target}`, + { stdio: 'inherit' }, + ); +} + +// Generate a tsconfig that points the "obsidian" import at the target typings. +const generated = join(root, 'tsconfig.compat.generated.json'); +writeFileSync(generated, `${JSON.stringify({ + extends: './tsconfig.json', + compilerOptions: { + noEmit: true, + skipLibCheck: true, + baseUrl: '.', + paths: { obsidian: [relative(root, dtsBase).split('\\').join('/')] }, + }, + include: ['src/**/*.ts'], +}, null, '\t')}\n`); + +console.log(`compat: type-checking src against Obsidian ${target} API...`); +execSync(`node "${join(root, 'node_modules', 'typescript', 'bin', 'tsc')}" -p "${generated}"`, { stdio: 'inherit' }); +console.log(`compat: ✓ src is compatible with Obsidian ${target}`); diff --git a/tsconfig.compat.json b/tsconfig.compat.json deleted file mode 100644 index 2874231..0000000 --- a/tsconfig.compat.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - // Type-checks src against the Obsidian 1.11 API typings (installed as the - // "obsidian-1_11" alias) instead of the latest ones. Because the plugin - // declares minAppVersion 1.11.0, any use of an API newer than 1.11 that is - // not runtime-guarded becomes a compile error here. Run via `npm run - // typecheck:compat`. Keep this passing whenever minAppVersion stays at 1.11. - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": true, - "skipLibCheck": true, - "baseUrl": ".", - "paths": { - "obsidian": ["node_modules/obsidian-1_11/obsidian"] - } - }, - "include": ["src/**/*.ts"] -}