@xlameiro/env-typegen 0.1.0 → 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/CHANGELOG.md +40 -2
- package/README.md +37 -20
- package/dist/cli.js +150 -45
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +66 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -6
- package/dist/index.d.ts +24 -6
- package/dist/index.js +66 -34
- package/dist/index.js.map +1 -1
- package/package.json +20 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Refresh the published npm package metadata and README to match the current repository documentation.
|
|
8
|
+
|
|
3
9
|
All notable changes to this project will be documented in this file.
|
|
4
10
|
|
|
5
11
|
This project adheres to [Semantic Versioning](https://semver.org/) and uses
|
|
@@ -7,7 +13,39 @@ This project adheres to [Semantic Versioning](https://semver.org/) and uses
|
|
|
7
13
|
|
|
8
14
|
<!-- Releases are added automatically by `changeset version` -->
|
|
9
15
|
|
|
10
|
-
## [0.1.
|
|
16
|
+
## [0.1.1] — 2026-03-17
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **A1** — VERSION was hardcoded as `"0.1.0"` in `cli.ts`; now read dynamically from `package.json` via `createRequire` so it stays in sync without manual edits.
|
|
21
|
+
- **A2** — Double-quotes in `@description` annotations were not escaped in the t3-generator output, corrupting the generated `createEnv(...)` call. Fixed with `.replace(/"/g, '\\"')`.
|
|
22
|
+
- **A3** — Prettier parse errors in `formatOutput` were silently swallowed; a `console.warn` is now emitted before the raw-content fallback.
|
|
23
|
+
- **A4** — Watch mode only listened for `"change"` events; it now also handles `"add"` and `"unlink"` so editor save-with-delete patterns are covered.
|
|
24
|
+
- **A5** — Config file changes during `--watch` were silently ignored; a second chokidar watcher now monitors the config file and reloads it on change.
|
|
25
|
+
- **C1** — `formatOutput` (Prettier) was called unnecessarily in dry-run mode; formatting now runs only when actually writing to disk.
|
|
26
|
+
- **C3** — Removed duplicate alias exports (`generateTypeScript`, `generateZod`); the canonical names `generateTypeScriptTypes` and `generateZodSchema` are the only public exports.
|
|
27
|
+
- **E1–E3** — README badge URLs had `YOUR_USERNAME` placeholders; replaced with the real GitHub/npm identifiers. Package name aligned to `@xlameiro/env-typegen` throughout all docs.
|
|
28
|
+
- **E4** — README `--format` example was misleading; corrected to use `--generator`/`-f` for generator selection and document `--no-format` for disabling Prettier.
|
|
29
|
+
- **E5** — `parseEnvFile` was documented as `async`/awaitable but is synchronous; removed erroneous `await` from all examples.
|
|
30
|
+
- **DRY** — `CONFIG_FILE_NAMES` was duplicated between `config.ts` and `watch.ts`; exported from `config.ts` and imported in `watch.ts`.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **B1** — `EnvTypegenConfig.input` now accepts `string | string[]`; CLI `--input`/`-i` can be repeated to process multiple `.env` files in one run.
|
|
35
|
+
- **B2** — `inferenceRules?: InferenceRule[]` added to `EnvTypegenConfig` and `RunGenerateOptions`; custom rules are prepended before the built-in ruleset.
|
|
36
|
+
- **B3** — Default generators changed from `["typescript"]` to all four: `["typescript", "zod", "t3", "declaration"]`, matching the spec.
|
|
37
|
+
- **B4** — Dry-run mode now prints each generated output block to stdout so users can preview content without touching the filesystem.
|
|
38
|
+
- **B5** — Watch mode debounces rapid file-change events (200 ms) to prevent multiple simultaneous pipeline runs on editor autosave.
|
|
39
|
+
|
|
40
|
+
### Tests
|
|
41
|
+
|
|
42
|
+
- Regression test for double-quotes in t3-generator descriptions (A2).
|
|
43
|
+
- CLI version tests read expected version from `package.json` (A1).
|
|
44
|
+
- Four `loadConfig()` unit tests with temp-dir fixtures (no stale state).
|
|
45
|
+
- Dry-run stdout spy test verifying preview content appears (B4).
|
|
46
|
+
- Integration tests expanded to five end-to-end scenarios via `execSync` against the built `dist/cli.js`.
|
|
47
|
+
|
|
48
|
+
[0.1.1]: https://github.com/xlameiro/env-typegen/compare/packages/env-typegen@0.1.0...packages/env-typegen@0.1.1
|
|
11
49
|
|
|
12
50
|
### Added
|
|
13
51
|
|
|
@@ -54,4 +92,4 @@ This project adheres to [Semantic Versioning](https://semver.org/) and uses
|
|
|
54
92
|
- Full TSDoc on every public export in `src/index.ts`
|
|
55
93
|
- README with Quick Start (CLI examples), Programmatic API, and Configuration sections
|
|
56
94
|
|
|
57
|
-
[0.1.0]: https://github.com/
|
|
95
|
+
[0.1.0]: https://github.com/xlameiro/env-typegen/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> From `.env.example` to TypeScript in one command.
|
|
4
4
|
|
|
5
|
-
[](https://npmjs.com/package/@xlameiro/env-typegen)
|
|
6
|
+
[](https://github.com/xlameiro/env-typegen/actions/workflows/ci.yml)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://www.typescriptlang.org/)
|
|
9
9
|
|
|
@@ -11,31 +11,53 @@
|
|
|
11
11
|
|
|
12
12
|
`env-typegen` reads `.env.example` files and automatically generates:
|
|
13
13
|
|
|
14
|
-
- TypeScript types (`type
|
|
14
|
+
- TypeScript types (`type EnvVars = { PORT: number; DATABASE_URL: string }`)
|
|
15
15
|
- Zod v4 schemas (`z.object({ PORT: z.coerce.number() })`)
|
|
16
16
|
- `@t3-oss/env-nextjs` `createEnv` configuration
|
|
17
17
|
- `.d.ts` declaration files that augment `NodeJS.ProcessEnv`
|
|
18
18
|
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add -D @xlameiro/env-typegen
|
|
23
|
+
# or
|
|
24
|
+
npm install --save-dev @xlameiro/env-typegen
|
|
25
|
+
```
|
|
26
|
+
|
|
19
27
|
## Quick Start
|
|
20
28
|
|
|
21
29
|
```bash
|
|
22
|
-
# Generate
|
|
30
|
+
# Generate all outputs (default: typescript + zod + t3 + declaration)
|
|
23
31
|
npx env-typegen --input .env.example --output src/env.generated.ts
|
|
24
32
|
|
|
25
|
-
# Generate a Zod schema
|
|
33
|
+
# Generate only a Zod schema
|
|
26
34
|
npx env-typegen -i .env.example -o src/env.schema.ts -g zod
|
|
27
35
|
|
|
28
|
-
# Generate multiple outputs
|
|
29
|
-
npx env-typegen -i .env.example -o src/env.ts -
|
|
36
|
+
# Generate multiple outputs explicitly
|
|
37
|
+
npx env-typegen -i .env.example -o src/env.ts -f typescript -f zod
|
|
38
|
+
|
|
39
|
+
# Generate without running Prettier
|
|
40
|
+
npx env-typegen -i .env.example -o src/env.ts --no-format
|
|
30
41
|
|
|
31
42
|
# Watch mode — regenerate on every change
|
|
32
43
|
npx env-typegen -i .env.example -o src/env.ts --watch
|
|
33
44
|
```
|
|
34
45
|
|
|
46
|
+
## Generator formats
|
|
47
|
+
|
|
48
|
+
Use `-f` / `--format` (or `-g` / `--generator` alias):
|
|
49
|
+
|
|
50
|
+
| Value | Meaning |
|
|
51
|
+
| -------------------- | ------------------------------------ |
|
|
52
|
+
| `ts` or `typescript` | Generate TypeScript types |
|
|
53
|
+
| `zod` | Generate Zod schema |
|
|
54
|
+
| `t3` | Generate `@t3-oss/env-nextjs` config |
|
|
55
|
+
| `declaration` | Generate `.d.ts` declaration |
|
|
56
|
+
|
|
35
57
|
## Programmatic API
|
|
36
58
|
|
|
37
59
|
```ts
|
|
38
|
-
import { runGenerate, parseEnvFile, generateTypeScriptTypes } from "env-typegen";
|
|
60
|
+
import { runGenerate, parseEnvFile, generateTypeScriptTypes } from "@xlameiro/env-typegen";
|
|
39
61
|
|
|
40
62
|
// High-level: full pipeline
|
|
41
63
|
await runGenerate({
|
|
@@ -46,7 +68,7 @@ await runGenerate({
|
|
|
46
68
|
});
|
|
47
69
|
|
|
48
70
|
// Low-level: parse then generate individually
|
|
49
|
-
const parsed =
|
|
71
|
+
const parsed = parseEnvFile(".env.example");
|
|
50
72
|
const ts = generateTypeScriptTypes(parsed);
|
|
51
73
|
```
|
|
52
74
|
|
|
@@ -55,7 +77,7 @@ const ts = generateTypeScriptTypes(parsed);
|
|
|
55
77
|
Create `env-typegen.config.ts` at your project root:
|
|
56
78
|
|
|
57
79
|
```ts
|
|
58
|
-
import { defineConfig } from "env-typegen";
|
|
80
|
+
import { defineConfig } from "@xlameiro/env-typegen";
|
|
59
81
|
|
|
60
82
|
export default defineConfig({
|
|
61
83
|
input: ".env.example",
|
|
@@ -67,16 +89,11 @@ export default defineConfig({
|
|
|
67
89
|
|
|
68
90
|
## Status
|
|
69
91
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
| 4 | Generators | ✅ |
|
|
76
|
-
| 5 | Config & Utils | ✅ |
|
|
77
|
-
| 6 | CLI | ✅ |
|
|
78
|
-
| 7 | Quality & Docs | ✅ |
|
|
79
|
-
| 8 | Publishing | 🔜 |
|
|
92
|
+
`env-typegen` is actively maintained and published on npm.
|
|
93
|
+
|
|
94
|
+
## Changelog
|
|
95
|
+
|
|
96
|
+
See [`CHANGELOG.md`](./CHANGELOG.md) for release notes.
|
|
80
97
|
|
|
81
98
|
## License
|
|
82
99
|
|
package/dist/cli.js
CHANGED
|
@@ -99,7 +99,9 @@ var require_picocolors = __commonJS({
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
// src/cli.ts
|
|
102
|
-
import
|
|
102
|
+
import { createRequire } from "module";
|
|
103
|
+
import { realpathSync } from "fs";
|
|
104
|
+
import path7 from "path";
|
|
103
105
|
import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "url";
|
|
104
106
|
import { inspect, parseArgs } from "util";
|
|
105
107
|
|
|
@@ -179,7 +181,7 @@ function generateT3Env(parsed) {
|
|
|
179
181
|
const effectiveType = variable.annotatedType ?? variable.inferredType;
|
|
180
182
|
let zodExpr = toT3ZodType(effectiveType);
|
|
181
183
|
if (variable.description !== void 0) {
|
|
182
|
-
zodExpr += `.describe("${variable.description}")`;
|
|
184
|
+
zodExpr += `.describe("${variable.description.replace(/"/g, '\\"')}")`;
|
|
183
185
|
}
|
|
184
186
|
if (variable.isOptional) {
|
|
185
187
|
zodExpr += ".optional()";
|
|
@@ -194,7 +196,7 @@ function generateT3Env(parsed) {
|
|
|
194
196
|
const effectiveType = variable.annotatedType ?? variable.inferredType;
|
|
195
197
|
let zodExpr = toT3ZodType(effectiveType);
|
|
196
198
|
if (variable.description !== void 0) {
|
|
197
|
-
zodExpr += `.describe("${variable.description}")`;
|
|
199
|
+
zodExpr += `.describe("${variable.description.replace(/"/g, '\\"')}")`;
|
|
198
200
|
}
|
|
199
201
|
if (variable.isOptional) {
|
|
200
202
|
zodExpr += ".optional()";
|
|
@@ -396,7 +398,9 @@ var inferenceRules = [
|
|
|
396
398
|
// src/inferrer/type-inferrer.ts
|
|
397
399
|
var sortedRules = [...inferenceRules].sort((left, right) => left.priority - right.priority);
|
|
398
400
|
function inferType(key, value, options) {
|
|
399
|
-
|
|
401
|
+
const extra = options?.extraRules;
|
|
402
|
+
const rules = extra && extra.length > 0 ? [...extra].sort((a, b) => a.priority - b.priority).concat(sortedRules) : sortedRules;
|
|
403
|
+
for (const rule of rules) {
|
|
400
404
|
if (rule.match(key, value)) {
|
|
401
405
|
return rule.type;
|
|
402
406
|
}
|
|
@@ -447,7 +451,7 @@ function parseCommentBlock(lines) {
|
|
|
447
451
|
// src/parser/env-parser.ts
|
|
448
452
|
var ENV_VAR_RE = /^([A-Z_][A-Z0-9_]*)=(.*)$/;
|
|
449
453
|
var SECTION_HEADER_RE = /^#\s+[-=]{3,}\s+(.+?)\s+[-=]{3,}\s*$/;
|
|
450
|
-
function parseEnvFileContent(content, filePath) {
|
|
454
|
+
function parseEnvFileContent(content, filePath, options) {
|
|
451
455
|
const lines = content.split("\n");
|
|
452
456
|
const vars = [];
|
|
453
457
|
const groups = [];
|
|
@@ -480,7 +484,11 @@ function parseEnvFileContent(content, filePath) {
|
|
|
480
484
|
const key = envMatch[1] ?? "";
|
|
481
485
|
const rawValue = envMatch[2] ?? "";
|
|
482
486
|
const annotations = parseCommentBlock(commentBlock);
|
|
483
|
-
const inferredType = inferType(
|
|
487
|
+
const inferredType = inferType(
|
|
488
|
+
key,
|
|
489
|
+
rawValue,
|
|
490
|
+
...options?.inferenceRules !== void 0 ? [{ extraRules: options.inferenceRules }] : []
|
|
491
|
+
);
|
|
484
492
|
const isRequired = rawValue.length > 0 || annotations.isRequired;
|
|
485
493
|
const isOptional = rawValue.length === 0 && !annotations.isRequired;
|
|
486
494
|
const isClientSide = key.startsWith("NEXT_PUBLIC_");
|
|
@@ -528,7 +536,11 @@ import { format } from "prettier";
|
|
|
528
536
|
async function formatOutput(content, parser = "typescript") {
|
|
529
537
|
try {
|
|
530
538
|
return await format(content, { parser });
|
|
531
|
-
} catch {
|
|
539
|
+
} catch (err) {
|
|
540
|
+
console.warn(
|
|
541
|
+
"env-typegen: Prettier formatting failed, writing unformatted output.",
|
|
542
|
+
err instanceof Error ? err.message : String(err)
|
|
543
|
+
);
|
|
532
544
|
return content;
|
|
533
545
|
}
|
|
534
546
|
}
|
|
@@ -550,7 +562,15 @@ function deriveOutputPath(base, generator, isSingle) {
|
|
|
550
562
|
if (isSingle) return base;
|
|
551
563
|
const ext = path5.extname(base);
|
|
552
564
|
const noExt = ext.length > 0 ? base.slice(0, -ext.length) : base;
|
|
553
|
-
|
|
565
|
+
const baseExt = ext.length > 0 ? ext : ".ts";
|
|
566
|
+
const outExt = generator === "declaration" ? ".d.ts" : baseExt;
|
|
567
|
+
return `${noExt}.${generator}${outExt}`;
|
|
568
|
+
}
|
|
569
|
+
function deriveOutputBaseForInput(output, inputPath) {
|
|
570
|
+
const dir = path5.dirname(output);
|
|
571
|
+
const ext = path5.extname(output);
|
|
572
|
+
const stem = path5.basename(inputPath, path5.extname(inputPath));
|
|
573
|
+
return path5.join(dir, `${stem}${ext}`);
|
|
554
574
|
}
|
|
555
575
|
function buildOutput(generator, parsed) {
|
|
556
576
|
switch (generator) {
|
|
@@ -577,7 +597,11 @@ async function persistOutput(params) {
|
|
|
577
597
|
}
|
|
578
598
|
if (dryRun) {
|
|
579
599
|
if (!silent) {
|
|
580
|
-
|
|
600
|
+
if (!isSingle) {
|
|
601
|
+
console.log(`// --- ${generator}: ${outputPath} ---`);
|
|
602
|
+
}
|
|
603
|
+
console.log(generated);
|
|
604
|
+
success(`Dry run: would write ${outputPath}`);
|
|
581
605
|
}
|
|
582
606
|
return;
|
|
583
607
|
}
|
|
@@ -594,47 +618,97 @@ async function runGenerate(options) {
|
|
|
594
618
|
format: shouldFormat,
|
|
595
619
|
stdout = false,
|
|
596
620
|
dryRun = false,
|
|
597
|
-
silent = false
|
|
621
|
+
silent = false,
|
|
622
|
+
inferenceRules: inferenceRules2
|
|
598
623
|
} = options;
|
|
624
|
+
const inputs = Array.isArray(input) ? input : [input];
|
|
625
|
+
const hasMultipleInputs = inputs.length > 1;
|
|
599
626
|
const isSingle = generators.length === 1;
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
627
|
+
for (const inputPath of inputs) {
|
|
628
|
+
const outputBase = hasMultipleInputs ? deriveOutputBaseForInput(output, inputPath) : output;
|
|
629
|
+
const content = await readEnvFile(inputPath);
|
|
630
|
+
const parsed = parseEnvFileContent(
|
|
631
|
+
content,
|
|
632
|
+
inputPath,
|
|
633
|
+
inferenceRules2 !== void 0 ? { inferenceRules: inferenceRules2 } : void 0
|
|
634
|
+
);
|
|
635
|
+
for (const generator of generators) {
|
|
636
|
+
let generated = buildOutput(generator, parsed);
|
|
637
|
+
if (shouldFormat && !dryRun) {
|
|
638
|
+
generated = await formatOutput(generated);
|
|
639
|
+
}
|
|
640
|
+
const outputPath = deriveOutputPath(outputBase, generator, isSingle);
|
|
641
|
+
await persistOutput({
|
|
642
|
+
generated,
|
|
643
|
+
generator,
|
|
644
|
+
outputPath,
|
|
645
|
+
isSingle,
|
|
646
|
+
stdout,
|
|
647
|
+
dryRun,
|
|
648
|
+
silent
|
|
649
|
+
});
|
|
606
650
|
}
|
|
607
|
-
const outputPath = deriveOutputPath(output, generator, isSingle);
|
|
608
|
-
await persistOutput({
|
|
609
|
-
generated,
|
|
610
|
-
generator,
|
|
611
|
-
outputPath,
|
|
612
|
-
isSingle,
|
|
613
|
-
stdout,
|
|
614
|
-
dryRun,
|
|
615
|
-
silent
|
|
616
|
-
});
|
|
617
651
|
}
|
|
618
652
|
}
|
|
619
653
|
|
|
620
654
|
// src/watch.ts
|
|
655
|
+
import path6 from "path";
|
|
621
656
|
import { watch } from "chokidar";
|
|
622
|
-
function
|
|
623
|
-
|
|
657
|
+
function debounce(fn, delay) {
|
|
658
|
+
let timer;
|
|
659
|
+
return (...args) => {
|
|
660
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
661
|
+
timer = setTimeout(() => {
|
|
662
|
+
timer = void 0;
|
|
663
|
+
fn(...args);
|
|
664
|
+
}, delay);
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function startWatch({ inputPath, runOptions, cwd = process.cwd() }) {
|
|
668
|
+
const inputLabel = Array.isArray(inputPath) ? inputPath.join(", ") : inputPath;
|
|
669
|
+
log(`Watching ${inputLabel} for changes...`);
|
|
624
670
|
void runGenerate(runOptions).catch((err) => {
|
|
625
671
|
const message = err instanceof Error ? err.message : JSON.stringify(err);
|
|
626
672
|
error(message);
|
|
627
673
|
});
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
log(`${inputPath} changed, regenerating...`);
|
|
674
|
+
const handleChange = debounce((eventPath) => {
|
|
675
|
+
log(`${eventPath} changed, regenerating...`);
|
|
631
676
|
void runGenerate(runOptions).catch((err) => {
|
|
632
677
|
const message = err instanceof Error ? err.message : JSON.stringify(err);
|
|
633
678
|
error(message);
|
|
634
679
|
});
|
|
635
|
-
});
|
|
680
|
+
}, 200);
|
|
681
|
+
const handleConfigChange = debounce(async (eventPath) => {
|
|
682
|
+
log(`Config file ${eventPath} changed, reloading...`);
|
|
683
|
+
try {
|
|
684
|
+
const reloaded = await loadConfig(cwd);
|
|
685
|
+
if (reloaded) {
|
|
686
|
+
runOptions.generators = reloaded.generators ?? runOptions.generators;
|
|
687
|
+
runOptions.format = reloaded.format ?? runOptions.format;
|
|
688
|
+
if (reloaded.inferenceRules !== void 0) {
|
|
689
|
+
runOptions.inferenceRules = reloaded.inferenceRules;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
void runGenerate(runOptions).catch((err) => {
|
|
693
|
+
const message = err instanceof Error ? err.message : JSON.stringify(err);
|
|
694
|
+
error(message);
|
|
695
|
+
});
|
|
696
|
+
} catch (err) {
|
|
697
|
+
const message = err instanceof Error ? err.message : JSON.stringify(err);
|
|
698
|
+
error(`Failed to reload config: ${message}`);
|
|
699
|
+
}
|
|
700
|
+
}, 200);
|
|
701
|
+
const inputWatcher = watch(inputPath, { persistent: true });
|
|
702
|
+
for (const event of ["add", "change", "unlink"]) {
|
|
703
|
+
inputWatcher.on(event, handleChange);
|
|
704
|
+
}
|
|
705
|
+
const configPaths = CONFIG_FILE_NAMES.map((name) => path6.resolve(cwd, name));
|
|
706
|
+
const configWatcher = watch(configPaths, { persistent: true, ignoreInitial: true });
|
|
707
|
+
for (const event of ["add", "change"]) {
|
|
708
|
+
configWatcher.on(event, (eventPath) => void handleConfigChange(eventPath));
|
|
709
|
+
}
|
|
636
710
|
process.on("SIGINT", () => {
|
|
637
|
-
void
|
|
711
|
+
void Promise.all([inputWatcher.close(), configWatcher.close()]).then(() => {
|
|
638
712
|
log("Watcher stopped.");
|
|
639
713
|
process.exit(0);
|
|
640
714
|
});
|
|
@@ -642,7 +716,8 @@ function startWatch({ inputPath, runOptions }) {
|
|
|
642
716
|
}
|
|
643
717
|
|
|
644
718
|
// src/cli.ts
|
|
645
|
-
var
|
|
719
|
+
var _require = createRequire(import.meta.url);
|
|
720
|
+
var VERSION = _require("../package.json").version;
|
|
646
721
|
var HELP_TEXT = [
|
|
647
722
|
"env-typegen \u2014 Generate TypeScript types from .env.example",
|
|
648
723
|
"",
|
|
@@ -650,7 +725,7 @@ var HELP_TEXT = [
|
|
|
650
725
|
" env-typegen -i <path> [options]",
|
|
651
726
|
"",
|
|
652
727
|
"Options:",
|
|
653
|
-
" -i, --input <path> Path to .env.example file",
|
|
728
|
+
" -i, --input <path> Path to .env.example file(s). May be specified multiple times.",
|
|
654
729
|
" -o, --output <path> Output file path (default: env.generated.ts)",
|
|
655
730
|
" -f, --format <name> Generator format: ts|zod|t3|declaration",
|
|
656
731
|
" May be specified multiple times.",
|
|
@@ -680,11 +755,29 @@ function getErrorMessage(errorValue) {
|
|
|
680
755
|
}
|
|
681
756
|
return inspect(errorValue, { depth: 2 });
|
|
682
757
|
}
|
|
758
|
+
function resolveConfigRelative(value, configDir) {
|
|
759
|
+
if (value === void 0 || path7.isAbsolute(value)) return value;
|
|
760
|
+
return path7.resolve(configDir, value);
|
|
761
|
+
}
|
|
762
|
+
function applyConfigPaths(config, configDir) {
|
|
763
|
+
let input;
|
|
764
|
+
if (Array.isArray(config.input)) {
|
|
765
|
+
input = config.input.map((v) => resolveConfigRelative(v, configDir) ?? v);
|
|
766
|
+
} else {
|
|
767
|
+
input = resolveConfigRelative(config.input, configDir);
|
|
768
|
+
}
|
|
769
|
+
const output = resolveConfigRelative(config.output, configDir);
|
|
770
|
+
return {
|
|
771
|
+
...config,
|
|
772
|
+
...input !== void 0 && { input },
|
|
773
|
+
...output !== void 0 && { output }
|
|
774
|
+
};
|
|
775
|
+
}
|
|
683
776
|
async function runCli(argv = process.argv.slice(2)) {
|
|
684
777
|
const { values } = parseArgs({
|
|
685
778
|
args: argv,
|
|
686
779
|
options: {
|
|
687
|
-
input: { type: "string", short: "i" },
|
|
780
|
+
input: { type: "string", short: "i", multiple: true },
|
|
688
781
|
output: { type: "string", short: "o" },
|
|
689
782
|
generator: { type: "string", short: "g", multiple: true },
|
|
690
783
|
format: { type: "string", short: "f", multiple: true },
|
|
@@ -707,14 +800,19 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
707
800
|
return;
|
|
708
801
|
}
|
|
709
802
|
let fileConfig;
|
|
710
|
-
if (values.config
|
|
711
|
-
const configPath = path6.resolve(values.config);
|
|
712
|
-
const mod = await import(pathToFileURL2(configPath).href);
|
|
713
|
-
fileConfig = mod.default;
|
|
714
|
-
} else {
|
|
803
|
+
if (values.config === void 0) {
|
|
715
804
|
fileConfig = await loadConfig(process.cwd());
|
|
805
|
+
} else {
|
|
806
|
+
const configPath = path7.resolve(values.config);
|
|
807
|
+
const configDir = path7.dirname(configPath);
|
|
808
|
+
const mod = await import(pathToFileURL2(configPath).href);
|
|
809
|
+
const rawConfig = mod.default;
|
|
810
|
+
if (rawConfig) {
|
|
811
|
+
fileConfig = applyConfigPaths(rawConfig, configDir);
|
|
812
|
+
}
|
|
716
813
|
}
|
|
717
|
-
const
|
|
814
|
+
const cliInput = values.input?.length ? values.input : void 0;
|
|
815
|
+
const input = cliInput ?? fileConfig?.input;
|
|
718
816
|
if (input === void 0) {
|
|
719
817
|
error("No input file specified. Use -i <path> or set input in env-typegen.config.ts");
|
|
720
818
|
process.exit(1);
|
|
@@ -733,7 +831,7 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
733
831
|
}
|
|
734
832
|
generators = [...new Set(normalizedGenerators)];
|
|
735
833
|
} else {
|
|
736
|
-
generators = fileConfig?.generators ?? ["typescript"];
|
|
834
|
+
generators = fileConfig?.generators ?? ["typescript", "zod", "t3", "declaration"];
|
|
737
835
|
}
|
|
738
836
|
const shouldFormat = values["no-format"] === true ? false : fileConfig?.format ?? true;
|
|
739
837
|
const useStdout = values.stdout ?? false;
|
|
@@ -747,7 +845,8 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
747
845
|
format: shouldFormat,
|
|
748
846
|
stdout: useStdout,
|
|
749
847
|
dryRun: isDryRun,
|
|
750
|
-
silent: isSilent
|
|
848
|
+
silent: isSilent,
|
|
849
|
+
...fileConfig?.inferenceRules !== void 0 && { inferenceRules: fileConfig.inferenceRules }
|
|
751
850
|
};
|
|
752
851
|
if (shouldWatch) {
|
|
753
852
|
startWatch({ inputPath: input, runOptions: options });
|
|
@@ -755,7 +854,13 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
755
854
|
await runGenerate(options);
|
|
756
855
|
}
|
|
757
856
|
}
|
|
758
|
-
if (process.argv[1] !== void 0 &&
|
|
857
|
+
if (process.argv[1] !== void 0 && (() => {
|
|
858
|
+
try {
|
|
859
|
+
return realpathSync(path7.resolve(process.argv[1])) === realpathSync(fileURLToPath(import.meta.url));
|
|
860
|
+
} catch {
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
})()) {
|
|
759
864
|
await runCli().catch((err) => {
|
|
760
865
|
error(getErrorMessage(err));
|
|
761
866
|
process.exit(1);
|