fix(dep): update for commander breaking changes

This commit is contained in:
Todd Bluhm 2025-07-01 23:52:55 -08:00
parent d09e904ca2
commit 1e7102607e
No known key found for this signature in database
GPG Key ID: 9CF312607477B8AB
7 changed files with 57 additions and 23 deletions

6
dist/env-cmd.js vendored
View File

@ -33,11 +33,15 @@ export async function EnvCmd({ command, commandArgs, envFile, rc, options = {},
command = expandEnvs(command, env);
commandArgs = commandArgs.map(arg => expandEnvs(arg, env));
}
if (!command) {
throw new Error('env-cmd cannot be used as a standalone command. ' +
'Refer to the documentation for usage examples: https://npm.im/env-cmd');
}
// Execute the command with the given environment variables
const proc = spawn(command, commandArgs, {
stdio: 'inherit',
shell: options.useShell,
env: env,
env,
});
// Handle any termination signals for parent and child proceses
const signals = new TermSignals({ verbose: options.verbose });

1
dist/parse-args.js vendored
View File

@ -94,5 +94,6 @@ export function parseArgsUsingCommander(args) {
.option('--use-shell', 'Execute the command in a new shell with the given environment')
.option('--verbose', 'Print helpful debugging information')
.allowUnknownOption(true)
.allowExcessArguments(true)
.parse(['_', '_', ...args], { from: 'node' });
}

View File

@ -19,3 +19,12 @@ export declare function stripComments(envString: string): string;
* Strips out newlines from env file string
*/
export declare function stripEmptyLines(envString: string): string;
/**
* If we load data from a file like .js, the user
* might export something which is not an object.
*
* This function ensures that the input is valid,
* and converts the object's values to strings, for
* consistincy. See issue #125 for details.
*/
export declare function normalizeEnvObject(input: unknown, absolutePath: string): Environment;

View File

@ -14,7 +14,7 @@ export async function getEnvFileVars(envFilePath) {
}
// Get the file extension
const ext = extname(absolutePath).toLowerCase();
let env = {};
let env;
if (IMPORT_HOOK_EXTENSIONS.includes(ext)) {
// For some reason in ES Modules, only JSON file types need to be specifically delinated when importing them
let attributeTypes = {};
@ -22,7 +22,7 @@ export async function getEnvFileVars(envFilePath) {
attributeTypes = { [importAttributesKeyword]: { type: 'json' } };
}
const res = await import(pathToFileURL(absolutePath).href, attributeTypes);
if ('default' in res) {
if (typeof res === 'object' && res && 'default' in res) {
env = res.default;
}
else {
@ -32,12 +32,14 @@ export async function getEnvFileVars(envFilePath) {
if (isPromise(env)) {
env = await env;
}
return normalizeEnvObject(env, absolutePath);
}
else {
const file = readFileSync(absolutePath, { encoding: 'utf8' });
env = parseEnvString(file);
const file = readFileSync(absolutePath, { encoding: 'utf8' });
switch (ext) {
// other loaders can be added here
default:
return parseEnvString(file);
}
return env;
}
/**
* Parse out all env vars from a given env file string and return an object
@ -61,23 +63,18 @@ export function parseEnvVars(envString) {
// Note: match[1] is the full env=var line
const key = match[2].trim();
let value = match[3].trim();
// remove any surrounding quotes
value = value
.replace(/(^['"]|['"]$)/g, '')
.replace(/\\n/g, '\n');
// Convert string to JS type if appropriate
if (value !== '' && !isNaN(+value)) {
matches[key] = +value;
}
else if (value === 'true') {
matches[key] = true;
}
else if (value === 'false') {
matches[key] = false;
// if the string is quoted, remove everything after the final
// quote. This implicitly removes inline comments.
if (value.startsWith("'") || value.startsWith('"')) {
value = value.slice(1, value.lastIndexOf(value[0]));
}
else {
matches[key] = value;
// if the string is not quoted, we need to explicitly remove
// inline comments.
value = value.split('#')[0].trim();
}
value = value.replace(/\\n/g, '\n');
matches[key] = value;
}
return JSON.parse(JSON.stringify(matches));
}
@ -95,3 +92,25 @@ export function stripEmptyLines(envString) {
const emptyLinesRegex = /(^\n)/gim;
return envString.replace(emptyLinesRegex, '');
}
/**
* If we load data from a file like .js, the user
* might export something which is not an object.
*
* This function ensures that the input is valid,
* and converts the object's values to strings, for
* consistincy. See issue #125 for details.
*/
export function normalizeEnvObject(input, absolutePath) {
if (typeof input !== 'object' || !input) {
throw new Error(`env-cmd cannot load “${absolutePath}” because it does not export an object.`);
}
const env = {};
for (const [key, value] of Object.entries(input)) {
// we're intentionally stringifying the value here, to
// match what `child_process.spawn` does when loading
// env variables.
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
env[key] = `${value}`;
}
return env;
}

2
dist/types.d.ts vendored
View File

@ -1,5 +1,5 @@
import type { Command } from '@commander-js/extra-typings';
export type Environment = Partial<Record<string, string | number | boolean>>;
export type Environment = Partial<Record<string, string>>;
export type RCEnvironment = Partial<Record<string, Environment>>;
export type CommanderOptions = Command<[], {
environments?: true | string[];

View File

@ -49,7 +49,7 @@
},
"homepage": "https://github.com/toddbluhm/env-cmd#readme",
"dependencies": {
"@commander-js/extra-typings": "^12.1.0",
"@commander-js/extra-typings": "^13.1.0",
"commander": "^13.1.0",
"cross-spawn": "^7.0.6"
},

View File

@ -108,5 +108,6 @@ export function parseArgsUsingCommander(args: string[]): CommanderOptions {
.option('--use-shell', 'Execute the command in a new shell with the given environment')
.option('--verbose', 'Print helpful debugging information')
.allowUnknownOption(true)
.allowExcessArguments(true)
.parse(['_', '_', ...args], { from: 'node' })
}