configenvy 0.1.1 → 0.1.2
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 +28 -0
- package/dist/index.js +75 -38
- package/package.json +5 -2
- package/src/index.ts +116 -59
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { writeFile } from 'node:fs/promises';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { buildMarkdownTable, explainVariable, scanProject, toJson, Diagnostic } from '@configenvy/core';
|
|
6
|
+
|
|
7
|
+
type DoctorOptions = {
|
|
8
|
+
format?: "text" | "json";
|
|
9
|
+
strict?: boolean;
|
|
10
|
+
ci?: boolean;
|
|
11
|
+
};
|
|
12
|
+
type CliDependencies = {
|
|
13
|
+
buildMarkdownTable: typeof buildMarkdownTable;
|
|
14
|
+
error: (...values: unknown[]) => void;
|
|
15
|
+
exit: (code: number) => never | void;
|
|
16
|
+
explainVariable: typeof explainVariable;
|
|
17
|
+
log: (...values: unknown[]) => void;
|
|
18
|
+
resolvePath: typeof resolve;
|
|
19
|
+
scanProject: typeof scanProject;
|
|
20
|
+
toJson: typeof toJson;
|
|
21
|
+
writeFile: typeof writeFile;
|
|
22
|
+
};
|
|
23
|
+
declare function createProgram(dependencies?: CliDependencies): Command;
|
|
24
|
+
declare function runCli(argv: string[], dependencies?: CliDependencies): Promise<void>;
|
|
25
|
+
declare function runDoctor(projectPath: string, options: DoctorOptions, dependencies?: CliDependencies): Promise<void>;
|
|
26
|
+
declare function resolveOutputPath(projectPath: string, outputPath: string, resolvePath?: typeof resolve): string;
|
|
27
|
+
declare function printHumanReport(diagnostics: Diagnostic[], log?: (...values: unknown[]) => void): void;
|
|
28
|
+
|
|
29
|
+
export { type CliDependencies, createProgram, printHumanReport, resolveOutputPath, runCli, runDoctor };
|
package/dist/index.js
CHANGED
|
@@ -2,61 +2,98 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { writeFile } from "fs/promises";
|
|
5
|
-
import { resolve } from "path";
|
|
5
|
+
import { isAbsolute, resolve } from "path";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
6
7
|
import { Command } from "commander";
|
|
7
8
|
import { buildMarkdownTable, explainVariable, scanProject, toJson } from "@configenvy/core";
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
var defaultDependencies = {
|
|
10
|
+
buildMarkdownTable,
|
|
11
|
+
error: console.error,
|
|
12
|
+
exit: process.exit,
|
|
13
|
+
explainVariable,
|
|
14
|
+
log: console.log,
|
|
15
|
+
resolvePath: resolve,
|
|
16
|
+
scanProject,
|
|
17
|
+
toJson,
|
|
18
|
+
writeFile
|
|
19
|
+
};
|
|
20
|
+
function createProgram(dependencies = defaultDependencies) {
|
|
21
|
+
const program = new Command();
|
|
22
|
+
program.name("configenvy").description("Find missing, unused, undocumented, and risky environment variables.").version("0.1.2");
|
|
23
|
+
program.command("doctor").argument("[path]", "project directory", ".").option("--format <format>", "output format: text or json", "text").option("--strict", "treat documentation warnings as errors").action(async (projectPath, options) => {
|
|
24
|
+
await runDoctor(projectPath, options, dependencies);
|
|
25
|
+
});
|
|
26
|
+
program.command("check").argument("[path]", "project directory", ".").option("--ci", "fail on warnings and errors").option("--format <format>", "output format: text or json", "text").action(async (projectPath, options) => {
|
|
27
|
+
await runDoctor(projectPath, { ...options, strict: Boolean(options.ci), ci: Boolean(options.ci) }, dependencies);
|
|
28
|
+
});
|
|
29
|
+
program.command("table").argument("[path]", "project directory", ".").option("--out <file>", "write markdown table to a file").action(async (projectPath, options) => {
|
|
30
|
+
const rootDir = dependencies.resolvePath(projectPath);
|
|
31
|
+
const result = await dependencies.scanProject({ rootDir });
|
|
32
|
+
const table = dependencies.buildMarkdownTable(result);
|
|
33
|
+
if (options.out) {
|
|
34
|
+
await dependencies.writeFile(resolveOutputPath(rootDir, options.out, dependencies.resolvePath), `${table}
|
|
21
35
|
`, "utf8");
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
});
|
|
26
|
-
program.command("explain").argument("<variable>", "environment variable name").argument("[path]", "project directory", ".").action(async (variable, projectPath) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
program
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
} else {
|
|
37
|
+
dependencies.log(table);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
program.command("explain").argument("<variable>", "environment variable name").argument("[path]", "project directory", ".").action(async (variable, projectPath) => {
|
|
41
|
+
const result = await dependencies.scanProject({ rootDir: dependencies.resolvePath(projectPath) });
|
|
42
|
+
dependencies.log(dependencies.explainVariable(result, variable));
|
|
43
|
+
});
|
|
44
|
+
return program;
|
|
45
|
+
}
|
|
46
|
+
async function runCli(argv, dependencies = defaultDependencies) {
|
|
47
|
+
const program = createProgram(dependencies);
|
|
48
|
+
await program.parseAsync(argv);
|
|
49
|
+
}
|
|
50
|
+
async function runDoctor(projectPath, options, dependencies = defaultDependencies) {
|
|
51
|
+
const result = await dependencies.scanProject({
|
|
52
|
+
rootDir: dependencies.resolvePath(projectPath),
|
|
53
|
+
strict: Boolean(options.strict)
|
|
54
|
+
});
|
|
36
55
|
if (options.format === "json") {
|
|
37
|
-
|
|
56
|
+
dependencies.log(dependencies.toJson(result));
|
|
38
57
|
} else {
|
|
39
|
-
printHumanReport(result.diagnostics);
|
|
58
|
+
printHumanReport(result.diagnostics, dependencies.log);
|
|
40
59
|
}
|
|
41
60
|
const hasError = result.diagnostics.some((diagnostic) => diagnostic.severity === "error");
|
|
42
61
|
const hasWarning = result.diagnostics.some((diagnostic) => diagnostic.severity === "warning");
|
|
43
|
-
if (hasError || options.ci && hasWarning)
|
|
44
|
-
if (hasWarning)
|
|
62
|
+
if (hasError || options.ci && hasWarning) dependencies.exit(2);
|
|
63
|
+
if (hasWarning) dependencies.exit(1);
|
|
64
|
+
}
|
|
65
|
+
function resolveOutputPath(projectPath, outputPath, resolvePath = resolve) {
|
|
66
|
+
if (isAbsolute(outputPath)) return outputPath;
|
|
67
|
+
return resolvePath(projectPath, outputPath);
|
|
45
68
|
}
|
|
46
|
-
function printHumanReport(diagnostics) {
|
|
69
|
+
function printHumanReport(diagnostics, log = defaultDependencies.log) {
|
|
47
70
|
if (diagnostics.length === 0) {
|
|
48
|
-
|
|
71
|
+
log("PASS configenvy found no environment variable issues.");
|
|
49
72
|
return;
|
|
50
73
|
}
|
|
51
74
|
for (const diagnostic of diagnostics) {
|
|
52
75
|
const label = diagnostic.severity === "error" ? "FAIL" : "WARN";
|
|
53
|
-
|
|
54
|
-
|
|
76
|
+
log(`${label} ${diagnostic.code} ${diagnostic.variable}`);
|
|
77
|
+
log(` ${diagnostic.message}`);
|
|
55
78
|
if (diagnostic.files.length > 0) {
|
|
56
|
-
|
|
79
|
+
log(` files: ${diagnostic.files.join(", ")}`);
|
|
57
80
|
}
|
|
58
81
|
}
|
|
59
82
|
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
60
83
|
const warnings = diagnostics.length - errors;
|
|
61
|
-
|
|
84
|
+
log(`Summary: ${errors} error(s), ${warnings} warning(s).`);
|
|
85
|
+
}
|
|
86
|
+
var invokedPath = process.argv[1];
|
|
87
|
+
if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) {
|
|
88
|
+
runCli(process.argv).catch((error) => {
|
|
89
|
+
defaultDependencies.error(error instanceof Error ? error.message : String(error));
|
|
90
|
+
defaultDependencies.exit(3);
|
|
91
|
+
});
|
|
62
92
|
}
|
|
93
|
+
export {
|
|
94
|
+
createProgram,
|
|
95
|
+
printHumanReport,
|
|
96
|
+
resolveOutputPath,
|
|
97
|
+
runCli,
|
|
98
|
+
runDoctor
|
|
99
|
+
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "configenvy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Find missing, unused, undocumented, and risky environment variables before setup breaks.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=18"
|
|
8
|
+
},
|
|
6
9
|
"repository": {
|
|
7
10
|
"type": "git",
|
|
8
11
|
"url": "git+https://github.com/sonsriver4815/configenvy.git",
|
|
@@ -32,7 +35,7 @@
|
|
|
32
35
|
"build": "tsup src/index.ts --format esm --dts --clean"
|
|
33
36
|
},
|
|
34
37
|
"dependencies": {
|
|
35
|
-
"@configenvy/core": "0.1.
|
|
38
|
+
"@configenvy/core": "0.1.2",
|
|
36
39
|
"commander": "^12.1.0"
|
|
37
40
|
},
|
|
38
41
|
"keywords": [
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { writeFile } from "node:fs/promises";
|
|
3
|
-
import { resolve } from "node:path";
|
|
3
|
+
import { isAbsolute, resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
4
5
|
import { Command } from "commander";
|
|
5
6
|
import { buildMarkdownTable, explainVariable, scanProject, toJson, type Diagnostic } from "@configenvy/core";
|
|
6
7
|
|
|
@@ -10,90 +11,146 @@ type DoctorOptions = {
|
|
|
10
11
|
ci?: boolean;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
export type CliDependencies = {
|
|
15
|
+
buildMarkdownTable: typeof buildMarkdownTable;
|
|
16
|
+
error: (...values: unknown[]) => void;
|
|
17
|
+
exit: (code: number) => never | void;
|
|
18
|
+
explainVariable: typeof explainVariable;
|
|
19
|
+
log: (...values: unknown[]) => void;
|
|
20
|
+
resolvePath: typeof resolve;
|
|
21
|
+
scanProject: typeof scanProject;
|
|
22
|
+
toJson: typeof toJson;
|
|
23
|
+
writeFile: typeof writeFile;
|
|
24
|
+
};
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
.
|
|
26
|
+
const defaultDependencies: CliDependencies = {
|
|
27
|
+
buildMarkdownTable,
|
|
28
|
+
error: console.error,
|
|
29
|
+
exit: process.exit,
|
|
30
|
+
explainVariable,
|
|
31
|
+
log: console.log,
|
|
32
|
+
resolvePath: resolve,
|
|
33
|
+
scanProject,
|
|
34
|
+
toJson,
|
|
35
|
+
writeFile
|
|
36
|
+
};
|
|
19
37
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
.argument("[path]", "project directory", ".")
|
|
23
|
-
.option("--format <format>", "output format: text or json", "text")
|
|
24
|
-
.option("--strict", "treat documentation warnings as errors")
|
|
25
|
-
.action(async (projectPath: string, options: DoctorOptions) => {
|
|
26
|
-
await runDoctor(projectPath, options);
|
|
27
|
-
});
|
|
38
|
+
export function createProgram(dependencies: CliDependencies = defaultDependencies): Command {
|
|
39
|
+
const program = new Command();
|
|
28
40
|
|
|
29
|
-
program
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.option("--format <format>", "output format: text or json", "text")
|
|
34
|
-
.action(async (projectPath: string, options: DoctorOptions) => {
|
|
35
|
-
await runDoctor(projectPath, { ...options, strict: Boolean(options.ci), ci: Boolean(options.ci) });
|
|
36
|
-
});
|
|
41
|
+
program
|
|
42
|
+
.name("configenvy")
|
|
43
|
+
.description("Find missing, unused, undocumented, and risky environment variables.")
|
|
44
|
+
.version("0.1.2");
|
|
37
45
|
|
|
38
|
-
program
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
await writeFile(resolve(options.out), `${table}\n`, "utf8");
|
|
47
|
-
} else {
|
|
48
|
-
console.log(table);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
46
|
+
program
|
|
47
|
+
.command("doctor")
|
|
48
|
+
.argument("[path]", "project directory", ".")
|
|
49
|
+
.option("--format <format>", "output format: text or json", "text")
|
|
50
|
+
.option("--strict", "treat documentation warnings as errors")
|
|
51
|
+
.action(async (projectPath: string, options: DoctorOptions) => {
|
|
52
|
+
await runDoctor(projectPath, options, dependencies);
|
|
53
|
+
});
|
|
51
54
|
|
|
52
|
-
program
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
program
|
|
56
|
+
.command("check")
|
|
57
|
+
.argument("[path]", "project directory", ".")
|
|
58
|
+
.option("--ci", "fail on warnings and errors")
|
|
59
|
+
.option("--format <format>", "output format: text or json", "text")
|
|
60
|
+
.action(async (projectPath: string, options: DoctorOptions) => {
|
|
61
|
+
await runDoctor(projectPath, { ...options, strict: Boolean(options.ci), ci: Boolean(options.ci) }, dependencies);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
program
|
|
65
|
+
.command("table")
|
|
66
|
+
.argument("[path]", "project directory", ".")
|
|
67
|
+
.option("--out <file>", "write markdown table to a file")
|
|
68
|
+
.action(async (projectPath: string, options: { out?: string }) => {
|
|
69
|
+
const rootDir = dependencies.resolvePath(projectPath);
|
|
70
|
+
const result = await dependencies.scanProject({ rootDir });
|
|
71
|
+
const table = dependencies.buildMarkdownTable(result);
|
|
72
|
+
if (options.out) {
|
|
73
|
+
await dependencies.writeFile(resolveOutputPath(rootDir, options.out, dependencies.resolvePath), `${table}\n`, "utf8");
|
|
74
|
+
} else {
|
|
75
|
+
dependencies.log(table);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
program
|
|
80
|
+
.command("explain")
|
|
81
|
+
.argument("<variable>", "environment variable name")
|
|
82
|
+
.argument("[path]", "project directory", ".")
|
|
83
|
+
.action(async (variable: string, projectPath: string) => {
|
|
84
|
+
const result = await dependencies.scanProject({ rootDir: dependencies.resolvePath(projectPath) });
|
|
85
|
+
dependencies.log(dependencies.explainVariable(result, variable));
|
|
86
|
+
});
|
|
60
87
|
|
|
61
|
-
program
|
|
62
|
-
|
|
63
|
-
process.exit(3);
|
|
64
|
-
});
|
|
88
|
+
return program;
|
|
89
|
+
}
|
|
65
90
|
|
|
66
|
-
async function
|
|
67
|
-
const
|
|
91
|
+
export async function runCli(argv: string[], dependencies: CliDependencies = defaultDependencies): Promise<void> {
|
|
92
|
+
const program = createProgram(dependencies);
|
|
93
|
+
await program.parseAsync(argv);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function runDoctor(
|
|
97
|
+
projectPath: string,
|
|
98
|
+
options: DoctorOptions,
|
|
99
|
+
dependencies: CliDependencies = defaultDependencies
|
|
100
|
+
): Promise<void> {
|
|
101
|
+
const result = await dependencies.scanProject({
|
|
102
|
+
rootDir: dependencies.resolvePath(projectPath),
|
|
103
|
+
strict: Boolean(options.strict)
|
|
104
|
+
});
|
|
68
105
|
|
|
69
106
|
if (options.format === "json") {
|
|
70
|
-
|
|
107
|
+
dependencies.log(dependencies.toJson(result));
|
|
71
108
|
} else {
|
|
72
|
-
printHumanReport(result.diagnostics);
|
|
109
|
+
printHumanReport(result.diagnostics, dependencies.log);
|
|
73
110
|
}
|
|
74
111
|
|
|
75
112
|
const hasError = result.diagnostics.some((diagnostic) => diagnostic.severity === "error");
|
|
76
113
|
const hasWarning = result.diagnostics.some((diagnostic) => diagnostic.severity === "warning");
|
|
77
|
-
if (hasError || (options.ci && hasWarning))
|
|
78
|
-
if (hasWarning)
|
|
114
|
+
if (hasError || (options.ci && hasWarning)) dependencies.exit(2);
|
|
115
|
+
if (hasWarning) dependencies.exit(1);
|
|
79
116
|
}
|
|
80
117
|
|
|
81
|
-
function
|
|
118
|
+
export function resolveOutputPath(
|
|
119
|
+
projectPath: string,
|
|
120
|
+
outputPath: string,
|
|
121
|
+
resolvePath: typeof resolve = resolve
|
|
122
|
+
): string {
|
|
123
|
+
if (isAbsolute(outputPath)) return outputPath;
|
|
124
|
+
return resolvePath(projectPath, outputPath);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function printHumanReport(
|
|
128
|
+
diagnostics: Diagnostic[],
|
|
129
|
+
log: (...values: unknown[]) => void = defaultDependencies.log
|
|
130
|
+
): void {
|
|
82
131
|
if (diagnostics.length === 0) {
|
|
83
|
-
|
|
132
|
+
log("PASS configenvy found no environment variable issues.");
|
|
84
133
|
return;
|
|
85
134
|
}
|
|
86
135
|
|
|
87
136
|
for (const diagnostic of diagnostics) {
|
|
88
137
|
const label = diagnostic.severity === "error" ? "FAIL" : "WARN";
|
|
89
|
-
|
|
90
|
-
|
|
138
|
+
log(`${label} ${diagnostic.code} ${diagnostic.variable}`);
|
|
139
|
+
log(` ${diagnostic.message}`);
|
|
91
140
|
if (diagnostic.files.length > 0) {
|
|
92
|
-
|
|
141
|
+
log(` files: ${diagnostic.files.join(", ")}`);
|
|
93
142
|
}
|
|
94
143
|
}
|
|
95
144
|
|
|
96
145
|
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
97
146
|
const warnings = diagnostics.length - errors;
|
|
98
|
-
|
|
147
|
+
log(`Summary: ${errors} error(s), ${warnings} warning(s).`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const invokedPath = process.argv[1];
|
|
151
|
+
if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) {
|
|
152
|
+
runCli(process.argv).catch((error: unknown) => {
|
|
153
|
+
defaultDependencies.error(error instanceof Error ? error.message : String(error));
|
|
154
|
+
defaultDependencies.exit(3);
|
|
155
|
+
});
|
|
99
156
|
}
|