diff --git a/.gitignore b/.gitignore index fbf0622..980d7f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -types \ No newline at end of file +types +node_modules diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..1ad600e --- /dev/null +++ b/dist/index.js @@ -0,0 +1 @@ +(()=>{var e={967:e=>{"use strict";function _interpolate(e,n,r){const o=e.match(/(.?\${*[\w]*(?::-)?[\w]*}*)/g)||[];return o.reduce((function(e,t,s){const c=/(.?)\${*([\w]*(?::-)?[\w]*)?}*/g.exec(t);if(!c||c.length===0){return e}const i=c[1];let a,l;if(i==="\\"){l=c[0];a=l.replace("\\$","$")}else{const t=c[2].split(":-");const p=t[0];l=c[0].substring(i.length);a=Object.prototype.hasOwnProperty.call(n,p)?n[p]:r.parsed[p]||t[1]||"";if(t.length>1&&a){const n=o[s+1];o[s+1]="";e=e.replace(n,"")}a=_interpolate(a,n,r)}return e.replace(l,a)}),e)}function expand(e){const n=e.ignoreProcessEnv?{}:process.env;for(const r in e.parsed){const o=Object.prototype.hasOwnProperty.call(n,r)?n[r]:e.parsed[r];e.parsed[r]=_interpolate(o,n,e)}for(const r in e.parsed){n[r]=e.parsed[r]}return e}e.exports.j=expand},639:(e,n,r)=>{"use strict";const o=r(147);const{resolve:t}=r(17);const s=r(437);function listDotenvFiles(e,n={}){const{node_env:r}=n;return[t(e,".env.defaults"),t(e,".env"),r!=="test"&&t(e,".env.local"),r&&t(e,`.env.${r}`),r&&t(e,`.env.${r}.local`)].filter((e=>Boolean(e)))}function parse(e,n={}){if(typeof e==="string"){return s.parse(o.readFileSync(e,n))}return e.reduce(((e,r)=>Object.assign(e,s.parse(o.readFileSync(r,n)))),{})}function load(e,n={}){try{const r=parse(e,{encoding:n.encoding});Object.keys(r).forEach((e=>{if(!process.env.hasOwnProperty(e)){process.env[e]=r[e]}else if(!n.silent){console.warn('dotenv-flow: "%s" is already defined in `process.env` and will not be overwritten',e)}}));return{parsed:r}}catch(e){return{error:e}}}function unload(e,n={}){const r=parse(e,n);Object.keys(r).forEach((e=>{if(process.env[e]===r[e]){delete process.env[e]}}))}function config(e={}){const n=e.node_env||process.env.NODE_ENV||e.default_node_env;let r;if(e.path){r=e.path}else if(e.cwd){console.warn("dotenv-flow: `options.cwd` is deprecated, please use `options.path` instead");r=e.cwd}else{r=process.cwd()}const{encoding:s=undefined,silent:c=false}=e;try{if(e.purge_dotenv){unload(t(r,".env"),{encoding:s})}const i=listDotenvFiles(r,{node_env:n}).filter((e=>o.existsSync(e)));return load(i,{encoding:s,silent:c})}catch(e){return{error:e}}}e.exports={listDotenvFiles:listDotenvFiles,parse:parse,load:load,unload:unload,config:config}},437:(e,n,r)=>{const o=r(147);const t=r(17);function log(e){console.log(`[dotenv][DEBUG] ${e}`)}const s="\n";const c=/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/;const i=/\\n/g;const a=/\n|\r|\r\n/;function parse(e,n){const r=Boolean(n&&n.debug);const o={};e.toString().split(a).forEach((function(e,n){const t=e.match(c);if(t!=null){const e=t[1];let n=t[2]||"";const r=n.length-1;const c=n[0]==='"'&&n[r]==='"';const a=n[0]==="'"&&n[r]==="'";if(a||c){n=n.substring(1,r);if(c){n=n.replace(i,s)}}else{n=n.trim()}o[e]=n}else if(r){log(`did not match key and value when parsing line ${n+1}: ${e}`)}}));return o}function config(e){let n=t.resolve(process.cwd(),".env");let r="utf8";let s=false;if(e){if(e.path!=null){n=e.path}if(e.encoding!=null){r=e.encoding}if(e.debug!=null){s=true}}try{const e=parse(o.readFileSync(n,{encoding:r}),{debug:s});Object.keys(e).forEach((function(n){if(!Object.prototype.hasOwnProperty.call(process.env,n)){process.env[n]=e[n]}else if(s){log(`"${n}" is already defined in \`process.env\` and will not be overwritten`)}}));return{parsed:e}}catch(e){return{error:e}}}e.exports.config=config;e.exports.parse=parse},147:e=>{"use strict";e.exports=require("fs")},17:e=>{"use strict";e.exports=require("path")}};var n={};function __nccwpck_require__(r){var o=n[r];if(o!==undefined){return o.exports}var t=n[r]={exports:{}};var s=true;try{e[r](t,t.exports,__nccwpck_require__);s=false}finally{if(s)delete n[r]}return t.exports}(()=>{__nccwpck_require__.n=e=>{var n=e&&e.__esModule?()=>e["default"]:()=>e;__nccwpck_require__.d(n,{a:n});return n}})();(()=>{__nccwpck_require__.d=(e,n)=>{for(var r in n){if(__nccwpck_require__.o(n,r)&&!__nccwpck_require__.o(e,r)){Object.defineProperty(e,r,{enumerable:true,get:n[r]})}}}})();(()=>{__nccwpck_require__.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n)})();(()=>{__nccwpck_require__.r=e=>{if(typeof Symbol!=="undefined"&&Symbol.toStringTag){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}Object.defineProperty(e,"__esModule",{value:true})}})();if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r={};(()=>{"use strict";__nccwpck_require__.r(r);__nccwpck_require__.d(r,{processEnv:()=>processEnv,loadEnvConfig:()=>loadEnvConfig});var e=__nccwpck_require__(147);var n=__nccwpck_require__.n(e);var o=__nccwpck_require__(639);var t=__nccwpck_require__.n(o);var s=__nccwpck_require__(967);let c=undefined;let i=[];function processEnv(e,n,r=console){var t;if(process.env.__NEXT_PROCESSED_ENV||e.length===0){return process.env}process.env.__NEXT_PROCESSED_ENV="true";const c=Object.assign({},process.env);const i={};for(const n of e){try{let e={};e.parsed=o.parse(n.path);e=(0,s.j)(e);if(e.parsed){r.info(`Loaded env from ${n.path}`)}for(const n of Object.keys(e.parsed||{})){if(typeof i[n]==="undefined"&&typeof c[n]==="undefined"){i[n]=(t=e.parsed)===null||t===void 0?void 0:t[n]}}}catch(e){r.error(`Failed to load env from ${n.path}`,e)}}return Object.assign(process.env,i)}function loadEnvConfig(n,r,t=console){if(c)return{combinedEnv:c,loadedEnvFiles:i};const s=process.env.NODE_ENV==="test";const a=r!==null&&r!==void 0?r:process.env.NODE_ENV==="development";const l=[process.env.BUILD_ENV,process.env.SITE_ENV,process.env.ENVIRONMENT,s?"test":a?"development":"production"].find(Boolean);const p=o.listDotenvFiles(n,{node_env:l});for(const n of p){try{const r=e.statSync(n);if(!r.isFile()){continue}const o=e.readFileSync(n,"utf8");i.push({path:n,contents:o})}catch(e){if(e.code!=="ENOENT"){t.error(`Failed to load env from ${n}`,e)}}}c=processEnv(i,n,t);return{combinedEnv:c,loadedEnvFiles:i}}})();module.exports=r})(); \ No newline at end of file diff --git a/index.ts b/index.ts index 97df967..8adca80 100644 --- a/index.ts +++ b/index.ts @@ -1,7 +1,6 @@ /* eslint-disable import/no-extraneous-dependencies */ import * as fs from 'fs' -import * as path from 'path' -import * as dotenv from 'dotenv' +import * as dotEnvFlow from 'dotenv-flow' import { expand as dotenvExpand } from 'dotenv-expand' export type Env = { [key: string]: string } @@ -33,17 +32,17 @@ export function processEnv( process.env.__NEXT_PROCESSED_ENV = 'true' const origEnv = Object.assign({}, process.env) - const parsed: dotenv.DotenvParseOutput = {} + const parsed: dotEnvFlow.DotenvParseOutput = {} for (const envFile of loadedEnvFiles) { try { - let result: dotenv.DotenvConfigOutput = {} - result.parsed = dotenv.parse(envFile.contents) + let result: dotEnvFlow.DotenvLoadOutput = {} + result.parsed = dotEnvFlow.parse(envFile.path) result = dotenvExpand(result) if (result.parsed) { - log.info(`Loaded env from ${path.join(dir || '', envFile.path)}`) + log.info(`Loaded env from ${envFile.path}`) } for (const key of Object.keys(result.parsed || {})) { @@ -56,7 +55,7 @@ export function processEnv( } } catch (err) { log.error( - `Failed to load env from ${path.join(dir || '', envFile.path)}`, + `Failed to load env from ${envFile.path}`, err ) } @@ -78,37 +77,37 @@ export function loadEnvConfig( if (combinedEnv) return { combinedEnv, loadedEnvFiles: cachedLoadedEnvFiles } const isTest = process.env.NODE_ENV === 'test' - const mode = isTest ? 'test' : dev ? 'development' : 'production' - const dotenvFiles = [ - `.env.${mode}.local`, - // Don't include `.env.local` for `test` environment - // since normally you expect tests to produce the same - // results for everyone - mode !== 'test' && `.env.local`, - `.env.${mode}`, - '.env', - ].filter(Boolean) as string[] - - for (const envFile of dotenvFiles) { + const isDev = dev ?? process.env.NODE_ENV === 'development' + const _environment = [ + process.env.BUILD_ENV, + process.env.SITE_ENV, + process.env.ENVIRONMENT, + isTest ? 'test' : isDev ? 'development' : 'production' + ].find(Boolean) as string + + // move to listFiles instead of listDotenvFiles after version 4.0.0 + const dotenvFiles = dotEnvFlow.listDotenvFiles(dir, { + node_env: _environment, + }) + + for (const dotEnvFile of dotenvFiles) { // only load .env if the user provided has an env config file - const dotEnvPath = path.join(dir, envFile) - try { - const stats = fs.statSync(dotEnvPath) + const stats = fs.statSync(dotEnvFile) // make sure to only attempt to read files if (!stats.isFile()) { continue } - const contents = fs.readFileSync(dotEnvPath, 'utf8') + const contents = fs.readFileSync(dotEnvFile, 'utf8') cachedLoadedEnvFiles.push({ - path: envFile, + path: dotEnvFile, contents, }) } catch (err: any) { if (err.code !== 'ENOENT') { - log.error(`Failed to load env from ${envFile}`, err) + log.error(`Failed to load env from ${dotEnvFile}`, err) } } } diff --git a/package.json b/package.json index 386bd09..3768db7 100644 --- a/package.json +++ b/package.json @@ -22,15 +22,19 @@ "types" ], "scripts": { + "tsc": "npx -p typescript tsc", "dev": "ncc build ./index.ts -w -o dist/", - "prerelease": "rimraf ./dist/", - "types": "tsc index.ts --declaration --emitDeclarationOnly --declarationDir types --esModuleInterop", + "prerelease": "npx rimraf@4.4.1 ./dist/", + "types": "yarn tsc index.ts --declaration --emitDeclarationOnly --declarationDir types --esModuleInterop", "release": "ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register", "prepublish": "yarn release && yarn types" }, "devDependencies": { + "@types/dotenv-flow": "3.2.0", + "@types/node": "18.19.1", "@vercel/ncc": "0.33.1", - "dotenv": "10.0.0", - "dotenv-expand": "8.0.1" + "dotenv-flow": "3.2.0", + "dotenv-expand": "8.0.1", + "typescript": "5.4.2" } } diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..7c5d9e6 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,47 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/dotenv-flow@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/dotenv-flow/-/dotenv-flow-3.2.0.tgz#3a632bcb72d5a6fa770da644a1f63d69a6c7075e" + integrity sha512-A79hbPwocbYkcTwGcDOFbKDuqyVo5mLAz/6Iq465YZ7R7Go5bT1PIM8I2jlPQkaD9u9fbotGVLkUPhX+9XUHfw== + +"@types/node@18.19.1": + version "18.19.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.1.tgz#e3ed7d5ab5ea21f33a4503decb2171e0d8f53070" + integrity sha512-mZJ9V11gG5Vp0Ox2oERpeFDl+JvCwK24PGy76vVY/UgBtjwJWc5rYBThFxmbnYOm9UPZNm6wEl/sxHt2SU7x9A== + dependencies: + undici-types "~5.26.4" + +"@vercel/ncc@0.33.1": + version "0.33.1" + resolved "https://registry.yarnpkg.com/@vercel/ncc/-/ncc-0.33.1.tgz#b240080a3c1ded9446a30955a06a79851bb38f71" + integrity sha512-Mlsps/P0PLZwsCFtSol23FGqT3FhBGb4B1AuGQ52JTAtXhak+b0Fh/4T55r0/SVQPeRiX9pNItOEHwakGPmZYA== + +dotenv-expand@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-8.0.1.tgz#332aa17c14b12e28e2e230f8d183eecc1c014fdc" + integrity sha512-j/Ih7bIERDR5PzI89Zu8ayd3tXZ6E3dbY0ljQ9Db0K87qBO8zdLsi2dIvDHMWtjC3Yxb8XixOTHAtia0fDHRpg== + +dotenv-flow@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/dotenv-flow/-/dotenv-flow-3.2.0.tgz#a5d79dd60ddb6843d457a4874aaf122cf659a8b7" + integrity sha512-GEB6RrR4AbqDJvNSFrYHqZ33IKKbzkvLYiD5eo4+9aFXr4Y4G+QaFrB/fNp0y6McWBmvaPn3ZNjIufnj8irCtg== + dependencies: + dotenv "^8.0.0" + +dotenv@^8.0.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + +typescript@5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372" + integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==