## 前提 以下がインストールされている。 - [[Bun]] - [[Jest]] 以下を利用する。 - [[Jest]] - [[husky]] - [[Chokidar]] ([[ホットリロード]]用) ## 手順 ### インストールコマンド ```console rm -rf package-lock.json node_moules bun add -D @types/bun husky@latest chokidar esbuild@latest esbuild-jest@latest ``` ### tsconfig.json `types`と`include`に追加。 ```json { "compilerOptions": { "types": ["@types/bun"] }, "include": ["**/*.ts", "**/*.mts"] } ``` > [!note] > `include`の`"**/*.mts"`を追加しないと、[[Top-Level await]]などが[[IDE]]上でエラーになってしまう。 ### package.json ```json "scripts": { "dev": "bun esbuild.config.mjs", "build": "tsc -noEmit -skipLibCheck && bun esbuild.config.mjs production", "prepare": "husky install", "pre:push": "tsc -noEmit -skipLibCheck && bun run test", "test": "jest", "ci": "bun install && bun run build && bun run test", "release": "bun ci && bun version-bump.mts ${VERSION} && git add package.json manifest-beta.json manifest.json versions.json bun.lockb && git commit -m ${VERSION} && git tag ${VERSION} && git push --tags && git push" } ``` ### esbuild.config.mts ```ts import fs from "fs"; import path from "path"; import builtins from "builtin-modules"; import chokidar from "chokidar"; import esbuild from "esbuild"; import process from "process"; // TODO: 自分のVaultパスを設定する const VAULT_DIR = "/mnt/c/Users/syoum/work/minerva"; const PLUGIN_DIR_NAME = "mobile-first-daily-interface"; const FILES = ["main.js", "manifest.json", "styles.css"]; // --- const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ `; const prod = process.argv[2] === "production"; const context = await esbuild.context({ banner: { js: banner, }, entryPoints: ["src/main.ts"], bundle: true, external: [ "obsidian", "electron", "@codemirror/autocomplete", "@codemirror/collab", "@codemirror/commands", "@codemirror/language", "@codemirror/lint", "@codemirror/search", "@codemirror/state", "@codemirror/view", "@lezer/common", "@lezer/highlight", "@lezer/lr", ...builtins, ], format: "cjs", target: "es2018", logLevel: "info", sourcemap: prod ? false : "inline", treeShaking: true, outfile: "main.js", }); if (prod) { await context.rebuild(); process.exit(0); } else { await context.watch(); const pluginDir = path.join( VAULT_DIR, ".obsidian/plugins", PLUGIN_DIR_NAME, ); console.log(`📁 Creating ${pluginDir} (if not existed)`); fs.mkdirSync(pluginDir, { recursive: true }); const hotreloadPath = path.join(pluginDir, ".hotreload", ""); console.log(`🌶️ Creating a ${hotreloadPath}`); fs.writeFileSync(hotreloadPath, ""); const watcher = chokidar.watch(FILES, { persistent: true }); watcher .on("add", (p) => { console.log(`♨️ ${p} is added`); fs.copyFileSync(p, path.join(pluginDir, p)); }) .on("change", (p) => { console.log(`♨️ ${p} is changed`); fs.copyFileSync(p, path.join(pluginDir, p)); }); } ``` ### version-bump.mts ```ts import { readFileSync, writeFileSync } from "fs"; import { exit } from "process"; function updateVersion(version: string) { const packageJson = JSON.parse(readFileSync("package.json", "utf8")); packageJson.version = version; writeFileSync("package.json", JSON.stringify(packageJson, null, " ")); const manifestBeta = JSON.parse(readFileSync("manifest-beta.json", "utf8")); manifestBeta.version = version; writeFileSync("manifest-beta.json", JSON.stringify(manifestBeta, null, " ")); if (version.includes("beta")) { return; } const manifest = JSON.parse(readFileSync("manifest.json", "utf8")); const { minAppVersion } = manifest; manifest.version = version; writeFileSync("manifest.json", JSON.stringify(manifest, null, " ")); // update versions.json with target version and minAppVersion from manifest.json const versions = JSON.parse(readFileSync("versions.json", "utf8")); versions[version] = minAppVersion; writeFileSync("versions.json", JSON.stringify(versions, null, " ")); } const [_1, _2, version] = Bun.argv; if (!version) { console.error("Required: ${version} (ex: bun version 1.2.3)"); exit(1); } if (!Boolean(version.match(/\d+\.\d+\.\d+/))) { console.error("The version is not valid (ex: bun version 1.2.3)"); exit(1); } updateVersion(version); ``` ### GitHub Actions `.github/workflows/test.yaml` ```yaml jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - run: bun ci ``` `.github/workflows/release.yaml` ```yaml jobs: release: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - run: bun ci - name: Create Release id: create_release uses: softprops/action-gh-release@v2 with: draft: true files: | main.js styles.css manifest.json manifest-beta.json ```