import dts from "unplugin-dts/vite"; import { copyFile, cp, mkdir, readdir, readFile, rm, writeFile, } from "node:fs/promises"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { defineConfig, build as viteBuild } from "vite"; import noBundlePlugin from "vite-plugin-no-bundle"; const __dirname = dirname(fileURLToPath(import.meta.url)); const outputPathPrefix = process.env.BUILD_ENV !== "production" ? "dist-temp" : "dist"; const browserOutPath = `${outputPathPrefix}-browser`; const browserOutDir = resolve(__dirname, browserOutPath); const demoOutDir = resolve(__dirname, `${outputPathPrefix}-demo`); const entry = "src/index.ts"; const globalConfig = { libraryName: "twtxt-lib", basePath: resolve(__dirname), outDir: resolve(__dirname, outputPathPrefix), builds: [ { entry, outDir: browserOutDir, outfile: "twtxt-lib.js", target: "browser", }, { entry, outDir: browserOutDir, outfile: "twtxt-lib.min.js", target: "browser", }, { entry, outDir: resolve(__dirname, `${outputPathPrefix}-node`), target: "node", }, ], licenseBegin: "", // assigned in init() licenseEnd: "", // assigned in init() licensePath: resolve(__dirname, ".license-fragments"), /** @type Record|null */ pkg: null, // assigned in init() async init() { this.pkg = JSON.parse( await readFile(resolve(this.basePath, "package.json")), ); this.licenseBegin = await readFile( resolve(this.licensePath, "license-begin.txt"), ); this.licenseEnd = await readFile( resolve(this.licensePath, "license-end.txt"), ); this.builds = this.builds.map((config) => { config.entry = resolve(globalConfig.basePath, config.entry); config.minify = config?.outfile?.includes(".min.") ?? false; config.standalone = config?.target?.includes("browser") ?? false; config.format = "es"; return config; }); }, }; const purgeOutDirs = async (config) => { await rm(config.outDir, { recursive: true, force: true }); }; const buildWithVite = async (config) => { const plugins = [ dts({ exclude: ["./vitest.setup.ts", "**/__tests__/*.*"] }), ]; if (config.target !== "browser") plugins.push(noBundlePlugin()); await viteBuild( defineConfig({ define: { "globalThis.__BUILD_VERSION__": JSON.stringify( globalConfig.pkg.version, ), }, build: { lib: { entry: config.entry, name: globalConfig.libraryName, formats: [config.format], fileName: () => config?.outfile ?? "[name].js", }, emptyOutDir: false, outDir: config.outDir, minify: config.minify || false, sourcemap: true, rollupOptions: { external: [], preserveEntrySignatures: "strict", shimMissingExports: true, treeshake: false, }, target: config.target === "browser" ? "es6" : "node22", }, plugins, publicDir: false, resolve: { extensions: [".ts", ".js"], alias: { "@": `${globalConfig.basePath}/src`, }, }, }), ); }; const wrapWithLicense = async (config) => { const fileText = await readFile( resolve(config.outDir, config.outfile ?? "index.js"), ); await writeFile( resolve(config.outDir, config.outfile ?? "index.js"), [globalConfig.licenseBegin, fileText, globalConfig.licenseEnd].join(""), { encoding: "utf8", flag: "w" }, ); }; const copyDir = async (source, destination) => { const entries = await readdir(source, { withFileTypes: true }); await mkdir(destination, { recursive: true }); return Promise.all( entries.map(async (entry) => { const sourcePath = resolve(source, entry.name); const destinationPath = resolve(destination, entry.name); return entry.isDirectory() ? copyDir(sourcePath, destinationPath) : copyFile(sourcePath, destinationPath); }), ); }; const createDemoSite = async () => { await rm(demoOutDir, { recursive: true, force: true }); await copyDir(resolve(__dirname, "public"), demoOutDir); await copyFile( resolve(__dirname, "index.html"), resolve(demoOutDir, "index.html"), ); await copyDir(browserOutDir, resolve(demoOutDir, browserOutPath)); }; async function main() { await globalConfig.init(); await rm(globalConfig.outDir, { recursive: true, force: true }); await Promise.all([ ...globalConfig.builds.map((config) => purgeOutDirs(config)), ]); await Promise.all([ ...globalConfig.builds.map((config) => buildWithVite(config)), ]); await Promise.all([ ...globalConfig.builds.map((config) => wrapWithLicense(config)), ]); await createDemoSite(); console.log("All library file builds complete."); } main();