@hypernym/bundler
Version:
ESM & TS module bundler.
170 lines (169 loc) • 4.75 kB
JavaScript
import { createArgs } from "@hypernym/args";
import process, { cwd } from "node:process";
import { cyan, dim } from "@hypernym/colors";
import { isAbsolute, resolve } from "node:path";
import { exists, read, write } from "@hypernym/utils/fs";
import { build } from "rolldown";
import { build as build$1 } from "../build/index.js";
//#region src/bin/meta.ts
const name = `Hyperbundler`;
const version = `0.32.3`;
//#endregion
//#region src/utils/logger.ts
const cl = console.log;
const separator = `|`;
const logger = {
info: (...args) => {
cl(name, dim(separator), ...args);
},
error: (...args) => {
cl();
cl(name, dim(separator), ...args);
cl();
},
exit: (message) => {
cl();
cl(name, dim(separator), message);
cl();
return process.exit();
}
};
//#endregion
//#region src/utils/error.ts
function error(err) {
logger.error("Something went wrong...");
console.error(err);
return process.exit();
}
//#endregion
//#region src/utils/format-ms.ts
function formatMs(ms) {
const s = 1e3;
const m = s * 60;
const h = m * 60;
const msAbs = Math.abs(ms);
if (msAbs >= h) return `${(ms / h).toFixed(2)}h`;
if (msAbs >= m) return `${(ms / m).toFixed(2)}m`;
if (msAbs >= s) return `${(ms / s).toFixed(2)}s`;
return `${ms}ms`;
}
//#endregion
//#region src/utils/format-bytes.ts
function formatBytes(bytes) {
const decimals = 2;
const units = [
"B",
"KB",
"MB",
"GB",
"TB"
];
if (bytes === 0) return `0 B`;
const k = 1024;
const dm = decimals;
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${units[i]}`;
}
//#endregion
//#region src/config.ts
const externals = [
/^node:/,
/^@types/,
/^@rollup/,
/^@rolldown/,
/^@hypernym/,
/^rollup/,
/^rolldown/
];
//#endregion
//#region src/bin/loader.ts
async function getTSConfigPath(cwd, filePath = "tsconfig.json") {
const tsconfigPath = resolve(cwd, filePath);
if (await exists(tsconfigPath)) return tsconfigPath;
}
async function loadConfig(filePath, defaults) {
const cwd = defaults.cwd;
const result = await build({
input: resolve(cwd, filePath),
write: false,
external: (id) => !(isAbsolute(id) || /^(\.|@\/|~\/)/.test(id)),
tsconfig: defaults.tsconfig,
output: { format: "esm" }
});
const tempConfig = resolve(cwd, "node_modules/.hypernym/bundler/config.js");
await write(tempConfig, result.output[0].code);
const config = (await import(tempConfig)).default;
return {
options: {
...defaults,
...config
},
path: filePath
};
}
async function createConfigLoader(args) {
const cwdir = args.cwd && args.cwd.trim() !== "" ? resolve(args.cwd) : cwd();
const tsconfig = await getTSConfigPath(cwdir, args.tsconfig);
const pkgFile = await read(resolve(cwdir, "package.json")).catch(error);
const { dependencies } = JSON.parse(pkgFile);
const defaults = {
cwd: cwdir,
tsconfig,
externals: [...Object.keys(dependencies || {}), ...externals],
entries: []
};
const warnMessage = `Missing required configuration. To start bundling, add the ${cyan(`'bundler.config.{js,mjs,ts,mts}'`)} file to the project's root.`;
if (args.config) {
const path = resolve(args.config);
if (await exists(path)) return await loadConfig(path, defaults);
else return logger.exit(warnMessage);
}
const configName = "bundler.config";
for (const ext of [
".ts",
".mts",
".mjs",
".js"
]) {
const path = resolve(cwdir, `${configName}${ext}`);
if (await exists(path)) return await loadConfig(path, defaults);
}
return logger.exit(warnMessage);
}
//#endregion
//#region src/bin/builder.ts
async function createBuilder(config) {
const { options, path: configPath } = config;
const { hooks } = options;
const cl = console.log;
await hooks?.["bundle:start"]?.(options);
cl();
logger.info(dim(`v${version}`));
cl("Config", dim(configPath));
cl();
cl("Processing specified entries...");
cl();
await build$1(options).then((stats) => {
const entriesLength = options.entries.length;
const totalEntries = `${entriesLength} ${entriesLength > 1 ? "entries" : "entry"}`;
const filesLength = stats.files.length;
const totalFiles = `${stats.files.length} file${filesLength > 1 ? "s" : ""}`;
const buildTime = formatMs(stats.buildTime);
const buildSize = formatBytes(stats.size);
cl();
cl("Stats:", dim(`${totalEntries}, ${totalFiles}, ${buildSize}, ${buildTime}`));
cl();
cl("All entries successfully processed.");
cl("Bundle is optimized and ready for production.");
cl();
}).catch(error);
await hooks?.["bundle:end"]?.(options);
}
//#endregion
//#region src/bin/index.ts
async function main() {
await createBuilder(await createConfigLoader(createArgs({ alias: { config: "c" } })));
}
main().catch(error);
//#endregion