@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/dist/index.cjs CHANGED
@@ -407,8 +407,8 @@ function generateEnvValidation(parsed) {
407
407
  function toZodType(envVarType) {
408
408
  if (envVarType === "number") return "z.coerce.number()";
409
409
  if (envVarType === "boolean") return "z.coerce.boolean()";
410
- if (envVarType === "url") return "z.string().url()";
411
- if (envVarType === "email") return "z.string().email()";
410
+ if (envVarType === "url") return "z.url()";
411
+ if (envVarType === "email") return "z.email()";
412
412
  return "z.string()";
413
413
  }
414
414
  function generateZodSchema(parsed) {
@@ -435,7 +435,10 @@ function generateZodSchema(parsed) {
435
435
  lines.push(
436
436
  "});",
437
437
  "",
438
- "export const envSchema = serverEnvSchema.merge(clientEnvSchema);",
438
+ "export const envSchema = z.object({",
439
+ " ...serverEnvSchema.shape,",
440
+ " ...clientEnvSchema.shape,",
441
+ "});",
439
442
  "export type Env = z.infer<typeof envSchema>;"
440
443
  );
441
444
  return lines.join("\n") + "\n";
@@ -474,8 +477,8 @@ function escapeJsStringLiteral(value) {
474
477
  function toT3ZodType(envVarType) {
475
478
  if (envVarType === "number") return "z.coerce.number()";
476
479
  if (envVarType === "boolean") return "z.coerce.boolean()";
477
- if (envVarType === "url") return "z.string().url()";
478
- if (envVarType === "email") return "z.string().email()";
480
+ if (envVarType === "url") return "z.url()";
481
+ if (envVarType === "email") return "z.email()";
479
482
  return "z.string()";
480
483
  }
481
484
  function buildZodExpr(variable) {
@@ -546,11 +549,7 @@ Rename it to ${filePath.replace(/\.ts$/, ".mjs")} and use ESM syntax (export def
546
549
  }
547
550
  return void 0;
548
551
  }
549
- var CONFIG_FILE_NAMES = [
550
- "env-typegen.config.mjs",
551
- "env-typegen.config.js",
552
- "env-typegen.config.ts"
553
- ];
552
+ var CONFIG_FILE_NAMES = ["env-typegen.config.mjs", "env-typegen.config.js"];
554
553
  function defineConfig(config) {
555
554
  return config;
556
555
  }
@@ -558,19 +557,6 @@ async function loadConfig(cwd = process.cwd()) {
558
557
  for (const name of CONFIG_FILE_NAMES) {
559
558
  const filePath = path6__default.default.resolve(cwd, name);
560
559
  if (fs.existsSync(filePath)) {
561
- if (filePath.endsWith(".ts")) {
562
- throw new Error(
563
- `Config file "${name}" was found but TypeScript files cannot be loaded directly at runtime.
564
- Rename it to "env-typegen.config.mjs" and use ESM export syntax:
565
-
566
- // env-typegen.config.mjs
567
- import { defineConfig } from "@xlameiro/env-typegen";
568
- export default defineConfig({ input: ".env.example" });
569
-
570
- Tip: keep env-typegen.config.ts for IDE autocompletion and create a sibling
571
- env-typegen.config.mjs for runtime loading.`
572
- );
573
- }
574
560
  const fileUrl = url.pathToFileURL(filePath).href;
575
561
  const mod = await import(fileUrl);
576
562
  return mod.default;
@@ -1847,7 +1833,7 @@ var HELP_TEXT = {
1847
1833
  "Usage: env-typegen diff [options]",
1848
1834
  "",
1849
1835
  "Options:",
1850
- " --targets <list> Comma-separated targets (default: .env,.env.example,.env.production)",
1836
+ " --targets <list> Comma-separated targets (default: .env,.env.production)",
1851
1837
  " --contract <path> Contract file path (default: env.contract.ts)",
1852
1838
  " --example <path> Fallback .env.example used to bootstrap contract",
1853
1839
  " --strict Validate extras as errors (default: true)",
@@ -1871,7 +1857,7 @@ var HELP_TEXT = {
1871
1857
  "",
1872
1858
  "Options:",
1873
1859
  " --env <path> Environment file to validate (default: .env)",
1874
- " --targets <list> Comma-separated targets for drift analysis",
1860
+ " --targets <list> Comma-separated targets for drift analysis (default: .env,.env.production)",
1875
1861
  " --contract <path> Contract file path (default: env.contract.ts)",
1876
1862
  " --example <path> Fallback .env.example used to bootstrap contract",
1877
1863
  " --strict Validate extras as errors (default: true)",
@@ -2004,7 +1990,58 @@ function parseTargets(values, fileConfig) {
2004
1990
  if (fileConfig?.diffTargets !== void 0 && fileConfig.diffTargets.length > 0) {
2005
1991
  return fileConfig.diffTargets;
2006
1992
  }
2007
- return [".env", ".env.example", ".env.production"];
1993
+ return [".env", ".env.production"];
1994
+ }
1995
+ function splitExistingAndMissingTargets(targets) {
1996
+ const existingTargets = [];
1997
+ const missingTargets = [];
1998
+ for (const target of targets) {
1999
+ const resolvedTarget = path6__default.default.resolve(target);
2000
+ if (fs.existsSync(resolvedTarget)) {
2001
+ existingTargets.push(target);
2002
+ continue;
2003
+ }
2004
+ missingTargets.push(target);
2005
+ }
2006
+ return { existingTargets, missingTargets };
2007
+ }
2008
+ function buildMissingTargetIssue(target, missingCount) {
2009
+ const suffix = missingCount === 1 ? "" : "s";
2010
+ return {
2011
+ code: "ENV_MISSING",
2012
+ type: "missing",
2013
+ severity: "error",
2014
+ key: "*",
2015
+ environment: target,
2016
+ message: `Target file ${target} was not found; treating as empty (${missingCount} missing variable${suffix}).`,
2017
+ value: null
2018
+ };
2019
+ }
2020
+ function summarizeDoctorMissingTargetIssues(report, missingTargets) {
2021
+ if (missingTargets.length === 0) return report;
2022
+ const missingTargetSet = new Set(missingTargets);
2023
+ const retainedIssues = report.issues.filter(
2024
+ (issue) => !(issue.code === "ENV_MISSING" && missingTargetSet.has(issue.environment))
2025
+ );
2026
+ const groupedIssues = missingTargets.map((target) => {
2027
+ const missingCount = report.issues.filter(
2028
+ (issue) => issue.code === "ENV_MISSING" && issue.environment === target
2029
+ ).length;
2030
+ return buildMissingTargetIssue(target, missingCount);
2031
+ });
2032
+ const issues = [...retainedIssues, ...groupedIssues];
2033
+ const errors = issues.filter((issue) => issue.severity === "error").length;
2034
+ const warnings = issues.filter((issue) => issue.severity === "warning").length;
2035
+ return {
2036
+ ...report,
2037
+ status: errors > 0 ? "fail" : "ok",
2038
+ issues,
2039
+ summary: {
2040
+ errors,
2041
+ warnings,
2042
+ total: issues.length
2043
+ }
2044
+ };
2008
2045
  }
2009
2046
  async function prepareCommonContext(values) {
2010
2047
  const fileConfig = await loadCommandConfig(values.config);
@@ -2089,8 +2126,13 @@ async function runDiffCommand(args) {
2089
2126
  await loadValidationContract(loadContractOptions),
2090
2127
  context.plugins
2091
2128
  );
2129
+ const targets = parseTargets(args.values, context.fileConfig);
2130
+ const { existingTargets, missingTargets } = splitExistingAndMissingTargets(targets);
2131
+ for (const missingTarget of missingTargets) {
2132
+ warn(`Target file not found: ${missingTarget} \u2014 treating as empty`);
2133
+ }
2092
2134
  const sources = {};
2093
- for (const target of parseTargets(args.values, context.fileConfig)) {
2135
+ for (const target of existingTargets) {
2094
2136
  const values = await loadEnvSource({ filePath: target, allowMissing: true });
2095
2137
  sources[target] = applySourcePlugins({ environment: target, values }, context.plugins);
2096
2138
  }
@@ -2144,8 +2186,13 @@ async function runDoctorCommand(args) {
2144
2186
  strict: context.strict,
2145
2187
  debugValues: context.debugValues
2146
2188
  });
2189
+ const targets = parseTargets(args.values, context.fileConfig);
2190
+ const { existingTargets, missingTargets } = splitExistingAndMissingTargets(targets);
2191
+ for (const missingTarget of missingTargets) {
2192
+ warn(`Target file not found: ${missingTarget} \u2014 treating as empty`);
2193
+ }
2147
2194
  const sources = {};
2148
- for (const target of parseTargets(args.values, context.fileConfig)) {
2195
+ for (const target of existingTargets) {
2149
2196
  const values = await loadEnvSource({ filePath: target, allowMissing: true });
2150
2197
  sources[target] = applySourcePlugins({ environment: target, values }, context.plugins);
2151
2198
  }
@@ -2167,10 +2214,9 @@ async function runDoctorCommand(args) {
2167
2214
  strict: context.strict,
2168
2215
  debugValues: context.debugValues
2169
2216
  });
2170
- const report = applyReportPlugins(
2171
- buildDoctorReport({ checkReport, diffReport }),
2172
- context.plugins
2173
- );
2217
+ const rawReport = buildDoctorReport({ checkReport, diffReport });
2218
+ const summarizedReport = summarizeDoctorMissingTargetIssues(rawReport, missingTargets);
2219
+ const report = applyReportPlugins(summarizedReport, context.plugins);
2174
2220
  return emitAndReturnExitCode(report, {
2175
2221
  jsonMode: args.jsonMode,
2176
2222
  ...context.outputFile !== void 0 && { outputFile: context.outputFile }