Skip to content

Commit 3ce71e1

Browse files
committed
feat: add quiet mode option to suppress console output
1 parent a475d82 commit 3ce71e1

File tree

7 files changed

+258
-2
lines changed

7 files changed

+258
-2
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ load({path: "../../configs/.env"});
212212
load({expand: false});
213213
```
214214

215+
### Load without quiet mode
216+
217+
```js
218+
// Suppress console output from dotenv
219+
load({quiet: false});
220+
```
221+
215222
### Change default filename
216223

217224
```js
@@ -304,10 +311,11 @@ dotenv-mono --cwd /path/to/project --extension server --depth 3 -- node server.j
304311
| `--help` | Print help message |
305312
| `--debug` | Output the files that would be processed but don't actually parse them or run the command |
306313
| `-e <path>` | Parse the file `<path>` as a `.env` file and add variables to the environment (multiple allowed) |
307-
| `-v <name>=<value>` | Put variable `<name>` into environment using `<value>` (multiple allowed) |
314+
| `-v <n>=<value>` | Put variable `<n>` into environment using `<value>` (multiple allowed) |
308315
| `-p <variable>` | Print value of `<variable>` to the console |
309316
| `--no-expand` | Skip variable expansion |
310317
| `--override` | Override system variables |
318+
| `--quiet` | Suppress console output from dotenv |
311319
| `--cwd <path>` | Specify the current working directory |
312320
| `--depth <number>` | Specify the max depth to reach when finding up the folder tree |
313321
| `--encoding <enc>` | Specify the encoding of your file containing environment variables |
@@ -331,6 +339,7 @@ dotenv-mono --cwd /path/to/project --extension server --depth 3 -- node server.j
331339
| `override` | Override any environment variables that have already been set on your machine with values from your `.env` file | `false` |
332340
| `path` | Specify a custom path if your file containing environment variables is located elsewhere | |
333341
| `priorities` | Specify the criteria of the filename priority to load as dotenv file | See [Priorities](#priorities) |
342+
| `quiet` | Turn on/off quiet mode to suppress console output from dotenv | `true` |
334343

335344
### Dotenv Methods
336345

src/cli.test.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,107 @@ describe("CLI Main Function", () => {
148148
expect(() => mainCli()).toThrow("Process exit called with code: 1");
149149
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining("Usage: dotenv-mono"));
150150
});
151+
152+
it("should handle --quiet flag", () => {
153+
process.argv = ["node", "cli.js", "--quiet", "--debug"];
154+
155+
expect(() => mainCli()).toThrow("Process exit called with code: 0");
156+
expect(consoleLogSpy).toHaveBeenCalledWith(
157+
"Configuration:",
158+
expect.objectContaining({quiet: true}),
159+
);
160+
});
161+
162+
it("should handle --quiet flag with command execution", () => {
163+
mockFs({
164+
"/test": {
165+
".env": "CLI_QUIET_TEST=quiet_value",
166+
},
167+
});
168+
169+
process.argv = ["node", "cli.js", "--quiet", "--cwd", "/test", "echo", "test"];
170+
171+
// Mock spawn to avoid actually executing commands in test environment
172+
const mockSpawn = jest.fn().mockReturnValue({
173+
on: jest.fn().mockImplementation((event, callback) => {
174+
if (event === "exit") {
175+
setTimeout(() => callback(0), 0); // Simulate successful command execution
176+
}
177+
return {
178+
kill: jest.fn(),
179+
};
180+
}),
181+
kill: jest.fn(),
182+
});
183+
184+
// Mock cross-spawn
185+
jest.doMock("cross-spawn", () => mockSpawn);
186+
187+
// The command should execute without throwing since we mock the child process
188+
expect(() => mainCli()).not.toThrow();
189+
});
190+
191+
it("should handle --quiet with -e flag", () => {
192+
process.argv = ["node", "cli.js", "--quiet", "-e", "/test/.env", "--debug"];
193+
194+
expect(() => mainCli()).toThrow("Process exit called with code: 0");
195+
expect(consoleLogSpy).toHaveBeenCalledWith(
196+
"Configuration:",
197+
expect.objectContaining({quiet: true}),
198+
);
199+
expect(consoleLogSpy).toHaveBeenCalledWith("Custom paths:", ["/test/.env"]);
200+
});
201+
202+
it("should handle --quiet with multiple flags", () => {
203+
process.argv = [
204+
"node",
205+
"cli.js",
206+
"--quiet",
207+
"--override",
208+
"--no-expand",
209+
"--depth",
210+
"5",
211+
"--debug",
212+
];
213+
214+
expect(() => mainCli()).toThrow("Process exit called with code: 0");
215+
expect(consoleLogSpy).toHaveBeenCalledWith(
216+
"Configuration:",
217+
expect.objectContaining({
218+
quiet: true,
219+
override: true,
220+
expand: false,
221+
depth: 5,
222+
}),
223+
);
224+
});
225+
226+
it("should handle --quiet with -v variables", () => {
227+
process.argv = ["node", "cli.js", "--quiet", "-v", "QUIET_VAR=quiet_test", "--debug"];
228+
229+
expect(() => mainCli()).toThrow("Process exit called with code: 0");
230+
expect(consoleLogSpy).toHaveBeenCalledWith(
231+
"Configuration:",
232+
expect.objectContaining({quiet: true}),
233+
);
234+
expect(consoleLogSpy).toHaveBeenCalledWith("Variables:", [["QUIET_VAR", "quiet_test"]]);
235+
});
236+
237+
it("should handle --quiet with -p print variable", () => {
238+
process.env.QUIET_PRINT_TEST = "quiet_print_value";
239+
process.argv = ["node", "cli.js", "--quiet", "-p", "QUIET_PRINT_TEST"];
240+
241+
expect(() => mainCli()).toThrow("Process exit called with code: 0");
242+
expect(consoleLogSpy).toHaveBeenCalledWith("quiet_print_value");
243+
});
244+
245+
it("should include --quiet in help output", () => {
246+
process.argv = ["node", "cli.js", "--help"];
247+
248+
expect(() => mainCli()).toThrow("Process exit called with code: 0");
249+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining("--quiet"));
250+
expect(consoleLogSpy).toHaveBeenCalledWith(
251+
expect.stringContaining("suppress console output from dotenv"),
252+
);
253+
});
151254
});

