Files
twtxt-lib/build.js
2026-02-22 21:26:15 -05:00

185 lines
4.5 KiB
JavaScript

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<string, any>|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();