@xlameiro/env-typegen 0.1.6 → 0.1.7
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/CHANGELOG.md +6 -0
- package/dist/cli.js +48 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +48 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +48 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -43,6 +43,21 @@ type ParsedEnvVar = {
|
|
|
43
43
|
*/
|
|
44
44
|
isSecret?: boolean;
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* A non-fatal issue detected during parsing (e.g. a duplicate key).
|
|
48
|
+
* Collected in {@link ParsedEnvFile.warnings} when the parser encounters
|
|
49
|
+
* degenerate-but-valid input.
|
|
50
|
+
*/
|
|
51
|
+
type ParsedEnvWarning = {
|
|
52
|
+
/** Machine-readable code for programmatic handling. */
|
|
53
|
+
code: "ENV_DUPLICATE_KEY";
|
|
54
|
+
/** Human-readable description of the issue. */
|
|
55
|
+
message: string;
|
|
56
|
+
/** 1-based line number of the earlier (discarded) occurrence. */
|
|
57
|
+
line: number;
|
|
58
|
+
/** The duplicated variable name. */
|
|
59
|
+
key: string;
|
|
60
|
+
};
|
|
46
61
|
/**
|
|
47
62
|
* The complete result of parsing a single .env.example file.
|
|
48
63
|
* Passed to generators to produce TypeScript / Zod / t3-env output.
|
|
@@ -54,6 +69,11 @@ type ParsedEnvFile = {
|
|
|
54
69
|
vars: ParsedEnvVar[];
|
|
55
70
|
/** Unique group names found in the file, in order of appearance */
|
|
56
71
|
groups: string[];
|
|
72
|
+
/**
|
|
73
|
+
* Non-fatal issues detected during parsing (e.g. duplicate keys).
|
|
74
|
+
* Undefined when no issues were found so the field is omitted in clean parses.
|
|
75
|
+
*/
|
|
76
|
+
warnings?: ParsedEnvWarning[];
|
|
57
77
|
};
|
|
58
78
|
|
|
59
79
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,21 @@ type ParsedEnvVar = {
|
|
|
43
43
|
*/
|
|
44
44
|
isSecret?: boolean;
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* A non-fatal issue detected during parsing (e.g. a duplicate key).
|
|
48
|
+
* Collected in {@link ParsedEnvFile.warnings} when the parser encounters
|
|
49
|
+
* degenerate-but-valid input.
|
|
50
|
+
*/
|
|
51
|
+
type ParsedEnvWarning = {
|
|
52
|
+
/** Machine-readable code for programmatic handling. */
|
|
53
|
+
code: "ENV_DUPLICATE_KEY";
|
|
54
|
+
/** Human-readable description of the issue. */
|
|
55
|
+
message: string;
|
|
56
|
+
/** 1-based line number of the earlier (discarded) occurrence. */
|
|
57
|
+
line: number;
|
|
58
|
+
/** The duplicated variable name. */
|
|
59
|
+
key: string;
|
|
60
|
+
};
|
|
46
61
|
/**
|
|
47
62
|
* The complete result of parsing a single .env.example file.
|
|
48
63
|
* Passed to generators to produce TypeScript / Zod / t3-env output.
|
|
@@ -54,6 +69,11 @@ type ParsedEnvFile = {
|
|
|
54
69
|
vars: ParsedEnvVar[];
|
|
55
70
|
/** Unique group names found in the file, in order of appearance */
|
|
56
71
|
groups: string[];
|
|
72
|
+
/**
|
|
73
|
+
* Non-fatal issues detected during parsing (e.g. duplicate keys).
|
|
74
|
+
* Undefined when no issues were found so the field is omitted in clean parses.
|
|
75
|
+
*/
|
|
76
|
+
warnings?: ParsedEnvWarning[];
|
|
57
77
|
};
|
|
58
78
|
|
|
59
79
|
/**
|
package/dist/index.js
CHANGED
|
@@ -245,6 +245,27 @@ function buildParsedVar(params, commentBlock, options) {
|
|
|
245
245
|
}
|
|
246
246
|
return parsedVar;
|
|
247
247
|
}
|
|
248
|
+
function deduplicateVars(vars) {
|
|
249
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
250
|
+
const deduped = [];
|
|
251
|
+
const warnings = [];
|
|
252
|
+
for (let i = vars.length - 1; i >= 0; i--) {
|
|
253
|
+
const variable = vars[i];
|
|
254
|
+
if (variable === void 0) continue;
|
|
255
|
+
if (seenKeys.has(variable.key)) {
|
|
256
|
+
warnings.push({
|
|
257
|
+
code: "ENV_DUPLICATE_KEY",
|
|
258
|
+
message: `Duplicate key "${variable.key}" at line ${variable.lineNumber} \u2014 last occurrence wins.`,
|
|
259
|
+
line: variable.lineNumber,
|
|
260
|
+
key: variable.key
|
|
261
|
+
});
|
|
262
|
+
} else {
|
|
263
|
+
seenKeys.add(variable.key);
|
|
264
|
+
deduped.unshift(variable);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return { deduped, warnings };
|
|
268
|
+
}
|
|
248
269
|
function parseEnvFileContent(content, filePath, options) {
|
|
249
270
|
const lines = content.split("\n");
|
|
250
271
|
const vars = [];
|
|
@@ -287,16 +308,13 @@ function parseEnvFileContent(content, filePath, options) {
|
|
|
287
308
|
);
|
|
288
309
|
commentBlock = [];
|
|
289
310
|
}
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return { filePath, vars: deduped, groups };
|
|
311
|
+
const { deduped, warnings } = deduplicateVars(vars);
|
|
312
|
+
return {
|
|
313
|
+
filePath,
|
|
314
|
+
vars: deduped,
|
|
315
|
+
groups,
|
|
316
|
+
...warnings.length > 0 && { warnings }
|
|
317
|
+
};
|
|
300
318
|
}
|
|
301
319
|
function parseEnvFile(filePath) {
|
|
302
320
|
const content = readFileSync(filePath, "utf8");
|
|
@@ -559,7 +577,8 @@ async function readEnvFile(filePath) {
|
|
|
559
577
|
return await readFile(resolved, "utf8");
|
|
560
578
|
} catch (err) {
|
|
561
579
|
if (err instanceof Error && err.code === "ENOENT") {
|
|
562
|
-
|
|
580
|
+
const displayPath = path6.isAbsolute(filePath) ? filePath : `${filePath} (resolved: ${resolved})`;
|
|
581
|
+
throw new Error(`File not found: ${displayPath}`);
|
|
563
582
|
}
|
|
564
583
|
throw err;
|
|
565
584
|
}
|
|
@@ -613,7 +632,8 @@ function deriveOutputPath(base, generator, isSingle) {
|
|
|
613
632
|
function deriveOutputBaseForInput(output, inputPath) {
|
|
614
633
|
const dir = path6.dirname(output);
|
|
615
634
|
const ext = path6.extname(output);
|
|
616
|
-
const
|
|
635
|
+
const rawBasename = path6.basename(inputPath);
|
|
636
|
+
const stem = rawBasename.startsWith(".") ? rawBasename.slice(1).replaceAll(".", "-") : path6.basename(inputPath, path6.extname(inputPath));
|
|
617
637
|
return path6.join(dir, `${stem}${ext}`);
|
|
618
638
|
}
|
|
619
639
|
function buildOutput(generator, parsed) {
|
|
@@ -650,6 +670,12 @@ async function persistOutput(params) {
|
|
|
650
670
|
success(`Generated ${outputPath}`);
|
|
651
671
|
}
|
|
652
672
|
}
|
|
673
|
+
function emitParserWarnings(parsed) {
|
|
674
|
+
if (parsed.warnings === void 0) return;
|
|
675
|
+
for (const w of parsed.warnings) {
|
|
676
|
+
warn(`[${w.code}] ${w.message}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
653
679
|
async function runGenerate(options) {
|
|
654
680
|
const {
|
|
655
681
|
input,
|
|
@@ -672,6 +698,7 @@ async function runGenerate(options) {
|
|
|
672
698
|
inputPath,
|
|
673
699
|
inferenceRules2 === void 0 ? void 0 : { inferenceRules: inferenceRules2 }
|
|
674
700
|
);
|
|
701
|
+
emitParserWarnings(parsed);
|
|
675
702
|
for (const generator of generators) {
|
|
676
703
|
let generated = buildOutput(generator, parsed);
|
|
677
704
|
if (shouldFormat && !dryRun) {
|
|
@@ -975,6 +1002,11 @@ function outputHuman(result) {
|
|
|
975
1002
|
async function runCheck(opts) {
|
|
976
1003
|
const contract = await resolveContract(opts.contract, opts.cwd);
|
|
977
1004
|
const parsed = parseEnvFile(opts.input);
|
|
1005
|
+
if (parsed.warnings !== void 0) {
|
|
1006
|
+
for (const w of parsed.warnings) {
|
|
1007
|
+
warn(`[${w.code}] ${w.message}`);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
978
1010
|
const validatorOpts = {};
|
|
979
1011
|
if (opts.environment !== void 0) validatorOpts.environment = opts.environment;
|
|
980
1012
|
if (opts.strict !== void 0) validatorOpts.strict = opts.strict;
|
|
@@ -1059,7 +1091,8 @@ async function loadCloudSource(options) {
|
|
|
1059
1091
|
raw = await readFile(resolvedPath, "utf8");
|
|
1060
1092
|
} catch (err) {
|
|
1061
1093
|
if (err instanceof Error && err.code === "ENOENT") {
|
|
1062
|
-
|
|
1094
|
+
const displayPath = path6.isAbsolute(options.filePath) ? options.filePath : `${options.filePath} (resolved: ${resolvedPath})`;
|
|
1095
|
+
throw new Error(`File not found: ${displayPath}`);
|
|
1063
1096
|
}
|
|
1064
1097
|
throw err;
|
|
1065
1098
|
}
|
|
@@ -1891,7 +1924,8 @@ async function loadCommandConfig(configPath) {
|
|
|
1891
1924
|
}
|
|
1892
1925
|
const resolvedPath = path6.resolve(configPath);
|
|
1893
1926
|
if (!existsSync(resolvedPath)) {
|
|
1894
|
-
|
|
1927
|
+
const displayPath = path6.isAbsolute(configPath) ? configPath : `${configPath} (resolved: ${resolvedPath})`;
|
|
1928
|
+
throw new Error(`Config file not found: ${displayPath}`);
|
|
1895
1929
|
}
|
|
1896
1930
|
const configDir = path6.dirname(resolvedPath);
|
|
1897
1931
|
const moduleValue = await import(pathToFileURL(resolvedPath).href);
|