A comprehensive set of build tools and configurations for LoopBack 4 and TypeScript projects providing CLI commands for compilation, linting, testing, and coverage
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Core utilities for running CLI commands and managing child processes with intelligent module resolution and cross-platform support.
Execute CLI tools with automatic module resolution and project-first dependency lookup.
/**
* Run a CLI command with intelligent module resolution
* @param cli - CLI module path (e.g., "typescript/lib/tsc", "eslint/bin/eslint")
* @param args - Command line arguments to pass to the CLI
* @param options - Execution options for process control and module resolution
* @returns ChildProcess when executed, string when dry run
*/
function runCLI(
cli: string,
args: string[],
options?: RunCLIOptions
): ChildProcess | string;
interface RunCLIOptions {
dryRun?: boolean; // Return command string instead of executing
cwd?: string; // Working directory for execution
resolveFromProjectFirst?: boolean; // Try project dependencies before @loopback/build
nodeArgs?: string[]; // Additional Node.js arguments
stdio?: string; // stdio configuration
env?: Record<string, string>; // Environment variables
}Usage Examples:
import { runCLI } from "@loopback/build";
// Run TypeScript compiler
const tscProcess = runCLI(
"typescript/lib/tsc",
["--project", "tsconfig.json"]
);
// Run with custom Node.js arguments
const eslintProcess = runCLI(
"eslint/bin/eslint",
["src/", "--fix"],
{ nodeArgs: ["--max-old-space-size=4096"] }
);
// Dry run to see command
const command = runCLI(
"prettier/bin/prettier.cjs",
["--write", "**/*.ts"],
{ dryRun: true }
);
console.log(command); // "node /path/to/prettier.cjs --write **/*.ts"
// Custom working directory
runCLI("mocha/bin/mocha", ["test/"], { cwd: "/path/to/project" });Execute shell commands with JSON-safe argument handling and cross-platform support.
/**
* Run a shell command with cross-platform support
* @param command - Command to execute (must be in PATH or absolute path)
* @param args - Command arguments (automatically JSON-quoted for safety)
* @param options - Execution options for process control
* @returns ChildProcess when executed, string when dry run
*/
function runShell(
command: string,
args: string[],
options?: RunShellOptions
): ChildProcess | string;
interface RunShellOptions {
dryRun?: boolean; // Return command string instead of executing
cwd?: string; // Working directory for execution
stdio?: string; // stdio configuration
env?: Record<string, string>; // Environment variables
shell?: boolean; // Enable shell execution (default: true)
}Usage Examples:
import { runShell } from "@loopback/build";
// Run npm commands
const npmProcess = runShell("npm", ["install", "--save-dev", "typescript"]);
// Run git commands
const gitProcess = runShell("git", ["add", "."]);
// Run with custom environment
const envProcess = runShell("node", ["script.js"], {
env: { NODE_ENV: "production", DEBUG: "myapp:*" }
});
// Dry run to see command
const command = runShell("docker", ["build", "-t", "myapp", "."], { dryRun: true });
console.log(command); // 'docker "build" "-t" "myapp" "."'Intelligent module resolution that prioritizes project dependencies over bundled tools.
/**
* Resolve CLI module path with project-first resolution
* @param cli - CLI module path to resolve
* @param options - Resolution options
* @returns Absolute path to the resolved CLI module
*/
function resolveCLI(
cli: string,
options?: { resolveFromProjectFirst?: boolean }
): string;
/**
* Resolve CLI from project dependencies only
* @param cli - CLI module path to resolve
* @param projectRootDir - Project root directory (defaults to process.cwd())
* @returns Absolute path to CLI module or undefined if not found
*/
function resolveCLIFromProject(
cli: string,
projectRootDir?: string
): string | undefined;
/**
* Extract package name from CLI module path
* @param cli - CLI module path (e.g., "typescript/lib/tsc", "@babel/cli/bin/babel")
* @returns Package name (e.g., "typescript", "@babel/cli")
*/
function getPackageName(cli: string): string;Resolution Examples:
import { resolveCLI, resolveCLIFromProject, getPackageName } from "@loopback/build";
// Resolve TypeScript compiler
const tscPath = resolveCLI("typescript/lib/tsc");
// Returns: "/project/node_modules/typescript/lib/tsc.js" (project version)
// Falls back to: "/node_modules/@loopback/build/node_modules/typescript/lib/tsc.js"
// Force resolution from project only
const projectTsc = resolveCLIFromProject("typescript/lib/tsc");
// Returns: "/project/node_modules/typescript/lib/tsc.js" or undefined
// Extract package names
const pkgName1 = getPackageName("typescript/lib/tsc"); // "typescript"
const pkgName2 = getPackageName("@babel/cli/bin/babel"); // "@babel/cli"
const pkgName3 = getPackageName("eslint/bin/eslint"); // "eslint"
// Skip project resolution (use bundled version)
const bundledEslint = resolveCLI("eslint/bin/eslint", {
resolveFromProjectFirst: false
});Helper functions for configuration file discovery and CLI option processing.
/**
* Get configuration file with fallback support
* @param name - Preferred configuration file name
* @param defaultName - Default fallback file name
* @returns Path to discovered configuration file
*/
function getConfigFile(name: string, defaultName?: string): string;
/**
* Get root directory of @loopback/build module
* @returns Absolute path to @loopback/build root directory
*/
function getRootDir(): string;
/**
* Get root directory of current npm package
* @returns Absolute path to current working directory
*/
function getPackageDir(): string;
/**
* Check if CLI options contain specified option names
* @param opts - Array of CLI option strings
* @param optionNames - Option names to check for
* @returns True if any option name is found
*/
function isOptionSet(opts: string[], ...optionNames: string[]): boolean;
/**
* Check if project has Mocha configuration files
* @returns True if any Mocha config file exists in project
*/
function mochaConfiguredForProject(): boolean;Utility Examples:
import {
getConfigFile,
getRootDir,
getPackageDir,
isOptionSet,
mochaConfiguredForProject
} from "@loopback/build";
// Find configuration files
const tsconfigPath = getConfigFile("tsconfig.build.json", "tsconfig.json");
const eslintrcPath = getConfigFile(".eslintrc.js", ".eslintrc.json");
// Get directory paths
const buildToolsRoot = getRootDir(); // "/node_modules/@loopback/build"
const projectRoot = getPackageDir(); // "/current/working/directory"
// Check CLI options
const opts = ["--fix", "--ext", ".ts", "src/"];
const hasFixFlag = isOptionSet(opts, "--fix"); // true
const hasConfigFlag = isOptionSet(opts, "-c", "--config"); // false
const hasExtFlag = isOptionSet(opts, "--ext"); // true
// Check project configuration
const hasMochaConfig = mochaConfiguredForProject();
// Checks for: .mocharc.js, .mocharc.json, .mocharc.yaml, .mocharc.ymlComprehensive process management with proper error handling and exit code propagation.
/**
* Process management features:
* - Proper stdio inheritance for interactive commands
* - Exit code propagation to parent process
* - Signal handling for graceful shutdowns
* - Cross-platform process spawning
* - Environment variable inheritance and customization
*/
interface ProcessFeatures {
stdioInheritance: "inherit"; // Pass stdio to child processes
exitCodePropagation: boolean; // Set process.exitCode on child exit
signalHandling: boolean; // Handle SIGTERM, SIGINT, etc.
crossPlatform: boolean; // Windows and Unix compatibility
environmentVariables: boolean; // Custom env var support
}Error Handling Examples:
import { runCLI, runShell } from "@loopback/build";
// Error handling with callbacks
const child = runCLI("eslint/bin/eslint", ["src/"]);
child.on('close', (code, signal) => {
if (code !== 0) {
console.error(`ESLint exited with code ${code}`);
process.exit(code);
}
});
// Shell command error handling
const shellChild = runShell("npm", ["test"]);
shellChild.on('error', (error) => {
console.error('Failed to start process:', error);
});
shellChild.on('close', (code, signal) => {
if (signal === 'SIGKILL') {
console.warn('Process was killed');
}
});Advanced patterns for complex build workflows and integrations.
Pipeline Execution:
import { runCLI, runShell } from "@loopback/build";
async function buildPipeline() {
// Clean build artifacts
await new Promise(resolve => {
const clean = runShell("rm", ["-rf", "dist"]);
clean.on('close', resolve);
});
// Compile TypeScript
await new Promise(resolve => {
const tsc = runCLI("typescript/lib/tsc", []);
tsc.on('close', resolve);
});
// Run tests
await new Promise(resolve => {
const mocha = runCLI("mocha/bin/mocha", ["dist/__tests__"]);
mocha.on('close', resolve);
});
}Custom Tool Integration:
import { runCLI, resolveCLI } from "@loopback/build";
// Custom TypeScript plugin workflow
function runWithTTypescript() {
try {
// Try to use project's ttypescript
const ttscPath = resolveCLI("ttypescript/lib/tsc");
return runCLI("ttypescript/lib/tsc", process.argv.slice(2));
} catch (error) {
// Fall back to regular TypeScript
console.warn("ttypescript not found, using regular tsc");
return runCLI("typescript/lib/tsc", process.argv.slice(2));
}
}
// Conditional tool execution
function runLinter(fix = false) {
const args = ["."];
if (fix) args.unshift("--fix");
return runCLI("eslint/bin/eslint", args, {
nodeArgs: ["--max-old-space-size=4096"] // More memory for large projects
});
}Special handling for TypeScript installation path discovery.
/**
* Resolved path to TypeScript installation
* Points to the directory containing TypeScript package
*/
const typeScriptPath: string;
// Usage example:
import { typeScriptPath } from "@loopback/build";
import path from "path";
const tscBin = path.join(typeScriptPath, "bin", "tsc");
const tsLib = path.join(typeScriptPath, "lib");