Add stylelint and configure eslint to add custom plugin.

- Split up main.ts into multiple files.
This commit is contained in:
Johannes Theiner
2024-07-31 22:59:59 +02:00
parent b9a0e401e0
commit 15e343739e
9 changed files with 10025 additions and 112 deletions

22
.eslintrc.js Normal file
View File

@@ -0,0 +1,22 @@
module.exports = {
"root": true,
"extends": [
"plugin:obsidian/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"ecmaVersion": 2020,
"sourceType": "module"
},
"env": {
"browser": true,
"node": false
},
"plugins": [
"obsidian"
],
"rules": {
}
}

9
.stylelintrc.json Normal file
View File

@@ -0,0 +1,9 @@
{
"extends": ["stylelint-config-standard"],
"rules": {
"at-rule-disallowed-list": ["import"],
"function-url-no-scheme-relative": true,
"function-url-scheme-allowed-list": ["data"],
"declaration-no-important": true
}
}

View File

@@ -15,7 +15,7 @@ const context = await esbuild.context({
banner: { banner: {
js: banner, js: banner,
}, },
entryPoints: ["main.ts"], entryPoints: ["src/main.ts"],
bundle: true, bundle: true,
external: [ external: [
"obsidian", "obsidian",
@@ -45,4 +45,4 @@ if (prod) {
process.exit(0); process.exit(0);
} else { } else {
await context.watch(); await context.watch();
} }

View File

@@ -1,56 +0,0 @@
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
{
languageOptions: {
parserOptions: {
sourceType: "module",
parser: "@typescript-eslint/parser",
project: "./tsconfig.json",
},
globals: {
...globals.browser, ...globals.worker
}
},
rules: {
"no-unused-vars": "off",
"no-prototype-bultins": "off",
"no-self-compare": "warn",
"no-eval": "error",
"no-implied-eval": "error",
"prefer-const": "off",
"no-console": [
"warn",
{
"allow": ["warn", "error", "debug"]
}
],
"no-restricted-globals": [
"error",
{
"name": "app",
"message": "Avoid using the global app object. Instead use the reference provided by your plugin instance."
}
],
"no-alert": "error",
"no-undef": "error",
"@typescript/eslint-ban-ts-comment": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"args": "none"
}
],
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/await-thenable": "warn",
"@typescript-eslint/no-invalid-this": "error",
"@typescript-eslint/no-require-imports": "warn",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-unnecessary-boolean-literal-compare": "warn"
}
},
);

9898
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,23 +7,34 @@
"dev": "node esbuild.config.mjs", "dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json", "version": "node version-bump.mjs && git add manifest.json versions.json",
"lint": "eslint src" "lint": "eslint --ext .js,.ts,.json src && stylelint styles.css",
"link": "npm link eslint-plugin-obsidian",
"test": "npm run link && npm run lint"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.6", "@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0", "builtin-modules": "3.3.0",
"@typescript-eslint/parser": "5.29.0",
"esbuild": "0.20.1", "esbuild": "0.20.1",
"eslint": "8.57.0", "stylelint": "^16.3.1",
"typescript-eslint": "7.1.0", "stylelint-config-standard": "^36.0.0",
"tslib": "2.4.0", "tslib": "2.4.0",
"typescript": "4.7.4", "typescript": "4.7.4",
"eslint": "8.56.0",
"typescript-eslint": "7.1.0",
"eslint-plugin-deprecation": "2.0.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-security": "2.1.1",
"eslint-plugin-json-schema-validator": "5.1.0",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"@microsoft/eslint-plugin-sdl": "0.2.2",
"globals": "14.0.0" "globals": "14.0.0"
}, },
"dependencies": { "dependencies": {
"eslint-plugin-obsidian": "file:../eslint-plugin",
"obsidian": "latest" "obsidian": "latest"
} }
} }

View File

