@decaf-ts/utils
Version:
module management utils for decaf-ts
778 lines • 33.8 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BuildScripts = void 0;
exports.parseList = parseList;
exports.packageToGlobal = packageToGlobal;
exports.getPackageDependencies = getPackageDependencies;
const command_1 = require("./../command.cjs");
const constants_1 = require("./../constants.cjs");
const utils_1 = require("./../../utils/index.cjs");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const rollup_1 = require("rollup");
const plugin_typescript_1 = __importDefault(require("@rollup/plugin-typescript"));
const plugin_commonjs_1 = __importDefault(require("@rollup/plugin-commonjs"));
const plugin_node_resolve_1 = require("@rollup/plugin-node-resolve");
const plugin_json_1 = __importDefault(require("@rollup/plugin-json"));
const module_1 = require("module");
const logging_1 = require("@decaf-ts/logging");
const ts = __importStar(require("typescript"));
const typescript_1 = require("typescript");
function parseList(input) {
if (!input)
return [];
if (Array.isArray(input))
return input.map((i) => `${i}`.trim()).filter(Boolean);
return `${input}`
.split(",")
.map((p) => p.trim())
.filter(Boolean);
}
function packageToGlobal(name) {
// Remove scope and split by non-alphanumeric chars, then camelCase
const withoutScope = name.replace(/^@/, "");
const parts = withoutScope.split(/[/\-_.]+/).filter(Boolean);
return parts
.map((p, i) => i === 0
? p.replace(/[^a-zA-Z0-9]/g, "")
: `${p.charAt(0).toUpperCase()}${p.slice(1)}`)
.join("");
}
function getPackageDependencies() {
// Try the current working directory first
let pkg;
try {
pkg = (0, utils_1.getPackage)(process.cwd());
}
catch {
pkg = undefined;
}
// If no dependencies found in cwd, try the package next to this source file (fallback for tests)
try {
const hasDeps = pkg &&
(Object.keys(pkg.dependencies || {}).length > 0 ||
Object.keys(pkg.devDependencies || {}).length > 0 ||
Object.keys(pkg.peerDependencies || {}).length > 0);
if (!hasDeps) {
const fallbackDir = path_1.default.resolve(__dirname, "../../..");
try {
pkg = (0, utils_1.getPackage)(fallbackDir);
}
catch {
// ignore and keep pkg as-is
}
}
}
catch {
// ignore
}
const deps = Object.keys((pkg && pkg.dependencies) || {});
const peer = Object.keys((pkg && pkg.peerDependencies) || {});
const dev = Object.keys((pkg && pkg.devDependencies) || {});
return Array.from(new Set([...deps, ...peer, ...dev]));
}
const VERSION_STRING = "##VERSION##";
const PACKAGE_STRING = "##PACKAGE##";
const PACKAGE_SIZE_STRING = "##PACKAGE_SIZE##";
var Modes;
(function (Modes) {
Modes["CJS"] = "commonjs";
Modes["ESM"] = "es2022";
})(Modes || (Modes = {}));
var BuildMode;
(function (BuildMode) {
BuildMode["BUILD"] = "build";
BuildMode["BUNDLE"] = "bundle";
BuildMode["ALL"] = "all";
})(BuildMode || (BuildMode = {}));
const options = {
prod: {
type: "boolean",
default: false,
},
dev: {
type: "boolean",
default: false,
},
buildMode: {
type: "string",
default: BuildMode.ALL,
},
includes: {
type: "string",
default: "",
},
externals: {
type: "string",
default: "",
},
docs: {
type: "boolean",
default: false,
},
commands: {
type: "boolean",
default: false,
},
banner: {
type: "boolean",
default: false,
},
};
const cjs2Transformer = (ext = ".cjs") => {
const log = BuildScripts.log.for(cjs2Transformer);
const resolutionCache = new Map();
return (transformationContext) => {
return (sourceFile) => {
const sourceDir = path_1.default.dirname(sourceFile.fileName);
function resolvePath(importPath) {
const cacheKey = JSON.stringify([sourceDir, importPath]);
const cachedValue = resolutionCache.get(cacheKey);
if (cachedValue != null)
return cachedValue;
let resolvedPath = importPath;
try {
resolvedPath = path_1.default.resolve(sourceDir, resolvedPath + ".ts");
}
catch (error) {
throw new Error(`Failed to resolve path ${importPath}: ${error}`);
}
let stat;
try {
stat = fs_1.default.statSync(resolvedPath);
}
catch (e) {
try {
log.verbose(`Testing existence of path ${resolvedPath} as a folder defaulting to index file`);
stat = fs_1.default.statSync(resolvedPath.replace(/\.ts$/gm, ""));
}
catch (e2) {
throw new Error(`Failed to resolve path ${importPath}: ${e}, ${e2}`);
}
}
if (stat.isDirectory())
resolvedPath = resolvedPath.replace(/\.ts$/gm, "/index.ts");
if (path_1.default.isAbsolute(resolvedPath)) {
const extension = (/\.tsx?$/.exec(path_1.default.basename(resolvedPath)) || [])[0] || void 0;
resolvedPath =
"./" +
path_1.default.relative(sourceDir, path_1.default.resolve(path_1.default.dirname(resolvedPath), path_1.default.basename(resolvedPath, extension) + ext));
}
resolutionCache.set(cacheKey, resolvedPath);
return resolvedPath;
}
function visitNode(node) {
if (shouldMutateModuleSpecifier(node)) {
if (ts.isImportDeclaration(node)) {
const resolvedPath = resolvePath(node.moduleSpecifier.text);
const newModuleSpecifier = transformationContext.factory.createStringLiteral(resolvedPath);
return transformationContext.factory.updateImportDeclaration(node, node.modifiers, node.importClause, newModuleSpecifier, undefined);
}
else if (ts.isExportDeclaration(node)) {
const resolvedPath = resolvePath(node.moduleSpecifier.text);
const newModuleSpecifier = transformationContext.factory.createStringLiteral(resolvedPath);
return transformationContext.factory.updateExportDeclaration(node, node.modifiers, node.isTypeOnly, node.exportClause, newModuleSpecifier, undefined);
}
}
return ts.visitEachChild(node, visitNode, transformationContext);
}
function shouldMutateModuleSpecifier(node) {
if (!ts.isImportDeclaration(node) && !ts.isExportDeclaration(node))
return false;
if (node.moduleSpecifier === undefined)
return false;
// only when module specifier is valid
if (!ts.isStringLiteral(node.moduleSpecifier))
return false;
// only when path is relative
if (!node.moduleSpecifier.text.startsWith("./") &&
!node.moduleSpecifier.text.startsWith("../"))
return false;
// only when module specifier has no extension
if (path_1.default.extname(node.moduleSpecifier.text) !== "")
return false;
return true;
}
return ts.visitNode(sourceFile, visitNode);
};
};
};
/**
* @description A command-line script for building and bundling TypeScript projects.
* @summary This class provides a comprehensive build script that handles TypeScript compilation,
* bundling with Rollup, and documentation generation. It supports different build modes
* (development, production), module formats (CJS, ESM), and can be extended with custom
* configurations.
* @class BuildScripts
*/
class BuildScripts extends command_1.Command {
constructor() {
super("BuildScripts", Object.assign({}, constants_1.DefaultCommandOptions, options));
this.replacements = {};
const pkg = (0, utils_1.getPackage)();
const { name, version } = pkg;
this.pkgName = name.includes("@") ? name.split("/")[1] : name;
this.pkgVersion = version;
this.replacements[VERSION_STRING] = this.pkgVersion;
this.replacements[PACKAGE_STRING] = name;
}
/**
* @description Patches files with version and package name.
* @summary This method reads all files in a directory, finds placeholders for version
* and package name, and replaces them with the actual values from package.json.
* @param {string} p - The path to the directory containing the files to patch.
*/
patchFiles(p) {
const log = this.log.for(this.patchFiles);
const { name, version } = (0, utils_1.getPackage)();
log.info(`Patching ${name} ${version} module in ${p}...`);
const stat = fs_1.default.statSync(p);
if (stat.isDirectory())
fs_1.default.readdirSync(p, { withFileTypes: true, recursive: true })
.filter((p) => p.isFile())
.forEach((file) => (0, utils_1.patchFile)(path_1.default.join(file.parentPath, file.name), Object.entries(this.replacements).reduce((acc, [key, val]) => {
switch (key) {
case VERSION_STRING:
log.debug("Found VERSION string to replace");
acc[`VERSION = "${VERSION_STRING}";`] =
`VERSION = "${val}";`;
break;
case PACKAGE_STRING:
log.debug("Found PACKAGE_NAME string to replace");
acc[`PACKAGE_NAME = "${PACKAGE_STRING}";`] =
`PACKAGE_NAME = "${val}";`;
break;
default:
acc[key] = val;
}
return acc;
}, {})));
log.verbose(`Module ${name} ${version} patched in ${p}...`);
}
reportDiagnostics(diagnostics, logLevel) {
const msg = this.formatDiagnostics(diagnostics);
try {
this.log[logLevel](msg);
}
catch (e) {
console.warn(`Failed to get logger for ${logLevel}`);
throw e;
}
return msg;
}
// Format diagnostics into a single string for throwing or logging
formatDiagnostics(diagnostics) {
return diagnostics
.map((diagnostic) => {
let message = "";
if (diagnostic.file && diagnostic.start) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
message += `${diagnostic.file.fileName} (${line + 1},${character + 1})`;
}
message +=
": " + ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
return message;
})
.join("\n");
}
readConfigFile(configFileName) {
// Read config file
const configFileText = fs_1.default.readFileSync(configFileName).toString();
// Parse JSON, after removing comments. Just fancier JSON.parse
const result = ts.parseConfigFileTextToJson(configFileName, configFileText);
const configObject = result.config;
if (!configObject) {
this.reportDiagnostics([result.error], logging_1.LogLevel.error);
}
// Extract config infromation
const configParseResult = ts.parseJsonConfigFileContent(configObject, ts.sys, path_1.default.dirname(configFileName));
if (configParseResult.errors.length > 0)
this.reportDiagnostics(configParseResult.errors, logging_1.LogLevel.error);
return configParseResult;
}
evalDiagnostics(diagnostics) {
if (diagnostics && diagnostics.length > 0) {
const errors = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Error);
const warnings = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Warning);
const suggestions = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Suggestion);
const messages = diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Message);
// Log diagnostics to console
if (warnings.length)
this.reportDiagnostics(warnings, logging_1.LogLevel.warn);
if (errors.length) {
this.reportDiagnostics(diagnostics, logging_1.LogLevel.error);
throw new Error(`TypeScript reported ${diagnostics.length} diagnostic(s) during check; aborting.`);
}
if (suggestions.length)
this.reportDiagnostics(suggestions, logging_1.LogLevel.info);
if (messages.length)
this.reportDiagnostics(messages, logging_1.LogLevel.info);
}
}
preCheckDiagnostics(program) {
const diagnostics = ts.getPreEmitDiagnostics(program);
this.evalDiagnostics(diagnostics);
}
// Create a TypeScript program for the current tsconfig and fail if there are any error diagnostics.
async checkTsDiagnostics(isDev, mode, bundle = false) {
const log = this.log.for(this.checkTsDiagnostics);
let tsConfig;
try {
tsConfig = this.readConfigFile("./tsconfig.json");
}
catch (e) {
throw new Error(`Failed to parse tsconfig.json: ${e}`);
}
if (bundle) {
tsConfig.options.module = typescript_1.ModuleKind.AMD;
tsConfig.options.outDir = "dist";
tsConfig.options.isolatedModules = false;
tsConfig.options.outFile = this.pkgName;
}
else {
tsConfig.options.outDir = `lib${mode === Modes.ESM ? "/esm" : ""}`;
tsConfig.options.module =
mode === Modes.ESM ? typescript_1.ModuleKind.ES2022 : typescript_1.ModuleKind.CommonJS;
}
// Ensure TypeScript emits inline source maps for both dev and prod (bundlers will control external maps)
// Keep comments in TS emit by default; bundling/minification will handle removal where requested.
// Emit external source maps from TypeScript so editors/debuggers can find them.
// Turn off inline maps/sources so bundlers (Rollup) can control whether maps are inlined or written externally.
tsConfig.options.inlineSourceMap = false;
tsConfig.options.inlineSources = false;
tsConfig.options.sourceMap = true;
const program = ts.createProgram(tsConfig.fileNames, tsConfig.options);
this.preCheckDiagnostics(program);
log.verbose(`TypeScript checks passed (${bundle ? "bundle" : "normal"} mode).`);
}
async buildTs(isDev, mode, bundle = false) {
const log = this.log.for(this.buildTs);
log.info(`Building ${this.pkgName} ${this.pkgVersion} module (${mode}) in ${isDev ? "dev" : "prod"} mode...`);
let tsConfig;
try {
tsConfig = this.readConfigFile("./tsconfig.json");
}
catch (e) {
throw new Error(`Failed to parse tsconfig.json: ${e}`);
}
if (bundle) {
tsConfig.options.module = typescript_1.ModuleKind.AMD;
tsConfig.options.outDir = "dist";
tsConfig.options.isolatedModules = false;
tsConfig.options.outFile = this.pkgName;
}
else {
tsConfig.options.outDir = `lib${mode === Modes.ESM ? "/esm" : ""}`;
tsConfig.options.module =
mode === Modes.ESM ? typescript_1.ModuleKind.ES2022 : typescript_1.ModuleKind.CommonJS;
}
// Always emit inline source maps from tsc (bundler will emit external maps for production bundles).
// For dev builds we want TypeScript to emit inline source maps so no separate .map files are produced.
// For production we emit external source maps so the bundler can further transform and emit them.
if (isDev) {
tsConfig.options.inlineSourceMap = true;
tsConfig.options.inlineSources = true;
tsConfig.options.sourceMap = false;
}
else {
tsConfig.options.inlineSourceMap = false;
tsConfig.options.inlineSources = false;
tsConfig.options.sourceMap = true;
}
// For production builds we still keep TypeScript comments (removeComments=false in tsconfig)
// Bundler/terser will strip comments for production bundles as requested.
const program = ts.createProgram(tsConfig.fileNames, tsConfig.options);
const transformations = {};
if (mode === Modes.CJS) {
transformations.before = [cjs2Transformer(".cjs")];
}
else if (mode === Modes.ESM) {
transformations.before = [cjs2Transformer(".js")];
}
const emitResult = program.emit(undefined, undefined, undefined, undefined, transformations);
const allDiagnostics = ts
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics);
this.evalDiagnostics(allDiagnostics);
}
async build(isDev, mode, bundle = false) {
const log = this.log.for(this.build);
await this.buildTs(isDev, mode, bundle);
log.verbose(`Module ${this.pkgName} ${this.pkgVersion} (${mode}) built in ${isDev ? "dev" : "prod"} mode...`);
if (mode === Modes.CJS && !bundle) {
const files = (0, utils_1.getAllFiles)("lib", (file) => file.endsWith(".js") && !file.includes("/esm/"));
for (const file of files) {
log.verbose(`Patching ${file}'s cjs imports...`);
const f = file.replace(".js", ".cjs");
await (0, utils_1.renameFile)(file, f);
}
}
}
/**
* @description Copies assets to the build output directory.
* @summary This method checks for the existence of an 'assets' directory in the source
* and copies it to the appropriate build output directory (lib or dist).
* @param {Modes} mode - The build mode (CJS or ESM).
*/
copyAssets(mode) {
const log = this.log.for(this.copyAssets);
let hasAssets = false;
try {
hasAssets = fs_1.default.statSync("./src/assets").isDirectory();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (e) {
return log.verbose(`No assets found in ./src/assets to copy`);
}
if (hasAssets)
(0, utils_1.copyFile)("./src/assets", `./${mode === Modes.CJS ? "lib" : "dist"}/assets`);
}
/**
* @description Bundles the project using Rollup.
* @summary This method configures and runs Rollup to bundle the project. It handles
* different module formats, development and production builds, and external dependencies.
* @param {Modes} mode - The module format (CJS or ESM).
* @param {boolean} isDev - Whether it's a development build.
* @param {boolean} isLib - Whether it's a library build.
* @param {string} [entryFile="src/index.ts"] - The entry file for the bundle.
* @param {string} [nameOverride=this.pkgName] - The name of the output bundle.
* @param {string|string[]} [externalsArg] - A list of external dependencies.
* @param {string|string[]} [includeArg] - A list of dependencies to include.
* @returns {Promise<void>}
*/
async bundle(mode, isDev, isLib, entryFile = "src/index.ts", nameOverride = this.pkgName, externalsArg, includeArg = [
"prompts",
"styled-string-builder",
"typed-object-accumulator",
"@decaf-ts/logging",
]) {
// Run a TypeScript-only diagnostic check for the bundling configuration and fail fast on any errors.
await this.checkTsDiagnostics(isDev, mode, true);
const isEsm = mode === Modes.ESM;
const pkgName = this.pkgName;
const log = this.log;
// normalize include and externals
const include = Array.from(new Set([...parseList(includeArg)]));
let externalsList = parseList(externalsArg);
if (externalsList.length === 0) {
// if no externals specified, list top-level packages in node_modules (expand scopes)
try {
externalsList = (0, utils_1.listNodeModulesPackages)(path_1.default.join(process.cwd(), "node_modules"));
}
catch {
// fallback to package.json dependencies if listing fails or yields nothing
}
if (!externalsList || externalsList.length === 0) {
externalsList = getPackageDependencies();
}
}
const ext = Array.from(new Set([
// builtins and always external runtime deps
...(function builtinList() {
try {
return (Array.isArray(module_1.builtinModules) ? module_1.builtinModules : []);
}
catch {
// fallback to a reasonable subset if `builtinModules` is unavailable
return [
"fs",
"path",
"process",
"child_process",
"util",
"https",
"http",
"os",
"stream",
"crypto",
"zlib",
"net",
"tls",
"url",
"querystring",
"assert",
"events",
"tty",
"dns",
"querystring",
];
}
})(),
...externalsList,
]));
// For plugin-typescript we want it to emit source maps (not inline) so Rollup can
// decide whether to inline or emit external files. The Rollup output.sourcemap
// controls final map placement. Do NOT set a non-standard `sourcemap` field on
// the rollup input options (Rollup will reject it).
const rollupSourceMapOutput = isDev
? "inline"
: true;
const plugins = [
(0, plugin_typescript_1.default)({
compilerOptions: {
module: "esnext",
declaration: false,
outDir: isLib ? "bin" : "dist",
// For dev bundles emit inline source maps (no separate .map files).
// For prod bundles emit external maps so Rollup can write them to disk.
sourceMap: isDev ? false : true,
inlineSourceMap: isDev ? true : false,
inlineSources: isDev ? true : false,
},
include: ["src/**/*.ts"],
exclude: ["node_modules", "**/*.spec.ts"],
tsconfig: "./tsconfig.json",
}),
(0, plugin_json_1.default)(),
];
if (isLib) {
plugins.push((0, plugin_commonjs_1.default)({
include: [],
exclude: externalsList,
}), (0, plugin_node_resolve_1.nodeResolve)({
resolveOnly: include,
}));
}
// production minification: add terser last so it sees prior source maps
try {
const terserMod = await Promise.resolve().then(() => __importStar(require("@rollup/plugin-terser")));
const terserFn = (terserMod && terserMod.terser) || terserMod.default || terserMod;
const terserOptionsDev = {
parse: { ecma: 2020 },
compress: false,
mangle: false,
format: {
comments: false,
beautify: true,
},
};
const terserOptionsProd = {
parse: { ecma: 2020 },
compress: {
ecma: 2020,
passes: 5,
drop_console: true,
drop_debugger: true,
toplevel: true,
module: isEsm,
unsafe: true,
unsafe_arrows: true,
unsafe_comps: true,
collapse_vars: true,
reduce_funcs: true,
reduce_vars: true,
},
mangle: {
toplevel: true,
},
format: {
comments: false,
ascii_only: true,
},
toplevel: true,
};
plugins.push(terserFn(isDev ? terserOptionsDev : terserOptionsProd));
}
catch {
// if terser isn't available, ignore
}
const input = {
input: entryFile,
plugins: plugins,
external: ext,
onwarn: undefined,
// enable tree-shaking for production bundles
treeshake: !isDev,
};
// prepare output globals mapping for externals
const globals = {};
// include all externals and builtins (ext) so Rollup won't guess names for builtins
ext.forEach((e) => {
globals[e] = packageToGlobal(e);
});
const outputs = [
{
file: `${isLib ? "bin/" : "dist/"}${nameOverride ? nameOverride : `.bundle.${!isDev ? "min" : ""}`}${isEsm ? ".js" : ".cjs"}`,
format: isLib ? "cjs" : isEsm ? "esm" : "umd",
name: pkgName,
esModule: isEsm,
// output sourcemap: inline for dev, external for prod
sourcemap: rollupSourceMapOutput,
globals: globals,
exports: "auto",
},
];
try {
const bundle = await (0, rollup_1.rollup)(input);
// only log watchFiles at verbose level to avoid noisy console output
log.verbose(bundle.watchFiles);
async function generateOutputs(bundle) {
for (const outputOptions of outputs) {
await bundle.write(outputOptions);
}
}
await generateOutputs(bundle);
}
catch (e) {
throw new Error(`Failed to bundle: ${e}`);
}
}
async buildByEnv(isDev, mode = BuildMode.ALL, includesArg, externalsArg) {
// note: includes and externals will be passed through from run() into this method by callers
try {
(0, utils_1.deletePath)("lib");
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (e) {
// do nothing
}
try {
(0, utils_1.deletePath)("dist");
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (e) {
// do nothing
}
fs_1.default.mkdirSync("lib");
fs_1.default.mkdirSync("dist");
if ([BuildMode.ALL, BuildMode.BUILD].includes(mode)) {
await this.build(isDev, Modes.ESM);
await this.build(isDev, Modes.CJS);
this.patchFiles("lib");
}
if ([BuildMode.ALL, BuildMode.BUNDLE].includes(mode)) {
await this.bundle(Modes.ESM, isDev, false, "src/index.ts", this.pkgName, externalsArg, includesArg);
await this.bundle(Modes.CJS, isDev, false, "src/index.ts", this.pkgName, externalsArg, includesArg);
this.patchFiles("dist");
}
this.copyAssets(Modes.CJS);
this.copyAssets(Modes.ESM);
}
/**
* @description Builds the project for development.
* @summary This method runs the build process with development-specific configurations.
* @param {BuildMode} [mode=BuildMode.ALL] - The build mode (build, bundle, or all).
* @param {string|string[]} [includesArg] - A list of dependencies to include.
* @param {string|string[]} [externalsArg] - A list of external dependencies.
* @returns {Promise<void>}
*/
async buildDev(mode = BuildMode.ALL, includesArg, externalsArg) {
return this.buildByEnv(true, mode, includesArg, externalsArg);
}
/**
* @description Builds the project for production.
* @summary This method runs the build process with production-specific configurations,
* including minification and other optimizations.
* @param {BuildMode} [mode=BuildMode.ALL] - The build mode (build, bundle, or all).
* @param {string|string[]} [includesArg] - A list of dependencies to include.
* @param {string|string[]} [externalsArg] - A list of external dependencies.
* @returns {Promise<void>}
*/
async buildProd(mode = BuildMode.ALL, includesArg, externalsArg) {
return this.buildByEnv(false, mode, includesArg, externalsArg);
}
/**
* @description Generates the project documentation.
* @summary This method uses JSDoc and other tools to generate HTML documentation for the project.
* It also patches the README.md file with version and package size information.
* @returns {Promise<void>}
*/
async buildDocs() {
await (0, utils_1.runCommand)(`npm install better-docs taffydb`).promise;
await (0, utils_1.runCommand)(`npx markdown-include ./workdocs/readme-md.json`).promise;
await (0, utils_1.runCommand)(`npx jsdoc -c ./workdocs/jsdocs.json -t ./node_modules/better-docs`).promise;
await (0, utils_1.runCommand)(`npm remove better-docs taffydb`).promise;
[
{
src: "workdocs/assets",
dest: "./docs/workdocs/assets",
},
{
src: "workdocs/reports/coverage",
dest: "./docs/workdocs/reports/coverage",
},
{
src: "workdocs/reports/html",
dest: "./docs/workdocs/reports/html",
},
{
src: "workdocs/resources",
dest: "./docs/workdocs/resources",
},
{
src: "LICENSE.md",
dest: "./docs/LICENSE.md",
},
].forEach((f) => {
const { src, dest } = f;
(0, utils_1.copyFile)(src, dest);
});
// patch ./README.md file to replace version/package/package size strings
try {
const sizeKb = await (0, utils_1.getFileSizeZipped)(path_1.default.resolve(path_1.default.join(process.cwd(), "dist")));
this.replacements[PACKAGE_SIZE_STRING] = `${sizeKb} KB`;
}
catch {
// if we couldn't compute size, leave placeholder or set to unknown
this.replacements[PACKAGE_SIZE_STRING] = "unknown";
}
// Patch README.md in project root
try {
(0, utils_1.patchFile)("./README.md", this.replacements);
}
catch (e) {
const log = this.log.for(this.buildDocs);
log.verbose(`Failed to patch README.md: ${e}`);
}
}
async run(answers) {
const { dev, prod, docs, buildMode, includes, externals } = answers;
if (dev) {
return await this.buildDev(buildMode, includes, externals);
}
if (prod) {
return await this.buildProd(buildMode, includes, externals);
}
if (docs) {
return await this.buildDocs();
}
}
}
exports.BuildScripts = BuildScripts;
//# sourceMappingURL=build-scripts.js.map