dotenvxjs
Version:
dotenvx is the official Node.js library for .envx files, offering advanced type validation, intelligent interpolation, and conditional logic. It makes environment management safer, more dynamic, and easier to maintain.
15 lines • 18.2 kB
JavaScript
;var commander=require('commander'),E=require('path'),fs=require('fs'),j=require('fs/promises'),Te=require('readline'),colorette=require('colorette'),_e=require('chokidar'),Ae=require('lodash.debounce');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var E__default=/*#__PURE__*/_interopDefault(E);var j__default=/*#__PURE__*/_interopDefault(j);var Te__default=/*#__PURE__*/_interopDefault(Te);var _e__default=/*#__PURE__*/_interopDefault(_e);var Ae__default=/*#__PURE__*/_interopDefault(Ae);var ue=Object.defineProperty;var pe=(e,t,n)=>t in e?ue(e,t,{enumerable:true,configurable:true,writable:true,value:n}):e[t]=n;var A=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,n)=>(typeof require<"u"?require:t)[n]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var D=(e,t,n)=>pe(e,t+"",n);var B={version:"0.0.12"};var I={input:"./.envx",outputs:{env:"./.env",types:"./types/envx.ts",metaFilePath:"."},overwrite:false};function C(){let e="envx.config.js",n=E__default.default.resolve("envx.config.json"),r=E__default.default.resolve(process.cwd(),e);if(fs.existsSync(n)){let i=fs.readFileSync(n,"utf-8");try{return JSON.parse(i)}catch{return console.warn("[envx:info:config] Failed to parse envx.config.json, using default config."),I}}if(fs.existsSync(r))try{return A(r)}catch{return console.warn("[envx:info:config] Failed to load envx.config.js. This file is only supported in CommonJS environments. Please try using 'envx.config.json' or check your project configuration."),I}return I}var U={fileDoesNotExist:e=>`The specified file does not exist: ${e}`,unsupportedType:(e,t)=>`Line ${e+1}: Unsupported type "${t}".`,validator:{invalidNumber:(e,t)=>`Invalid value for "${e}": expected a number but received "${t}".`,invalidBoolean:(e,t)=>`Invalid boolean for "${e}": expected "true" or "false" but received "${t}".`,missingEnumValues:e=>`Enum values are not defined for "${e}". Please specify allowed values.`,invalidEnum:(e,t,n)=>`Invalid enum value for "${e}": expected one of [${n.join(", ")}] but received "${t}".`,invalidEmail:(e,t)=>`Invalid email format for "${e}": received "${t}". Please provide a valid email address.`,invalidUrl:(e,t)=>`Invalid URL format for "${e}": received "${t}". Please provide a valid URL.`,unsupportedType:(e,t)=>`Unsupported type "${t}" specified for "${e}".`,requiredMissing:e=>`Required environment variable "${e}" is missing and has no default value.`},resolver:{circularDependency:e=>`Circular dependency detected while resolving "${e}".
\u2192 Check for variables referencing each other in a loop.`,interpolationFailed:e=>`Failed to resolve variable "${e}". No value found for interpolation.`,requiredMissingInResolve:e=>`Environment variable "${e}" is not set and no default is defined.
\u2192 Define "${e}" in your .envx file or provide a default value in the schema.`},parser:{schemaBlockEmpty:e=>`Line ${e+1}: Schema block name cannot be empty.`,schemaKeyDuplicate:(e,t)=>`Line ${e+1}: Schema key "${t}" is already defined. Duplicate schema definitions are not allowed.`,typeAlreadyDefined:e=>`Line ${e+1}: "type" is already defined for this variable. You cannot redefine it.`,unknownSchemaProperty:(e,t)=>`Line ${e+1}: Unknown schema property "${t}".`,missingDelimiter:e=>`Line ${e+1}: Missing "=" delimiter in assignment.`,variableNameEmpty:e=>`Line ${e+1}: Variable name cannot be empty.`,typeRequiredBeforeDefault:e=>`Line ${e+1}: "type" must be defined before "default".`,defaultMustBeNumber:e=>`Line ${e+1}: Default value must be a valid number.`,defaultMustBeBoolean:e=>`Line ${e+1}: Default value must be "true" or "false".`,defaultNotInEnum:(e,t)=>`Line ${e+1}: Default "${t}" is not in enum values.`,invalidValuesJson:e=>`Line ${e+1}: "values" must be a valid JSON string array.`}};var p={lib:U};var q=["string","number","boolean","enum","email","url"],$={};var c=class extends Error{constructor(t){super(t),this.name="EnvxError";}};function F(e){let t=e.split(/\r?\n/),n=[],r={},i=null,o=null,s=[];for(let a=0;a<t.length;a++){let u=t[a],m=u.indexOf("#");m!=-1&&(u=u.slice(0,m));let f=u.trim();if(!f||f.startsWith("#"))continue;if(o){f.endsWith('"""')?(s.push(f.slice(0,-3)),n.push({key:o,value:s.join(`
`)}),o=null,s=[]):s.push(u);continue}if(ge(f)){if(m>-1&&m>u.indexOf("]"))throw new c(`[envx:error] Line ${a+1}: Schema header line cannot contain inline comments.`);i=de(f,a,r);continue}if(i){ve(f,a,r[i],()=>(a++,a>=t.length?null:t[a].trim()));continue}let{key:v,value:x,isMultilineStart:w}=ye(f,a);w?(o=v,s.push(x)):n.push({key:v,value:x});}return {env:n,schema:r}}function de(e,t,n){let r=e.slice(1,-1).trim();if(!r)throw new c(p.lib.parser.schemaBlockEmpty(t));if(!/^[A-Za-z0-9_-]+$/.test(r))throw new c(`[envx:error] Line ${t+1}: Schema block name "${r}" contains invalid characters. Only letters, numbers, underscore and hyphen are allowed.`);if(n[r])throw new c(p.lib.parser.schemaKeyDuplicate(t,r));return n[r]=n[r]??{},r}function ve(e,t,n,r){let[i,o]=W(e,t),s=i.trim(),a=G(o.trim());if(s==="description"&&o.trim().startsWith('"""')){let u=[],m=o.trim().slice(3);if(m.endsWith('"""')){m=m.slice(0,-3),n.description=m;return}for(u.push(m);r;){let f=r();if(f===null)throw new c(`Line ${t+1}: Unterminated multiline description.`);if(f.endsWith('"""')){u.push(f.slice(0,-3));break}else u.push(f);}n.description=u.join(`
`);return}switch(s){case "required":n.required=a==="true";break;case "type":if(n.type!==void 0)throw new c(p.lib.parser.typeAlreadyDefined(t));if(!q.includes(a))throw new c(p.lib.unsupportedType(t,a));n.type=a;break;case "default":he(a,t,n);break;case "values":try{let u=JSON.parse(a);if(!Array.isArray(u)||!u.every(m=>typeof m=="string"))throw new c(`[envx:error] Line ${t+1}: The parsed "values" field must be an array of strings. Please ensure the JSON array contains only string elements.`);n.values=u;}catch{throw new c(p.lib.parser.invalidValuesJson(t))}break;case "deprecated":n.deprecated=a;break;case "description":n.description=a;break;default:throw new c(p.lib.parser.unknownSchemaProperty(t,s))}}function ge(e){return /^\[.*\]$/.test(e)}function he(e,t,n){let{type:r,values:i}=n;if(!r)throw new c(p.lib.parser.typeRequiredBeforeDefault(t));switch(r){case "number":let o=Number(e);if(isNaN(o))throw new c(p.lib.parser.defaultMustBeNumber(t));n.default=o;break;case "boolean":if(e!=="true"&&e!=="false")throw new c(p.lib.parser.defaultMustBeBoolean(t));n.default=e==="true";break;case "enum":if(!i||!i.includes(e))throw new c(p.lib.parser.defaultNotInEnum(t,e));n.default=e;break;default:n.default=e;}}function ye(e,t){let[n,r]=W(e,t),i=n.trim();if(!i)throw new c(p.lib.parser.variableNameEmpty(t));let o=r.trim();return o.startsWith('"""')?{key:i,value:o.slice(3),isMultilineStart:true}:{key:i,value:G(o),isMultilineStart:false}}function W(e,t){let n=e.indexOf("=");if(n===-1)throw new c(p.lib.parser.missingDelimiter(t));return [e.slice(0,n),e.slice(n+1)]}function G(e){let t=e[0],n=e[e.length-1];return t==='"'&&n==='"'||t==="'"&&n==="'"?e.slice(1,-1):e}function H(e){if(typeof e=="boolean")return e;if(typeof e=="string"){if(e.toLowerCase()==="true")return true;if(e.toLowerCase()==="false")return false}return typeof e=="number"?e===1:false}var K=e=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);function Ee(e){return typeof e=="boolean"?"boolean":!isNaN(Number(e))&&e!==""&&e!==null?"number":"string"}function L(e,t){let n={...$,...t};return e.forEach(({key:r,value:i})=>{n[r]||(n[r]={type:Ee(i),required:false});}),n}function z(e,t,n,r){switch(n){case "string":return t;case "number":{let i=Number(t);if(isNaN(i))throw new c(p.lib.validator.invalidNumber(e,t));return i}case "boolean":{let i=t.trim().toLowerCase();if(i==="true")return true;if(i==="false")return false;throw new c(p.lib.validator.invalidBoolean(e,t))}case "enum":{if(!r?.length)throw new c(p.lib.validator.missingEnumValues(e));if(!r.includes(t))throw new c(p.lib.validator.invalidEnum(e,t,r));return t}case "email":{if(!K(t))throw new c(p.lib.validator.invalidEmail(e,t));return t}case "url":try{return new URL(t),t}catch{throw new c(p.lib.validator.invalidUrl(e,t))}default:throw new c(p.lib.validator.unsupportedType(e,n))}}function X(e,t){let n={},r=new Map(e.map(({key:i,value:o})=>[i,o]));for(let i in t){let{type:o,required:s,default:a,values:u}=t[i],m=r.get(i);if(m!==void 0){n[i]=z(i,m,o,o==="enum"?u:void 0);continue}if(a!==void 0){n[i]=z(i,String(a),o,o==="enum"?u:void 0);continue}if(s)throw new c(p.lib.validator.requiredMissing(i))}for(let[i,o]of r.entries())i in n||(n[i]=o);return n}function Q(e,t){let n={},r={};for(let o of e)n[o.key]=o.value;if(t)for(let o in t)n[o]===void 0&&t[o].default!==void 0&&(n[o]=t[o].default);let i=new Set;for(let o of Object.keys(n))r[o]=R(n[o],n,r,i,o);if(t){for(let o in t)if(t[o].required&&n[o]===void 0)throw new c(p.lib.resolver.requiredMissingInResolve(o))}return r}function R(e,t,n,r,i){if(typeof e!="string")return e;if(r.has(i))throw new c(p.lib.resolver.circularDependency(i));return r.add(i),we(e)&&(e=Se(e,t,n,r)),be(e)&&(e=xe(e,t,n,r)),r.delete(i),e}function be(e){return typeof e=="string"&&e.includes("${")}function xe(e,t,n,r){return e.replace(/\${([^}]+)}/g,(i,o)=>{let s=o.trim();if(n[s]!==void 0)return String(n[s]);if(t[s]!==void 0)return String(R(t[s],t,n,r,s));throw new c(p.lib.resolver.interpolationFailed(s))})}function we(e){return /^\s*\${[^}]+}(?:\s*(==|!=)\s*"[^"]*")?\s*\?\s*"[^"]*"\s*:\s*"[^"]*"\s*$/.test(e.trim())}function Se(e,t,n,r){let o=e.trim().match(/^\s*\${([^}]+)}(?:\s*(==|!=)\s*"([^"]*)")?\s*\?\s*"([^"]*)"\s*:\s*"([^"]*)"\s*$/);if(!o)return e;let[,s,a,u,m,f]=o,v=s.trim(),x=n[v]!==void 0?n[v]:R(t[v],t,n,r,v),w;if(a==="=="||a==="!="){let k=String(x)===u;w=a==="=="?k:!k;}else w=H(x);return w?m:f}function y(e,t){let{env:n,schema:r}=F(e),i=L(n,r),o=Object.keys(i).length>0?i:void 0,s=Q(n,o);return o?{result:X(Object.entries(s).map(([u,m])=>({key:u,value:m})),o),schema:o}:{result:s,schema:o}}var O=".envx.meta.json",Y=".env",Z=".envx";function ee(e){let t={};for(let n in e){t[n]={type:e[n].type||"string",required:e[n].required||false,description:e[n].description||void 0,values:e[n].values||void 0,default:e[n].default||void 0};for(let r in t[n])t[n][r]===void 0&&delete t[n][r];}return JSON.stringify(t,null,2)}var T=async(e,t)=>{let n=ee(e);await j__default.default.writeFile(t,n);};var N=class{constructor(t){D(this,"silent");this.silent=t?.silent??false;}prefix(t){switch(t){case "info":return colorette.blue("[envx:info] \u2139\uFE0F ");case "success":return colorette.green(colorette.bold("[envx:success] \u2714 "));case "error":return colorette.red(colorette.bold("[envx:error] \u274C "));case "warn":return colorette.yellow(colorette.bold("[envx:warn] \u26A0\uFE0F "));case "debug":return colorette.blue(colorette.bold("[envx:debug] \u{1F41E} "));default:return "[envx]"}}log(t){this.silent||console.log(`${t}`);}info(t){this.silent||console.log(`${this.prefix("info")} ${t}`);}success(t){this.silent||console.log(`${this.prefix("success")} ${t}`);}error(t){if(!this.silent){let n=typeof t=="string"?t:t.message;console.error(`${this.prefix("error")} ${n}`);}}warn(t){this.silent||console.warn(`${this.prefix("warn")} ${t}`);}debug(t){this.silent||console.debug(`${this.prefix("debug")} ${t}`);}},l=new N({silent:false});async function Me(e){return new Promise(t=>{let n=Te__default.default.createInterface({input:process.stdin,output:process.stdout,terminal:true});n.question(`\u26A0\uFE0F "${e}" exists. Overwrite? (y/N): `,r=>{n.close(),t(r.trim().toLowerCase()==="y");});})}async function g(e){let t=E__default.default.resolve(process.cwd(),e.input),n=E__default.default.resolve(process.cwd(),e.output),r=E__default.default.relative(process.cwd(),n),i=e.overwrite,o=e?.noTypes??false;!i&&fs.existsSync(n)&&(await Me(r)||(l.info("\u2716 Operation cancelled."),process.exit(0)));let s=await j__default.default.readFile(t,"utf-8"),a=y(s),u=Object.entries(a.result).map(([f,v])=>`${f}="${String(v).replace(/\n/g,"\\n")}"`).join(`
`);if(!o){let f=E__default.default.join(e.metaFilePath||".",O);await T(a.schema||$,f),l.success(`Meta JSON schema generated at: ${E__default.default.relative(process.cwd(),f)}`);}let m=E__default.default.dirname(n);fs.existsSync(m)||await j__default.default.mkdir(m,{recursive:true}),await j__default.default.writeFile(n,u),l.success(`.env file generated at: ${r}`);}async function re(e){try{let t=E__default.default.resolve(process.cwd(),e.input),n=await j__default.default.readFile(t,"utf-8"),r=y(n);l.success("No validation errors found in .envx file.");}catch(t){l.error(`Error reading or validating .envx: ${t.message||t}`),process.exit(1);}}async function ie(e){try{let t=E__default.default.resolve(process.cwd(),e.input),n=await j__default.default.readFile(t,"utf-8"),r=y(n);console.log(JSON.stringify(r.result,null,2));}catch(t){l.error(`Error reading .envx file: ${t.message||t}`),process.exit(1);}}function Ne(e,t){switch(e){case "string":case "email":case "url":return "string";case "number":return "number";case "boolean":return "boolean";case "enum":return t.values&&t.values.length>1?t.values.map(n=>`"${n}"`).join(" | "):"string";default:return "any"}}function oe(e){let t=[];t.push(`// AUTO GENERATED FILE - DO NOT EDIT
`),t.push("export interface Envx {");for(let n in e){let{type:r="string",required:i}=e[n],o=Ne(r,e[n]),s=i?"":"?";t.push(` ${n}${s}: ${o};`);}return t.push(`}
`),t.join(`
`)}async function h(e){try{let t=E__default.default.resolve(process.cwd(),e.input),n=E__default.default.resolve(process.cwd(),e.output),r=E__default.default.relative(process.cwd(),n),i=await j__default.default.readFile(t,"utf-8"),{env:o,schema:s}=F(i),a=L(o,s),u=oe(a);await j__default.default.writeFile(n,u);let m=E__default.default.join(e.metaFilePath||".",O);await T(a,m),l.success(`TypeScript definitions generated at: ${r}`),l.success(`Meta JSON schema generated at: ${E__default.default.relative(process.cwd(),m)}`);}catch(t){l.error(`Error generating types: ${t.message||t}`),process.exit(1);}}async function ce(e){try{await g(e),l.success(colorette.blue(colorette.bold(`.env build completed.
`))),await h({...e,output:e.typesOutput}),l.success(colorette.blue(colorette.bold(`Type generation completed.
`)));}catch(t){l.error(colorette.red(colorette.bold(`Error during generation: ${t.message||t}`)));}}async function le(e){let t=e.input||Z,n=e.metaFilePath||".",r=e.output||Y,i=e.typesOutput||"envx.ts";e.silent||(l.log(colorette.bold(colorette.cyan("\u{1F331} envx is now watching for changes..."))),l.log(`\u{1F50D} Watching ${t} for changes...`));try{e.noBuild||(await g({input:t,output:r,metaFilePath:n,overwrite:!0}),e.silent||l.success(colorette.blue(colorette.bold(".env build completed.")))),e.noTypes||(await h({input:t,output:i,metaFilePath:n}),e.silent||l.success(colorette.blue(colorette.bold("Type generation completed."))));}catch(s){l.error(`Error during initial generation: ${s.message||s}`);}let o=Ae__default.default(async()=>{l.log(colorette.dim(`
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`)),e.silent||l.log(colorette.bold(colorette.yellow(`
\u{1F501} Change detected. Regenerating...`)));try{e.noBuild||(await g({input:t,output:r,metaFilePath:n,overwrite:!0}),e.silent||l.success(colorette.blue(colorette.bold(".env build completed.")))),e.noTypes||(await h({input:t,output:i,metaFilePath:n}),e.silent||l.success(colorette.blue(colorette.bold("Type generation completed."))));}catch(s){l.error(`Error during regeneration: ${s.message||s}`);}},400);_e__default.default.watch(t,{ignoreInitial:true}).on("change",o);}async function Ce(){let e=new commander.Command,t=C(),n=B.version;e.name("dotenvx").description("Enhanced CLI for .envx - Manage .envx files effortlessly").version(n),e.command("build").description("Build .env file from .envx").option("-i, --input <file>","Input .envx file",t.input||".envx").option("-o, --output <file>","Output .env file",t.outputs?.env||".env").option("-m, --metaFilePath <file>","Output metaJsonFile Folder Path (.envx.meta.json)",t.outputs?.metaFilePath||"envx.ts").option("--overwrite","Overwrite output",t.overwrite||false).action(g),e.command("generate").alias("gen").description("Generate types and build .env from .envx").option("-i, --input <file>","Input .envx file",t.input||".envx").option("-o, --output <file>","Output .env file",t.outputs?.env||".env").option("-t, --typesOutput <file>","Output .ts file",t.outputs?.types||"/types/envx.ts").option("-m, --metaFilePath <path>","Meta JSON output folder path",t.outputs?.metaFilePath||".").option("--overwrite","Overwrite output",t.overwrite||false).action(r=>{let i={...r,output:r.output,typesOutput:r.typesOutput,metaFilePath:r.metaFilePath,noTypes:true};return ce(i)}),e.command("watch").description("Watch .envx and regenerate .env + types on change").option("-i, --input <file>","Input .envx file",t.input||".envx").option("-o, --output <file>","Output .env file",t.outputs?.env||".env").option("-t, --typesOutput <file>","Output .ts file",t.outputs?.types||"/types/envx.ts").option("-m, --metaFilePath <path>","Meta JSON output folder path",t.outputs?.metaFilePath||".").option("--no-types","Do not generate TypeScript types").option("--no-build","Do not generate .env file").option("--silent","Don't log output to console").action(le),e.command("check").description("Validate .envx against its schema").option("-i, --input <file>","Input .envx file",t.input||".envx").action(re),e.command("print").description("Print parsed .envx as JSON").option("-i, --input <file>","Input .envx file",t.input||".envx").action(ie),e.command("types").description("Generate TypeScript definitions from schema").option("-i, --input <file>","Input .envx file",t.input||".envx").option("-o, --output <file>","Output .ts file",t.outputs?.types||"/types/envx.ts").option("-m, --metaFilePath <file>","Output metaJsonFile Folder Path (.envx.meta.json)",t.outputs?.metaFilePath||"envx.ts").action(h),e.parse();}Ce();//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map