@storm-software/tsdown
Version:
A package containing `tsdown` utilities for building Storm Software libraries and applications
477 lines (467 loc) • 22.6 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// bin/tsdown.ts
var import_get_config2 = require("@storm-software/config-tools/get-config");
var import_console3 = require("@storm-software/config-tools/logger/console");
var import_find_workspace_root2 = require("@storm-software/config-tools/utilities/find-workspace-root");
var import_process_handler = require("@storm-software/config-tools/utilities/process-handler");
var import_commander = require("commander");
// src/build.ts
var import_devkit = require("@nx/devkit");
var import_build_tools = require("@storm-software/build-tools");
var import_get_config = require("@storm-software/config-tools/get-config");
var import_console2 = require("@storm-software/config-tools/logger/console");
var import_get_log_level = require("@storm-software/config-tools/logger/get-log-level");
var import_correct_paths = require("@storm-software/config-tools/utilities/correct-paths");
// ../../node_modules/.pnpm/defu@6.1.4/node_modules/defu/dist/defu.mjs
function isPlainObject(value) {
if (value === null || typeof value !== "object") {
return false;
}
const prototype = Object.getPrototypeOf(value);
if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
return false;
}
if (Symbol.iterator in value) {
return false;
}
if (Symbol.toStringTag in value) {
return Object.prototype.toString.call(value) === "[object Module]";
}
return true;
}
__name(isPlainObject, "isPlainObject");
function _defu(baseObject, defaults, namespace = ".", merger) {
if (!isPlainObject(defaults)) {
return _defu(baseObject, {}, namespace, merger);
}
const object = Object.assign({}, defaults);
for (const key in baseObject) {
if (key === "__proto__" || key === "constructor") {
continue;
}
const value = baseObject[key];
if (value === null || value === void 0) {
continue;
}
if (merger && merger(object, key, value, namespace)) {
continue;
}
if (Array.isArray(value) && Array.isArray(object[key])) {
object[key] = [...value, ...object[key]];
} else if (isPlainObject(value) && isPlainObject(object[key])) {
object[key] = _defu(
value,
object[key],
(namespace ? `${namespace}.` : "") + key.toString(),
merger
);
} else {
object[key] = value;
}
}
return object;
}
__name(_defu, "_defu");
function createDefu(merger) {
return (...arguments_) => (
// eslint-disable-next-line unicorn/no-array-reduce
arguments_.reduce((p, c) => _defu(p, c, "", merger), {})
);
}
__name(createDefu, "createDefu");
var defu = createDefu();
var defuFn = createDefu((object, key, currentValue) => {
if (object[key] !== void 0 && typeof currentValue === "function") {
object[key] = currentValue(object[key]);
return true;
}
});
var defuArrayFn = createDefu((object, key, currentValue) => {
if (Array.isArray(object[key]) && typeof currentValue === "function") {
object[key] = currentValue(object[key]);
return true;
}
});
// src/build.ts
var import_node_fs = require("fs");
var import_promises2 = __toESM(require("fs/promises"), 1);
var import_find_workspace_root = require("nx/src/utils/find-workspace-root");
var import_tsdown = require("tsdown");
// src/clean.ts
var import_console = require("@storm-software/config-tools/logger/console");
var import_promises = require("fs/promises");
async function clean(name = "TSDown", directory, config) {
(0, import_console.writeDebug)(` \u{1F9F9} Cleaning ${name} output path: ${directory}`, config);
const stopwatch = (0, import_console.getStopwatch)(`${name} output clean`);
await cleanDirectories(name, directory, config);
stopwatch();
}
__name(clean, "clean");
async function cleanDirectories(name = "TSDown", directory, config) {
await (0, import_promises.rm)(directory, {
recursive: true,
force: true
});
}
__name(cleanDirectories, "cleanDirectories");
// src/config.ts
var DEFAULT_BUILD_OPTIONS = {
platform: "node",
target: "node22",
format: [
"esm",
"cjs"
],
tsconfig: "tsconfig.json",
mode: "production",
globalName: "globalThis",
unused: {
level: "error"
},
injectShims: true,
watch: false,
bundle: true,
treeshake: true,
clean: true,
debug: false
};
// src/build.ts
var resolveOptions = /* @__PURE__ */ __name(async (userOptions) => {
const projectRoot = userOptions.projectRoot;
const workspaceRoot = (0, import_find_workspace_root.findWorkspaceRoot)(projectRoot);
if (!workspaceRoot) {
throw new Error("Cannot find Nx workspace root");
}
const config = await (0, import_get_config.getConfig)(workspaceRoot.dir);
(0, import_console2.writeDebug)(" \u2699\uFE0F Resolving build options", config);
const stopwatch = (0, import_console2.getStopwatch)("Build options resolution");
const projectGraph = await (0, import_devkit.createProjectGraphAsync)({
exitOnError: true
});
const projectJsonPath = (0, import_correct_paths.joinPaths)(workspaceRoot.dir, projectRoot, "project.json");
if (!(0, import_node_fs.existsSync)(projectJsonPath)) {
throw new Error("Cannot find project.json configuration");
}
const projectJsonFile = await import_promises2.default.readFile(projectJsonPath, "utf8");
const projectJson = JSON.parse(projectJsonFile);
const projectName = projectJson.name;
const projectConfigurations = (0, import_devkit.readProjectsConfigurationFromProjectGraph)(projectGraph);
if (!projectConfigurations?.projects?.[projectName]) {
throw new Error("The Build process failed because the project does not have a valid configuration in the project.json file. Check if the file exists in the root of the project.");
}
const options = defu(userOptions, DEFAULT_BUILD_OPTIONS);
options.name ??= `${projectName}-${options.format}`;
options.target ??= import_build_tools.DEFAULT_TARGET;
const packageJsonPath = (0, import_correct_paths.joinPaths)(workspaceRoot.dir, options.projectRoot, "package.json");
if (!(0, import_node_fs.existsSync)(packageJsonPath)) {
throw new Error("Cannot find package.json configuration");
}
const env = (0, import_build_tools.getEnv)("tsdown", options);
const result = {
...options,
config,
...userOptions,
tsconfig: (0, import_correct_paths.joinPaths)(projectRoot, userOptions.tsconfig ? userOptions.tsconfig.replace(projectRoot, "") : "tsconfig.json"),
format: options.format || "cjs",
entryPoints: await (0, import_build_tools.getEntryPoints)(config, projectRoot, projectJson.sourceRoot, userOptions.entry || [
"./src/index.ts"
], userOptions.emitOnAll),
outdir: userOptions.outputPath || (0, import_correct_paths.joinPaths)("dist", projectRoot),
plugins: [],
name: userOptions.name || projectName,
projectConfigurations,
projectName,
projectGraph,
sourceRoot: userOptions.sourceRoot || projectJson.sourceRoot || (0, import_correct_paths.joinPaths)(projectRoot, "src"),
minify: userOptions.minify || !userOptions.debug,
verbose: userOptions.verbose || (0, import_get_log_level.isVerbose)() || userOptions.debug === true,
includeSrc: userOptions.includeSrc === true,
metafile: userOptions.metafile !== false,
generatePackageJson: userOptions.generatePackageJson !== false,
clean: userOptions.clean !== false,
emitOnAll: userOptions.emitOnAll === true,
dts: userOptions.emitTypes === true ? {
transformer: "oxc"
} : userOptions.emitTypes,
bundleDts: userOptions.emitTypes,
assets: userOptions.assets ?? [],
shims: userOptions.injectShims !== true,
bundle: userOptions.bundle !== false,
watch: userOptions.watch === true,
define: {
STORM_FORMAT: JSON.stringify(options.format || "cjs"),
...options.format === "cjs" && options.injectShims ? {
"import.meta.url": "importMetaUrl"
} : {},
...Object.keys(env || {}).reduce((res, key) => {
const value = JSON.stringify(env[key]);
return {
...res,
[`process.env.${key}`]: value,
[`import.meta.env.${key}`]: value
};
}, {}),
...options.define
}
};
stopwatch();
return result;
}, "resolveOptions");
async function generatePackageJson(options) {
if (options.generatePackageJson !== false && (0, import_node_fs.existsSync)((0, import_correct_paths.joinPaths)(options.projectRoot, "package.json"))) {
(0, import_console2.writeDebug)(" \u270D\uFE0F Writing package.json file", options.config);
const stopwatch = (0, import_console2.getStopwatch)("Write package.json file");
const packageJsonPath = (0, import_correct_paths.joinPaths)(options.projectRoot, "project.json");
if (!(0, import_node_fs.existsSync)(packageJsonPath)) {
throw new Error("Cannot find package.json configuration");
}
const packageJsonFile = await import_promises2.default.readFile((0, import_correct_paths.joinPaths)(options.config.workspaceRoot, options.projectRoot, "package.json"), "utf8");
if (!packageJsonFile) {
throw new Error("Cannot find package.json configuration file");
}
let packageJson = JSON.parse(packageJsonFile);
packageJson = await (0, import_build_tools.addPackageDependencies)(options.config.workspaceRoot, options.projectRoot, options.projectName, packageJson);
packageJson = await (0, import_build_tools.addWorkspacePackageJsonFields)(options.config, options.projectRoot, options.sourceRoot, options.projectName, false, packageJson);
packageJson.exports ??= {};
packageJson.exports["./package.json"] ??= "./package.json";
packageJson.exports["."] ??= (0, import_build_tools.addPackageJsonExport)("index", packageJson.type, options.sourceRoot);
let entryPoints = [
{
in: "./src/index.ts",
out: "./src/index.ts"
}
];
if (options.entryPoints) {
if (Array.isArray(options.entryPoints)) {
entryPoints = options.entryPoints.map((entryPoint) => typeof entryPoint === "string" ? {
in: entryPoint,
out: entryPoint
} : entryPoint);
}
for (const entryPoint of entryPoints) {
const split = entryPoint.out.split(".");
split.pop();
const entry = split.join(".").replaceAll("\\", "/");
packageJson.exports[`./${entry}`] ??= (0, import_build_tools.addPackageJsonExport)(entry, packageJson.type, options.sourceRoot);
}
}
packageJson.main = packageJson.type === "commonjs" ? "./dist/index.js" : "./dist/index.cjs";
packageJson.module = packageJson.type === "module" ? "./dist/index.js" : "./dist/index.mjs";
packageJson.types = "./dist/index.d.ts";
packageJson.exports = Object.keys(packageJson.exports).reduce((ret, key) => {
if (key.endsWith("/index") && !ret[key.replace("/index", "")]) {
ret[key.replace("/index", "")] = packageJson.exports[key];
}
return ret;
}, packageJson.exports);
await (0, import_devkit.writeJsonFile)((0, import_correct_paths.joinPaths)(options.outdir, "package.json"), packageJson);
stopwatch();
}
return options;
}
__name(generatePackageJson, "generatePackageJson");
async function executeTSDown(options) {
(0, import_console2.writeDebug)(` \u{1F680} Running ${options.name} build`, options.config);
const stopwatch = (0, import_console2.getStopwatch)(`${options.name} build`);
await (0, import_tsdown.build)({
...options,
entry: options.entryPoints,
outDir: options.outdir,
config: false
});
stopwatch();
return options;
}
__name(executeTSDown, "executeTSDown");
async function copyBuildAssets(options) {
(0, import_console2.writeDebug)(` \u{1F4CB} Copying asset files to output directory: ${options.outdir}`, options.config);
const stopwatch = (0, import_console2.getStopwatch)(`${options.name} asset copy`);
await (0, import_build_tools.copyAssets)(options.config, options.assets ?? [], options.outdir, options.projectRoot, options.sourceRoot, true, false);
stopwatch();
return options;
}
__name(copyBuildAssets, "copyBuildAssets");
async function reportResults(options) {
(0, import_console2.writeSuccess)(` \u{1F4E6} The ${options.name} build completed successfully`, options.config);
}
__name(reportResults, "reportResults");
async function cleanOutputPath(options) {
if (options.clean !== false && options.outdir) {
(0, import_console2.writeDebug)(` \u{1F9F9} Cleaning ${options.name} output path: ${options.outdir}`, options.config);
const stopwatch = (0, import_console2.getStopwatch)(`${options.name} output clean`);
await cleanDirectories(options.name, options.outdir, options.config);
stopwatch();
}
return options;
}
__name(cleanOutputPath, "cleanOutputPath");
async function build(options) {
(0, import_console2.writeDebug)(` \u26A1 Executing Storm TSDown pipeline`);
const stopwatch = (0, import_console2.getStopwatch)("TSDown pipeline");
try {
const opts = Array.isArray(options) ? options : [
options
];
if (opts.length === 0) {
throw new Error("No build options were provided");
}
const resolved = await Promise.all(opts.map(async (opt) => await resolveOptions(opt)));
if (resolved.length > 0) {
await cleanOutputPath(resolved[0]);
await generatePackageJson(resolved[0]);
await Promise.all(resolved.map(async (opt) => {
await executeTSDown(opt);
await copyBuildAssets(opt);
await reportResults(opt);
}));
} else {
(0, import_console2.writeWarning)(" \u{1F6A7} No options were passed to TSBuild. Please check the parameters passed to the `build` function.");
}
(0, import_console2.writeSuccess)(" \u{1F3C1} TSDown pipeline build completed successfully");
} catch (error) {
(0, import_console2.writeFatal)("Fatal errors that the build process could not recover from have occured. The build process has been terminated.");
throw error;
} finally {
stopwatch();
}
}
__name(build, "build");
// bin/tsdown.ts
async function createProgram(config) {
try {
(0, import_console3.writeInfo)("\u26A1 Running Storm TSDown pipeline", config);
const root = (0, import_find_workspace_root2.findWorkspaceRootSafe)();
process.env.STORM_WORKSPACE_ROOT ??= root;
process.env.NX_WORKSPACE_ROOT_PATH ??= root;
if (root) {
process.chdir(root);
}
const program = new import_commander.Command("storm-tsdown");
program.version("1.0.0", "-v --version", "display CLI version");
program.description("\u26A1 Run the Storm TSDown pipeline").showHelpAfterError().showSuggestionAfterError();
const projectRootOption = new import_commander.Option("-p --project-root <path>", "The path to the root of the project to build. This path is defined relative to the workspace root.").makeOptionMandatory(true);
const sourceRootOption = new import_commander.Option("-s --source-root <path>", "The path of the project's source folder to build");
const nameOption = new import_commander.Option("-n --name <value>", "The name of the project to build");
const outputPathOption = new import_commander.Option("-o --output-path <path>", "The path of the project's source folder to build").default("dist/{projectRoot}");
const platformOption = new import_commander.Option("-p --platform <value>", "The platform to build the distribution for").choices([
"node",
"neutral",
"browser"
]).default("node");
const formatOption = new import_commander.Option("-f, --format <value...>", "The format to build the distribution in").choices([
"esm",
"cjs",
"iife"
]).argParser((value, previous) => {
if (previous === void 0) {
return [
value
];
} else if (!previous.includes(value)) {
previous.push(value);
}
return previous;
}).default("esm");
const cleanOption = new import_commander.Option("-c --clean", "Should the output directory be cleaned before building").default(true);
const noCleanOption = new import_commander.Option("--no-clean", "Should the output directory be cleaned before building").default(false);
const bundleOption = new import_commander.Option("-b --bundle", "Should the output be bundled").default(true);
const noBundleOption = new import_commander.Option("--no-bundle", "Should the output be bundled").default(false);
const targetOption = new import_commander.Option("-t --target <value>", "The target to build the distribution for").choices([
"ESNext",
"ES2015",
"ES2016",
"ES2017",
"ES2018",
"ES2019",
"ES2020",
"ES2021",
"ES2022",
"ES2023"
]).default("ESNext");
const watchOption = new import_commander.Option("-w --watch", "Should the build process watch for changes").default(false);
const debugOption = new import_commander.Option("-d --debug", "Should the build process run in debug mode").default(false);
const bannerOption = new import_commander.Option("--banner <value>", "The banner to prepend to the output");
const footerOption = new import_commander.Option("--footer <value>", "The footer to prepend to the output");
const splittingOption = new import_commander.Option("--splitting", "Should the output be split into multiple files").default(true);
const treeShakingOption = new import_commander.Option("--tree-shaking", "Should tree shaking be enabled").default(true);
const generatePackageJsonOption = new import_commander.Option("--generate-package-json", "Should a package.json be generated for the output").default(true);
const emitOnAllOption = new import_commander.Option("--emit-on-all", "Should the output be emitted on all platforms").default(false);
const metafileOption = new import_commander.Option("--metafile", "Should a metafile be generated for the output").default(true);
const minifyOption = new import_commander.Option("--minify", "Should the output be minified").default(true);
const includeSrcOption = new import_commander.Option("--include-src", "Should the source files be included in the output").default(false);
const verboseOption = new import_commander.Option("--verbose", "Should the build process be verbose").default(false);
const injectShimsOption = new import_commander.Option("--inject-shims", "Should shims be injected into the output").default(true);
const emitTypesOption = new import_commander.Option("--emit-types", "Should types be emitted for the output").default(true);
program.command("build", {
isDefault: true
}).alias("bundle").description("Run a TypeScript build using TSDown, API-Extractor, and TSC (for type generation).").addOption(nameOption).addOption(projectRootOption).addOption(sourceRootOption).addOption(outputPathOption).addOption(platformOption).addOption(formatOption).addOption(targetOption).addOption(bundleOption).addOption(noBundleOption).addOption(cleanOption).addOption(noCleanOption).addOption(watchOption).addOption(debugOption).addOption(bannerOption).addOption(footerOption).addOption(splittingOption).addOption(treeShakingOption).addOption(generatePackageJsonOption).addOption(emitOnAllOption).addOption(metafileOption).addOption(minifyOption).addOption(includeSrcOption).addOption(verboseOption).addOption(injectShimsOption).addOption(emitTypesOption).action(buildAction(config));
program.command("clean").alias("clear").description("Clean the output directory of the project. This command will remove the 'dist' folder.").addOption(nameOption).action(cleanAction(config));
return program;
} catch (e) {
(0, import_console3.writeFatal)(`A fatal error occurred while running the program: ${e.message}`, config);
process.exit(1);
}
}
__name(createProgram, "createProgram");
var buildAction = /* @__PURE__ */ __name((config) => async (options) => {
try {
await build({
...options,
format: options.format
});
} catch (e) {
(0, import_console3.writeFatal)(`A fatal error occurred while cleaning the TSDown output directory: ${e.message}`, config);
(0, import_process_handler.exitWithError)(config);
process.exit(1);
}
}, "buildAction");
var cleanAction = /* @__PURE__ */ __name((config) => async (options) => {
try {
await clean(options.name, options.output, config);
} catch (e) {
(0, import_console3.writeFatal)(`A fatal error occurred while cleaning the TSDown output directory: ${e.message}`, config);
(0, import_process_handler.exitWithError)(config);
process.exit(1);
}
}, "cleanAction");
void (async () => {
const config = await (0, import_get_config2.getConfig)();
const stopwatch = (0, import_console3.getStopwatch)("Storm TSDown executable");
try {
(0, import_process_handler.handleProcess)(config);
const program = await createProgram(config);
await program.parseAsync(process.argv);
(0, import_console3.writeSuccess)(`\u{1F389} Storm TSDown executable has completed successfully!`, config);
(0, import_process_handler.exitWithSuccess)(config);
} catch (error) {
(0, import_console3.writeFatal)(`A fatal error occurred while running Storm TSDown executable:
${error.message}`, config);
(0, import_process_handler.exitWithError)(config);
process.exit(1);
} finally {
stopwatch();
}
})();