@karmaniverous/get-dotenv 5.0.0-2 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -64
- package/dist/cliHost.cjs +172 -3
- package/dist/cliHost.d.cts +115 -3
- package/dist/cliHost.d.mts +115 -3
- package/dist/cliHost.d.ts +115 -3
- package/dist/cliHost.mjs +172 -4
- package/dist/getdotenv.cli.mjs +200 -4
- package/dist/index.cjs +25 -0
- package/dist/index.mjs +25 -0
- package/dist/plugins-aws.d.cts +98 -0
- package/dist/plugins-aws.d.mts +98 -0
- package/dist/plugins-aws.d.ts +98 -0
- package/dist/plugins-batch.d.cts +98 -0
- package/dist/plugins-batch.d.mts +98 -0
- package/dist/plugins-batch.d.ts +98 -0
- package/dist/plugins-init.d.cts +98 -0
- package/dist/plugins-init.d.mts +98 -0
- package/dist/plugins-init.d.ts +98 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
## Requirements
|
|
4
|
-
|
|
5
|
-
- Node.js >= 20 (this repository pins 22.19.0 for CI/reproducibility)
|
|
1
|
+
> **_Load, expand, and compose environment variables from a deterministic dotenv cascade, then execute commands under that context. Use `get-dotenv` as a library, a CLI, or a plugin-first host to build dotenv-aware tooling with cross‑platform shell control, CI‑friendly capture, and clear diagnostics._**
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
Generated API documentation is hosted at:
|
|
9
|
-
- https://docs.karmanivero.us/get-dotenv
|
|
3
|
+
# get-dotenv
|
|
10
4
|
|
|
11
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@karmaniverous/get-dotenv)
|
|
6
|
+
 <!-- TYPEDOC_EXCLUDE -->
