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.
6 lines • 10.3 kB
JavaScript
import S,{existsSync,readFileSync}from'fs';import y from'path';var T=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(n,r)=>(typeof require<"u"?require:n)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var s=class extends Error{constructor(n){super(n),this.name="EnvxError";}};var F={fileDoesNotExist:e=>`The specified file does not exist: ${e}`,unsupportedType:(e,n)=>`Line ${e+1}: Unsupported type "${n}".`,validator:{invalidNumber:(e,n)=>`Invalid value for "${e}": expected a number but received "${n}".`,invalidBoolean:(e,n)=>`Invalid boolean for "${e}": expected "true" or "false" but received "${n}".`,missingEnumValues:e=>`Enum values are not defined for "${e}". Please specify allowed values.`,invalidEnum:(e,n,r)=>`Invalid enum value for "${e}": expected one of [${r.join(", ")}] but received "${n}".`,invalidEmail:(e,n)=>`Invalid email format for "${e}": received "${n}". Please provide a valid email address.`,invalidUrl:(e,n)=>`Invalid URL format for "${e}": received "${n}". Please provide a valid URL.`,unsupportedType:(e,n)=>`Unsupported type "${n}" 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,n)=>`Line ${e+1}: Schema key "${n}" 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,n)=>`Line ${e+1}: Unknown schema property "${n}".`,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,n)=>`Line ${e+1}: Default "${n}" is not in enum values.`,invalidValuesJson:e=>`Line ${e+1}: "values" must be a valid JSON string array.`}};var a={lib:F};var k=["string","number","boolean","enum","email","url"],w={};function _(e){let n=e.split(/\r?\n/),r=[],t={},i=null,o=null,c=[];for(let l=0;l<n.length;l++){let u=n[l],f=u.indexOf("#");f!=-1&&(u=u.slice(0,f));let m=u.trim();if(!m||m.startsWith("#"))continue;if(o){m.endsWith('"""')?(c.push(m.slice(0,-3)),r.push({key:o,value:c.join(`
`)}),o=null,c=[]):c.push(u);continue}if(z(m)){if(f>-1&&f>u.indexOf("]"))throw new s(`[envx:error] Line ${l+1}: Schema header line cannot contain inline comments.`);i=H(m,l,t);continue}if(i){K(m,l,t[i],()=>(l++,l>=n.length?null:n[l].trim()));continue}let{key:v,value:h,isMultilineStart:E}=Q(m,l);E?(o=v,c.push(h)):r.push({key:v,value:h});}return {env:r,schema:t}}function H(e,n,r){let t=e.slice(1,-1).trim();if(!t)throw new s(a.lib.parser.schemaBlockEmpty(n));if(!/^[A-Za-z0-9_-]+$/.test(t))throw new s(`[envx:error] Line ${n+1}: Schema block name "${t}" contains invalid characters. Only letters, numbers, underscore and hyphen are allowed.`);if(r[t])throw new s(a.lib.parser.schemaKeyDuplicate(n,t));return r[t]=r[t]??{},t}function K(e,n,r,t){let[i,o]=N(e,n),c=i.trim(),l=V(o.trim());if(c==="description"&&o.trim().startsWith('"""')){let u=[],f=o.trim().slice(3);if(f.endsWith('"""')){f=f.slice(0,-3),r.description=f;return}for(u.push(f);t;){let m=t();if(m===null)throw new s(`Line ${n+1}: Unterminated multiline description.`);if(m.endsWith('"""')){u.push(m.slice(0,-3));break}else u.push(m);}r.description=u.join(`
`);return}switch(c){case "required":r.required=l==="true";break;case "type":if(r.type!==void 0)throw new s(a.lib.parser.typeAlreadyDefined(n));if(!k.includes(l))throw new s(a.lib.unsupportedType(n,l));r.type=l;break;case "default":X(l,n,r);break;case "values":try{let u=JSON.parse(l);if(!Array.isArray(u)||!u.every(f=>typeof f=="string"))throw new s(`[envx:error] Line ${n+1}: The parsed "values" field must be an array of strings. Please ensure the JSON array contains only string elements.`);r.values=u;}catch{throw new s(a.lib.parser.invalidValuesJson(n))}break;case "deprecated":r.deprecated=l;break;case "description":r.description=l;break;default:throw new s(a.lib.parser.unknownSchemaProperty(n,c))}}function z(e){return /^\[.*\]$/.test(e)}function X(e,n,r){let{type:t,values:i}=r;if(!t)throw new s(a.lib.parser.typeRequiredBeforeDefault(n));switch(t){case "number":let o=Number(e);if(isNaN(o))throw new s(a.lib.parser.defaultMustBeNumber(n));r.default=o;break;case "boolean":if(e!=="true"&&e!=="false")throw new s(a.lib.parser.defaultMustBeBoolean(n));r.default=e==="true";break;case "enum":if(!i||!i.includes(e))throw new s(a.lib.parser.defaultNotInEnum(n,e));r.default=e;break;default:r.default=e;}}function Q(e,n){let[r,t]=N(e,n),i=r.trim();if(!i)throw new s(a.lib.parser.variableNameEmpty(n));let o=t.trim();return o.startsWith('"""')?{key:i,value:o.slice(3),isMultilineStart:true}:{key:i,value:V(o),isMultilineStart:false}}function N(e,n){let r=e.indexOf("=");if(r===-1)throw new s(a.lib.parser.missingDelimiter(n));return [e.slice(0,r),e.slice(r+1)]}function V(e){let n=e[0],r=e[e.length-1];return n==='"'&&r==='"'||n==="'"&&r==="'"?e.slice(1,-1):e}function I(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 A=e=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);function Y(e){return typeof e=="boolean"?"boolean":!isNaN(Number(e))&&e!==""&&e!==null?"number":"string"}function O(e,n){let r={...w,...n};return e.forEach(({key:t,value:i})=>{r[t]||(r[t]={type:Y(i),required:false});}),r}function D(e){return {...w,...e||{}}}function P(e,n,r,t){switch(r){case "string":return n;case "number":{let i=Number(n);if(isNaN(i))throw new s(a.lib.validator.invalidNumber(e,n));return i}case "boolean":{let i=n.trim().toLowerCase();if(i==="true")return true;if(i==="false")return false;throw new s(a.lib.validator.invalidBoolean(e,n))}case "enum":{if(!t?.length)throw new s(a.lib.validator.missingEnumValues(e));if(!t.includes(n))throw new s(a.lib.validator.invalidEnum(e,n,t));return n}case "email":{if(!A(n))throw new s(a.lib.validator.invalidEmail(e,n));return n}case "url":try{return new URL(n),n}catch{throw new s(a.lib.validator.invalidUrl(e,n))}default:throw new s(a.lib.validator.unsupportedType(e,r))}}function B(e,n){let r={},t=new Map(e.map(({key:i,value:o})=>[i,o]));for(let i in n){let{type:o,required:c,default:l,values:u}=n[i],f=t.get(i);if(f!==void 0){r[i]=P(i,f,o,o==="enum"?u:void 0);continue}if(l!==void 0){r[i]=P(i,String(l),o,o==="enum"?u:void 0);continue}if(c)throw new s(a.lib.validator.requiredMissing(i))}for(let[i,o]of t.entries())i in r||(r[i]=o);return r}function j(e,n){let r={},t={};for(let o of e)r[o.key]=o.value;if(n)for(let o in n)r[o]===void 0&&n[o].default!==void 0&&(r[o]=n[o].default);let i=new Set;for(let o of Object.keys(r))t[o]=x(r[o],r,t,i,o);if(n){for(let o in n)if(n[o].required&&r[o]===void 0)throw new s(a.lib.resolver.requiredMissingInResolve(o))}return t}function x(e,n,r,t,i){if(typeof e!="string")return e;if(t.has(i))throw new s(a.lib.resolver.circularDependency(i));return t.add(i),ne(e)&&(e=re(e,n,r,t)),Z(e)&&(e=ee(e,n,r,t)),t.delete(i),e}function Z(e){return typeof e=="string"&&e.includes("${")}function ee(e,n,r,t){return e.replace(/\${([^}]+)}/g,(i,o)=>{let c=o.trim();if(r[c]!==void 0)return String(r[c]);if(n[c]!==void 0)return String(x(n[c],n,r,t,c));throw new s(a.lib.resolver.interpolationFailed(c))})}function ne(e){return /^\s*\${[^}]+}(?:\s*(==|!=)\s*"[^"]*")?\s*\?\s*"[^"]*"\s*:\s*"[^"]*"\s*$/.test(e.trim())}function re(e,n,r,t){let o=e.trim().match(/^\s*\${([^}]+)}(?:\s*(==|!=)\s*"([^"]*)")?\s*\?\s*"([^"]*)"\s*:\s*"([^"]*)"\s*$/);if(!o)return e;let[,c,l,u,f,m]=o,v=c.trim(),h=r[v]!==void 0?r[v]:x(n[v],n,r,t,v),E;if(l==="=="||l==="!="){let b=String(h)===u;E=l==="=="?b:!b;}else E=I(h);return E?f:m}function $(e,n){let{env:r,schema:t}=_(e),i=O(r,t),o=n?{...i,...n}:Object.keys(i).length>0?i:void 0,c=j(r,o);return o?{result:B(Object.entries(c).map(([u,f])=>({key:u,value:f})),o),schema:o}:{result:c,schema:o}}var g=".envx.meta.json",U=".env",L=".envx";function C(e){let n=e??y.join(process.cwd(),g);if(!S.existsSync(n))throw new Error(`Meta file not found at ${n}`);let r=S.readFileSync(n,"utf-8");try{return JSON.parse(r)}catch(t){throw new Error(`Invalid JSON in ${n}: ${t.message}`)}}var R={input:"./.envx",outputs:{env:"./.env",types:"./types/envx.ts",metaFilePath:"."},overwrite:false};function W(){let e="envx.config.js",r=y.resolve("envx.config.json"),t=y.resolve(process.cwd(),e);if(existsSync(r)){let i=readFileSync(r,"utf-8");try{return JSON.parse(i)}catch{return console.warn("[envx:info:config] Failed to parse envx.config.json, using default config."),R}}if(existsSync(t))try{return T(t)}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."),R}return R}var M=W();function hn(e=U){let n=y.join(M?.outputs?.metaFilePath||".",g),r=y.resolve(process.cwd(),n),t=M?.outputs?.env||e,i=y.resolve(process.cwd(),t);if(!S.existsSync(i))throw new s(a.lib.fileDoesNotExist(i));let o=S.readFileSync(i,{encoding:"utf-8"}),c=C(r);return $(o,c).result}function oe(e=L,n){let r=M?.outputs?.env||e,t=y.resolve(process.cwd(),r);if(!S.existsSync(t))throw new s(a.lib.fileDoesNotExist(t));let i=S.readFileSync(t,{encoding:"utf-8"}),o=D(n);return $(i,o).result}function En(e=L,n){let r=oe(e,n);return Object.entries(r).forEach(([t,i])=>{process.env[t]=String(i);}),r}export{hn as getEnv,oe as getEnvx,En as loadEnvx};//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map