const esbuild = require("esbuild"); const fs = require("node:fs"); const path = require("node:path"); const production = process.argv.includes("--production"); const watch = process.argv.includes("--watch"); /** * Needed by `syncCommandsPlugin`, which evaluates the commands, because `vscode` is not * a real package. `vscode` imports only make sense in the extension host context. We can't * just do `external: ["vscode"]` because we aren't bundling the extension in its host context. * * @type {import("esbuild").Plugin} */ const vscodeStubPlugin = { name: "stub-vscode", setup(build) { build.onResolve({ filter: /^vscode$/ }, (args) => ({ path: args.path, namespace: "vscode-stub" })); build.onLoad({ filter: /.*/, namespace: "vscode-stub" }, () => ({ loader: "js", contents: ` function stub() { return new Proxy(function () {}, { get: stub, apply: stub, construct: stub }); } module.exports = stub(); `, })); }, }; /** * Rewrites package.json's `contributes.commands` based on `src/commands/index.ts`, to * have a single source of truth. * * @type {import("esbuild").Plugin} */ const syncCommandsPlugin = { name: "sync-commands", setup(build) { build.onStart(async () => { const packageJsonPath = path.join(__dirname, "package.json"); const result = await esbuild.build({ entryPoints: [path.join(__dirname, "src/commands/index.ts")], bundle: true, write: false, format: "cjs", platform: "node", logLevel: "silent", plugins: [vscodeStubPlugin], }); // any command file has `export default defineCommand` const mod = { exports: {} }; const runModule = new Function("module", "exports", result.outputFiles[0].text); runModule(mod, mod.exports); const { commands } = mod.exports; const currentJSON = fs.readFileSync(packageJsonPath, "utf8"); const package = JSON.parse(currentJSON); package.contributes ??= {}; package.contributes.commands = commands .map(({ id, title }) => ({ command: `tatr-vsc.${id}`, title, })); const newJSON = `${JSON.stringify(package, null, 2)}\n`; if (newJSON !== currentJSON) { fs.writeFileSync(packageJsonPath, newJSON); console.log("Updated package.json contributes.commands."); } }); }, }; /** * @type {import("esbuild").Plugin} */ const esbuildProblemMatcherPlugin = { name: "esbuild-problem-matcher", setup(build) { build.onStart(() => { console.log("[watch] build started"); }); build.onEnd((result) => { result.errors.forEach(({ text, location }) => { console.error(`✘ [ERROR] ${text}`); console.error(` ${location.file}:${location.line}:${location.column}:`); }); console.log("[watch] build finished"); }); }, }; async function main() { const ctx = await esbuild.context({ entryPoints: [ "src/extension.ts" ], bundle: true, format: "cjs", minify: production, sourcemap: !production, sourcesContent: false, platform: "node", outfile: "dist/extension.js", external: ["vscode"], logLevel: "silent", plugins: [ syncCommandsPlugin, esbuildProblemMatcherPlugin, ], }); if (watch) { await ctx.watch(); } else { await ctx.rebuild(); await ctx.dispose(); } } main().catch(e => { console.error(e); process.exit(1); });