import { build } from "rolldown"; import { type ExtensionBuildSpec, isExternal } from "./spec.ts"; export interface BundleOptions { release?: boolean; } export interface BundleResult { /** Entry output files relative to the output directory. */ entries: string[]; /** Every emitted file relative to the output directory. */ files: string[]; } /** * Bundle one extension into `outDir`. * Chunks stay flat in the package root so `import.meta.url` relative resource * lookups resolve identically from the entry and from any chunk. */ export async function bundleExtension( spec: ExtensionBuildSpec, outDir: string, options: BundleOptions = {}, ): Promise { const result = await build({ input: spec.entries, cwd: spec.dir, platform: "node", // Source imports sibling modules as `./x.js`; the file on disk is `x.ts`. resolve: { extensionAlias: { ".js": [".ts", ".js"] } }, external: (id, _parent, isResolved) => isResolved ? false : isExternal(id, spec.externals), logLevel: "warn", onLog(level, log, defaultHandler) { if (level === "warn" && /import\.meta|circular/iu.test(log.message)) return; defaultHandler(level, log); }, output: { dir: outDir, format: "esm", entryFileNames: "[name].js", chunkFileNames: "[name]-[hash].js", hashCharacters: "base36", sourcemap: !options.release, minify: Boolean(options.release), }, }); const files: string[] = []; const entries: string[] = []; for (const output of result.output) { files.push(output.fileName); if (output.type === "chunk" && output.isEntry) entries.push(output.fileName); if (output.type === "chunk" && output.sourcemapFileName) files.push(output.sourcemapFileName); } return { entries, files }; }