UNPKG

@storm-software/workspace-tools

Version:

Tools for managing a Storm workspace, including various Nx generators and executors for common development tasks.

365 lines (358 loc) • 11.4 kB
import { DEFAULT_TARGET, addPackageDependencies, addPackageJsonExport, addWorkspacePackageJsonFields, copyAssets, getEnv } from "./chunk-I4X5CIJM.mjs"; import { withRunExecutor } from "./chunk-EYZGKQNH.mjs"; import { getWorkspaceConfig } from "./chunk-4PKTZSV2.mjs"; import { brandIcon, getStopwatch, writeDebug, writeFatal, writeInfo, writeSuccess, writeWarning } from "./chunk-MMA4S6LZ.mjs"; import { findWorkspaceRoot } from "./chunk-3J2CP54B.mjs"; import { joinPaths } from "./chunk-TBW5MCN6.mjs"; // ../tsdown/src/build.ts import { createProjectGraphAsync, readProjectsConfigurationFromProjectGraph, writeJsonFile } from "@nx/devkit"; import defu from "defu"; import { existsSync } from "node:fs"; import hf from "node:fs/promises"; import { build as tsdown } from "tsdown"; // ../tsdown/src/clean.ts import { rm } from "node:fs/promises"; async function cleanDirectories(name = "TSDown", directory, config) { await rm(directory, { recursive: true, force: true }); } // ../tsdown/src/config.ts function getDefaultOptions(config) { return { entry: ["./src/*.ts"], platform: "node", target: "esnext", mode: "production", dts: true, unused: { level: "error", ignore: ["typescript"] }, publint: true, fixedExtension: true, ...config }; } function toTSDownFormat(format) { if (!format || Array.isArray(format) && format.length === 0) { return ["cjs", "es"]; } else if (format === "esm") { return "es"; } else if (Array.isArray(format)) { return format.map((f) => f === "esm" ? "es" : f); } return format; } // ../tsdown/src/build.ts var resolveOptions = async (userOptions) => { const options = getDefaultOptions(userOptions); const workspaceRoot = findWorkspaceRoot(options.projectRoot); if (!workspaceRoot) { throw new Error("Cannot find Nx workspace root"); } const workspaceConfig = await getWorkspaceConfig(options.debug === true, { workspaceRoot }); writeDebug(" \u2699\uFE0F Resolving build options", workspaceConfig); const stopwatch = getStopwatch("Build options resolution"); const projectGraph = await createProjectGraphAsync({ exitOnError: true }); const projectJsonPath = joinPaths( workspaceRoot, options.projectRoot, "project.json" ); if (!existsSync(projectJsonPath)) { throw new Error("Cannot find project.json configuration"); } const projectJsonFile = await hf.readFile(projectJsonPath, "utf8"); const projectJson = JSON.parse(projectJsonFile); const projectName = projectJson.name; const projectConfigurations = 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 packageJsonPath = joinPaths( workspaceRoot, options.projectRoot, "package.json" ); if (!existsSync(packageJsonPath)) { throw new Error("Cannot find package.json configuration"); } const debug = options.debug ?? (options.mode || workspaceConfig.mode) === "development"; const sourceRoot = projectJson.sourceRoot || joinPaths(options.projectRoot, "src"); const result = { name: projectName, mode: "production", target: DEFAULT_TARGET, generatePackageJson: true, outDir: joinPaths("dist", options.projectRoot), minify: !debug, plugins: [], assets: [], dts: true, shims: true, silent: !debug, logLevel: workspaceConfig.logLevel === "success" || workspaceConfig.logLevel === "performance" || workspaceConfig.logLevel === "debug" || workspaceConfig.logLevel === "trace" || workspaceConfig.logLevel === "all" ? "info" : workspaceConfig.logLevel === "fatal" ? "error" : workspaceConfig.logLevel, sourcemap: debug ? "inline" : false, clean: false, fixedExtension: true, nodeProtocol: true, tsconfig: joinPaths(options.projectRoot, "tsconfig.json"), debug, sourceRoot, cwd: workspaceConfig.workspaceRoot, entry: { ["index"]: joinPaths(sourceRoot, "index.ts") }, workspace: true, ...options, treeshake: options.treeShaking !== false, format: toTSDownFormat(options.format), workspaceConfig, projectName, projectGraph, projectConfigurations }; result.env = defu( options.env, getEnv("tsdown", result) ); stopwatch(); return result; }; async function generatePackageJson(options) { if (options.generatePackageJson !== false && existsSync(joinPaths(options.projectRoot, "package.json"))) { writeDebug(" \u270D\uFE0F Writing package.json file", options.workspaceConfig); const stopwatch = getStopwatch("Write package.json file"); const packageJsonPath = joinPaths(options.projectRoot, "project.json"); if (!existsSync(packageJsonPath)) { throw new Error("Cannot find package.json configuration"); } const packageJsonFile = await hf.readFile( joinPaths( options.workspaceConfig.workspaceRoot, options.projectRoot, "package.json" ), "utf8" ); if (!packageJsonFile) { throw new Error("Cannot find package.json configuration file"); } let packageJson = JSON.parse(packageJsonFile); packageJson = await addPackageDependencies( options.workspaceConfig.workspaceRoot, options.projectRoot, options.projectName, packageJson ); packageJson = await addWorkspacePackageJsonFields( options.workspaceConfig, options.projectRoot, options.sourceRoot, options.projectName, false, packageJson ); packageJson.exports ??= {}; packageJson.exports["./package.json"] ??= "./package.json"; packageJson.exports["."] ??= addPackageJsonExport( "index", packageJson.type, options.sourceRoot ); let entry = [{ in: "./src/index.ts", out: "./src/index.ts" }]; if (options.entry) { if (Array.isArray(options.entry)) { entry = options.entry.map( (entryPoint) => typeof entryPoint === "string" ? { in: entryPoint, out: entryPoint } : entryPoint ); } for (const entryPoint of entry) { const split = entryPoint.out.split("."); split.pop(); const entry2 = split.join(".").replaceAll("\\", "/"); packageJson.exports[`./${entry2}`] ??= addPackageJsonExport( entry2, options.fixedExtension ? "fixed" : packageJson.type, options.sourceRoot ); } } packageJson.main = !options.fixedExtension && packageJson.type === "commonjs" ? "./dist/index.js" : "./dist/index.cjs"; packageJson.module = !options.fixedExtension && packageJson.type === "module" ? "./dist/index.js" : "./dist/index.mjs"; packageJson.types = `./dist/index.d.${!options.fixedExtension ? "ts" : "mts"}`; 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 writeJsonFile(joinPaths(options.outDir, "package.json"), packageJson); stopwatch(); } return options; } async function executeTSDown(options) { writeDebug(` \u{1F680} Running ${options.name} build`, options.workspaceConfig); const stopwatch = getStopwatch(`${options.name} build`); await tsdown({ ...options, entry: options.entry, config: false }); stopwatch(); return options; } async function copyBuildAssets(options) { writeDebug( ` \u{1F4CB} Copying asset files to output directory: ${options.outDir}`, options.workspaceConfig ); const stopwatch = getStopwatch(`${options.name} asset copy`); await copyAssets( options.workspaceConfig, options.assets ?? [], options.outDir, options.projectRoot, options.sourceRoot, true, false ); stopwatch(); return options; } async function reportResults(options) { writeSuccess( ` \u{1F4E6} The ${options.name} build completed successfully`, options.workspaceConfig ); } async function cleanOutputPath(options) { if (options.clean !== false && options.workspaceConfig) { writeDebug( ` \u{1F9F9} Cleaning ${options.name} output path: ${options.workspaceConfig}`, options.workspaceConfig ); const stopwatch = getStopwatch(`${options.name} output clean`); await cleanDirectories( options.name, options.outDir, options.workspaceConfig ); stopwatch(); } return options; } async function build(options) { writeDebug(` ${brandIcon()} Executing Storm TSDown pipeline`); const stopwatch = 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 { writeWarning( " \u{1F6A7} No options were passed to TSBuild. Please check the parameters passed to the `build` function." ); } writeSuccess(" \u{1F3C1} TSDown pipeline build completed successfully"); } catch (error) { writeFatal( "Fatal errors that the build process could not recover from have occured. The build process has been terminated." ); throw error; } finally { stopwatch(); } } // src/executors/tsdown/executor.ts async function tsdownExecutorFn(options, context, config) { writeInfo("\u{1F4E6} Running Storm TSDown executor on the workspace", config); if (!context.projectsConfigurations?.projects || !context.projectName || !context.projectsConfigurations.projects[context.projectName] || !context.projectsConfigurations.projects[context.projectName]?.root) { throw new Error( "The Build process failed because the context is not valid. Please run this command from a workspace." ); } await build({ ...options, projectRoot: ( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion context.projectsConfigurations.projects?.[context.projectName].root ), name: context.projectName, sourceRoot: context.projectsConfigurations.projects?.[context.projectName]?.sourceRoot, format: options.format, platform: options.platform }); return { success: true }; } var executor_default = withRunExecutor( "Storm TSDown build", tsdownExecutorFn, { skipReadingConfig: false, hooks: { applyDefaultOptions: async (options) => { options.entry ??= ["src/index.ts"]; options.outputPath ??= "dist/{projectRoot}"; options.tsconfig ??= "{projectRoot}/tsconfig.json"; return options; } } } ); export { tsdownExecutorFn, executor_default };