configenvy 0.1.5 → 0.1.6
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/dist/index.d.ts +10 -2
- package/dist/index.js +45 -4
- package/package.json +2 -2
- package/src/index.ts +61 -4
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ type InitOptions = {
|
|
|
13
13
|
dryRun?: boolean;
|
|
14
14
|
envExample?: boolean;
|
|
15
15
|
force?: boolean;
|
|
16
|
+
preset?: string;
|
|
16
17
|
};
|
|
17
18
|
type CliDependencies = {
|
|
18
19
|
buildMarkdownTable: typeof buildMarkdownTable;
|
|
@@ -28,16 +29,23 @@ type CliDependencies = {
|
|
|
28
29
|
writeFile: typeof writeFile;
|
|
29
30
|
};
|
|
30
31
|
declare function createProgram(dependencies?: CliDependencies): Command;
|
|
32
|
+
type StarterConfig = {
|
|
33
|
+
docs: string[];
|
|
34
|
+
ignore: string[];
|
|
35
|
+
optional: string[];
|
|
36
|
+
required: string[];
|
|
37
|
+
};
|
|
31
38
|
type InitFile = {
|
|
32
39
|
content: string;
|
|
33
40
|
path: string;
|
|
34
41
|
};
|
|
42
|
+
declare const cliVersion: string;
|
|
35
43
|
declare const tableBlockStart = "<!-- configenvy:start -->";
|
|
36
44
|
declare const tableBlockEnd = "<!-- configenvy:end -->";
|
|
37
45
|
declare function runCli(argv: string[], dependencies?: CliDependencies): Promise<void>;
|
|
38
46
|
declare function runDoctor(projectPath: string, options: DoctorOptions, dependencies?: CliDependencies): Promise<void>;
|
|
39
47
|
declare function runInit(projectPath: string, options?: InitOptions, dependencies?: CliDependencies): Promise<void>;
|
|
40
|
-
declare function buildInitFiles(rootDir: string, result: ScanResult, includeEnvExample: boolean, resolvePath?: typeof resolve, existingEnvExample?: string): InitFile[];
|
|
48
|
+
declare function buildInitFiles(rootDir: string, result: ScanResult, includeEnvExample: boolean, resolvePath?: typeof resolve, existingEnvExample?: string, preset?: Partial<StarterConfig>): InitFile[];
|
|
41
49
|
declare function runTableUpdate(rootDir: string, table: string, options: {
|
|
42
50
|
dryRun?: boolean;
|
|
43
51
|
force?: boolean;
|
|
@@ -48,4 +56,4 @@ declare function resolveOutputPath(projectPath: string, outputPath: string, reso
|
|
|
48
56
|
declare function printHumanReport(diagnostics: Diagnostic[], log?: (...values: unknown[]) => void): void;
|
|
49
57
|
declare function printGitHubAnnotations(diagnostics: Diagnostic[], log?: (...values: unknown[]) => void): void;
|
|
50
58
|
|
|
51
|
-
export { type CliDependencies, buildInitFiles, createProgram, printGitHubAnnotations, printHumanReport, resolveOutputPath, runCli, runDoctor, runInit, runTableUpdate, tableBlockEnd, tableBlockStart, updateMarkdownTableBlock };
|
|
59
|
+
export { type CliDependencies, buildInitFiles, cliVersion, createProgram, printGitHubAnnotations, printHumanReport, resolveOutputPath, runCli, runDoctor, runInit, runTableUpdate, tableBlockEnd, tableBlockStart, updateMarkdownTableBlock };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { readFile, writeFile } from "fs/promises";
|
|
5
|
+
import { createRequire } from "module";
|
|
5
6
|
import { isAbsolute, resolve } from "path";
|
|
6
7
|
import { pathToFileURL } from "url";
|
|
7
8
|
import { Command } from "commander";
|
|
@@ -27,7 +28,7 @@ var defaultDependencies = {
|
|
|
27
28
|
};
|
|
28
29
|
function createProgram(dependencies = defaultDependencies) {
|
|
29
30
|
const program = new Command();
|
|
30
|
-
program.name("configenvy").description("Find missing, unused, undocumented, and risky environment variables.").version(
|
|
31
|
+
program.name("configenvy").description("Find missing, unused, undocumented, and risky environment variables.").version(cliVersion);
|
|
31
32
|
program.command("doctor").argument("[path]", "project directory", ".").option("--format <format>", "output format: text, json, or sarif", "text").option("--strict", "treat documentation warnings as errors").action(async (projectPath, options) => {
|
|
32
33
|
await runDoctor(projectPath, options, dependencies);
|
|
33
34
|
});
|
|
@@ -47,7 +48,7 @@ function createProgram(dependencies = defaultDependencies) {
|
|
|
47
48
|
dependencies.log(table);
|
|
48
49
|
}
|
|
49
50
|
});
|
|
50
|
-
program.command("init").argument("[path]", "project directory", ".").description("create starter configenvy files").option("--dry-run", "print planned files instead of writing them").option("--env-example", "also create a .env.example draft from detected variables").option("--force", "overwrite generated files if they already exist").action(async (projectPath, options) => {
|
|
51
|
+
program.command("init").argument("[path]", "project directory", ".").description("create starter configenvy files").option("--dry-run", "print planned files instead of writing them").option("--env-example", "also create a .env.example draft from detected variables").option("--force", "overwrite generated files if they already exist").option("--preset <name>", `apply a preset: ${availablePresetList}`).action(async (projectPath, options) => {
|
|
51
52
|
await runInit(projectPath, options, dependencies);
|
|
52
53
|
});
|
|
53
54
|
program.command("explain").argument("<variable>", "environment variable name").argument("[path]", "project directory", ".").action(async (variable, projectPath) => {
|
|
@@ -62,6 +63,28 @@ var starterConfig = {
|
|
|
62
63
|
ignore: ["NODE_ENV"],
|
|
63
64
|
docs: ["README.md", "docs"]
|
|
64
65
|
};
|
|
66
|
+
var presetConfigs = {
|
|
67
|
+
docker: {
|
|
68
|
+
optional: ["COMPOSE_PROJECT_NAME"],
|
|
69
|
+
ignore: ["HOSTNAME"]
|
|
70
|
+
},
|
|
71
|
+
nextjs: {
|
|
72
|
+
optional: ["NEXT_PUBLIC_APP_URL"],
|
|
73
|
+
ignore: ["NEXT_RUNTIME"]
|
|
74
|
+
},
|
|
75
|
+
vercel: {
|
|
76
|
+
ignore: ["VERCEL", "VERCEL_ENV", "VERCEL_URL", "VERCEL_BRANCH_URL", "VERCEL_PROJECT_PRODUCTION_URL", "VERCEL_REGION"]
|
|
77
|
+
},
|
|
78
|
+
vite: {
|
|
79
|
+
optional: ["VITE_PUBLIC_URL"],
|
|
80
|
+
ignore: ["BASE_URL", "DEV", "MODE", "PROD", "SSR"]
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var availablePresetNames = Object.keys(presetConfigs).sort();
|
|
84
|
+
var availablePresetList = availablePresetNames.join(", ");
|
|
85
|
+
var require2 = createRequire(import.meta.url);
|
|
86
|
+
var cliPackage = require2("../package.json");
|
|
87
|
+
var cliVersion = cliPackage.version;
|
|
65
88
|
var tableBlockStart = "<!-- configenvy:start -->";
|
|
66
89
|
var tableBlockEnd = "<!-- configenvy:end -->";
|
|
67
90
|
async function runCli(argv, dependencies = defaultDependencies) {
|
|
@@ -91,8 +114,10 @@ async function runDoctor(projectPath, options, dependencies = defaultDependencie
|
|
|
91
114
|
async function runInit(projectPath, options = {}, dependencies = defaultDependencies) {
|
|
92
115
|
const rootDir = dependencies.resolvePath(projectPath);
|
|
93
116
|
const result = await dependencies.scanProject({ rootDir });
|
|
117
|
+
const preset = resolvePreset(options.preset, dependencies);
|
|
118
|
+
if (options.preset && !preset) return;
|
|
94
119
|
const existingEnvExample = options.envExample && options.force ? await readOptionalText(dependencies.resolvePath(rootDir, ".env.example"), dependencies) : void 0;
|
|
95
|
-
const files = buildInitFiles(rootDir, result, Boolean(options.envExample), dependencies.resolvePath, existingEnvExample);
|
|
120
|
+
const files = buildInitFiles(rootDir, result, Boolean(options.envExample), dependencies.resolvePath, existingEnvExample, preset);
|
|
96
121
|
if (options.dryRun) {
|
|
97
122
|
for (const file of files) {
|
|
98
123
|
dependencies.log(`Would write ${file.path}`);
|
|
@@ -118,10 +143,13 @@ async function runInit(projectPath, options = {}, dependencies = defaultDependen
|
|
|
118
143
|
dependencies.log(`Created ${file.path}`);
|
|
119
144
|
}
|
|
120
145
|
}
|
|
121
|
-
function buildInitFiles(rootDir, result, includeEnvExample, resolvePath = resolve, existingEnvExample) {
|
|
146
|
+
function buildInitFiles(rootDir, result, includeEnvExample, resolvePath = resolve, existingEnvExample, preset) {
|
|
122
147
|
const required = detectedRuntimeVariables(result);
|
|
123
148
|
const config = {
|
|
124
149
|
...starterConfig,
|
|
150
|
+
docs: mergeUnique(starterConfig.docs, preset?.docs ?? []),
|
|
151
|
+
ignore: mergeUnique(starterConfig.ignore, preset?.ignore ?? []),
|
|
152
|
+
optional: mergeUnique(starterConfig.optional, preset?.optional ?? []),
|
|
125
153
|
required
|
|
126
154
|
};
|
|
127
155
|
const files = [
|
|
@@ -139,6 +167,18 @@ function buildInitFiles(rootDir, result, includeEnvExample, resolvePath = resolv
|
|
|
139
167
|
}
|
|
140
168
|
return files;
|
|
141
169
|
}
|
|
170
|
+
function resolvePreset(name, dependencies) {
|
|
171
|
+
if (!name) return void 0;
|
|
172
|
+
if (name in presetConfigs) {
|
|
173
|
+
return presetConfigs[name];
|
|
174
|
+
}
|
|
175
|
+
dependencies.error(`Unknown preset "${name}". Available presets: ${availablePresetList}.`);
|
|
176
|
+
dependencies.exit(1);
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
function mergeUnique(base, extra) {
|
|
180
|
+
return [.../* @__PURE__ */ new Set([...base, ...extra])].sort();
|
|
181
|
+
}
|
|
142
182
|
async function ensureInitTargetsDoNotExist(files, dependencies) {
|
|
143
183
|
for (const file of files) {
|
|
144
184
|
const existing = await readOptionalText(file.path, dependencies);
|
|
@@ -273,6 +313,7 @@ if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) {
|
|
|
273
313
|
}
|
|
274
314
|
export {
|
|
275
315
|
buildInitFiles,
|
|
316
|
+
cliVersion,
|
|
276
317
|
createProgram,
|
|
277
318
|
printGitHubAnnotations,
|
|
278
319
|
printHumanReport,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "configenvy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Find missing, unused, undocumented, and risky environment variables before setup breaks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"build": "tsup src/index.ts --format esm --dts --clean"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@configenvy/core": "0.1.
|
|
38
|
+
"@configenvy/core": "0.1.6",
|
|
39
39
|
"commander": "^12.1.0"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import { isAbsolute, resolve } from "node:path";
|
|
4
5
|
import { pathToFileURL } from "node:url";
|
|
5
6
|
import { Command } from "commander";
|
|
@@ -23,6 +24,7 @@ type InitOptions = {
|
|
|
23
24
|
dryRun?: boolean;
|
|
24
25
|
envExample?: boolean;
|
|
25
26
|
force?: boolean;
|
|
27
|
+
preset?: string;
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
export type CliDependencies = {
|
|
@@ -59,7 +61,7 @@ export function createProgram(dependencies: CliDependencies = defaultDependencie
|
|
|
59
61
|
program
|
|
60
62
|
.name("configenvy")
|
|
61
63
|
.description("Find missing, unused, undocumented, and risky environment variables.")
|
|
62
|
-
.version(
|
|
64
|
+
.version(cliVersion);
|
|
63
65
|
|
|
64
66
|
program
|
|
65
67
|
.command("doctor")
|
|
@@ -106,6 +108,7 @@ export function createProgram(dependencies: CliDependencies = defaultDependencie
|
|
|
106
108
|
.option("--dry-run", "print planned files instead of writing them")
|
|
107
109
|
.option("--env-example", "also create a .env.example draft from detected variables")
|
|
108
110
|
.option("--force", "overwrite generated files if they already exist")
|
|
111
|
+
.option("--preset <name>", `apply a preset: ${availablePresetList}`)
|
|
109
112
|
.action(async (projectPath: string, options: InitOptions) => {
|
|
110
113
|
await runInit(projectPath, options, dependencies);
|
|
111
114
|
});
|
|
@@ -122,18 +125,52 @@ export function createProgram(dependencies: CliDependencies = defaultDependencie
|
|
|
122
125
|
return program;
|
|
123
126
|
}
|
|
124
127
|
|
|
125
|
-
|
|
128
|
+
type StarterConfig = {
|
|
129
|
+
docs: string[];
|
|
130
|
+
ignore: string[];
|
|
131
|
+
optional: string[];
|
|
132
|
+
required: string[];
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const starterConfig: StarterConfig = {
|
|
126
136
|
required: [],
|
|
127
137
|
optional: [],
|
|
128
138
|
ignore: ["NODE_ENV"],
|
|
129
139
|
docs: ["README.md", "docs"]
|
|
130
140
|
};
|
|
131
141
|
|
|
142
|
+
type PresetName = keyof typeof presetConfigs;
|
|
143
|
+
|
|
144
|
+
const presetConfigs = {
|
|
145
|
+
docker: {
|
|
146
|
+
optional: ["COMPOSE_PROJECT_NAME"],
|
|
147
|
+
ignore: ["HOSTNAME"]
|
|
148
|
+
},
|
|
149
|
+
nextjs: {
|
|
150
|
+
optional: ["NEXT_PUBLIC_APP_URL"],
|
|
151
|
+
ignore: ["NEXT_RUNTIME"]
|
|
152
|
+
},
|
|
153
|
+
vercel: {
|
|
154
|
+
ignore: ["VERCEL", "VERCEL_ENV", "VERCEL_URL", "VERCEL_BRANCH_URL", "VERCEL_PROJECT_PRODUCTION_URL", "VERCEL_REGION"]
|
|
155
|
+
},
|
|
156
|
+
vite: {
|
|
157
|
+
optional: ["VITE_PUBLIC_URL"],
|
|
158
|
+
ignore: ["BASE_URL", "DEV", "MODE", "PROD", "SSR"]
|
|
159
|
+
}
|
|
160
|
+
} satisfies Record<string, Partial<StarterConfig>>;
|
|
161
|
+
|
|
162
|
+
const availablePresetNames = Object.keys(presetConfigs).sort();
|
|
163
|
+
const availablePresetList = availablePresetNames.join(", ");
|
|
164
|
+
|
|
132
165
|
type InitFile = {
|
|
133
166
|
content: string;
|
|
134
167
|
path: string;
|
|
135
168
|
};
|
|
136
169
|
|
|
170
|
+
const require = createRequire(import.meta.url);
|
|
171
|
+
const cliPackage = require("../package.json") as { version: string };
|
|
172
|
+
export const cliVersion = cliPackage.version;
|
|
173
|
+
|
|
137
174
|
export const tableBlockStart = "<!-- configenvy:start -->";
|
|
138
175
|
export const tableBlockEnd = "<!-- configenvy:end -->";
|
|
139
176
|
|
|
@@ -176,10 +213,12 @@ export async function runInit(
|
|
|
176
213
|
): Promise<void> {
|
|
177
214
|
const rootDir = dependencies.resolvePath(projectPath);
|
|
178
215
|
const result = await dependencies.scanProject({ rootDir });
|
|
216
|
+
const preset = resolvePreset(options.preset, dependencies);
|
|
217
|
+
if (options.preset && !preset) return;
|
|
179
218
|
const existingEnvExample = options.envExample && options.force
|
|
180
219
|
? await readOptionalText(dependencies.resolvePath(rootDir, ".env.example"), dependencies)
|
|
181
220
|
: undefined;
|
|
182
|
-
const files = buildInitFiles(rootDir, result, Boolean(options.envExample), dependencies.resolvePath, existingEnvExample);
|
|
221
|
+
const files = buildInitFiles(rootDir, result, Boolean(options.envExample), dependencies.resolvePath, existingEnvExample, preset);
|
|
183
222
|
|
|
184
223
|
if (options.dryRun) {
|
|
185
224
|
for (const file of files) {
|
|
@@ -215,11 +254,15 @@ export function buildInitFiles(
|
|
|
215
254
|
result: ScanResult,
|
|
216
255
|
includeEnvExample: boolean,
|
|
217
256
|
resolvePath: typeof resolve = resolve,
|
|
218
|
-
existingEnvExample?: string
|
|
257
|
+
existingEnvExample?: string,
|
|
258
|
+
preset?: Partial<StarterConfig>
|
|
219
259
|
): InitFile[] {
|
|
220
260
|
const required = detectedRuntimeVariables(result);
|
|
221
261
|
const config = {
|
|
222
262
|
...starterConfig,
|
|
263
|
+
docs: mergeUnique(starterConfig.docs, preset?.docs ?? []),
|
|
264
|
+
ignore: mergeUnique(starterConfig.ignore, preset?.ignore ?? []),
|
|
265
|
+
optional: mergeUnique(starterConfig.optional, preset?.optional ?? []),
|
|
223
266
|
required
|
|
224
267
|
};
|
|
225
268
|
const files: InitFile[] = [
|
|
@@ -239,6 +282,20 @@ export function buildInitFiles(
|
|
|
239
282
|
return files;
|
|
240
283
|
}
|
|
241
284
|
|
|
285
|
+
function resolvePreset(name: string | undefined, dependencies: CliDependencies): Partial<StarterConfig> | undefined {
|
|
286
|
+
if (!name) return undefined;
|
|
287
|
+
if (name in presetConfigs) {
|
|
288
|
+
return presetConfigs[name as PresetName];
|
|
289
|
+
}
|
|
290
|
+
dependencies.error(`Unknown preset "${name}". Available presets: ${availablePresetList}.`);
|
|
291
|
+
dependencies.exit(1);
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function mergeUnique(base: string[], extra: readonly string[]): string[] {
|
|
296
|
+
return [...new Set([...base, ...extra])].sort();
|
|
297
|
+
}
|
|
298
|
+
|
|
242
299
|
async function ensureInitTargetsDoNotExist(files: InitFile[], dependencies: CliDependencies): Promise<boolean> {
|
|
243
300
|
for (const file of files) {
|
|
244
301
|
const existing = await readOptionalText(file.path, dependencies);
|