Pre-release plugin verification checklist for Obsidian community plugins.
Use when preparing to release, reviewing before submission,
or validating plugin quality before publishing.
Trigger with phrases like "obsidian release checklist", "publish obsidian plugin",
"obsidian plugin submission", "obsidian prod ready".
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Pre-release plugin verification checklist for Obsidian community plugins.
Use when preparing to release, reviewing before submission,
or validating plugin quality before publishing.
Trigger with phrases like "obsidian release checklist", "publish obsidian plugin",
"obsidian plugin submission", "obsidian prod ready".
allowed-tools
Read, Grep, Bash(npm:*)
version
1.13.0
license
MIT
author
Jeremy Longshore <jeremy@intentsolutions.io>
tags
["saas","obsidian","obsidian-prod"]
compatibility
Designed for Claude Code, also compatible with Codex and OpenClaw
Obsidian Prod Checklist
Overview
Pre-release verification for Obsidian plugins covering manifest validation, production build quality, mobile compatibility, memory leak prevention, settings migration, and community plugin submission readiness.
Prerequisites
Completed plugin development with all features working
Tested in at least one vault manually
GitHub repository with source code committed
Node.js build toolchain configured
Instructions
Step 1: Validate manifest.json
// Run: node -e '<paste this>'const m = require('./manifest.json');
const required = ['id', 'name', 'version', 'minAppVersion', 'description', 'author'];
const missing = required.filter(f => !m[f]);
if (missing.length) {
console.error('FAIL: Missing fields:', missing.join(', '));
process.exit(1);
}
// id must be kebab-case, no spacesif (!/^[a-z0-9-]+$/.test(m.id)) {
console.error('FAIL: id must be lowercase alphanumeric with hyphens:', m.id);
process.exit(1);
}
// minAppVersion should be a recent Obsidian versionconst [major, minor] = m.minAppVersion.().();
(major < || (major === && minor < )) {
.(, m., );
}
.(, m., + m., + m. + );
split
'.'
map
Number
if
1
1
4
console
warn
'WARN: minAppVersion'
minAppVersion
'is very old — consider 1.5.0+'
console
log
'manifest.json OK:'
id
'v'
version
'(requires Obsidian >='
minAppVersion
')'
Step 2: Validate versions.json
// Run: node -e '<paste this>'const manifest = require('./manifest.json');
const versions = require('./versions.json');
const pkg = require('./package.json');
let fail = false;
// manifest.version should match package.json versionif (manifest.version !== pkg.version) {
console.error('FAIL: manifest.version (' + manifest.version + ') !== package.json (' + pkg.version + ')');
fail = true;
}
// versions.json must have an entry for current versionif (!versions[manifest.version]) {
console.error('FAIL: versions.json missing entry for', manifest.version);
fail = true;
} elseif (versions[manifest.version] !== manifest.minAppVersion) {
console.error('FAIL: versions.json[' + manifest.version + '] = ' +
versions[manifest.version] + ' but manifest.minAppVersion = ' + manifest.minAppVersion);
fail = true;
}
if (fail) process.exit(1);
console.log('versions.json OK: all versions consistent');
Step 3: Production Build Checks
set -euo pipefail
# Clean buildrm -f main.js
npm ci
npm run build
# Verify main.js exists and is reasonable sizetest -f main.js || { echo"FAIL: main.js not generated"; exit 1; }
SIZE=$(wc -c < main.js)
echo"main.js: $SIZE bytes"# No inline source maps in production (increases file size significantly)if grep -q "sourceMappingURL=data:" main.js; thenecho"WARN: Inline sourcemaps detected — remove for production"echo" Set sourcemap: false in esbuild.config.mjs"fi# No sourcemap file should shipif [ -f main.js.map ]; thenecho"WARN: main.js.map exists — exclude from release assets"fi# styles.css checkif [ -f styles.css ]; thenecho"styles.css: $(wc -c < styles.css) bytes — will be included in release"elseecho"No styles.css (OK if plugin has no custom styles)"fi
Step 4: Code Quality — No console.log in Production
set -euo pipefail
# Obsidian reviewers reject plugins with console.log in production code# Check source files (not the built main.js which may be minified)
HITS=$(grep -rn "console\.log\|console\.warn\|console\.info" src/ --include="*.ts" | grep -v "// DEBUG" | grep -v "\.test\." || true)
if [ -n "$HITS" ]; thenecho"WARN: console statements found in source (remove or guard with DEBUG flag):"echo"$HITS"elseecho"OK: No unguarded console statements in src/"fi# Check for eval() or Function() constructor — immediate rejection
DANGEROUS=$(grep -rn "eval(\|new Function(" src/ --include="*.ts" || true)
if [ -n "$DANGEROUS" ]; thenecho"FAIL: eval/Function() found — Obsidian team will reject this:"echo"$DANGEROUS"exit 1
fi
// GOOD: All resources cleaned up in onunloadexportdefaultclassMyPluginextendsPlugin {
privateobserver: MutationObserver | null = null;
privateintervalId: number | null = null;
asynconload() {
// Register events via this.registerEvent — auto-cleanedthis.registerEvent(
this.app.workspace.on('file-open', this.handleFileOpen.bind(this))
);
// Register intervals via this.registerInterval — auto-cleanedthis.intervalId = window.setInterval(() =>this.sync(), 60000);
this.registerInterval(this.intervalId);
// DOM observers need manual cleanupthis.observer = newMutationObserver(this.handleMutation.bind(this));
this.observer.observe(document.body, { childList: true });
}
onunload() {
// Clean up anything NOT registered via this.register*this.observer?.disconnect();
this.observer = null;
}
}
Common leak sources to audit:
setInterval / setTimeout not using this.registerInterval
addEventListener without matching removeEventListener
MutationObserver or ResizeObserver without disconnect()
WebSocket or EventSource connections without close()
Detached DOM nodes held in class properties
Step 6: Mobile Compatibility
// Check if running on mobileimport { Platform } from'obsidian';
if (Platform.isMobile) {
// Disable features that only work on desktop// - No child_process or fs access// - No Electron APIs (clipboard, shell, dialog)// - Touch targets must be >= 44px
}
// If your plugin is desktop-only, set in manifest.json:// "isDesktopOnly": true
Test on mobile:
Build and release (even a beta via BRAT)
Install on iOS/Android Obsidian
Verify: settings tab renders, commands work, no crashes on open/close
Check touch targets are large enough (44px minimum)