|
|
7
|
+
[](https://docs.karmanivero.us/get-dotenv)
|
|
8
|
+
[](https://github.com/karmaniverous/get-dotenv/tree/main/CHANGELOG.md)<!-- /TYPEDOC_EXCLUDE -->
|
|
9
|
+
[](https://github.com/karmaniverous/get-dotenv/tree/main/LICENSE.md)
|
|
12
10
|
|
|
13
11
|
Load environment variables with a cascade of environment-aware dotenv files. You can:
|
|
14
12
|
|
|
@@ -44,6 +42,18 @@ You can always use `getdotenv` directly on the command line, but its REAL power
|
|
|
44
42
|
|
|
45
43
|
When you plug your own [`commander`](https://www.npmjs.com/package/commander) CLI commands into the `getdotenv` base, they will execute within all of the environmental context created above!
|
|
46
44
|
|
|
45
|
+
## Requirements
|
|
46
|
+
|
|
47
|
+
- Node.js >= 20 (this repository pins 22.19.0 for CI/reproducibility)
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
Generated API documentation is hosted at:
|
|
52
|
+
|
|
53
|
+
- https://docs.karmanivero.us/get-dotenv
|
|
54
|
+
|
|
55
|
+
The site is built with TypeDoc from the source code in this repository.
|
|
56
|
+
|
|
47
57
|
## Testing
|
|
48
58
|
|
|
49
59
|
This project uses Vitest with the V8 coverage provider. Run:
|
|
@@ -60,8 +70,7 @@ npm install @karmaniverous/get-dotenv
|
|
|
60
70
|
|
|
61
71
|
## Scaffold
|
|
62
72
|
|
|
63
|
-
You can scaffold config files and a host-based CLI skeleton using the built-in
|
|
64
|
-
init command. Templates are shipped with the package and copied verbatim.
|
|
73
|
+
You can scaffold config files and a host-based CLI skeleton using the built-in init command. Templates are shipped with the package and copied verbatim.
|
|
65
74
|
|
|
66
75
|
Examples:
|
|
67
76
|
|
|
@@ -83,20 +92,13 @@ npx getdotenv init ./apps/toolbox \
|
|
|
83
92
|
|
|
84
93
|
Collision flow (when a destination file exists):
|
|
85
94
|
|
|
86
|
-
- Interactive prompt: [o]verwrite, [e]xample, [s]kip, or their “all” variants
|
|
87
|
-
|
|
88
|
-
-
|
|
89
|
-
- Treated as `--yes` (Skip All) unless `--force` is provided (Overwrite All).
|
|
90
|
-
- Considered non-interactive when stdin or stdout is not a TTY OR when a
|
|
91
|
-
CI-like environment variable is present (`CI`, `GITHUB_ACTIONS`,
|
|
92
|
-
`BUILDKITE`, `TEAMCITY_VERSION`, `TF_BUILD`).
|
|
93
|
-
- Precedence:
|
|
94
|
-
- `--force` > `--yes` > auto-detect (non-interactive => Skip All).
|
|
95
|
+
- Interactive prompt: [o]verwrite, [e]xample, [s]kip, or their “all” variants [O]/[E]/[S].
|
|
96
|
+
- Non-interactive detection: Treated as `--yes` (Skip All) unless `--force` is provided (Overwrite All). Considered non-interactive when stdin or stdout is not a TTY OR when a CI-like environment variable is present (`CI`, `GITHUB_ACTIONS`, `BUILDKITE`, `TEAMCITY_VERSION`, `TF_BUILD`).
|
|
97
|
+
- Precedence: `--force` > `--yes` > auto-detect (non-interactive => Skip All).
|
|
95
98
|
- Options overview:
|
|
96
99
|
- `--config-format <json|yaml|js|ts>`
|
|
97
100
|
- `--with-local` to generate `.local` alongside public config (JSON/YAML)
|
|
98
|
-
- `--cli-name <string>` for token substitution (`__CLI_NAME__`) in the CLI
|
|
99
|
-
skeleton
|
|
101
|
+
- `--cli-name <string>` for token substitution (`__CLI_NAME__`) in the CLI skeleton
|
|
100
102
|
- `--force` to overwrite all; `--yes` to skip all
|
|
101
103
|
|
|
102
104
|
Notes:
|
|
@@ -106,14 +108,15 @@ Notes:
|
|
|
106
108
|
|
|
107
109
|
## Usage
|
|
108
110
|
|
|
109
|
-
|
|
111
|
+
```js
|
|
110
112
|
import { getDotenv } from '@karmaniverous/get-dotenv';
|
|
111
113
|
|
|
112
|
-
const dotenv = await getDotenv(options)
|
|
114
|
+
const dotenv = await getDotenv(options);
|
|
115
|
+
```
|
|
113
116
|
|
|
114
117
|
Options can be passed programmatically or set in a `getdotenv.config.json` file in your project root directory. The same file also sets default options for the `getdotenv` CLI or any child CLI you spawn from it.
|
|
115
118
|
|
|
116
|
-
See the [child CLI example repo](https://github.com/karmaniverous/get-dotenv-child#configuration) for an
|
|
119
|
+
See the [child CLI example repo](https://github.com/karmaniverous/get-dotenv-child#configuration) for an extensive discussion of the various config options and how & where to set them.
|
|
117
120
|
|
|
118
121
|
## Dynamic Processing
|
|
119
122
|
|
|
@@ -126,10 +129,10 @@ export default {
|
|
|
126
129
|
SOME_DYNAMIC_VARIABLE: (dotenv) => someLogic(dotenv),
|
|
127
130
|
ANOTHER_DYNAMIC_VARIABLE: (dotenv) =>
|
|
128
131
|
someOtherLogic(dotenv.SOME_DYNAMIC_VARIABLE),
|
|
129
|
-
ONE_MORE_TIME: ({
|
|
130
|
-
|
|
132
|
+
ONE_MORE_TIME: ({ DESTRUCTURED_VARIABLE, ANOTHER_DYNAMIC_VARIABLE }) =>
|
|
133
|
+
DESTRUCTURED_VARIABLE + ANOTHER_DYNAMIC_VARIABLE,
|
|
131
134
|
};
|
|
132
|
-
|
|
135
|
+
```
|
|
133
136
|
|
|
134
137
|
If the value corresponding to a key is a function, it will be executed with the current state of `dotenv` as its single argument and the result applied back to the `dotenv` object. Otherwise, the value will just be applied back to `dotenv`. (Although if you're going to do that then you might as well just create a public global variable in the first place.)
|
|
135
138
|
|
|
@@ -146,9 +149,7 @@ export default {
|
|
|
146
149
|
};
|
|
147
150
|
```
|
|
148
151
|
|
|
149
|
-
If `esbuild` is not installed and a direct import fails, get-dotenv attempts a
|
|
150
|
-
simple fallback for single-file `.ts` modules without imports; otherwise it will
|
|
151
|
-
throw with clear guidance to install `esbuild`.
|
|
152
|
+
If `esbuild` is not installed and a direct import fails, get-dotenv attempts a simple fallback for single-file `.ts` modules without imports; otherwise it will throw with clear guidance to install `esbuild`.
|
|
152
153
|
|
|
153
154
|
Programmatic users can skip files entirely and pass dynamic variables directly:
|
|
154
155
|
|
|
@@ -175,8 +176,7 @@ Notes:
|
|
|
175
176
|
- Install `esbuild` (`npm i -D esbuild`).
|
|
176
177
|
|
|
177
178
|
- “Unable to load dynamic TypeScript file …”:
|
|
178
|
-
- Install `esbuild`. A simple transpile fallback exists only for trivial
|
|
179
|
-
single-file modules; any imports in `dynamic.ts` require `esbuild` bundling.
|
|
179
|
+
- Install `esbuild`. A simple transpile fallback exists only for trivial single-file modules; any imports in `dynamic.ts` require `esbuild` bundling.
|
|
180
180
|
|
|
181
181
|
## Command Line Interface
|
|
182
182
|
|
|
@@ -218,7 +218,8 @@ You can also use `getdotenv` from the command line:
|
|
|
218
218
|
# --dynamic-path <string> dynamic variables path (.js or .ts; .ts is auto-compiled when esbuild is available, otherwise precompile)
|
|
219
219
|
# --paths <string> dotenv-expanded delimited list of paths to dotenv directory (default: "./")
|
|
220
220
|
# --paths-delimiter <string> paths delimiter string (default: " ")
|
|
221
|
-
# --paths-delimiter-pattern <string> paths delimiter regex pattern
|
|
221
|
+
# --paths-delimiter-pattern <string> paths delimiter regex pattern
|
|
222
|
+
# --private-token <string> dotenv-expanded token indicating private variables (default: "local")
|
|
222
223
|
# --vars-delimiter <string> vars delimiter string (default: " ")
|
|
223
224
|
# --vars-delimiter-pattern <string> vars delimiter regex pattern
|
|
224
225
|
# --vars-assignor <string> vars assignment operator string (default: "=")
|
|
@@ -277,7 +278,7 @@ Note that `batch` executes its commands in sequence, rather than in parallel!
|
|
|
277
278
|
|
|
278
279
|
To understand why, imagine running `npm install` in a dozen repos from the same command line. The visual feedback would be impossible to follow, and if something broke you'd have a really hard time figuring out why.
|
|
279
280
|
|
|
280
|
-
Instead, everything runs in sequence, and you get a clear record of exactly what
|
|
281
|
+
Instead, everything runs in sequence, and you get a clear record of exactly what happened and where. Also worth noting that many complex processes are resource hogs: you would not _want_ to run a dozen Serverless deployments at once!
|
|
281
282
|
|
|
282
283
|
Meanwhile, [this issue](https://github.com/karmaniverous/get-dotenv/issues/7) documents the parallel-processing option requirement. Feel free to submit a PR!
|
|
283
284
|
|
|
@@ -285,9 +286,7 @@ Meanwhile, [this issue](https://github.com/karmaniverous/get-dotenv/issues/7) do
|
|
|
285
286
|
|
|
286
287
|
### Authoring npm scripts and the `-c`/`--cmd` alias
|
|
287
288
|
|
|
288
|
-
When you run commands via `npm run`, flags after `--` are forwarded to your script
|
|
289
|
-
and may be applied to the inner shell command instead of `getdotenv` unless you
|
|
290
|
-
structure your script carefully.
|
|
289
|
+
When you run commands via `npm run`, flags after `--` are forwarded to your script and may be applied to the inner shell command instead of `getdotenv` unless you structure your script carefully.
|
|
291
290
|
|
|
292
291
|
- Anti-pattern:
|
|
293
292
|
|
|
@@ -301,22 +300,17 @@ structure your script carefully.
|
|
|
301
300
|
```json
|
|
302
301
|
{ "scripts": { "script": "getdotenv -c 'echo $APP_SETTING'" } }
|
|
303
302
|
```
|
|
304
|
-
Now `npm run script -- -e dev` applies `-e` to `getdotenv`, which loads and expands
|
|
305
|
-
variables before executing the inner command.
|
|
303
|
+
Now `npm run script -- -e dev` applies `-e` to `getdotenv`, which loads and expands variables before executing the inner command.
|
|
306
304
|
|
|
307
305
|
Notes:
|
|
308
306
|
|
|
309
307
|
- `-c`/`--cmd` is an alias of the `cmd` subcommand; do not use both in a single invocation.
|
|
310
|
-
- On POSIX shells, prefer single quotes to prevent the outer shell from expanding `$VAR`
|
|
311
|
-
|
|
312
|
-
- Script-level shell overrides (`scripts[name].shell`) still take precedence over the global
|
|
313
|
-
`--shell`.
|
|
308
|
+
- On POSIX shells, prefer single quotes to prevent the outer shell from expanding `$VAR` before Node sees it. On PowerShell, single quotes are also literal.
|
|
309
|
+
- Script-level shell overrides (`scripts[name].shell`) still take precedence over the global `--shell`.
|
|
314
310
|
|
|
315
311
|
Important:
|
|
316
312
|
|
|
317
|
-
- When using the parent alias `--cmd` with a Node eval payload, quote the entire
|
|
318
|
-
payload as a single token so Commander does not treat `-e/--eval` as
|
|
319
|
-
getdotenv’s `-e, --env` flag.
|
|
313
|
+
- When using the parent alias `--cmd` with a Node eval payload, quote the entire payload as a single token so Commander does not treat `-e/--eval` as getdotenv’s `-e, --env` flag.
|
|
320
314
|
- POSIX example:
|
|
321
315
|
```
|
|
322
316
|
getdotenv --cmd 'node -e "console.log(process.env.APP_SETTING ?? \"\")"'
|
|
@@ -325,23 +319,19 @@ Important:
|
|
|
325
319
|
```
|
|
326
320
|
getdotenv --cmd 'node -e "console.log(process.env.APP_SETTING ?? \"\")"'
|
|
327
321
|
```
|
|
328
|
-
- If you do not need to pass additional parent flags after the command, you can
|
|
329
|
-
prefer the subcommand form instead:
|
|
322
|
+
- If you do not need to pass additional parent flags after the command, you can prefer the subcommand form instead:
|
|
330
323
|
```
|
|
331
324
|
getdotenv --shell-off cmd node -e "console.log(process.env.APP_SETTING ?? '')"
|
|
332
325
|
```
|
|
333
326
|
|
|
334
327
|
Diagnostics and CI capture:
|
|
335
328
|
|
|
336
|
-
- To capture child stdout/stderr deterministically (e.g., in CI), either set
|
|
337
|
-
the environment variable `GETDOTENV_STDIO=pipe` or pass `--capture`. Outputs
|
|
338
|
-
are buffered and re-emitted after completion.
|
|
329
|
+
- To capture child stdout/stderr deterministically (e.g., in CI), either set the environment variable `GETDOTENV_STDIO=pipe` or pass `--capture`. Outputs are buffered and re-emitted after completion.
|
|
339
330
|
- For debugging environment composition, use:
|
|
340
331
|
```
|
|
341
332
|
getdotenv --trace [keys...] cmd node -e "0"
|
|
342
333
|
```
|
|
343
|
-
When provided without keys, `--trace` emits a concise origin line for every
|
|
344
|
-
key (parent | dotenv | unset) to stderr before the child process launches.
|
|
334
|
+
When provided without keys, `--trace` emits a concise origin line for every key (parent | dotenv | unset) to stderr before the child process launches.
|
|
345
335
|
|
|
346
336
|
---
|
|
347
337
|
|
|
@@ -349,21 +339,17 @@ Diagnostics and CI capture:
|
|
|
349
339
|
|
|
350
340
|
- [Cascade and precedence](./guides/cascade.md)
|
|
351
341
|
- [Shell execution behavior and quoting](./guides/shell.md)
|
|
342
|
+
- [Config files and overlays](./guides/config.md)
|
|
343
|
+
- [Plugin-first host and plugins](./guides/plugins.md)
|
|
352
344
|
|
|
353
345
|
The guides are also included in the [hosted API docs](https://docs.karmanivero.us/get-dotenv).
|
|
354
346
|
|
|
355
|
-
---
|
|
356
|
-
|
|
357
347
|
## Generated CLI
|
|
358
348
|
|
|
359
|
-
This package still supports generating a standalone CLI for your projects.
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
better subprocess control and diagnostics. If you prefer a thin, fixed
|
|
363
|
-
command surface with defaults baked into config, the generated CLI can be a
|
|
364
|
-
good fit.
|
|
349
|
+
This package still supports generating a standalone CLI for your projects. For most use cases we recommend the new plugin-first host because it resolves dotenv context once per invocation, supports composable plugins, and provides better subprocess control and diagnostics. If you prefer a thin, fixed command surface with defaults baked into config, the generated CLI can be a good fit.
|
|
350
|
+
|
|
351
|
+
See the [Generated CLI guide](https://docs.karmanivero.us/get-dotenv/guides/generated-cli) for details
|
|
365
352
|
|
|
366
|
-
|
|
367
|
-
https://docs.karmanivero.us/get-dotenv/guides/generated-cli
|
|
353
|
+
---
|
|
368
354
|
|
|
369
355
|
See more great templates & tools on [my GitHub Profile](https://github.com/karmaniverous)!
|
package/dist/cliHost.cjs
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
var commander = require('commander');
|
|
4
4
|
var fs = require('fs-extra');
|
|
5
5
|
var packageDirectory = require('package-directory');
|
|
6
|
+
var url = require('url');
|
|
6
7
|
var path = require('path');
|
|
7
8
|
var zod = require('zod');
|
|
8
|
-
var url = require('url');
|
|
9
9
|
var YAML = require('yaml');
|
|
10
10
|
var nanoid = require('nanoid');
|
|
11
11
|
var dotenv = require('dotenv');
|
|
@@ -984,6 +984,8 @@ const computeContext = async (customOptions, plugins, hostMetaUrl) => {
|
|
|
984
984
|
|
|
985
985
|
const HOST_META_URL = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cliHost.cjs', document.baseURI).href));
|
|
986
986
|
const CTX_SYMBOL = Symbol('GetDotenvCli.ctx');
|
|
987
|
+
const OPTS_SYMBOL = Symbol('GetDotenvCli.options');
|
|
988
|
+
const HELP_HEADER_SYMBOL = Symbol('GetDotenvCli.helpHeader');
|
|
987
989
|
/**
|
|
988
990
|
* Plugin-first CLI host for get-dotenv. Extends Commander.Command.
|
|
989
991
|
*
|
|
@@ -1000,15 +1002,33 @@ class GetDotenvCli extends commander.Command {
|
|
|
1000
1002
|
_plugins = [];
|
|
1001
1003
|
/** One-time installation guard */
|
|
1002
1004
|
_installed = false;
|
|
1005
|
+
/** Optional header line to prepend in help output */
|
|
1006
|
+
[HELP_HEADER_SYMBOL];
|
|
1003
1007
|
constructor(alias = 'getdotenv') {
|
|
1004
1008
|
super(alias);
|
|
1005
1009
|
// Ensure subcommands that use passThroughOptions can be attached safely.
|
|
1006
1010
|
// Commander requires parent commands to enable positional options when a
|
|
1007
1011
|
// child uses passThroughOptions.
|
|
1008
1012
|
this.enablePositionalOptions();
|
|
1013
|
+
// Configure grouped help: show only base options in default "Options";
|
|
1014
|
+
// append App/Plugin sections after default help.
|
|
1015
|
+
this.configureHelp({
|
|
1016
|
+
visibleOptions: (cmd) => {
|
|
1017
|
+
const all = cmd.options ??
|
|
1018
|
+
[];
|
|
1019
|
+
return all.filter((opt) => {
|
|
1020
|
+
const group = opt.__group;
|
|
1021
|
+
return group === 'base';
|
|
1022
|
+
});
|
|
1023
|
+
},
|
|
1024
|
+
});
|
|
1025
|
+
this.addHelpText('beforeAll', () => {
|
|
1026
|
+
const header = this[HELP_HEADER_SYMBOL];
|
|
1027
|
+
return header && header.length > 0 ? `${header}\n\n` : '';
|
|
1028
|
+
});
|
|
1029
|
+
this.addHelpText('afterAll', (ctx) => this.#renderOptionGroups(ctx.command));
|
|
1009
1030
|
// Skeleton preSubcommand hook: produce a context if absent, without
|
|
1010
|
-
// mutating process.env. The passOptions hook (when installed) will
|
|
1011
|
-
// compute the final context using merged CLI options; keeping
|
|
1031
|
+
// mutating process.env. The passOptions hook (when installed) will // compute the final context using merged CLI options; keeping
|
|
1012
1032
|
// loadProcess=false here avoids leaking dotenv values into the parent
|
|
1013
1033
|
// process env before subcommands execute.
|
|
1014
1034
|
this.hook('preSubcommand', async () => {
|
|
@@ -1040,11 +1060,96 @@ class GetDotenvCli extends commander.Command {
|
|
|
1040
1060
|
getCtx() {
|
|
1041
1061
|
return this[CTX_SYMBOL];
|
|
1042
1062
|
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Retrieve the merged root CLI options bag (if set by passOptions()).
|
|
1065
|
+
* Downstream-safe: no generics required.
|
|
1066
|
+
*/
|
|
1067
|
+
getOptions() {
|
|
1068
|
+
return this[OPTS_SYMBOL];
|
|
1069
|
+
}
|
|
1070
|
+
/** Internal: set the merged root options bag for this run. */
|
|
1071
|
+
_setOptionsBag(bag) {
|
|
1072
|
+
this[OPTS_SYMBOL] = bag;
|
|
1073
|
+
}
|
|
1043
1074
|
/** * Convenience helper to create a namespaced subcommand.
|
|
1044
1075
|
*/
|
|
1045
1076
|
ns(name) {
|
|
1046
1077
|
return this.command(name);
|
|
1047
1078
|
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Tag options added during the provided callback as 'app' for grouped help.
|
|
1081
|
+
* Allows downstream apps to demarcate their root-level options.
|
|
1082
|
+
*/
|
|
1083
|
+
tagAppOptions(fn) {
|
|
1084
|
+
const root = this;
|
|
1085
|
+
const originalAddOption = root.addOption.bind(root);
|
|
1086
|
+
const originalOption = root.option.bind(root);
|
|
1087
|
+
const tagLatest = (cmd, group) => {
|
|
1088
|
+
const optsArr = cmd.options;
|
|
1089
|
+
if (Array.isArray(optsArr) && optsArr.length > 0) {
|
|
1090
|
+
const last = optsArr[optsArr.length - 1];
|
|
1091
|
+
last.__group = group;
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
root.addOption = function patchedAdd(opt) {
|
|
1095
|
+
opt.__group = 'app';
|
|
1096
|
+
return originalAddOption(opt);
|
|
1097
|
+
};
|
|
1098
|
+
root.option = function patchedOption(...args) {
|
|
1099
|
+
const ret = originalOption(...args);
|
|
1100
|
+
tagLatest(this, 'app');
|
|
1101
|
+
return ret;
|
|
1102
|
+
};
|
|
1103
|
+
try {
|
|
1104
|
+
return fn(root);
|
|
1105
|
+
}
|
|
1106
|
+
finally {
|
|
1107
|
+
root.addOption = originalAddOption;
|
|
1108
|
+
root.option = originalOption;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Branding helper: set CLI name/description/version and optional help header.
|
|
1113
|
+
* If version is omitted and importMetaUrl is provided, attempts to read the
|
|
1114
|
+
* nearest package.json version (best-effort; non-fatal on failure).
|
|
1115
|
+
*/
|
|
1116
|
+
async brand(args) {
|
|
1117
|
+
const { name, description, version, importMetaUrl, helpHeader } = args;
|
|
1118
|
+
if (typeof name === 'string' && name.length > 0)
|
|
1119
|
+
this.name(name);
|
|
1120
|
+
if (typeof description === 'string')
|
|
1121
|
+
this.description(description);
|
|
1122
|
+
let v = version;
|
|
1123
|
+
if (!v && importMetaUrl) {
|
|
1124
|
+
try {
|
|
1125
|
+
const fromUrl = url.fileURLToPath(importMetaUrl);
|
|
1126
|
+
const pkgDir = await packageDirectory.packageDirectory({ cwd: fromUrl });
|
|
1127
|
+
if (pkgDir) {
|
|
1128
|
+
const txt = await fs.readFile(`${pkgDir}/package.json`, 'utf-8');
|
|
1129
|
+
const pkg = JSON.parse(txt);
|
|
1130
|
+
if (pkg.version)
|
|
1131
|
+
v = pkg.version;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
catch {
|
|
1135
|
+
// best-effort only
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (v)
|
|
1139
|
+
this.version(v);
|
|
1140
|
+
// Help header:
|
|
1141
|
+
// - If caller provides helpHeader, use it.
|
|
1142
|
+
// - Otherwise, when a version is known, default to "<name> v<version>".
|
|
1143
|
+
if (typeof helpHeader === 'string') {
|
|
1144
|
+
this[HELP_HEADER_SYMBOL] = helpHeader;
|
|
1145
|
+
}
|
|
1146
|
+
else if (v) {
|
|
1147
|
+
// Use the current command name (possibly overridden by 'name' above).
|
|
1148
|
+
const header = `${this.name()} v${v}`;
|
|
1149
|
+
this[HELP_HEADER_SYMBOL] = header;
|
|
1150
|
+
}
|
|
1151
|
+
return this;
|
|
1152
|
+
}
|
|
1048
1153
|
/**
|
|
1049
1154
|
* Register a plugin for installation (parent level).
|
|
1050
1155
|
* Installation occurs on first resolveAndLoad() (or explicit install()).
|
|
@@ -1083,7 +1188,71 @@ class GetDotenvCli extends commander.Command {
|
|
|
1083
1188
|
for (const p of this._plugins)
|
|
1084
1189
|
await run(p);
|
|
1085
1190
|
}
|
|
1191
|
+
// Render App/Plugin grouped options appended after default help.
|
|
1192
|
+
#renderOptionGroups(cmd) {
|
|
1193
|
+
const all = cmd.options ?? [];
|
|
1194
|
+
const byGroup = new Map();
|
|
1195
|
+
for (const o of all) {
|
|
1196
|
+
const opt = o;
|
|
1197
|
+
const g = opt.__group;
|
|
1198
|
+
if (!g || g === 'base')
|
|
1199
|
+
continue; // base handled by default help
|
|
1200
|
+
const rows = byGroup.get(g) ?? [];
|
|
1201
|
+
rows.push({
|
|
1202
|
+
flags: opt.flags ?? '',
|
|
1203
|
+
description: opt.description ?? '',
|
|
1204
|
+
});
|
|
1205
|
+
byGroup.set(g, rows);
|
|
1206
|
+
}
|
|
1207
|
+
if (byGroup.size === 0)
|
|
1208
|
+
return '';
|
|
1209
|
+
const renderRows = (title, rows) => {
|
|
1210
|
+
const width = Math.min(40, rows.reduce((m, r) => Math.max(m, r.flags.length), 0));
|
|
1211
|
+
const lines = rows
|
|
1212
|
+
.map((r) => {
|
|
1213
|
+
const pad = ' '.repeat(Math.max(2, width - r.flags.length + 2));
|
|
1214
|
+
return ` ${r.flags}${pad}${r.description}`.trimEnd();
|
|
1215
|
+
})
|
|
1216
|
+
.join('\n');
|
|
1217
|
+
return `\n${title}:\n${lines}\n`;
|
|
1218
|
+
};
|
|
1219
|
+
let out = '';
|
|
1220
|
+
// App options (if any)
|
|
1221
|
+
const app = byGroup.get('app');
|
|
1222
|
+
if (app && app.length > 0) {
|
|
1223
|
+
out += renderRows('App options', app);
|
|
1224
|
+
}
|
|
1225
|
+
// Plugin groups sorted by id
|
|
1226
|
+
const pluginKeys = Array.from(byGroup.keys()).filter((k) => k.startsWith('plugin:'));
|
|
1227
|
+
pluginKeys.sort((a, b) => a.localeCompare(b));
|
|
1228
|
+
for (const k of pluginKeys) {
|
|
1229
|
+
const id = k.slice('plugin:'.length) || '(unknown)';
|
|
1230
|
+
const rows = byGroup.get(k) ?? [];
|
|
1231
|
+
if (rows.length > 0) {
|
|
1232
|
+
out += renderRows(`Plugin options — ${id}`, rows);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return out;
|
|
1236
|
+
}
|
|
1086
1237
|
}
|
|
1087
1238
|
|
|
1239
|
+
/**
|
|
1240
|
+
* Helper to retrieve the merged root options bag from any action handler
|
|
1241
|
+
* that only has access to thisCommand. Avoids structural casts.
|
|
1242
|
+
*/
|
|
1243
|
+
const readMergedOptions = (cmd) => {
|
|
1244
|
+
// Ascend to the root command
|
|
1245
|
+
let root = cmd;
|
|
1246
|
+
while (root.parent) {
|
|
1247
|
+
root = root.parent;
|
|
1248
|
+
}
|
|
1249
|
+
const hostAny = root;
|
|
1250
|
+
return typeof hostAny.getOptions === 'function'
|
|
1251
|
+
? hostAny.getOptions()
|
|
1252
|
+
: root
|
|
1253
|
+
.getDotenvCliOptions;
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1088
1256
|
exports.GetDotenvCli = GetDotenvCli;
|
|
1089
1257
|
exports.definePlugin = definePlugin;
|
|
1258
|
+
exports.readMergedOptions = readMergedOptions;
|
package/dist/cliHost.d.cts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { ZodType } from 'zod';
|
|
2
1
|
import { Command } from 'commander';
|
|
2
|
+
import { ZodType } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scripts table shape (configurable shell type).
|
|
6
|
+
*/
|
|
7
|
+
type ScriptsTable<TShell extends string | boolean = string | boolean> = Record<string, string | {
|
|
8
|
+
cmd: string;
|
|
9
|
+
shell?: TShell;
|
|
10
|
+
}>;
|
|
3
11
|
|
|
4
12
|
/**
|
|
5
13
|
* A minimal representation of an environment key/value mapping.
|
|
@@ -95,6 +103,76 @@ interface GetDotenvOptions {
|
|
|
95
103
|
useConfigLoader?: boolean;
|
|
96
104
|
}
|
|
97
105
|
|
|
106
|
+
type Scripts = Record<string, string | {
|
|
107
|
+
cmd: string;
|
|
108
|
+
shell?: string | boolean;
|
|
109
|
+
}>;
|
|
110
|
+
/**
|
|
111
|
+
* Options passed programmatically to `getDotenvCli`.
|
|
112
|
+
*/
|
|
113
|
+
interface GetDotenvCliOptions extends Omit<GetDotenvOptions, 'paths' | 'vars'> {
|
|
114
|
+
/**
|
|
115
|
+
* Logs CLI internals when true.
|
|
116
|
+
*/
|
|
117
|
+
debug?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* When true, capture child stdout/stderr and re-emit after completion.
|
|
120
|
+
* Useful for tests/CI. Default behavior is streaming via stdio: 'inherit'.
|
|
121
|
+
*/
|
|
122
|
+
capture?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* A delimited string of paths to dotenv files.
|
|
125
|
+
*/
|
|
126
|
+
paths?: string;
|
|
127
|
+
/**
|
|
128
|
+
* A delimiter string with which to split `paths`. Only used if
|
|
129
|
+
* `pathsDelimiterPattern` is not provided.
|
|
130
|
+
*/
|
|
131
|
+
pathsDelimiter?: string;
|
|
132
|
+
/**
|
|
133
|
+
* A regular expression pattern with which to split `paths`. Supersedes
|
|
134
|
+
* `pathsDelimiter`.
|
|
135
|
+
*/
|
|
136
|
+
pathsDelimiterPattern?: string;
|
|
137
|
+
/**
|
|
138
|
+
* Scripts that can be executed from the CLI, either individually or via the batch subcommand.
|
|
139
|
+
*/
|
|
140
|
+
scripts?: Scripts;
|
|
141
|
+
/**
|
|
142
|
+
* Determines how commands and scripts are executed. If `false` or
|
|
143
|
+
* `undefined`, commands are executed as plain Javascript using the default
|
|
144
|
+
* execa parser. If `true`, commands are executed using the default OS shell
|
|
145
|
+
* parser. Otherwise the user may provide a specific shell string (e.g.
|
|
146
|
+
* `/bin/bash`)
|
|
147
|
+
*/
|
|
148
|
+
shell?: string | boolean;
|
|
149
|
+
/**
|
|
150
|
+
* A delimited string of key-value pairs declaratively specifying variables &
|
|
151
|
+
* values to be loaded in addition to any dotenv files.
|
|
152
|
+
*/
|
|
153
|
+
vars?: string;
|
|
154
|
+
/**
|
|
155
|
+
* A string with which to split keys from values in `vars`. Only used if
|
|
156
|
+
* `varsDelimiterPattern` is not provided.
|
|
157
|
+
*/
|
|
158
|
+
varsAssignor?: string;
|
|
159
|
+
/**
|
|
160
|
+
* A regular expression pattern with which to split variable names from values
|
|
161
|
+
* in `vars`. Supersedes `varsAssignor`.
|
|
162
|
+
*/
|
|
163
|
+
varsAssignorPattern?: string;
|
|
164
|
+
/**
|
|
165
|
+
* A string with which to split `vars` into key-value pairs. Only used if
|
|
166
|
+
* `varsDelimiterPattern` is not provided.
|
|
167
|
+
*/
|
|
168
|
+
varsDelimiter?: string;
|
|
169
|
+
/**
|
|
170
|
+
* A regular expression pattern with which to split `vars` into key-value
|
|
171
|
+
* pairs. Supersedes `varsDelimiter`.
|
|
172
|
+
*/
|
|
173
|
+
varsDelimiterPattern?: string;
|
|
174
|
+
}
|
|
175
|
+
|
|
98
176
|
/** * Per-invocation context shared with plugins and actions. */
|
|
99
177
|
type GetDotenvCliCtx<TOptions extends GetDotenvOptions = GetDotenvOptions> = {
|
|
100
178
|
optionsResolved: TOptions;
|
|
@@ -102,6 +180,7 @@ type GetDotenvCliCtx<TOptions extends GetDotenvOptions = GetDotenvOptions> = {
|
|
|
102
180
|
plugins?: Record<string, unknown>;
|
|
103
181
|
pluginConfigs?: Record<string, unknown>;
|
|
104
182
|
};
|
|
183
|
+
declare const HELP_HEADER_SYMBOL: unique symbol;
|
|
105
184
|
/**
|
|
106
185
|
* Plugin-first CLI host for get-dotenv. Extends Commander.Command.
|
|
107
186
|
*
|
|
@@ -114,10 +193,13 @@ type GetDotenvCliCtx<TOptions extends GetDotenvOptions = GetDotenvOptions> = {
|
|
|
114
193
|
* NOTE: This host is additive and does not alter the legacy CLI.
|
|
115
194
|
*/
|
|
116
195
|
declare class GetDotenvCli<TOptions extends GetDotenvOptions = GetDotenvOptions> extends Command {
|
|
196
|
+
#private;
|
|
117
197
|
/** Registered top-level plugins (composition happens via .use()) */
|
|
118
198
|
private _plugins;
|
|
119
199
|
/** One-time installation guard */
|
|
120
200
|
private _installed;
|
|
201
|
+
/** Optional header line to prepend in help output */
|
|
202
|
+
private [HELP_HEADER_SYMBOL];
|
|
121
203
|
constructor(alias?: string);
|
|
122
204
|
/**
|
|
123
205
|
* Resolve options (strict) and compute dotenv context. * Stores the context on the instance under a symbol.
|
|
@@ -127,9 +209,33 @@ declare class GetDotenvCli<TOptions extends GetDotenvOptions = GetDotenvOptions>
|
|
|
127
209
|
* Retrieve the current invocation context (if any).
|
|
128
210
|
*/
|
|
129
211
|
getCtx(): GetDotenvCliCtx<TOptions> | undefined;
|
|
212
|
+
/**
|
|
213
|
+
* Retrieve the merged root CLI options bag (if set by passOptions()).
|
|
214
|
+
* Downstream-safe: no generics required.
|
|
215
|
+
*/
|
|
216
|
+
getOptions(): GetDotenvCliOptions | undefined;
|
|
217
|
+
/** Internal: set the merged root options bag for this run. */
|
|
218
|
+
_setOptionsBag(bag: GetDotenvCliOptions): void;
|
|
130
219
|
/** * Convenience helper to create a namespaced subcommand.
|
|
131
220
|
*/
|
|
132
221
|
ns(name: string): Command;
|
|
222
|
+
/**
|
|
223
|
+
* Tag options added during the provided callback as 'app' for grouped help.
|
|
224
|
+
* Allows downstream apps to demarcate their root-level options.
|
|
225
|
+
*/
|
|
226
|
+
tagAppOptions<T>(fn: (root: Command) => T): T;
|
|
227
|
+
/**
|
|
228
|
+
* Branding helper: set CLI name/description/version and optional help header.
|
|
229
|
+
* If version is omitted and importMetaUrl is provided, attempts to read the
|
|
230
|
+
* nearest package.json version (best-effort; non-fatal on failure).
|
|
231
|
+
*/
|
|
232
|
+
brand(args: {
|
|
233
|
+
name?: string;
|
|
234
|
+
description?: string;
|
|
235
|
+
version?: string;
|
|
236
|
+
importMetaUrl?: string;
|
|
237
|
+
helpHeader?: string;
|
|
238
|
+
}): Promise<this>;
|
|
133
239
|
/**
|
|
134
240
|
* Register a plugin for installation (parent level).
|
|
135
241
|
* Installation occurs on first resolveAndLoad() (or explicit install()).
|
|
@@ -187,5 +293,11 @@ type DefineSpec = Omit<GetDotenvCliPlugin, 'children' | 'use'> & {
|
|
|
187
293
|
*/
|
|
188
294
|
declare const definePlugin: (spec: DefineSpec) => GetDotenvCliPlugin;
|
|
189
295
|
|
|
190
|
-
|
|
191
|
-
|
|
296
|
+
/**
|
|
297
|
+
* Helper to retrieve the merged root options bag from any action handler
|
|
298
|
+
* that only has access to thisCommand. Avoids structural casts.
|
|
299
|
+
*/
|
|
300
|
+
declare const readMergedOptions: (cmd: Command) => GetDotenvCliOptions | undefined;
|
|
301
|
+
|
|
302
|
+
export { GetDotenvCli, definePlugin, readMergedOptions };
|
|
303
|
+
export type { DefineSpec, GetDotenvCliCtx, GetDotenvCliOptions, GetDotenvCliPlugin, ScriptsTable };
|