@xlameiro/env-typegen 0.1.7 → 0.1.9
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 +12 -0
- package/dist/cli.js +121 -45
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +78 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +78 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -210,21 +210,21 @@ declare function generateEnvValidation(parsed: ParsedEnvFile): string;
|
|
|
210
210
|
* import { z } from "zod";
|
|
211
211
|
*
|
|
212
212
|
* export const serverEnvSchema = z.object({
|
|
213
|
-
* DATABASE_URL: z.
|
|
213
|
+
* DATABASE_URL: z.url(),
|
|
214
214
|
* PORT: z.coerce.number(),
|
|
215
215
|
* });
|
|
216
216
|
*
|
|
217
217
|
* export const clientEnvSchema = z.object({
|
|
218
|
-
* NEXT_PUBLIC_API_URL: z.
|
|
218
|
+
* NEXT_PUBLIC_API_URL: z.url(),
|
|
219
219
|
* });
|
|
220
220
|
*
|
|
221
|
-
* export const envSchema = serverEnvSchema.
|
|
221
|
+
* export const envSchema = z.object({ ...serverEnvSchema.shape, ...clientEnvSchema.shape });
|
|
222
222
|
* export type Env = z.infer<typeof envSchema>;
|
|
223
223
|
* ```
|
|
224
224
|
*
|
|
225
225
|
* - Server-side vars (non-`NEXT_PUBLIC_`) go into `serverEnvSchema`
|
|
226
226
|
* - Client-side vars (`NEXT_PUBLIC_` prefix) go into `clientEnvSchema`
|
|
227
|
-
* - `envSchema`
|
|
227
|
+
* - `envSchema` merges both shapes via object spread to avoid deprecated `.merge()`
|
|
228
228
|
* - `annotatedType` takes precedence over `inferredType` when both are set
|
|
229
229
|
* - Optional vars have `.optional()` appended to their Zod expression
|
|
230
230
|
*/
|
|
@@ -666,7 +666,7 @@ type EnvTypegenConfig = {
|
|
|
666
666
|
declare function defineConfig(config: EnvTypegenConfig): EnvTypegenConfig;
|
|
667
667
|
/**
|
|
668
668
|
* Loads env-typegen config by searching for a config file in `cwd`.
|
|
669
|
-
* Searches for env-typegen.config.
|
|
669
|
+
* Searches for env-typegen.config.mjs → .js in order.
|
|
670
670
|
* Returns `undefined` when no config file is found.
|
|
671
671
|
*/
|
|
672
672
|
declare function loadConfig(cwd?: string): Promise<EnvTypegenConfig | undefined>;
|
package/dist/index.d.ts
CHANGED
|
@@ -210,21 +210,21 @@ declare function generateEnvValidation(parsed: ParsedEnvFile): string;
|
|
|
210
210
|
* import { z } from "zod";
|
|
211
211
|
*
|
|
212
212
|
* export const serverEnvSchema = z.object({
|
|
213
|
-
* DATABASE_URL: z.
|
|
213
|
+
* DATABASE_URL: z.url(),
|
|
214
214
|
* PORT: z.coerce.number(),
|
|
215
215
|
* });
|
|
216
216
|
*
|
|
217
217
|
* export const clientEnvSchema = z.object({
|
|
218
|
-
* NEXT_PUBLIC_API_URL: z.
|
|
218
|
+
* NEXT_PUBLIC_API_URL: z.url(),
|
|
219
219
|
* });
|
|
220
220
|
*
|
|
221
|
-
* export const envSchema = serverEnvSchema.
|
|
221
|
+
* export const envSchema = z.object({ ...serverEnvSchema.shape, ...clientEnvSchema.shape });
|
|
222
222
|
* export type Env = z.infer<typeof envSchema>;
|
|
223
223
|
* ```
|
|
224
224
|
*
|
|
225
225
|
* - Server-side vars (non-`NEXT_PUBLIC_`) go into `serverEnvSchema`
|
|
226
226
|
* - Client-side vars (`NEXT_PUBLIC_` prefix) go into `clientEnvSchema`
|
|
227
|
-
* - `envSchema`
|
|
227
|
+
* - `envSchema` merges both shapes via object spread to avoid deprecated `.merge()`
|
|
228
228
|
* - `annotatedType` takes precedence over `inferredType` when both are set
|
|
229
229
|
* - Optional vars have `.optional()` appended to their Zod expression
|
|
230
230
|
*/
|
|
@@ -666,7 +666,7 @@ type EnvTypegenConfig = {
|
|
|
666
666
|
declare function defineConfig(config: EnvTypegenConfig): EnvTypegenConfig;
|
|
667
667
|
/**
|
|
668
668
|
* Loads env-typegen config by searching for a config file in `cwd`.
|
|
669
|
-
* Searches for env-typegen.config.
|
|
669
|
+
* Searches for env-typegen.config.mjs → .js in order.
|
|
670
670
|
* Returns `undefined` when no config file is found.
|
|
671
671
|
*/
|
|
672
672
|
declare function loadConfig(cwd?: string): Promise<EnvTypegenConfig | undefined>;
|
package/dist/index.js
CHANGED
|
@@ -400,8 +400,8 @@ function generateEnvValidation(parsed) {
|
|
|
400
400
|
function toZodType(envVarType) {
|
|
401
401
|
if (envVarType === "number") return "z.coerce.number()";
|
|
402
402
|
if (envVarType === "boolean") return "z.coerce.boolean()";
|
|
403
|
-
if (envVarType === "url") return "z.
|
|
404
|
-
if (envVarType === "email") return "z.
|
|
403
|
+
if (envVarType === "url") return "z.url()";
|
|
404
|
+
if (envVarType === "email") return "z.email()";
|
|
405
405
|
return "z.string()";
|
|
406
406
|
}
|
|
407
407
|
function generateZodSchema(parsed) {
|
|
@@ -428,7 +428,10 @@ function generateZodSchema(parsed) {
|
|
|
428
428
|
lines.push(
|
|
429
429
|
"});",
|
|
430
430
|
"",
|
|
431
|
-
"export const envSchema =
|
|
431
|
+
"export const envSchema = z.object({",
|
|
432
|
+
" ...serverEnvSchema.shape,",
|
|
433
|
+
" ...clientEnvSchema.shape,",
|
|
434
|
+
"});",
|
|
432
435
|
"export type Env = z.infer<typeof envSchema>;"
|
|
433
436
|
);
|
|
434
437
|
return lines.join("\n") + "\n";
|
|
@@ -467,8 +470,8 @@ function escapeJsStringLiteral(value) {
|
|
|
467
470
|
function toT3ZodType(envVarType) {
|
|
468
471
|
if (envVarType === "number") return "z.coerce.number()";
|
|
469
472
|
if (envVarType === "boolean") return "z.coerce.boolean()";
|
|
470
|
-
if (envVarType === "url") return "z.
|
|
471
|
-
if (envVarType === "email") return "z.
|
|
473
|
+
if (envVarType === "url") return "z.url()";
|
|
474
|
+
if (envVarType === "email") return "z.email()";
|
|
472
475
|
return "z.string()";
|
|
473
476
|
}
|
|
474
477
|
function buildZodExpr(variable) {
|
|
@@ -539,11 +542,7 @@ Rename it to ${filePath.replace(/\.ts$/, ".mjs")} and use ESM syntax (export def
|
|
|
539
542
|
}
|
|
540
543
|
return void 0;
|
|
541
544
|
}
|
|
542
|
-
var CONFIG_FILE_NAMES = [
|
|
543
|
-
"env-typegen.config.mjs",
|
|
544
|
-
"env-typegen.config.js",
|
|
545
|
-
"env-typegen.config.ts"
|
|
546
|
-
];
|
|
545
|
+
var CONFIG_FILE_NAMES = ["env-typegen.config.mjs", "env-typegen.config.js"];
|
|
547
546
|
function defineConfig(config) {
|
|
548
547
|
return config;
|
|
549
548
|
}
|
|
@@ -551,19 +550,6 @@ async function loadConfig(cwd = process.cwd()) {
|
|
|
551
550
|
for (const name of CONFIG_FILE_NAMES) {
|
|
552
551
|
const filePath = path6.resolve(cwd, name);
|
|
553
552
|
if (existsSync(filePath)) {
|
|
554
|
-
if (filePath.endsWith(".ts")) {
|
|
555
|
-
throw new Error(
|
|
556
|
-
`Config file "${name}" was found but TypeScript files cannot be loaded directly at runtime.
|
|
557
|
-
Rename it to "env-typegen.config.mjs" and use ESM export syntax:
|
|
558
|
-
|
|
559
|
-
// env-typegen.config.mjs
|
|
560
|
-
import { defineConfig } from "@xlameiro/env-typegen";
|
|
561
|
-
export default defineConfig({ input: ".env.example" });
|
|
562
|
-
|
|
563
|
-
Tip: keep env-typegen.config.ts for IDE autocompletion and create a sibling
|
|
564
|
-
env-typegen.config.mjs for runtime loading.`
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
553
|
const fileUrl = pathToFileURL(filePath).href;
|
|
568
554
|
const mod = await import(fileUrl);
|
|
569
555
|
return mod.default;
|
|
@@ -1840,7 +1826,7 @@ var HELP_TEXT = {
|
|
|
1840
1826
|
"Usage: env-typegen diff [options]",
|
|
1841
1827
|
"",
|
|
1842
1828
|
"Options:",
|
|
1843
|
-
" --targets <list> Comma-separated targets (default: .env,.env.
|
|
1829
|
+
" --targets <list> Comma-separated targets (default: .env,.env.production)",
|
|
1844
1830
|
" --contract <path> Contract file path (default: env.contract.ts)",
|
|
1845
1831
|
" --example <path> Fallback .env.example used to bootstrap contract",
|
|
1846
1832
|
" --strict Validate extras as errors (default: true)",
|
|
@@ -1864,7 +1850,7 @@ var HELP_TEXT = {
|
|
|
1864
1850
|
"",
|
|
1865
1851
|
"Options:",
|
|
1866
1852
|
" --env <path> Environment file to validate (default: .env)",
|
|
1867
|
-
" --targets <list> Comma-separated targets for drift analysis",
|
|
1853
|
+
" --targets <list> Comma-separated targets for drift analysis (default: .env,.env.production)",
|
|
1868
1854
|
" --contract <path> Contract file path (default: env.contract.ts)",
|
|
1869
1855
|
" --example <path> Fallback .env.example used to bootstrap contract",
|
|
1870
1856
|
" --strict Validate extras as errors (default: true)",
|
|
@@ -1997,7 +1983,58 @@ function parseTargets(values, fileConfig) {
|
|
|
1997
1983
|
if (fileConfig?.diffTargets !== void 0 && fileConfig.diffTargets.length > 0) {
|
|
1998
1984
|
return fileConfig.diffTargets;
|
|
1999
1985
|
}
|
|
2000
|
-
return [".env", ".env.
|
|
1986
|
+
return [".env", ".env.production"];
|
|
1987
|
+
}
|
|
1988
|
+
function splitExistingAndMissingTargets(targets) {
|
|
1989
|
+
const existingTargets = [];
|
|
1990
|
+
const missingTargets = [];
|
|
1991
|
+
for (const target of targets) {
|
|
1992
|
+
const resolvedTarget = path6.resolve(target);
|
|
1993
|
+
if (existsSync(resolvedTarget)) {
|
|
1994
|
+
existingTargets.push(target);
|
|
1995
|
+
continue;
|
|
1996
|
+
}
|
|
1997
|
+
missingTargets.push(target);
|
|
1998
|
+
}
|
|
1999
|
+
return { existingTargets, missingTargets };
|
|
2000
|
+
}
|
|
2001
|
+
function buildMissingTargetIssue(target, missingCount) {
|
|
2002
|
+
const suffix = missingCount === 1 ? "" : "s";
|
|
2003
|
+
return {
|
|
2004
|
+
code: "ENV_MISSING",
|
|
2005
|
+
type: "missing",
|
|
2006
|
+
severity: "error",
|
|
2007
|
+
key: "*",
|
|
2008
|
+
environment: target,
|
|
2009
|
+
message: `Target file ${target} was not found; treating as empty (${missingCount} missing variable${suffix}).`,
|
|
2010
|
+
value: null
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
function summarizeDoctorMissingTargetIssues(report, missingTargets) {
|
|
2014
|
+
if (missingTargets.length === 0) return report;
|
|
2015
|
+
const missingTargetSet = new Set(missingTargets);
|
|
2016
|
+
const retainedIssues = report.issues.filter(
|
|
2017
|
+
(issue) => !(issue.code === "ENV_MISSING" && missingTargetSet.has(issue.environment))
|
|
2018
|
+
);
|
|
2019
|
+
const groupedIssues = missingTargets.map((target) => {
|
|
2020
|
+
const missingCount = report.issues.filter(
|
|
2021
|
+
(issue) => issue.code === "ENV_MISSING" && issue.environment === target
|
|
2022
|
+
).length;
|
|
2023
|
+
return buildMissingTargetIssue(target, missingCount);
|
|
2024
|
+
});
|
|
2025
|
+
const issues = [...retainedIssues, ...groupedIssues];
|
|
2026
|
+
const errors = issues.filter((issue) => issue.severity === "error").length;
|
|
2027
|
+
const warnings = issues.filter((issue) => issue.severity === "warning").length;
|
|
2028
|
+
return {
|
|
2029
|
+
...report,
|
|
2030
|
+
status: errors > 0 ? "fail" : "ok",
|
|
2031
|
+
issues,
|
|
2032
|
+
summary: {
|
|
2033
|
+
errors,
|
|
2034
|
+
warnings,
|
|
2035
|
+
total: issues.length
|
|
2036
|
+
}
|
|
2037
|
+
};
|
|
2001
2038
|
}
|
|
2002
2039
|
async function prepareCommonContext(values) {
|
|
2003
2040
|
const fileConfig = await loadCommandConfig(values.config);
|
|
@@ -2082,8 +2119,13 @@ async function runDiffCommand(args) {
|
|
|
2082
2119
|
await loadValidationContract(loadContractOptions),
|
|
2083
2120
|
context.plugins
|
|
2084
2121
|
);
|
|
2122
|
+
const targets = parseTargets(args.values, context.fileConfig);
|
|
2123
|
+
const { existingTargets, missingTargets } = splitExistingAndMissingTargets(targets);
|
|
2124
|
+
for (const missingTarget of missingTargets) {
|
|
2125
|
+
warn(`Target file not found: ${missingTarget} \u2014 treating as empty`);
|
|
2126
|
+
}
|
|
2085
2127
|
const sources = {};
|
|
2086
|
-
for (const target of
|
|
2128
|
+
for (const target of existingTargets) {
|
|
2087
2129
|
const values = await loadEnvSource({ filePath: target, allowMissing: true });
|
|
2088
2130
|
sources[target] = applySourcePlugins({ environment: target, values }, context.plugins);
|
|
2089
2131
|
}
|
|
@@ -2137,8 +2179,13 @@ async function runDoctorCommand(args) {
|
|
|
2137
2179
|
strict: context.strict,
|
|
2138
2180
|
debugValues: context.debugValues
|
|
2139
2181
|
});
|
|
2182
|
+
const targets = parseTargets(args.values, context.fileConfig);
|
|
2183
|
+
const { existingTargets, missingTargets } = splitExistingAndMissingTargets(targets);
|
|
2184
|
+
for (const missingTarget of missingTargets) {
|
|
2185
|
+
warn(`Target file not found: ${missingTarget} \u2014 treating as empty`);
|
|
2186
|
+
}
|
|
2140
2187
|
const sources = {};
|
|
2141
|
-
for (const target of
|
|
2188
|
+
for (const target of existingTargets) {
|
|
2142
2189
|
const values = await loadEnvSource({ filePath: target, allowMissing: true });
|
|
2143
2190
|
sources[target] = applySourcePlugins({ environment: target, values }, context.plugins);
|
|
2144
2191
|
}
|
|
@@ -2160,10 +2207,9 @@ async function runDoctorCommand(args) {
|
|
|
2160
2207
|
strict: context.strict,
|
|
2161
2208
|
debugValues: context.debugValues
|
|
2162
2209
|
});
|
|
2163
|
-
const
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
);
|
|
2210
|
+
const rawReport = buildDoctorReport({ checkReport, diffReport });
|
|
2211
|
+
const summarizedReport = summarizeDoctorMissingTargetIssues(rawReport, missingTargets);
|
|
2212
|
+
const report = applyReportPlugins(summarizedReport, context.plugins);
|
|
2167
2213
|
return emitAndReturnExitCode(report, {
|
|
2168
2214
|
jsonMode: args.jsonMode,
|
|
2169
2215
|
...context.outputFile !== void 0 && { outputFile: context.outputFile }
|