The core execution system provides the main functionality for spawning commands with environment variables loaded from files. It handles process management, signal propagation, and environment variable processing.
Main function for executing commands with environment variables loaded from various file sources.
/**
* The main env-cmd program. This will spawn a new process and run the given command using
* various environment file solutions.
*
* @param options - Configuration options for command execution
* @returns Promise resolving to the environment variables used
*/
function EnvCmd(
{
command,
commandArgs,
envFile,
rc,
options = {},
}: EnvCmdOptions,
): Promise<Environment>;
interface EnvCmdOptions extends GetEnvVarOptions {
/** Command to execute */
command: string;
/** Arguments to pass to the command */
commandArgs: string[];
/** Options controlling execution behavior */
options?: {
/** Expand $var and ${var} in command and arguments */
expandEnvs?: boolean;
/** Recursively expand environment variables */
recursive?: boolean;
/** Don't override existing process environment variables */
noOverride?: boolean;
/** Silent mode - suppress error output */
silent?: boolean;
/** Use shell for command execution */
useShell?: boolean;
/** Verbose logging output */
verbose?: boolean;
};
}Usage Examples:
import { EnvCmd } from "env-cmd";
// Basic command execution with .env file
const env = await EnvCmd({
command: "node",
commandArgs: ["server.js"],
envFile: { filePath: ".env" }
});
// Execute with variable expansion
const env = await EnvCmd({
command: "echo",
commandArgs: ["API URL: $API_URL"],
envFile: { filePath: ".env.production" },
options: {
expandEnvs: true,
verbose: true
}
});
// Use RC file with multiple environments
const env = await EnvCmd({
command: "npm",
commandArgs: ["test"],
rc: {
environments: ["test", "staging"],
filePath: ".env-cmdrc"
},
options: {
recursive: true,
useShell: true
}
});Process signal management for proper termination handling between parent and child processes.
/**
* Signal handling class for managing process termination
*/
class TermSignals {
/** Internal flag to track if exit has been called */
_exitCalled: boolean;
/**
* Create a new signal handler instance
* @param options - Configuration options
*/
constructor(options?: { verbose?: boolean });
/**
* Handle termination signals for the given child process
* Sets up bidirectional signal handling between parent and child processes
* @param proc - Child process to manage signals for
*/
handleTermSignals(proc: ChildProcess): void;
/**
* Handle uncaught exceptions in the parent process
* Enables catching of unhandled exceptions with proper cleanup
*/
handleUncaughtExceptions(): void;
/**
* Terminate parent process helper with signal or exit code
* @param signal - Signal name or exit code to use for termination
*/
_terminateProcess(signal?: NodeJS.Signals | number): void;
/**
* Clean up all registered process event listeners
*/
_removeProcessListeners(): void;
/**
* General exception handler for uncaught exceptions
* @param e - Error object from uncaught exception
*/
_uncaughtExceptionHandler(e: Error): void;
}Usage Examples:
import { TermSignals } from "env-cmd/dist/signal-termination.js";
import { spawn } from "child_process";
// Create signal handler with verbose logging
const signals = new TermSignals({ verbose: true });
// Spawn a child process
const proc = spawn("node", ["server.js"], {
stdio: 'inherit',
env: process.env
});
// Set up signal handling for proper cleanup
signals.handleTermSignals(proc);
signals.handleUncaughtExceptions();Handled Signals:
The TermSignals class handles the following POSIX signals:
SIGINT - Interrupt signal (Ctrl+C)SIGTERM - Termination requestSIGHUP - Hangup signalSignal Flow:
_exitCalled flag prevents signal/exit loopsThe EnvCmd function performs several processing steps on environment variables:
Processing Order:
// 1. Load from file source
let env = await getEnvVars({ envFile, rc, verbose });
// 2. Merge with system environment
if (options.noOverride === true) {
env = Object.assign({}, env, process.env);
} else {
env = Object.assign({}, process.env, env);
}
// 3. Recursive expansion if enabled
if (options.recursive === true) {
for (const key of Object.keys(env)) {
if (env[key] !== undefined) {
env[key] = expandEnvs(env[key], env);
}
}
}
// 4. Command expansion if enabled
if (options.expandEnvs === true) {
command = expandEnvs(command, env);
commandArgs = commandArgs.map(arg => expandEnvs(arg, env));
}The EnvCmd function handles various error conditions:
Silent Mode Behavior:
try {
env = await getEnvVars({ envFile, rc, verbose: options.verbose });
} catch (e) {
if (!(options.silent ?? false)) {
throw e;
}
// Continue with empty environment in silent mode
}Commands are executed using the cross-spawn library for cross-platform compatibility:
const proc = spawn(command, commandArgs, {
stdio: 'inherit',
shell: options.useShell,
env,
});Features: