Fix Obsidian community scorecard warnings

- Add eslint-plugin-obsidianmd with typescript-eslint type-checked rules
- Remove console.log from onload/onunload
- Fix type safety: proper types, unused params prefixed with _
- Use createDiv() instead of createEl("div")
- Remove !important from CSS via increased selector specificity
- Replace deprecated builtin-modules with native builtinModules
- Update esbuild config for v0.24 watch API
- Add pnpm-lock.yaml for reproducible builds
- Add GitHub release workflow with artifact attestation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@gapmiss 2026-05-22 22:39:20 -05:00
parent 30b42e0182
commit 6850afbefd
11 changed files with 3656 additions and 114 deletions

View file

@ -1,2 +0,0 @@
npm node_modules
build

View file

@ -1,23 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"@typescript-eslint/ban-ts-comment": "off",
"no-prototype-builtins": "off",
"@typescript-eslint/no-empty-function": "off"
}
}

13
.gitignore vendored
View file

@ -3,18 +3,7 @@
# Dependency directories
node_modules/
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
.DS_Store
package-lock.json
archive
main.js
.claude

View file

@ -1,6 +1,6 @@
import esbuild from "esbuild";
import process from "process";
import builtins from 'builtin-modules'
import { builtinModules } from "module";
const banner =
`/*
@ -11,7 +11,7 @@ if you want to view the source, please visit the github repository of this plugi
const prod = (process.argv[2] === 'production');
esbuild.build({
const buildOptions = {
banner: {
js: banner,
},
@ -31,12 +31,20 @@ esbuild.build({
'@lezer/common',
'@lezer/highlight',
'@lezer/lr',
...builtins],
...builtinModules.map(m => m),
...builtinModules.map(m => `node:${m}`),
],
format: 'cjs',
watch: !prod,
target: 'es2018',
logLevel: "info",
sourcemap: prod ? false : false,
sourcemap: prod ? false : 'inline',
treeShaking: true,
outfile: 'main.js',
}).catch(() => process.exit(1));
};
if (prod) {
await esbuild.build(buildOptions);
} else {
const ctx = await esbuild.context(buildOptions);
await ctx.watch();
}

29
eslint.config.mjs Normal file
View file

@ -0,0 +1,29 @@
import tsParser from "@typescript-eslint/parser";
import tseslint from "typescript-eslint";
import obsidianmd from "eslint-plugin-obsidianmd";
export default [
{ ignores: ["node_modules/**", "main.js", "*.mjs", "package.json", "package-lock.json", "pnpm-lock.yaml", "versions.json", "tsconfig.json"] },
...tseslint.configs.recommendedTypeChecked.map(config => ({
...config,
files: ["**/*.ts"],
})),
...obsidianmd.configs.recommended,
{
files: ["**/*.ts"],
languageOptions: {
parser: tsParser,
parserOptions: {
project: "./tsconfig.json",
sourceType: "module",
},
},
rules: {
"no-console": ["error", { allow: ["warn", "error", "debug"] }],
"@typescript-eslint/no-unused-vars": ["error", {
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
}],
},
},
];

36
main.ts
View file

@ -1,30 +1,20 @@
import { MarkdownPostProcessor, Plugin} from 'obsidian';
enum ComponentChoice {
Default = "Default",
}
import { MarkdownPostProcessor, MarkdownPostProcessorContext, Plugin } from 'obsidian';
export default class BlurPlugin extends Plugin {
async onload() {
this.registerMarkdownCodeBlockProcessor("blur", this.blurBlockHandler.bind(this, null));
this.registerMarkdownCodeBlockProcessor("blur-brick", this.blurBlockHandler.bind(this, null));
this.registerMarkdownCodeBlockProcessor("blur-bone", this.blurBlockHandler.bind(this, null));
onload(): void {
this.registerMarkdownCodeBlockProcessor("blur", (source, el, ctx) => this.blurBlockHandler(source, el, ctx));
this.registerMarkdownCodeBlockProcessor("blur-brick", (source, el, ctx) => this.blurBlockHandler(source, el, ctx));
this.registerMarkdownCodeBlockProcessor("blur-bone", (source, el, ctx) => this.blurBlockHandler(source, el, ctx));
this.registerMarkdownPostProcessor(
buildPostProcessor()
);
console.log("%c Blur plugin loaded", 'color:lime;');
}
onunload() {
console.log("%c Blur plugin unloaded", 'color:lime;');
}
async blurBlockHandler(type: ComponentChoice, source: string, el: HTMLElement, ctx: any): Promise<any> {
blurBlockHandler(source: string, el: HTMLElement, _ctx: MarkdownPostProcessorContext): void {
if (el.className==='block-language-blur-brick') {
const block = el.createEl("div", {cls: "blur-brick-block"})
let inputElement: HTMLElement
inputElement = block.createEl("div", {text: '', cls: "blur-brick-innerblock"})
const block = el.createDiv({cls: "blur-brick-block"});
const inputElement = block.createDiv({cls: "blur-brick-innerblock"});
source.split(/\W+/).forEach((w:string) => {
let word = w.trim();
if (word !== '') {
@ -34,9 +24,8 @@ export default class BlurPlugin extends Plugin {
})
}
else if (el.className==='block-language-blur-bone') {
const block = el.createEl("div", {cls: "blur-bone-block"})
let inputElement: HTMLElement
inputElement = block.createEl("div", {text: '', cls: "blur-bone-innerblock"})
const block = el.createDiv({cls: "blur-bone-block"});
const inputElement = block.createDiv({cls: "blur-bone-innerblock"});
source.split(/\W+/).forEach((w:string) => {
let word = w.trim();
if (word !== '') {
@ -45,9 +34,8 @@ export default class BlurPlugin extends Plugin {
})
}
else if (el.className==='block-language-blur') {
const block = el.createEl("div", {cls: "blur-block"})
let inputElement: HTMLElement
inputElement = block.createEl("div", {text: '', cls: "blur-innerblock"})
const block = el.createDiv({cls: "blur-block"});
const inputElement = block.createDiv({cls: "blur-innerblock"});
source.split(/\W+/).forEach((w:string) => {
let word = w.trim();
if (word !== '') {

View file

@ -6,19 +6,21 @@
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"lint": "eslint .",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.14.47",
"@types/node": "^22.0.0",
"@typescript-eslint/parser": "^8.0.0",
"esbuild": "^0.24.0",
"eslint": "^9.0.0",
"eslint-plugin-obsidianmd": "^0.3.0",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"
"tslib": "^2.8.0",
"typescript": "^5.6.0",
"typescript-eslint": "^8.0.0"
}
}

3154
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

332
release.mjs Normal file
View file

@ -0,0 +1,332 @@
import { readFileSync, writeFileSync } from 'fs';
import { execSync } from 'child_process';
import path from 'path';
/**
* Version Update and Deployment Automation Script
*
* 1. Update version in package.json
* 2. Synchronize version in manifest.json, versions.json
* 3. Run build
* 4. Create Git commit
* 5. Create Git tag
* 6. Push changes to GitHub (REQUIRES Github CLI: https://cli.github.com)
*/
// Version type definitions
const VERSION_TYPES = {
PATCH: 'patch',
MINOR: 'minor',
MAJOR: 'major'
};
// Default settings
const DEFAULT_VERSION_TYPE = VERSION_TYPES.PATCH;
const RELEASE_FILES = ['main.js', 'manifest.json', 'styles.css'];
const MIN_APP_VERSION = "0.15.0"
// Store original versions for rollback if needed
let originalPackageVersion = '';
let originalManifestVersion = '';
/**
* Updates version value from the version string.
* @param {string} version - Current version (e.g., '0.2.2')
* @param {string} type - Update type ('patch', 'minor', 'major')
* @returns {string} Updated version
*/
const updateVersion = (version, type = DEFAULT_VERSION_TYPE) => {
const [major, minor, patch] = version.split('.').map(Number);
switch (type) {
case VERSION_TYPES.MAJOR:
return `${major + 1}.0.0`;
case VERSION_TYPES.MINOR:
return `${major}.${minor + 1}.0`;
case VERSION_TYPES.PATCH:
default:
return `${major}.${minor}.${patch + 1}`;
}
};
/**
* Gets version information before any changes are made.
* @returns {string} Previous version
*/
const getPreviousVersion = () => {
const manifestPath = path.resolve(process.cwd(), 'manifest.json');
const manifestData = JSON.parse(readFileSync(manifestPath, 'utf8'));
return manifestData.version;
}
/**
* Updates version information in package.json file.
* @param {string} versionType - Version type to update
* @returns {string} New version
*/
const updatePackageVersion = (versionType) => {
const packagePath = path.resolve(process.cwd(), 'package.json');
const packageData = JSON.parse(readFileSync(packagePath, 'utf8'));
const currentVersion = packageData.version;
originalPackageVersion = currentVersion; // Store original version for possible rollback
const newVersion = updateVersion(currentVersion, versionType);
packageData.version = newVersion;
writeFileSync(packagePath, JSON.stringify(packageData, null, '\t') + '\n');
console.log(`📦 Updated package.json version: ${currentVersion}${newVersion}`);
return newVersion;
};
/**
* Updates version information in manifest.json file.
* @param {string} newVersion - Version to update
*/
const updateManifestVersion = (newVersion) => {
const manifestPath = path.resolve(process.cwd(), 'manifest.json');
const manifestData = JSON.parse(readFileSync(manifestPath, 'utf8'));
const currentVersion = manifestData.version;
originalManifestVersion = currentVersion; // Store original version for possible rollback
manifestData.version = newVersion;
writeFileSync(manifestPath, JSON.stringify(manifestData, null, '\t') + '\n');
console.log(`📋 Updated manifest.json version: ${currentVersion}${newVersion}`);
};
/**
* Updates version information in versions.json file.
* @param {string} previousVersion - Previous version
* @param {string} newVersion - Version to update
*/
const updateVersionsVersion = (previousVersion, newVersion, minAppVersion) => {
const versionsPath = path.resolve(process.cwd(), 'versions.json');
const versionsData = JSON.parse(readFileSync(versionsPath, 'utf8'));
const manifestPath = path.resolve(process.cwd(), 'manifest.json');
const manifestData = JSON.parse(readFileSync(manifestPath, 'utf8'));
const currentVersion = manifestData.version;
versionsData[newVersion] = minAppVersion;
writeFileSync(versionsPath, JSON.stringify(versionsData, null, '\t') + '\n');
console.log(`📋 Updated versions.json version: ${previousVersion}${newVersion}`);
};
/**
* Run project build
*/
const buildProject = () => {
try {
console.log('🔨 Starting project build...');
execSync('npm run build', { stdio: 'inherit' });
console.log('✅ Build completed');
return true;
} catch (error) {
console.error('❌ Build failed:', error.message);
return false;
}
};
/**
* Rollback version changes if the release process fails
*/
const rollbackVersions = () => {
if (originalPackageVersion) {
try {
const packagePath = path.resolve(process.cwd(), 'package.json');
const packageData = JSON.parse(readFileSync(packagePath, 'utf8'));
const currentVersion = packageData.version;
packageData.version = originalPackageVersion;
writeFileSync(packagePath, JSON.stringify(packageData, null, '\t') + '\n');
console.log(`♻️ Rolled back package.json version: ${currentVersion}${originalPackageVersion}`);
} catch (error) {
console.error('❌ Failed to rollback package.json version:', error.message);
}
}
if (originalManifestVersion) {
try {
const manifestPath = path.resolve(process.cwd(), 'manifest.json');
const manifestData = JSON.parse(readFileSync(manifestPath, 'utf8'));
const currentVersion = manifestData.version;
const versionsPath = path.resolve(process.cwd(), 'versions.json');
const versionsData = JSON.parse(readFileSync(versionsPath, 'utf8'));
// remove last version from JSON
let keys = Object.keys(versionsData)
delete versionsData[keys[keys.length-1]]
writeFileSync(versionsPath, JSON.stringify(versionsData, null, '\t') + '\n');
console.log(`♻️ Rolled back versions.json version: ${currentVersion}${originalManifestVersion}`);
} catch (error) {
console.error('❌ Failed to rollback versions.json version:', error.message);
}
}
if (originalManifestVersion) {
try {
const manifestPath = path.resolve(process.cwd(), 'manifest.json');
const manifestData = JSON.parse(readFileSync(manifestPath, 'utf8'));
const currentVersion = manifestData.version;
manifestData.version = originalManifestVersion;
writeFileSync(manifestPath, JSON.stringify(manifestData, null, '\t') + '\n');
console.log(`♻️ Rolled back manifest.json version: ${currentVersion}${originalManifestVersion}`);
} catch (error) {
console.error('❌ Failed to rollback manifest.json version:', error.message);
}
}
};
/**
* Create Git commit and tag
* @param {string} version - New version
*/
const createGitCommitAndTag = (version) => {
try {
// Stage changed files
try {
execSync('git add package.json manifest.json versions.json', { stdio: 'inherit' });
} catch (error) {
console.error('❌ Failed to stage package.json, versions.json and manifest.json:', error.message);
return false;
}
// Try to stage release files
try {
execSync(`git add ${RELEASE_FILES.join(' ')}`, { stdio: 'inherit' });
} catch (error) {
console.warn('⚠️ Note: Some release files could not be staged. This may be normal if they are gitignored.');
}
// Create commit
const commitMessage = `chore: release ${version}`;
execSync(`git commit -m "${commitMessage}"`, { stdio: 'inherit' });
console.log(`✅ Commit created: ${commitMessage}`);
// Create tag
const tagName = `${version}`;
execSync(`git tag -a ${tagName} -m "Release ${tagName}"`, { stdio: 'inherit' });
console.log(`🏷️ Tag created: ${tagName}`);
// Push changes
execSync('git push', { stdio: 'inherit' });
execSync('git push --tags', { stdio: 'inherit' });
console.log('🚀 Changes pushed to GitHub');
console.log('📦 GitHub Actions will create the release with artifact attestations.');
return true; // Successfully created commit, tag, and pushed
} catch (error) {
console.error('❌ Error during Git operations:', error.message);
return false; // Failed to complete Git operations
}
};
/**
* Check if Git working tree is clean
* @returns {boolean} Whether the working tree is clean
*/
const isGitWorkingTreeClean = () => {
try {
// Check for uncommitted changes
const output = execSync('git status --porcelain', { encoding: 'utf-8' });
return output.trim() === '';
} catch (error) {
console.error('❌ Error checking Git status:', error.message);
return false;
}
};
/**
* Main function
*/
const main = () => {
let success = true;
let newVersion = '';
try {
// Check for uncommitted changes
if (!isGitWorkingTreeClean()) {
console.error('❌ Cannot proceed with release: You have uncommitted changes.');
console.log('Please commit or stash your changes before running the release script.');
process.exit(1);
}
// Check command line arguments
const args = process.argv.slice(2);
const versionType = args[0] || DEFAULT_VERSION_TYPE;
if (!Object.values(VERSION_TYPES).includes(versionType)) {
console.error(`❌ Invalid version type: ${versionType}`);
console.log(`Valid options: ${Object.values(VERSION_TYPES).join(', ')}`);
process.exit(1);
}
let previousVersion = getPreviousVersion()
// Step 1: Update package.json version
try {
newVersion = updatePackageVersion(versionType);
} catch (error) {
console.error('❌ Failed to update package.json version:', error.message);
success = false;
}
// Step 2: Update manifest.json version
if (success) {
try {
updateManifestVersion(newVersion);
} catch (error) {
console.error('❌ Failed to update manifest.json version:', error.message);
success = false;
}
}
// Step 2.5: Update versions.json version
if (success) {
try {
updateVersionsVersion(previousVersion, newVersion, MIN_APP_VERSION);
} catch (error) {
console.error('❌ Failed to update versions.json version:', error.message);
success = false;
}
}
// Step 3: Build the project
if (success) {
if (!buildProject()) {
console.error('❌ Build process failed.');
success = false;
}
}
// Step 4: Git operations
if (success) {
if (!createGitCommitAndTag(newVersion)) {
console.error('❌ Git operations failed.');
success = false;
}
}
// Check overall success
if (success) {
console.log(`\n🎉 Release ${newVersion} completed successfully!`);
} else {
console.error('❌ Release process failed. Rolling back version changes...');
rollbackVersions();
process.exit(1);
}
} catch (error) {
console.error('❌ Unexpected error during release process:', error.message);
rollbackVersions();
process.exit(1);
}
};
// Run script
main();

View file

@ -1,58 +1,74 @@
:root {
--obsidian-blur-bone-border-radius:1.33em;
--obsidian-blur-bone-line-height:1;
--obsidian-blur-brick-border-radius:1px;
--obsidian-blur-brick-line-height:1;
--obsidian-blur-filter:5px;
--obsidian-blur-bone-border-radius: 1.33em;
--obsidian-blur-bone-line-height: 1;
--obsidian-blur-brick-border-radius: 1px;
--obsidian-blur-brick-line-height: 1;
--obsidian-blur-filter: 5px;
}
body.theme-dark {
--obsidian-blur-brick-color:hsla(220,100%,100%,1);
--obsidian-blur-bone-color:hsla(220,100%,100%,1);
--obsidian-blur-brick-color: hsla(220, 100%, 100%, 1);
--obsidian-blur-bone-color: hsla(220, 100%, 100%, 1);
}
body.theme-light {
--obsidian-blur-brick-color:hsla(220,19%,6%,1);
--obsidian-blur-bone-color: hsla(220,19%,6%,1);
--obsidian-blur-brick-color: hsla(220, 19%, 6%, 1);
--obsidian-blur-bone-color: hsla(220, 19%, 6%, 1);
}
div.blur-block {
filter: blur(var(--obsidian-blur-filter)) !important;
-webkit-filter: blur(var(--obsidian-blur-filter)) !important;
body .markdown-rendered div.blur-block,
body .markdown-preview-view div.blur-block {
filter: blur(var(--obsidian-blur-filter));
-webkit-filter: blur(var(--obsidian-blur-filter));
}
.obsidian-blur-hover div.blur-block:hover {
filter: blur(0px) !important;
-webkit-filter: blur(0px) !important;
body .obsidian-blur-hover div.blur-block:hover {
filter: blur(0px);
-webkit-filter: blur(0px);
}
code.blur-inline {
filter: blur(var(--obsidian-blur-filter)) !important;
-webkit-filter: blur(var(--obsidian-blur-filter)) !important;
display:inline-block !important;
body .markdown-rendered code.blur-inline,
body .markdown-preview-view code.blur-inline {
filter: blur(var(--obsidian-blur-filter));
-webkit-filter: blur(var(--obsidian-blur-filter));
display: inline-block;
}
.obsidian-blur-hover code.blur-inline:hover {
filter: blur(0) !important;
-webkit-filter: blur(0px) !important;
body .obsidian-blur-hover code.blur-inline:hover {
filter: blur(0);
-webkit-filter: blur(0px);
}
code.blur-bone {
background-color: var(--obsidian-blur-bone-color) !important;
color: var(--obsidian-blur-bone-color) !important;
border-radius: var(--obsidian-blur-bone-border-radius) !important;
line-height: var(--obsidian-blur-bone-line-height) !important;
display:inline-block !important;
body .markdown-rendered code.blur-bone,
body .markdown-preview-view code.blur-bone {
background-color: var(--obsidian-blur-bone-color);
color: var(--obsidian-blur-bone-color);
border-radius: var(--obsidian-blur-bone-border-radius);
line-height: var(--obsidian-blur-bone-line-height);
display: inline-block;
}
.obsidian-blur-hover code.blur-bone:hover {
background-color: transparent !important;
body .obsidian-blur-hover code.blur-bone:hover {
background-color: transparent;
}
code.blur-brick {
background-color: var(--obsidian-blur-brick-color) !important;
color: var(--obsidian-blur-brick-color) !important;
border-radius: var(--obsidian-blur-brick-border-radius) !important;
line-height: var(--obsidian-blur-brick-line-height) !important;
display:inline-block !important;
body .markdown-rendered code.blur-brick,
body .markdown-preview-view code.blur-brick {
background-color: var(--obsidian-blur-brick-color);
color: var(--obsidian-blur-brick-color);
border-radius: var(--obsidian-blur-brick-border-radius);
line-height: var(--obsidian-blur-brick-line-height);
display: inline-block;
}
.obsidian-blur-hover code.blur-brick:hover {
background-color: transparent !important;
body .obsidian-blur-hover code.blur-brick:hover {
background-color: transparent;
}
.blur-bone-innerblock code.blur-bone {
margin: .25em;
margin: 0.25em;
}
.blur-brick-innerblock code.blur-brick {
margin: .25em;
margin: 0.25em;
}

49
workflows/release.yml Normal file
View file

@ -0,0 +1,49 @@
name: Release
on:
push:
tags:
- '*.*.*'
permissions:
contents: write
id-token: write
attestations: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Attest main.js
uses: actions/attest-build-provenance@v2
with:
subject-path: main.js
- name: Attest styles.css
uses: actions/attest-build-provenance@v2
with:
subject-path: styles.css
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
main.js
manifest.json
styles.css
generate_release_notes: true