UNPKG

insta-toc

Version:

Simultaneously generate, update, and maintain a table of contents for your notes in real time.

212 lines (184 loc) 7.76 kB
import builtins from "builtin-modules"; import esbuild from "esbuild"; import esbuildSvelte from "esbuild-svelte"; import { copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from "fs"; import path from "path"; import process from "process"; import config from "./svelte.config.js"; 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.includes("production"); const shouldLog = process.argv.includes("logger"); let logs = []; const __dirname = process.cwd(); // Load the package.json file const packageJsonPath = path.join(__dirname, "package.json"); const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); const manifestJsonPath = path.join(__dirname, "manifest.json"); const manifestJson = JSON.parse(readFileSync(manifestJsonPath, "utf-8")); const dataJsonPath = path.join(__dirname, "data.json"); if (!existsSync(dataJsonPath)) { writeFileSync(dataJsonPath, "{}", "utf-8"); } const dataJson = JSON.parse(readFileSync(dataJsonPath, "utf-8")); // Retrieve the name of the package const packageName = packageJson.name; const packageVersion = packageJson.version; const packageMain = prod ? "dist/build/main.js" : "dist/dev/main.js"; packageJson.main = packageMain; writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 4), "utf-8"); logs.push(`Package Name: ${packageName}`); logs.push(`Package Version: ${packageVersion}`); logs.push(`Set main.js directory to ${packageJson.main}`); const { pluginRoot, vaultRoot } = { pluginRoot: __dirname, vaultRoot: decodeURI(__dirname.replace(/\/\.obsidian.*/, "")) }; logs.push(`pluginRoot: ${pluginRoot}\nvaultRoot: ${vaultRoot}`); const vaultName = vaultRoot.split("/").pop(); logs.push(`vaultName: ${vaultName}`); const runtimeStylesheetPath = path.join(__dirname, "styles.css"); const bareStylesheetImportPattern = /@import\s+["'](?![./])[A-Za-z@][^"']*["'];?/; const unsupportedBuildDirectivePattern = /@(plugin|config|theme|utility|variant|source|reference)\b/; function ensureRuntimeStylesheetIsBrowserReady(filePath) { const stylesheet = readFileSync(filePath, "utf-8"); const forbiddenDirectives = [ bareStylesheetImportPattern, unsupportedBuildDirectivePattern ]; if (!forbiddenDirectives.some((pattern) => pattern.test(stylesheet))) { return; } throw new Error( `The shipped runtime stylesheet (${ path.basename(filePath) }) must contain browser-ready CSS only. Remove package imports or build-time directives before releasing the plugin.` ); } ensureRuntimeStylesheetIsBrowserReady(runtimeStylesheetPath); const sourcePath = path.resolve(`${pluginRoot}/${packageMain}`); const targetPath = path.resolve(`${pluginRoot}/main.js`); logs.push(`Source Path: ${sourcePath}`); logs.push(`Target Path: ${targetPath}`); const context = await esbuild .context({ banner: { js: banner }, logLevel: "warning", plugins: [ esbuildSvelte({ preprocess: config.preprocess, compilerOptions: { ...config.compilerOptions, sourcemap: prod ? false : "inline" }, moduleCompilerOptions: { dev: !prod, experimental: { async: true }, rootDir: `${__dirname}/src/svelte` } // esbuildTsTransformOptions: {} }), { name: "copy-main-js", setup(build) { /** @type {Map<string, number>} filePath -> mtimeMs */ const inputMtimes = new Map(); let isFirstBuild = true; build.onStart(() => { if (isFirstBuild) return; // Detect which file triggered the rebuild let changedFile = null; for (const [ filePath, lastMtime ] of inputMtimes) { try { const { mtimeMs } = statSync(filePath); if (mtimeMs > lastMtime) { changedFile = filePath; break; } } catch { // File may have been deleted — that itself is a change changedFile = filePath; break; } } const changeInfo = changedFile ? ` (change: "${path.relative(__dirname, changedFile)}")` : ""; console.log(`[watch] build started${changeInfo}`); }); build.onEnd((result) => { if (result.errors.length === 0) { copyFileSync(sourcePath, targetPath); if (shouldLog) { console.log(`Copied: ${path.basename(sourcePath)} -> ${packageMain}`); console.log("[watch] build finished"); } // Snapshot input file mtimes for next rebuild's change detection if (result.metafile) { inputMtimes.clear(); for (const filePath of Object.keys(result.metafile.inputs)) { const absPath = path.resolve(__dirname, filePath); try { inputMtimes.set(absPath, statSync(absPath).mtimeMs); } catch { // skip files that can't be stat'd } } } } else { console.log("[watch] build skipped"); } isFirstBuild = false; }); } } ], 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 ], platform: "node", format: "cjs", target: "es2022", absWorkingDir: __dirname, sourcemap: prod ? false : "inline", treeShaking: true, metafile: !prod, outfile: packageMain, minify: prod, mainFields: [ "svelte", "browser", "module", "main" ], conditions: [ "svelte", "browser" ] }) .catch((error) => { console.error(error); process.exit(1); }); function copyMainJs() { try { copyFileSync(sourcePath, targetPath); logs.push(`Copied file: ${sourcePath} -> ${targetPath}`); logs = logs.join("\n"); if (shouldLog) console.log(logs); } catch (error) { console.error(`Error copying main.js: ${error}\nLogs:\n${logs.join("\n")}`); process.exit(1); } } if (prod) { await context.rebuild(); copyMainJs(); await context.dispose(); } else { if (shouldLog) console.log(logs.join("\n")); await context.watch(); }