src/cli.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const HELP_OPTIONS = [
2323
},
2424
{flag: "--no-expand", desc: "skip variable expansion"},
2525
{flag: "--override", desc: "override system variables"},
26+
{flag: "--quiet", desc: "suppress console output from dotenv"},
2627
{flag: "--cwd <path>", desc: "specify the current working directory"},
2728
{
2829
flag: "--depth <number>",
@@ -49,7 +50,7 @@ const HELP_OPTIONS = [
4950

5051
function printHelp(): void {
5152
console.log(
52-
"Usage: dotenv-mono [--help] [--debug] [-e <path>] [-v <n>=<value>] [-p <variable>] [--no-expand] [--override] [-- command]",
53+
"Usage: dotenv-mono [--help] [--debug] [-e <path>] [-v <n>=<value>] [-p <variable>] [--no-expand] [--override] [-- command] [--quiet] [--cwd <path>] [--depth <number>] [--encoding <enc>] [--extension <ext>] [--defaults <file>] [--priorities <json>]\n",
5354
);
5455
for (const opt of HELP_OPTIONS) {
5556
// For the 'command' line, print the description on a new line for clarity
@@ -91,6 +92,7 @@ function main(): void {
9192
if (argv.encoding) config.encoding = argv.encoding;
9293
if (argv.override !== undefined) config.override = argv.override;
9394
if (argv.extension) config.extension = argv.extension;
95+
if (argv.quiet !== undefined) config.quiet = argv.quiet;
9496

9597
// Handle expand flag (--no-expand sets it to false)
9698
if (argv["no-expand"] !== undefined) {

src/index.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,98 @@ EQUALS_IN_NAME=WITH=EQUALS=value
13281328
const unixInstance = new Dotenv({path: unixPath});
13291329
expect(unixInstance.path).toBe(unixPath);
13301330
});
1331+
1332+
it("should handle quiet mode", () => {
1333+
const quietInstance = new Dotenv({quiet: true});
1334+
expect(quietInstance.quiet).toBe(true);
1335+
1336+
quietInstance.quiet = false;
1337+
expect(quietInstance.quiet).toBe(false);
1338+
1339+
// Test with undefined (should not change current value)
1340+
const currentQuiet = quietInstance.quiet;
1341+
quietInstance.quiet = undefined;
1342+
expect(quietInstance.quiet).toBe(currentQuiet);
1343+
});
1344+
1345+
it("should pass quiet option to dotenv.config", () => {
1346+
mockFs({
1347+
"/quiet-test": {
1348+
".env": "QUIET_TEST=value",
1349+
},
1350+
});
1351+
1352+
jest.spyOn(process, "cwd").mockReturnValue("/quiet-test");
1353+
1354+
// Test with quiet enabled
1355+
const quietInstance = new Dotenv({cwd: "/quiet-test", quiet: true});
1356+
expect(() => quietInstance.load()).not.toThrow();
1357+
expect(quietInstance.env.QUIET_TEST).toBe("value");
1358+
expect(quietInstance.quiet).toBe(true);
1359+
1360+
// Test with quiet disabled
1361+
const normalInstance = new Dotenv({cwd: "/quiet-test", quiet: false});
1362+
expect(() => normalInstance.load()).not.toThrow();
1363+
expect(normalInstance.env.QUIET_TEST).toBe("value");
1364+
expect(normalInstance.quiet).toBe(false);
1365+
});
1366+
1367+
it("should have quiet option default to true", () => {
1368+
const defaultInstance = new Dotenv();
1369+
expect(defaultInstance.quiet).toBe(true);
1370+
});
1371+
1372+
it("should handle quiet option in constructor", () => {
1373+
const quietInstance = new Dotenv({
1374+
quiet: true,
1375+
debug: false,
1376+
override: true,
1377+
});
1378+
1379+
expect(quietInstance.quiet).toBe(true);
1380+
expect(quietInstance.debug).toBe(false);
1381+
expect(quietInstance.override).toBe(true);
1382+
});
1383+
1384+
it("should handle quiet option with loadFile method", () => {
1385+
mockFs({
1386+
"/quiet-loadfile": {
1387+
".env": "LOADFILE_QUIET=test",
1388+
},
1389+
});
1390+
1391+
const instance = new Dotenv({path: "/quiet-loadfile/.env", quiet: true});
1392+
expect(() => instance.loadFile()).not.toThrow();
1393+
expect(instance.env.LOADFILE_QUIET).toBe("test");
1394+
expect(instance.quiet).toBe(true);
1395+
});
1396+
1397+
it("should work with dotenvLoad function and quiet option", () => {
1398+
mockFs({
1399+
"/function-quiet": {
1400+
".env": "FUNCTION_QUIET=value",
1401+
},
1402+
});
1403+
1404+
jest.spyOn(process, "cwd").mockReturnValue("/function-quiet");
1405+
1406+
const dotenv = dotenvLoad({cwd: "/function-quiet", quiet: true});
1407+
expect(dotenv.env.FUNCTION_QUIET).toBe("value");
1408+
expect(dotenv.quiet).toBe(true);
1409+
});
1410+
1411+
it("should work with dotenvConfig function and quiet option", () => {
1412+
mockFs({
1413+
"/config-quiet": {
1414+
".env": "CONFIG_QUIET=value",
1415+
},
1416+
});
1417+
1418+
jest.spyOn(process, "cwd").mockReturnValue("/config-quiet");
1419+
1420+
const output = dotenvConfig({cwd: "/config-quiet", quiet: true});
1421+
expect(output.parsed?.CONFIG_QUIET).toBe("value");
1422+
});
13311423
});
13321424

13331425
function toStringArray(object: GenericObject): string[] {

src/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ export type DotenvConfig = {
9494
* @example `require('dotenv-mono').load({ priorities: { '.env.overwrite': 100 } })`
9595
*/
9696
priorities?: DotenvPriorities;
97+
/**
98+
* Turn on/off quiet mode to suppress console output.
99+
* @defaultValue `false`
100+
* @example `require('dotenv-mono').load({ quiet: true })`
101+
*/
102+
quiet?: boolean;
97103
};
98104

99105
/**
@@ -116,18 +122,21 @@ export class Dotenv {
116122
#_override: boolean = false;
117123
#_path: string = "";
118124
#_priorities: DotenvPriorities = {};
125+
#_quiet: boolean = true;
119126

120127
/**
121128
* Constructor.
122129
* @param cwd - current Working Directory
123130
* @param debug - turn on/off debugging
131+
* @param defaults - defaults dotenv filename
124132
* @param depth - max walking up depth
125133
* @param encoding - file encoding
126134
* @param expand - turn on/off dotenv-expand plugin
127135
* @param extension - add dotenv extension
128136
* @param override - override process variables
129137
* @param path - dotenv path
130138
* @param priorities - priorities
139+
* @param quiet - turn on/off quiet mode
131140
*/
132141
constructor({
133142
cwd,
@@ -140,6 +149,7 @@ export class Dotenv {
140149
override,
141150
path,
142151
priorities,
152+
quiet,
143153
}: DotenvConfig = {}) {
144154
this.cwd = cwd;
145155
this.debug = debug;
@@ -151,6 +161,7 @@ export class Dotenv {
151161
this.override = override;
152162
this.path = path;
153163
this.priorities = priorities;
164+
this.quiet = quiet;
154165
// Auto-bind matchers
155166
this.dotenvDefaultsMatcher = this.dotenvDefaultsMatcher.bind(this);
156167
this.dotenvMatcher = this.dotenvMatcher.bind(this);
@@ -315,6 +326,21 @@ export class Dotenv {
315326
if (value != null) this.#_priorities = value;
316327
}
317328

329+
/**
330+
* Get quiet mode.
331+
*/
332+
public get quiet(): boolean {
333+
return this.#_quiet;
334+
}
335+
336+
/**
337+
* Set quiet mode.
338+
* @param value
339+
*/
340+
public set quiet(value: boolean | undefined) {
341+
if (value != null) this.#_quiet = value;
342+
}
343+
318344
/**
319345
* Parses a string or buffer in the .env file format into an object.
320346
* @see https://docs.dotenv.org
@@ -363,6 +389,7 @@ export class Dotenv {
363389
debug: this.debug,
364390
encoding: this.encoding,
365391
override: !defaults && this.override,
392+
quiet: this.quiet,
366393
})
367394
: {
368395
parsed: this.parse(plain),

src/node-cli.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,4 +422,26 @@ describe("Parse Option", () => {
422422
expect(dotenv.debug).toBe(true);
423423
expect(dotenv.depth).toBe(3);
424424
});
425+
426+
it("should handle quiet option in environmental options", () => {
427+
process.env.DOTENV_CONFIG_QUIET = "true";
428+
const dotenv = runNodeCli(load) as Dotenv;
429+
expect(dotenv.quiet).toBe(true);
430+
431+
// Test with false
432+
process.env.DOTENV_CONFIG_QUIET = "false";
433+
const dotenv2 = runNodeCli(load) as Dotenv;
434+
expect(dotenv2.quiet).toBe(false);
435+
});
436+
437+
it("should handle quiet option in argv options", () => {
438+
process.argv = ["node", "script.js", "dotenv_config_quiet=true"];
439+
const dotenv = runNodeCli(load) as Dotenv;
440+
expect(dotenv.quiet).toBe(true);
441+
442+
// Test with false
443+
process.argv = ["node", "script.js", "dotenv_config_quiet=false"];
444+
const dotenv2 = runNodeCli(load) as Dotenv;
445+
expect(dotenv2.quiet).toBe(false);
446+
});
425447
});

src/node-cli.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const DotenvOptionsType: GenericObject<OptionType> = {
3939
path: OptionType.string,
4040
override: OptionType.boolean,
4141
priorities: OptionType.mapOfNumbers,
42+
quiet: OptionType.boolean,
4243
};
4344

4445
/**

0 commit comments

Comments
 (0)