@@ -1,15 +1,9 @@
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; import {App, Editor, MarkdownView, Modal, moment, Notice, Plugin} from 'obsidian';
import {DEFAULT_SETTINGS, MyPluginSettings, SampleSettingTab} from "./settings";
import {readFile} from "fs/promises";
// Remember to rename these classes and interfaces! // Remember to rename these classes and interfaces!
interface MyPluginSettings {
mySetting: string;
}
const DEFAULT_SETTINGS: MyPluginSettings = {
mySetting: 'default'
}
export default class MyPlugin extends Plugin { export default class MyPlugin extends Plugin {
settings: MyPluginSettings; settings: MyPluginSettings;
@@ -17,21 +11,26 @@ export default class MyPlugin extends Plugin {
await this.loadSettings(); await this.loadSettings();
// This creates an icon in the left ribbon. // This creates an icon in the left ribbon.
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => { this.addRibbonIcon('dice', 'Sample', (evt: MouseEvent) => {
// Called when the user clicks the icon. // Called when the user clicks the icon.
new Notice('This is a notice!'); new Notice('This is a Notice!');
const content = readFile('test');
this.app.workspace.activeLeaf?.detach();
const el = document.createDiv();
el.style.color = 'white';
el.style.backgroundColor = 'red';
}); });
// Perform additional things with the ribbon
ribbonIconEl.addClass('my-plugin-ribbon-class');
// This adds a status bar item to the bottom of the app. Does not work on mobile apps. // This adds a status bar item to the bottom of the app. Does not work on mobile apps.
const statusBarItemEl = this.addStatusBarItem(); const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText('Status Bar Text'); statusBarItemEl.innerHTML = "Hello world";
statusBarItemEl.innerHTML = "<span>" + this.settings.mySetting + "</span>"
statusBarItemEl.setText('Status bar text');
// This adds a simple command that can be triggered anywhere // This adds a simple command that can be triggered anywhere
this.addCommand({ this.addCommand({
id: 'open-sample-modal-simple', id: 'open-sample-plugin-modal-simple',
name: 'Open sample modal (simple)', name: 'Open sample plugin modal (simple)',
callback: () => { callback: () => {
new SampleModal(this.app).open(); new SampleModal(this.app).open();
} }
@@ -39,16 +38,16 @@ export default class MyPlugin extends Plugin {
// This adds an editor command that can perform some operation on the current editor instance // This adds an editor command that can perform some operation on the current editor instance
this.addCommand({ this.addCommand({
id: 'sample-editor-command', id: 'sample-editor-command',
name: 'Sample editor command', name: 'Sample plugin editor command',
editorCallback: (editor: Editor, view: MarkdownView) => { editorCallback: (editor: Editor, view: MarkdownView) => {
console.log(editor.getSelection()); editor.replaceSelection('Sample editor command');
editor.replaceSelection('Sample Editor Command');
} }
}); });
// This adds a complex command that can check whether the current state of the app allows execution of the command // This adds a complex command that can check whether the current state of the app allows execution of the command
this.addCommand({ this.addCommand({
id: 'open-sample-modal-complex', id: 'open-sample-modal-complex',
name: 'Open sample modal (complex)', name: 'Open sample modal (complex)',
hotkeys: [{modifiers: ['Mod'], key: 'a'}],
checkCallback: (checking: boolean) => { checkCallback: (checking: boolean) => {
// Conditions to check // Conditions to check
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
@@ -62,6 +61,7 @@ export default class MyPlugin extends Plugin {
// This command will only show up in Command Palette when the check function returns true // This command will only show up in Command Palette when the check function returns true
return true; return true;
} }
return false;
} }
}); });
@@ -71,19 +71,24 @@ export default class MyPlugin extends Plugin {
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
// Using this function will automatically remove the event listener when this plugin is disabled. // Using this function will automatically remove the event listener when this plugin is disabled.
this.registerDomEvent(document, 'click', (evt: MouseEvent) => { this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
console.log('click', evt); new Notice("click");
"test".replace(/(?<=([ab]+)([bc]+))$/, "test");
}); });
// When registering intervals, this function will automatically clear the interval when the plugin is disabled. // When registering intervals, this function will automatically clear the interval when the plugin is disabled.
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
moment().format('YYYY-MM-DD HH:mm:ss');
} }
onunload() { onunload() {
const {workspace} = this.app;
workspace.detachLeavesOfType('test');
} }
async loadSettings() { async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
this.settings = Object.assign(DEFAULT_SETTINGS, await this.loadData());
} }
async saveSettings() { async saveSettings() {
@@ -97,38 +102,12 @@ class SampleModal extends Modal {
} }
onOpen() { onOpen() {
const {contentEl} = this; let {contentEl} = this;
contentEl.setText('Woah!'); contentEl.setText('Woah!');
} }
onClose() { onClose() {
const {contentEl} = this; var {contentEl} = this;
contentEl.empty(); contentEl.empty();
} }
} }
class SampleSettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const {containerEl} = this;
containerEl.empty();
new Setting(containerEl)
.setName('Setting #1')
.setDesc('It\'s a secret')
.addText(text => text
.setPlaceholder('Enter your secret')
.setValue(this.plugin.settings.mySetting)
.onChange(async (value) => {
this.plugin.settings.mySetting = value;
await this.plugin.saveSettings();
}));
}
}

42
src/settings.ts Normal file
View File

@@ -0,0 +1,42 @@
import {App, PluginSettingTab, Setting} from "obsidian";
import MyPlugin from "./main";
export interface MyPluginSettings {
mySetting: string;
}
export const DEFAULT_SETTINGS: MyPluginSettings = {
mySetting: 'default'
}
export class SampleSettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const {containerEl} = this;
containerEl.empty();
containerEl.createEl('h1', {text: 'Settings'});
new Setting(containerEl).setName("Settings for Sample plugin").setHeading();
new Setting(containerEl).setName("General settings").setHeading();
new Setting(containerEl)
.setName('Settings #1')
.setDesc('It\'s a secret')
.addText(text => text
.setPlaceholder('Enter your secret')
.setValue(this.plugin.settings.mySetting)
.onChange(async (value) => {
this.plugin.settings.mySetting = value;
await this.plugin.saveSettings();
}));
}
}

View File

@@ -6,3 +6,11 @@ available in the app when your plugin is enabled.
If your plugin does not need CSS, delete this file. If your plugin does not need CSS, delete this file.
*/ */
@import url('https://fonts.googleapis.com/css2?family=Jersey+10&display=swap');
a { background-image: url('https://www.example.com/file.jpg'); }
div { background-image: url('//example.com/file.jpg'); }
a.test { color: pink !important; }