UNPKG

env-sentinel

Version:

Zero-dependency tool that auto-validates .env files against schema.env, with optional fallback and secure warnings.

89 lines (88 loc) 3.17 kB
import { existsSync, readFileSync } from 'node:fs'; import { inferType } from './infer-type.js'; export function generateSchemaFromEnv(envPath, lintResults) { if (!existsSync(envPath)) { throw new Error(`File not found: ${envPath}`); } const raw = readFileSync(envPath, 'utf-8'); const lines = raw.split(/\r?\n/); const schemaEntries = []; const errorLines = new Set(); if (lintResults) { lintResults.issues .filter((issue) => issue.severity === 'error') .forEach((issue) => { if (issue.line) { errorLines.add(issue.line); } }); } const envVars = new Map(); lines.forEach((line, lineIndex) => { const lineNumber = lineIndex + 1; const trimmedLine = line.trim(); if (!trimmedLine || trimmedLine.startsWith('#')) { return; } if (errorLines.has(lineNumber)) { return; } const equalIndex = trimmedLine.indexOf('='); if (equalIndex === -1) { return; } const key = trimmedLine.substring(0, equalIndex).trim(); const value = trimmedLine.substring(equalIndex + 1).trim(); if (!key || key.includes(' ') || key.includes('"') || key.includes("'")) { return; } let cleanValue = value; if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { cleanValue = value.slice(1, -1); } if (cleanValue === '') { return; } envVars.set(key, cleanValue); }); lines.forEach((line, lineIndex) => { const lineNumber = lineIndex + 1; const trimmedLine = line.trim(); if (!trimmedLine || trimmedLine.startsWith('#')) { return; } if (errorLines.has(lineNumber)) { return; } const equalIndex = trimmedLine.indexOf('='); if (equalIndex === -1) { return; } const key = trimmedLine.substring(0, equalIndex).trim(); const value = trimmedLine.substring(equalIndex + 1).trim(); if (!key || key.includes(' ') || key.includes('"') || key.includes("'")) { return; } let cleanValue = value; if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { cleanValue = value.slice(1, -1); } if (cleanValue === '') { return; } let resolvedValue = cleanValue; if (cleanValue.startsWith('$') && cleanValue.length > 1) { const refKey = cleanValue.substring(1); const refValue = envVars.get(refKey); if (refValue !== undefined) { resolvedValue = refValue; } } const type = inferType(resolvedValue); const rule = type ? `required|${type}` : 'required'; schemaEntries.push({ key, rule }); }); return (`# Generated from ${envPath}\n` + schemaEntries.map((entry) => `${entry.key}=${entry.rule}`).join('\n') + '\n'); }