@chengyixu/envshow 1.0.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/envshow.js +224 -0
- package/package.json +31 -0
package/envshow.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { env, stdout, exit, stderr } = process;
|
|
4
|
+
|
|
5
|
+
const HELP = `envshow — pretty-print environment variables
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
envshow [options]
|
|
9
|
+
echo "KEY=val" | envshow --stdin Merge stdin .env lines into output
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--filter, -f <pattern> Only show keys matching pattern (case-insensitive substring)
|
|
13
|
+
--mask, -m Mask values: show first 4 + last 4 characters
|
|
14
|
+
--json, -j Output as JSON
|
|
15
|
+
--keys, -k Show only keys, no values
|
|
16
|
+
--no-sort, --sort -s Sort alphabetically by key (default: --sort)
|
|
17
|
+
--stdin, -i Read KEY=value lines from stdin and merge into env
|
|
18
|
+
--prefix, -p <prefix> Only show keys starting with this prefix
|
|
19
|
+
--help, -h Show this help
|
|
20
|
+
--version, -v Show version
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
envshow All env vars, sorted
|
|
24
|
+
envshow -f NODE Only keys containing "NODE"
|
|
25
|
+
envshow -p AWS_ Only keys starting with "AWS_"
|
|
26
|
+
envshow -mj Masked values as JSON
|
|
27
|
+
envshow -k -f PATH Just keys matching PATH (handy for checking)
|
|
28
|
+
cat .env | envshow --stdin Merge .env file into current env and display
|
|
29
|
+
envshow --stdin < .env Same as above
|
|
30
|
+
envshow -f proxy -m Find proxy settings with masked values
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const VERSION = '1.0.0';
|
|
34
|
+
|
|
35
|
+
function parseArgs(argv) {
|
|
36
|
+
const flags = {
|
|
37
|
+
filter: null,
|
|
38
|
+
mask: false,
|
|
39
|
+
json: false,
|
|
40
|
+
keysOnly: false,
|
|
41
|
+
sort: true,
|
|
42
|
+
stdin: false,
|
|
43
|
+
prefix: null,
|
|
44
|
+
help: false,
|
|
45
|
+
version: false,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Expand combined short flags: -mj -> -m -j
|
|
49
|
+
const expanded = [];
|
|
50
|
+
for (let i = 2; i < argv.length; i++) {
|
|
51
|
+
const arg = argv[i];
|
|
52
|
+
if (arg.length > 2 && arg.startsWith('-') && !arg.startsWith('--') && arg[1] !== 'f' && arg[1] !== 'p') {
|
|
53
|
+
// Split -mj into -m -j (but not -f or -p since they take values)
|
|
54
|
+
for (let j = 1; j < arg.length; j++) {
|
|
55
|
+
expanded.push('-' + arg[j]);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
expanded.push(arg);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < expanded.length; i++) {
|
|
63
|
+
const arg = expanded[i];
|
|
64
|
+
switch (arg) {
|
|
65
|
+
case '--filter': case '-f':
|
|
66
|
+
flags.filter = expanded[++i];
|
|
67
|
+
if (!flags.filter || flags.filter.startsWith('-')) { stderr.write('envshow: --filter requires a pattern\n'); exit(1); }
|
|
68
|
+
break;
|
|
69
|
+
case '--mask': case '-m':
|
|
70
|
+
flags.mask = true;
|
|
71
|
+
break;
|
|
72
|
+
case '--json': case '-j':
|
|
73
|
+
flags.json = true;
|
|
74
|
+
break;
|
|
75
|
+
case '--keys': case '-k':
|
|
76
|
+
flags.keysOnly = true;
|
|
77
|
+
break;
|
|
78
|
+
case '--sort': case '-s':
|
|
79
|
+
flags.sort = true;
|
|
80
|
+
break;
|
|
81
|
+
case '--no-sort':
|
|
82
|
+
flags.sort = false;
|
|
83
|
+
break;
|
|
84
|
+
case '--stdin': case '-i':
|
|
85
|
+
flags.stdin = true;
|
|
86
|
+
break;
|
|
87
|
+
case '--prefix': case '-p':
|
|
88
|
+
flags.prefix = expanded[++i];
|
|
89
|
+
if (!flags.prefix || flags.prefix.startsWith('-')) { stderr.write('envshow: --prefix requires a value\n'); exit(1); }
|
|
90
|
+
break;
|
|
91
|
+
case '--help': case '-h':
|
|
92
|
+
flags.help = true;
|
|
93
|
+
break;
|
|
94
|
+
case '--version': case '-v':
|
|
95
|
+
flags.version = true;
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
if (arg.startsWith('-')) {
|
|
99
|
+
stderr.write(`envshow: unknown option '${arg}'\n`);
|
|
100
|
+
exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return flags;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function maskValue(val) {
|
|
109
|
+
if (val.length <= 8) return '*'.repeat(val.length);
|
|
110
|
+
return val.slice(0, 4) + '*'.repeat(Math.max(val.length - 8, 1)) + val.slice(-4);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function readStdin() {
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
if (process.stdin.isTTY) return resolve('');
|
|
116
|
+
let data = '';
|
|
117
|
+
process.stdin.setEncoding('utf8');
|
|
118
|
+
process.stdin.on('data', (chunk) => { data += chunk; });
|
|
119
|
+
process.stdin.on('end', () => resolve(data));
|
|
120
|
+
// If nothing comes in after 100ms, resolve
|
|
121
|
+
setTimeout(() => resolve(data), 100);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function parseEnvLines(input) {
|
|
126
|
+
const result = {};
|
|
127
|
+
for (const line of input.split('\n')) {
|
|
128
|
+
const trimmed = line.trim();
|
|
129
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
130
|
+
const eqIdx = trimmed.indexOf('=');
|
|
131
|
+
if (eqIdx === -1) continue;
|
|
132
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
133
|
+
let val = trimmed.slice(eqIdx + 1).trim();
|
|
134
|
+
// Strip surrounding quotes
|
|
135
|
+
if ((val.startsWith('"') && val.endsWith('"')) ||
|
|
136
|
+
(val.startsWith("'") && val.endsWith("'"))) {
|
|
137
|
+
val = val.slice(1, -1);
|
|
138
|
+
}
|
|
139
|
+
if (key) result[key] = val;
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function main() {
|
|
145
|
+
const flags = parseArgs(process.argv);
|
|
146
|
+
|
|
147
|
+
if (flags.help) {
|
|
148
|
+
stdout.write(HELP);
|
|
149
|
+
exit(0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (flags.version) {
|
|
153
|
+
stdout.write(`envshow v${VERSION}\n`);
|
|
154
|
+
exit(0);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let entries = Object.entries(env);
|
|
158
|
+
|
|
159
|
+
// Merge stdin if requested
|
|
160
|
+
if (flags.stdin) {
|
|
161
|
+
const stdinData = await readStdin();
|
|
162
|
+
if (stdinData) {
|
|
163
|
+
const parsed = parseEnvLines(stdinData);
|
|
164
|
+
const merged = { ...env, ...parsed };
|
|
165
|
+
entries = Object.entries(merged);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Filter by prefix
|
|
170
|
+
if (flags.prefix) {
|
|
171
|
+
const pfx = flags.prefix.toUpperCase();
|
|
172
|
+
entries = entries.filter(([k]) => k.toUpperCase().startsWith(pfx));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Filter by substring
|
|
176
|
+
if (flags.filter) {
|
|
177
|
+
const f = flags.filter.toUpperCase();
|
|
178
|
+
entries = entries.filter(([k]) => k.toUpperCase().includes(f));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Sort
|
|
182
|
+
if (flags.sort) {
|
|
183
|
+
entries.sort(([a], [b]) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// JSON output
|
|
187
|
+
if (flags.json) {
|
|
188
|
+
const obj = {};
|
|
189
|
+
for (const [k, v] of entries) {
|
|
190
|
+
obj[k] = flags.keysOnly ? '' : (flags.mask ? maskValue(v) : v);
|
|
191
|
+
}
|
|
192
|
+
stdout.write(JSON.stringify(obj, null, 2) + '\n');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Find max key length for alignment
|
|
197
|
+
const maxKeyLen = entries.length > 0
|
|
198
|
+
? Math.max(...entries.map(([k]) => k.length))
|
|
199
|
+
: 0;
|
|
200
|
+
|
|
201
|
+
// Terminal color support (basic check)
|
|
202
|
+
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
203
|
+
|
|
204
|
+
const CYAN = useColor ? '\x1b[36m' : '';
|
|
205
|
+
const YELLOW = useColor ? '\x1b[33m' : '';
|
|
206
|
+
const DIM = useColor ? '\x1b[2m' : '';
|
|
207
|
+
const RESET = useColor ? '\x1b[0m' : '';
|
|
208
|
+
|
|
209
|
+
for (const [key, val] of entries) {
|
|
210
|
+
if (flags.keysOnly) {
|
|
211
|
+
stdout.write(`${CYAN}${key}${RESET}\n`);
|
|
212
|
+
} else {
|
|
213
|
+
const displayVal = flags.mask ? maskValue(val) : val;
|
|
214
|
+
const valColor = flags.mask ? YELLOW : '';
|
|
215
|
+
const pad = ' '.repeat(Math.max(maxKeyLen - key.length, 0));
|
|
216
|
+
stdout.write(`${CYAN}${key}${RESET}${pad} ${DIM}=${RESET} ${valColor}${displayVal}${RESET}\n`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
main().catch((err) => {
|
|
222
|
+
stderr.write(`envshow: ${err.message}\n`);
|
|
223
|
+
exit(1);
|
|
224
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chengyixu/envshow",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Pretty-print environment variables with sorting, filtering, masking, and JSON output. Zero dependencies.",
|
|
5
|
+
"main": "envshow.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"envshow": "envshow.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"envshow.js"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"env",
|
|
14
|
+
"environment",
|
|
15
|
+
"variables",
|
|
16
|
+
"pretty-print",
|
|
17
|
+
"cli",
|
|
18
|
+
"dotenv",
|
|
19
|
+
"debug",
|
|
20
|
+
"devtools"
|
|
21
|
+
],
|
|
22
|
+
"author": "chengyixu",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/chengyixu/envshow.git"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=14"
|
|
30
|
+
}
|
|
31
|
+
}
|