next.js icon indicating copy to clipboard operation
next.js copied to clipboard

feat: type check for `env`

Open devjiwonchoi opened this issue 1 year ago • 3 comments

Port of #48029

Why?

Users can benefit the autocomplete and type-safe environment variable experience.

What?

This PR added experimental.typedEnv that generates a .d.ts file on Dev Server. It generates from the loaded env files, except production specific files like .env.production.local.

How?

When starting the dev server, we read off the env files and next config.

If we validate that there is experimental.typedEnv set as true in next config, we generate a env.d.ts file inside the distDir (.next by default).

If there is a change in the env files, we rewrite the env.d.ts file.

Fixes NEXT-542

devjiwonchoi avatar Jun 24 '24 15:06 devjiwonchoi

Tests Passed

ijjk avatar Jun 24 '24 15:06 ijjk

Stats from current PR

Default Build
General Overall increase ⚠️
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
buildDuration 17s 14.5s N/A
buildDurationCached 8.4s 6.7s N/A
nodeModulesSize 359 MB 359 MB ⚠️ +22.4 kB
nextStartRea..uration (ms) 418ms 426ms N/A
Client Bundles (main, webpack)
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
1780.HASH.js gzip 167 B 167 B
5453-HASH.js gzip 35.8 kB 35.8 kB N/A
7514-HASH.js gzip 5.06 kB 5.05 kB N/A
a7a62840-HASH.js gzip 51.7 kB 51.7 kB N/A
framework-HASH.js gzip 56.7 kB 56.7 kB N/A
main-app-HASH.js gzip 221 B 222 B N/A
main-HASH.js gzip 32.3 kB 32.3 kB N/A
webpack-HASH.js gzip 1.71 kB 1.71 kB
Overall change 1.88 kB 1.88 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
_app-HASH.js gzip 193 B 193 B
_error-HASH.js gzip 192 B 192 B
amp-HASH.js gzip 509 B 510 B N/A
css-HASH.js gzip 341 B 341 B
dynamic-HASH.js gzip 2.52 kB 2.52 kB
edge-ssr-HASH.js gzip 264 B 266 B N/A
head-HASH.js gzip 362 B 363 B N/A
hooks-HASH.js gzip 391 B 390 B N/A
image-HASH.js gzip 4.26 kB 4.26 kB
index-HASH.js gzip 268 B 268 B
link-HASH.js gzip 2.69 kB 2.69 kB N/A
routerDirect..HASH.js gzip 326 B 325 B N/A
script-HASH.js gzip 396 B 397 B N/A
withRouter-HASH.js gzip 320 B 321 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 7.89 kB 7.89 kB
Client Build Manifests
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
_buildManifest.js gzip 485 B 483 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
index.html gzip 522 B 524 B N/A
link.html gzip 537 B 538 B N/A
withRouter.html gzip 520 B 520 B
Overall change 520 B 520 B
Edge SSR bundle Size
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
edge-ssr.js gzip 127 kB 127 kB
page.js gzip 166 kB 166 kB N/A
Overall change 127 kB 127 kB
Middleware size
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
middleware-b..fest.js gzip 660 B 660 B
middleware-r..fest.js gzip 156 B 155 B N/A
middleware.js gzip 29.5 kB 29.5 kB N/A
edge-runtime..pack.js gzip 1.03 kB 1.03 kB
Overall change 1.69 kB 1.69 kB
Next Runtimes
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
app-page-exp...dev.js gzip 183 kB 183 kB
app-page-exp..prod.js gzip 112 kB 112 kB
app-page-tur..prod.js gzip 123 kB 123 kB
app-page-tur..prod.js gzip 118 kB 118 kB
app-page.run...dev.js gzip 178 kB 178 kB
app-page.run..prod.js gzip 108 kB 108 kB
app-route-ex...dev.js gzip 23.3 kB 23.3 kB
app-route-ex..prod.js gzip 18.7 kB 18.7 kB
app-route-tu..prod.js gzip 18.7 kB 18.7 kB
app-route-tu..prod.js gzip 18.6 kB 18.6 kB
app-route.ru...dev.js gzip 24.6 kB 24.6 kB
app-route.ru..prod.js gzip 18.6 kB 18.6 kB
pages-api-tu..prod.js gzip 9.55 kB 9.55 kB
pages-api.ru...dev.js gzip 9.82 kB 9.82 kB
pages-api.ru..prod.js gzip 9.55 kB 9.55 kB
pages-turbo...prod.js gzip 21.6 kB 21.6 kB
pages.runtim...dev.js gzip 22.1 kB 22.1 kB
pages.runtim..prod.js gzip 21.6 kB 21.6 kB
server.runti..prod.js gzip 51.7 kB 51.7 kB N/A
Overall change 1.04 MB 1.04 MB
build cache
vercel/next.js canary devjiwonchoi/next.js feat/type-check-env Change
0.pack gzip 1.67 MB 1.67 MB N/A
index.pack gzip 132 kB 132 kB N/A
Overall change 0 B 0 B
Diff details
Diff for page.js

Diff too large to display

Diff for middleware.js

Diff too large to display

Diff for edge-ssr.js

Diff too large to display

Diff for image-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [8358],
   {
-    /***/ 1362: /***/ (
+    /***/ 9618: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/image",
         function () {
-          return __webpack_require__(2160);
+          return __webpack_require__(699);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 537: /***/ (module, exports, __webpack_require__) => {
+    /***/ 9451: /***/ (module, exports, __webpack_require__) => {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -40,15 +40,15 @@
         __webpack_require__(3537)
       );
       const _head = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(7092)
+        __webpack_require__(6490)
       );
-      const _getimgprops = __webpack_require__(9834);
-      const _imageconfig = __webpack_require__(5676);
-      const _imageconfigcontextsharedruntime = __webpack_require__(387);
-      const _warnonce = __webpack_require__(451);
-      const _routercontextsharedruntime = __webpack_require__(5357);
+      const _getimgprops = __webpack_require__(3646);
+      const _imageconfig = __webpack_require__(535);
+      const _imageconfigcontextsharedruntime = __webpack_require__(4724);
+      const _warnonce = __webpack_require__(6321);
+      const _routercontextsharedruntime = __webpack_require__(1759);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3945)
+        __webpack_require__(1882)
       );
       // This is replaced by webpack define plugin
       const configEnv = {
@@ -376,7 +376,7 @@
       /***/
     },
 
-    /***/ 9834: /***/ (
+    /***/ 3646: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -392,9 +392,9 @@
           return getImgProps;
         },
       });
-      const _warnonce = __webpack_require__(451);
-      const _imageblursvg = __webpack_require__(3547);
-      const _imageconfig = __webpack_require__(5676);
+      const _warnonce = __webpack_require__(6321);
+      const _imageblursvg = __webpack_require__(8297);
+      const _imageconfig = __webpack_require__(535);
       const VALID_LOADING_VALUES =
         /* unused pure expression or super */ null && [
           "lazy",
@@ -766,7 +766,7 @@
       /***/
     },
 
-    /***/ 3547: /***/ (__unused_webpack_module, exports) => {
+    /***/ 8297: /***/ (__unused_webpack_module, exports) => {
       "use strict";
       /**
        * A shared function, used on both client and server, to generate a SVG blur placeholder.
@@ -821,7 +821,7 @@
       /***/
     },
 
-    /***/ 6850: /***/ (
+    /***/ 973: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -848,10 +848,10 @@
         },
       });
       const _interop_require_default = __webpack_require__(1478);
-      const _getimgprops = __webpack_require__(9834);
-      const _imagecomponent = __webpack_require__(537);
+      const _getimgprops = __webpack_require__(3646);
+      const _imagecomponent = __webpack_require__(9451);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3945)
+        __webpack_require__(1882)
       );
       function getImageProps(imgProps) {
         const { props } = (0, _getimgprops.getImgProps)(imgProps, {
@@ -883,7 +883,7 @@
       /***/
     },
 
-    /***/ 3945: /***/ (__unused_webpack_module, exports) => {
+    /***/ 1882: /***/ (__unused_webpack_module, exports) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -918,7 +918,7 @@
       /***/
     },
 
-    /***/ 2160: /***/ (
+    /***/ 699: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -935,8 +935,8 @@
 
       // EXTERNAL MODULE: ./node_modules/.pnpm/[email protected]/node_modules/react/jsx-runtime.js
       var jsx_runtime = __webpack_require__(898);
-      // EXTERNAL MODULE: ./node_modules/.pnpm/next@file+..+main-repo+packages+next+next-packed.tgz_react-dom@19.0.0-rc-6230622a1a-20240610__zehaskxadtwcczqqbmt6koh6bq/node_modules/next/image.js
-      var next_image = __webpack_require__(6793);
+      // EXTERNAL MODULE: ./node_modules/.pnpm/next@file+..+diff-repo+packages+next+next-packed.tgz_react-dom@19.0.0-rc-6230622a1a-20240610__uulzbengwsfwhwaa2ambxampcy/node_modules/next/image.js
+      var next_image = __webpack_require__(1428);
       var image_default = /*#__PURE__*/ __webpack_require__.n(next_image); // CONCATENATED MODULE: ./pages/nextjs.png
       /* harmony default export */ const nextjs = {
         src: "/_next/static/media/nextjs.cae0b805.png",
@@ -966,12 +966,12 @@
       /***/
     },
 
-    /***/ 6793: /***/ (
+    /***/ 1428: /***/ (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) => {
-      module.exports = __webpack_require__(6850);
+      module.exports = __webpack_require__(973);
 
       /***/
     },
@@ -981,7 +981,7 @@
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [2888, 9774, 179], () =>
-      __webpack_exec__(1362)
+      __webpack_exec__(9618)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for main-HASH.js

Diff too large to display

Diff for server.runtime.prod.js
@@ -1,4 +1,4 @@
-(()=>{var e={"../next-env/dist/index.js":(e,t,r)=>{(()=>{var t={383:e=>{"use strict";e.exports.j=function(e){let t=e.ignoreProcessEnv?{}:process.env;for(let r in e.parsed){let i=Object.prototype.hasOwnProperty.call(t,r)?t[r]:e.parsed[r];e.parsed[r]=(function e(t,r,i){let n=function(e,t){let r=Array.from(e.matchAll(t));return r.length>0?r.slice(-1)[0].index:-1}(t,/(?!(?<=\\))\$/g);if(-1===n)return t;let s=t.slice(n).match(/((?!(?<=\\))\${?([\w]+)(?::-([^}\\]*))?}?)/);if(null!=s){let[,n,a,o]=s;return e(t.replace(n,r[a]||o||i.parsed[a]||""),r,i)}return t})(i,t,e).replace(/\\\$/g,"$")}for(let r in e.parsed)t[r]=e.parsed[r];return e}},234:(e,t,r)=>{let i=r(147),n=r(17),s=r(37),a=r(113),o=r(803).version,l=/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;function h(e){console.log(`[dotenv@${o}][DEBUG] ${e}`)}function d(e){return e&&e.DOTENV_KEY&&e.DOTENV_KEY.length>0?e.DOTENV_KEY:process.env.DOTENV_KEY&&process.env.DOTENV_KEY.length>0?process.env.DOTENV_KEY:""}function u(e){let t=n.resolve(process.cwd(),".env");return e&&e.path&&e.path.length>0&&(t=e.path),t.endsWith(".vault")?t:`${t}.vault`}let c={configDotenv:function(e){let t=n.resolve(process.cwd(),".env"),r="utf8",a=!!(e&&e.debug);if(e){if(null!=e.path){var o;t="~"===(o=e.path)[0]?n.join(s.homedir(),o.slice(1)):o}null!=e.encoding&&(r=e.encoding)}try{let n=c.parse(i.readFileSync(t,{encoding:r})),s=process.env;return e&&null!=e.processEnv&&(s=e.processEnv),c.populate(s,n,e),{parsed:n}}catch(e){return a&&h(`Failed to load ${t} ${e.message}`),{error:e}}},_configVault:function(e){console.log(`[dotenv@${o}][INFO] Loading env from encrypted .env.vault`);let t=c._parseVault(e),r=process.env;return e&&null!=e.processEnv&&(r=e.processEnv),c.populate(r,t,e),{parsed:t}},_parseVault:function(e){let t;let r=u(e),i=c.configDotenv({path:r});if(!i.parsed)throw Error(`MISSING_DATA: Cannot parse ${r} for an unknown reason`);let n=d(e).split(","),s=n.length;for(let e=0;e<s;e++)try{let r=n[e].trim(),s=function(e,t){let r;try{r=new URL(t)}catch(e){if("ERR_INVALID_URL"===e.code)throw Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:[email protected]/vault/.env.vault?environment=development");throw e}let i=r.password;if(!i)throw Error("INVALID_DOTENV_KEY: Missing key part");let n=r.searchParams.get("environment");if(!n)throw Error("INVALID_DOTENV_KEY: Missing environment part");let s=`DOTENV_VAULT_${n.toUpperCase()}`,a=e.parsed[s];if(!a)throw Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${s} in your .env.vault file.`);return{ciphertext:a,key:i}}(i,r);t=c.decrypt(s.ciphertext,s.key);break}catch(t){if(e+1>=s)throw t}return c.parse(t)},config:function(e){let t=u(e);if(0===d(e).length)return c.configDotenv(e);if(!i.existsSync(t)){var r;return r=`You set DOTENV_KEY but you are missing a .env.vault file at ${t}. Did you forget to build it?`,console.log(`[dotenv@${o}][WARN] ${r}`),c.configDotenv(e)}return c._configVault(e)},decrypt:function(e,t){let r=Buffer.from(t.slice(-64),"hex"),i=Buffer.from(e,"base64"),n=i.slice(0,12),s=i.slice(-16);i=i.slice(12,-16);try{let e=a.createDecipheriv("aes-256-gcm",r,n);return e.setAuthTag(s),`${e.update(i)}${e.final()}`}catch(i){let e=i instanceof RangeError,t="Invalid key length"===i.message,r="Unsupported state or unable to authenticate data"===i.message;if(e||t)throw Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");if(r)throw Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");throw console.error("Error: ",i.code),console.error("Error: ",i.message),i}},parse:function(e){let t;let r={},i=e.toString();for(i=i.replace(/\r\n?/gm,"\n");null!=(t=l.exec(i));){let e=t[1],i=t[2]||"",n=(i=i.trim())[0];i=i.replace(/^(['"`])([\s\S]*)\1$/gm,"$2"),'"'===n&&(i=(i=i.replace(/\\n/g,"\n")).replace(/\\r/g,"\r")),r[e]=i}return r},populate:function(e,t,r={}){let i=!!(r&&r.debug),n=!!(r&&r.override);if("object"!=typeof t)throw Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");for(let r of Object.keys(t))Object.prototype.hasOwnProperty.call(e,r)?(!0===n&&(e[r]=t[r]),i&&(!0===n?h(`"${r}" is already defined and WAS overwritten`):h(`"${r}" is already defined and was NOT overwritten`))):e[r]=t[r]}};e.exports.configDotenv=c.configDotenv,e.exports._configVault=c._configVault,e.exports._parseVault=c._parseVault,e.exports.config=c.config,e.exports.decrypt=c.decrypt,e.exports.parse=c.parse,e.exports.populate=c.populate,e.exports=c},113:e=>{"use strict";e.exports=r("crypto")},147:e=>{"use strict";e.exports=r("fs")},37:e=>{"use strict";e.exports=r("os")},17:e=>{"use strict";e.exports=r("path")},803:e=>{"use strict";e.exports=JSON.parse('{"name":"dotenv","version":"16.3.1","description":"Loads environment variables from .env file","main":"lib/main.js","types":"lib/main.d.ts","exports":{".":{"types":"./lib/main.d.ts","require":"./lib/main.js","default":"./lib/main.js"},"./config":"./config.js","./config.js":"./config.js","./lib/env-options":"./lib/env-options.js","./lib/env-options.js":"./lib/env-options.js","./lib/cli-options":"./lib/cli-options.js","./lib/cli-options.js":"./lib/cli-options.js","./package.json":"./package.json"},"scripts":{"dts-check":"tsc --project tests/types/tsconfig.json","lint":"standard","lint-readme":"standard-markdown","pretest":"npm run lint && npm run dts-check","test":"tap tests/*.js --100 -Rspec","prerelease":"npm test","release":"standard-version"},"repository":{"type":"git","url":"git://github.com/motdotla/dotenv.git"},"funding":"https://github.com/motdotla/dotenv?sponsor=1","keywords":["dotenv","env",".env","environment","variables","config","settings"],"readmeFilename":"README.md","license":"BSD-2-Clause","devDependencies":{"@definitelytyped/dtslint":"^0.0.133","@types/node":"^18.11.3","decache":"^4.6.1","sinon":"^14.0.1","standard":"^17.0.0","standard-markdown":"^7.1.0","standard-version":"^9.5.0","tap":"^16.3.0","tar":"^6.1.11","typescript":"^4.8.4"},"engines":{"node":">=12"},"browser":{"fs":false}}')}},i={};function n(e){var r=i[e];if(void 0!==r)return r.exports;var s=i[e]={exports:{}},a=!0;try{t[e](s,s.exports,n),a=!1}finally{a&&delete i[e]}return s.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.ab=__dirname+"/";var s={};(()=>{"use strict";let e,t;n.r(s),n.d(s,{initialEnv:()=>e,updateInitialEnv:()=>d,processEnv:()=>c,resetEnv:()=>p,loadEnvConfig:()=>f});var r=n(147);n.n(r);var i=n(17);n.n(i);var a=n(234);n.n(a);var o=n(383);let l=[],h=[];function d(t){Object.assign(e||{},t)}function u(e){Object.keys(process.env).forEach(t=>{t.startsWith("__NEXT_PRIVATE")||void 0!==e[t]&&""!==e[t]||delete process.env[t]}),Object.entries(e).forEach(([e,t])=>{process.env[e]=t})}function c(t,r,n=console,s=!1,l){var d;if(e||(e=Object.assign({},process.env)),!s&&(process.env.__NEXT_PROCESSED_ENV||0===t.length))return process.env;process.env.__NEXT_PROCESSED_ENV="true";let u=Object.assign({},e),p={};for(let e of t)try{let t={};for(let r of(t.parsed=a.parse(e.contents),(t=(0,o.j)(t)).parsed&&!h.some(t=>t.contents===e.contents&&t.path===e.path)&&(null==l||l(e.path)),Object.keys(t.parsed||{})))void 0===p[r]&&void 0===u[r]&&(p[r]=null===(d=t.parsed)||void 0===d?void 0:d[r])}catch(t){n.error(`Failed to load env from ${i.join(r||"",e.path)}`,t)}return Object.assign(process.env,p)}function p(){e&&u(e)}function f(n,s,a=console,o=!1,d){if(e||(e=Object.assign({},process.env)),t&&!o)return{combinedEnv:t,loadedEnvFiles:l};u(e),h=l,l=[];let p=s?"development":"production";for(let e of[`.env.${p}.local`,"test"!==p&&".env.local",`.env.${p}`,".env"].filter(Boolean)){let t=i.join(n,e);try{if(!r.statSync(t).isFile())continue;let i=r.readFileSync(t,"utf8");l.push({path:e,contents:i})}catch(t){"ENOENT"!==t.code&&a.error(`Failed to load env from ${e}`,t)}}return{combinedEnv:t=c(l,n,a,o,d),loadedEnvFiles:l}}})(),e.exports=s})()},"./dist/compiled/@edge-runtime/cookies/index.js":e=>{"use strict";var t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,n=Object.prototype.hasOwnProperty,s={};function a(e){var t;let r=["path"in e&&e.path&&`Path=${e.path}`,"expires"in e&&(e.expires||0===e.expires)&&`Expires=${("number"==typeof e.expires?new Date(e.expires):e.expires).toUTCString()}`,"maxAge"in e&&"number"==typeof e.maxAge&&`Max-Age=${e.maxAge}`,"domain"in e&&e.domain&&`Domain=${e.domain}`,"secure"in e&&e.secure&&"Secure","httpOnly"in e&&e.httpOnly&&"HttpOnly","sameSite"in e&&e.sameSite&&`SameSite=${e.sameSite}`,"partitioned"in e&&e.partitioned&&"Partitioned","priority"in e&&e.priority&&`Priority=${e.priority}`].filter(Boolean),i=`${e.name}=${encodeURIComponent(null!=(t=e.value)?t:"")}`;return 0===r.length?i:`${i}; ${r.join("; ")}`}function o(e){let t=new Map;for(let r of e.split(/; */)){if(!r)continue;let e=r.indexOf("=");if(-1===e){t.set(r,"true");continue}let[i,n]=[r.slice(0,e),r.slice(e+1)];try{t.set(i,decodeURIComponent(null!=n?n:"true"))}catch{}}return t}function l(e){var t,r;if(!e)return;let[[i,n],...s]=o(e),{domain:a,expires:l,httponly:u,maxage:c,path:p,samesite:f,secure:m,partitioned:g,priority:v}=Object.fromEntries(s.map(([e,t])=>[e.toLowerCase(),t]));return function(e){let t={};for(let r in e)e[r]&&(t[r]=e[r]);return t}({name:i,value:decodeURIComponent(n),domain:a,...l&&{expires:new Date(l)},...u&&{httpOnly:!0},..."string"==typeof c&&{maxAge:Number(c)},path:p,...f&&{sameSite:h.includes(t=(t=f).toLowerCase())?t:void 0},...m&&{secure:!0},...v&&{priority:d.includes(r=(r=v).toLowerCase())?r:void 0},...g&&{partitioned:!0}})}((e,r)=>{for(var i in r)t(e,i,{get:r[i],enumerable:!0})})(s,{RequestCookies:()=>u,ResponseCookies:()=>c,parseCookie:()=>o,parseSetCookie:()=>l,stringifyCookie:()=>a}),e.exports=((e,s,a,o)=>{if(s&&"object"==typeof s||"function"==typeof s)for(let l of i(s))n.call(e,l)||l===a||t(e,l,{get:()=>s[l],enumerable:!(o=r(s,l))||o.enumerable});return e})(t({},"__esModule",{value:!0}),s);var h=["strict","lax","none"],d=["low","medium","high"],u=class{constructor(e){this._parsed=new Map,this._headers=e;let t=e.get("cookie");if(t)for(let[e,r]of o(t))this._parsed.set(e,{name:e,value:r})}[Symbol.iterator](){return this._parsed[Symbol.iterator]()}get size(){return this._parsed.size}get(...e){let t="string"==typeof e[0]?e[0]:e[0].name;return this._parsed.get(t)}getAll(...e){var t;let r=Array.from(this._parsed);if(!e.length)return r.map(([e,t])=>t);let i="string"==typeof e[0]?e[0]:null==(t=e[0])?void 0:t.name;return r.filter(([e])=>e===i).map(([e,t])=>t)}has(e){return this._parsed.has(e)}set(...e){let[t,r]=1===e.length?[e[0].name,e[0].value]:e,i=this._parsed;return i.set(t,{name:t,value:r}),this._headers.set("cookie",Array.from(i).map(([e,t])=>a(t)).join("; ")),this}delete(e){let t=this._parsed,r=Array.isArray(e)?e.map(e=>t.delete(e)):t.delete(e);return this._headers.set("cookie",Array.from(t).map(([e,t])=>a(t)).join("; ")),r}clear(){return this.delete(Array.from(this._parsed.keys())),this}[Symbol.for("edge-runtime.inspect.custom")](){return`RequestCookies ${JSON.stringify(Object.fromEntries(this._parsed))}`}toString(){return[...this._parsed.values()].map(e=>`${e.name}=${encodeURIComponent(e.value)}`).join("; ")}},c=class{constructor(e){var t,r,i;this._parsed=new Map,this._headers=e;let n=null!=(i=null!=(r=null==(t=e.getSetCookie)?void 0:t.call(e))?r:e.get("set-cookie"))?i:[];for(let e of Array.isArray(n)?n:function(e){if(!e)return[];var t,r,i,n,s,a=[],o=0;function l(){for(;o<e.length&&/\s/.test(e.charAt(o));)o+=1;return o<e.length}for(;o<e.length;){for(t=o,s=!1;l();)if(","===(r=e.charAt(o))){for(i=o,o+=1,l(),n=o;o<e.length&&"="!==(r=e.charAt(o))&&";"!==r&&","!==r;)o+=1;o<e.length&&"="===e.charAt(o)?(s=!0,o=n,a.push(e.substring(t,i)),t=o):o=i+1}else o+=1;(!s||o>=e.length)&&a.push(e.substring(t,e.length))}return a}(n)){let t=l(e);t&&this._parsed.set(t.name,t)}}get(...e){let t="string"==typeof e[0]?e[0]:e[0].name;return this._parsed.get(t)}getAll(...e){var t;let r=Array.from(this._parsed.values());if(!e.length)return r;let i="string"==typeof e[0]?e[0]:null==(t=e[0])?void 0:t.name;return r.filter(e=>e.name===i)}has(e){return this._parsed.has(e)}set(...e){let[t,r,i]=1===e.length?[e[0].name,e[0].value,e[0]]:e,n=this._parsed;return n.set(t,function(e={name:"",value:""}){return"number"==typeof e.expires&&(e.expires=new Date(e.expires)),e.maxAge&&(e.expires=new Date(Date.now()+1e3*e.maxAge)),(null===e.path||void 0===e.path)&&(e.path="/"),e}({name:t,value:r,...i})),function(e,t){for(let[,r]of(t.delete("set-cookie"),e)){let e=a(r);t.append("set-cookie",e)}}(n,this._headers),this}delete(...e){let[t,r,i]="string"==typeof e[0]?[e[0]]:[e[0].name,e[0].path,e[0].domain];return this.set({name:t,path:r,domain:i,value:"",expires:new Date(0)})}[Symbol.for("edge-runtime.inspect.custom")](){return`ResponseCookies ${JSON.stringify(Object.fromEntries(this._parsed))}`}toString(){return[...this._parsed.values()].map(a).join("; ")}}},"./dist/compiled/cookie/index.js":e=>{(()=>{"use strict";"undefined"!=typeof __nccwpck_require__&&(__nccwpck_require__.ab=__dirname+"/");var t={};(()=>{/*!
+(()=>{var e={"../next-env/dist/index.js":(e,t,r)=>{(()=>{var t={383:e=>{"use strict";e.exports.j=function(e){let t=e.ignoreProcessEnv?{}:process.env;for(let r in e.parsed){let i=Object.prototype.hasOwnProperty.call(t,r)?t[r]:e.parsed[r];e.parsed[r]=(function e(t,r,i){let n=function(e,t){let r=Array.from(e.matchAll(t));return r.length>0?r.slice(-1)[0].index:-1}(t,/(?!(?<=\\))\$/g);if(-1===n)return t;let s=t.slice(n).match(/((?!(?<=\\))\${?([\w]+)(?::-([^}\\]*))?}?)/);if(null!=s){let[,n,a,o]=s;return e(t.replace(n,r[a]||o||i.parsed[a]||""),r,i)}return t})(i,t,e).replace(/\\\$/g,"$")}for(let r in e.parsed)t[r]=e.parsed[r];return e}},234:(e,t,r)=>{let i=r(147),n=r(17),s=r(37),a=r(113),o=r(803).version,l=/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;function h(e){console.log(`[dotenv@${o}][DEBUG] ${e}`)}function d(e){return e&&e.DOTENV_KEY&&e.DOTENV_KEY.length>0?e.DOTENV_KEY:process.env.DOTENV_KEY&&process.env.DOTENV_KEY.length>0?process.env.DOTENV_KEY:""}function u(e){let t=n.resolve(process.cwd(),".env");return e&&e.path&&e.path.length>0&&(t=e.path),t.endsWith(".vault")?t:`${t}.vault`}let c={configDotenv:function(e){let t=n.resolve(process.cwd(),".env"),r="utf8",a=!!(e&&e.debug);if(e){if(null!=e.path){var o;t="~"===(o=e.path)[0]?n.join(s.homedir(),o.slice(1)):o}null!=e.encoding&&(r=e.encoding)}try{let n=c.parse(i.readFileSync(t,{encoding:r})),s=process.env;return e&&null!=e.processEnv&&(s=e.processEnv),c.populate(s,n,e),{parsed:n}}catch(e){return a&&h(`Failed to load ${t} ${e.message}`),{error:e}}},_configVault:function(e){console.log(`[dotenv@${o}][INFO] Loading env from encrypted .env.vault`);let t=c._parseVault(e),r=process.env;return e&&null!=e.processEnv&&(r=e.processEnv),c.populate(r,t,e),{parsed:t}},_parseVault:function(e){let t;let r=u(e),i=c.configDotenv({path:r});if(!i.parsed)throw Error(`MISSING_DATA: Cannot parse ${r} for an unknown reason`);let n=d(e).split(","),s=n.length;for(let e=0;e<s;e++)try{let r=n[e].trim(),s=function(e,t){let r;try{r=new URL(t)}catch(e){if("ERR_INVALID_URL"===e.code)throw Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:[email protected]/vault/.env.vault?environment=development");throw e}let i=r.password;if(!i)throw Error("INVALID_DOTENV_KEY: Missing key part");let n=r.searchParams.get("environment");if(!n)throw Error("INVALID_DOTENV_KEY: Missing environment part");let s=`DOTENV_VAULT_${n.toUpperCase()}`,a=e.parsed[s];if(!a)throw Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${s} in your .env.vault file.`);return{ciphertext:a,key:i}}(i,r);t=c.decrypt(s.ciphertext,s.key);break}catch(t){if(e+1>=s)throw t}return c.parse(t)},config:function(e){let t=u(e);if(0===d(e).length)return c.configDotenv(e);if(!i.existsSync(t)){var r;return r=`You set DOTENV_KEY but you are missing a .env.vault file at ${t}. Did you forget to build it?`,console.log(`[dotenv@${o}][WARN] ${r}`),c.configDotenv(e)}return c._configVault(e)},decrypt:function(e,t){let r=Buffer.from(t.slice(-64),"hex"),i=Buffer.from(e,"base64"),n=i.slice(0,12),s=i.slice(-16);i=i.slice(12,-16);try{let e=a.createDecipheriv("aes-256-gcm",r,n);return e.setAuthTag(s),`${e.update(i)}${e.final()}`}catch(i){let e=i instanceof RangeError,t="Invalid key length"===i.message,r="Unsupported state or unable to authenticate data"===i.message;if(e||t)throw Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");if(r)throw Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");throw console.error("Error: ",i.code),console.error("Error: ",i.message),i}},parse:function(e){let t;let r={},i=e.toString();for(i=i.replace(/\r\n?/gm,"\n");null!=(t=l.exec(i));){let e=t[1],i=t[2]||"",n=(i=i.trim())[0];i=i.replace(/^(['"`])([\s\S]*)\1$/gm,"$2"),'"'===n&&(i=(i=i.replace(/\\n/g,"\n")).replace(/\\r/g,"\r")),r[e]=i}return r},populate:function(e,t,r={}){let i=!!(r&&r.debug),n=!!(r&&r.override);if("object"!=typeof t)throw Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");for(let r of Object.keys(t))Object.prototype.hasOwnProperty.call(e,r)?(!0===n&&(e[r]=t[r]),i&&(!0===n?h(`"${r}" is already defined and WAS overwritten`):h(`"${r}" is already defined and was NOT overwritten`))):e[r]=t[r]}};e.exports.configDotenv=c.configDotenv,e.exports._configVault=c._configVault,e.exports._parseVault=c._parseVault,e.exports.config=c.config,e.exports.decrypt=c.decrypt,e.exports.parse=c.parse,e.exports.populate=c.populate,e.exports=c},113:e=>{"use strict";e.exports=r("crypto")},147:e=>{"use strict";e.exports=r("fs")},37:e=>{"use strict";e.exports=r("os")},17:e=>{"use strict";e.exports=r("path")},803:e=>{"use strict";e.exports=JSON.parse('{"name":"dotenv","version":"16.3.1","description":"Loads environment variables from .env file","main":"lib/main.js","types":"lib/main.d.ts","exports":{".":{"types":"./lib/main.d.ts","require":"./lib/main.js","default":"./lib/main.js"},"./config":"./config.js","./config.js":"./config.js","./lib/env-options":"./lib/env-options.js","./lib/env-options.js":"./lib/env-options.js","./lib/cli-options":"./lib/cli-options.js","./lib/cli-options.js":"./lib/cli-options.js","./package.json":"./package.json"},"scripts":{"dts-check":"tsc --project tests/types/tsconfig.json","lint":"standard","lint-readme":"standard-markdown","pretest":"npm run lint && npm run dts-check","test":"tap tests/*.js --100 -Rspec","prerelease":"npm test","release":"standard-version"},"repository":{"type":"git","url":"git://github.com/motdotla/dotenv.git"},"funding":"https://github.com/motdotla/dotenv?sponsor=1","keywords":["dotenv","env",".env","environment","variables","config","settings"],"readmeFilename":"README.md","license":"BSD-2-Clause","devDependencies":{"@definitelytyped/dtslint":"^0.0.133","@types/node":"^18.11.3","decache":"^4.6.1","sinon":"^14.0.1","standard":"^17.0.0","standard-markdown":"^7.1.0","standard-version":"^9.5.0","tap":"^16.3.0","tar":"^6.1.11","typescript":"^4.8.4"},"engines":{"node":">=12"},"browser":{"fs":false}}')}},i={};function n(e){var r=i[e];if(void 0!==r)return r.exports;var s=i[e]={exports:{}},a=!0;try{t[e](s,s.exports,n),a=!1}finally{a&&delete i[e]}return s.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.ab=__dirname+"/";var s={};(()=>{"use strict";let e,t,r;n.r(s),n.d(s,{initialEnv:()=>e,updateInitialEnv:()=>u,processEnv:()=>p,resetEnv:()=>f,loadEnvConfig:()=>m});var i=n(147);n.n(i);var a=n(17);n.n(a);var o=n(234);n.n(o);var l=n(383);let h=[],d=[];function u(t){Object.assign(e||{},t)}function c(e){Object.keys(process.env).forEach(t=>{t.startsWith("__NEXT_PRIVATE")||void 0!==e[t]&&""!==e[t]||delete process.env[t]}),Object.entries(e).forEach(([e,t])=>{process.env[e]=t})}function p(t,r,i=console,n=!1,s){var h;if(e||(e=Object.assign({},process.env)),!n&&(process.env.__NEXT_PROCESSED_ENV||0===t.length))return[process.env];process.env.__NEXT_PROCESSED_ENV="true";let u=Object.assign({},e),c={};for(let e of t)try{let t={};for(let r of(t.parsed=o.parse(e.contents),(t=(0,l.j)(t)).parsed&&!d.some(t=>t.contents===e.contents&&t.path===e.path)&&(null==s||s(e.path)),Object.keys(t.parsed||{})))void 0===c[r]&&void 0===u[r]&&(c[r]=null===(h=t.parsed)||void 0===h?void 0:h[r])}catch(t){i.error(`Failed to load env from ${a.join(r||"",e.path)}`,t)}return[Object.assign(process.env,c),c]}function f(){e&&c(e)}function m(n,s,o=console,l=!1,u){if(e||(e=Object.assign({},process.env)),t&&!l)return{combinedEnv:t,parsedEnv:r,loadedEnvFiles:h};c(e),d=h,h=[];let f=s?"development":"production";for(let e of[`.env.${f}.local`,"test"!==f&&".env.local",`.env.${f}`,".env"].filter(Boolean)){let t=a.join(n,e);try{if(!i.statSync(t).isFile())continue;let r=i.readFileSync(t,"utf8");h.push({path:e,contents:r})}catch(t){"ENOENT"!==t.code&&o.error(`Failed to load env from ${e}`,t)}}return[t,r]=p(h,n,o,l,u),{combinedEnv:t,parsedEnv:r,loadedEnvFiles:h}}})(),e.exports=s})()},"./dist/compiled/@edge-runtime/cookies/index.js":e=>{"use strict";var t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,n=Object.prototype.hasOwnProperty,s={};function a(e){var t;let r=["path"in e&&e.path&&`Path=${e.path}`,"expires"in e&&(e.expires||0===e.expires)&&`Expires=${("number"==typeof e.expires?new Date(e.expires):e.expires).toUTCString()}`,"maxAge"in e&&"number"==typeof e.maxAge&&`Max-Age=${e.maxAge}`,"domain"in e&&e.domain&&`Domain=${e.domain}`,"secure"in e&&e.secure&&"Secure","httpOnly"in e&&e.httpOnly&&"HttpOnly","sameSite"in e&&e.sameSite&&`SameSite=${e.sameSite}`,"partitioned"in e&&e.partitioned&&"Partitioned","priority"in e&&e.priority&&`Priority=${e.priority}`].filter(Boolean),i=`${e.name}=${encodeURIComponent(null!=(t=e.value)?t:"")}`;return 0===r.length?i:`${i}; ${r.join("; ")}`}function o(e){let t=new Map;for(let r of e.split(/; */)){if(!r)continue;let e=r.indexOf("=");if(-1===e){t.set(r,"true");continue}let[i,n]=[r.slice(0,e),r.slice(e+1)];try{t.set(i,decodeURIComponent(null!=n?n:"true"))}catch{}}return t}function l(e){var t,r;if(!e)return;let[[i,n],...s]=o(e),{domain:a,expires:l,httponly:u,maxage:c,path:p,samesite:f,secure:m,partitioned:g,priority:v}=Object.fromEntries(s.map(([e,t])=>[e.toLowerCase(),t]));return function(e){let t={};for(let r in e)e[r]&&(t[r]=e[r]);return t}({name:i,value:decodeURIComponent(n),domain:a,...l&&{expires:new Date(l)},...u&&{httpOnly:!0},..."string"==typeof c&&{maxAge:Number(c)},path:p,...f&&{sameSite:h.includes(t=(t=f).toLowerCase())?t:void 0},...m&&{secure:!0},...v&&{priority:d.includes(r=(r=v).toLowerCase())?r:void 0},...g&&{partitioned:!0}})}((e,r)=>{for(var i in r)t(e,i,{get:r[i],enumerable:!0})})(s,{RequestCookies:()=>u,ResponseCookies:()=>c,parseCookie:()=>o,parseSetCookie:()=>l,stringifyCookie:()=>a}),e.exports=((e,s,a,o)=>{if(s&&"object"==typeof s||"function"==typeof s)for(let l of i(s))n.call(e,l)||l===a||t(e,l,{get:()=>s[l],enumerable:!(o=r(s,l))||o.enumerable});return e})(t({},"__esModule",{value:!0}),s);var h=["strict","lax","none"],d=["low","medium","high"],u=class{constructor(e){this._parsed=new Map,this._headers=e;let t=e.get("cookie");if(t)for(let[e,r]of o(t))this._parsed.set(e,{name:e,value:r})}[Symbol.iterator](){return this._parsed[Symbol.iterator]()}get size(){return this._parsed.size}get(...e){let t="string"==typeof e[0]?e[0]:e[0].name;return this._parsed.get(t)}getAll(...e){var t;let r=Array.from(this._parsed);if(!e.length)return r.map(([e,t])=>t);let i="string"==typeof e[0]?e[0]:null==(t=e[0])?void 0:t.name;return r.filter(([e])=>e===i).map(([e,t])=>t)}has(e){return this._parsed.has(e)}set(...e){let[t,r]=1===e.length?[e[0].name,e[0].value]:e,i=this._parsed;return i.set(t,{name:t,value:r}),this._headers.set("cookie",Array.from(i).map(([e,t])=>a(t)).join("; ")),this}delete(e){let t=this._parsed,r=Array.isArray(e)?e.map(e=>t.delete(e)):t.delete(e);return this._headers.set("cookie",Array.from(t).map(([e,t])=>a(t)).join("; ")),r}clear(){return this.delete(Array.from(this._parsed.keys())),this}[Symbol.for("edge-runtime.inspect.custom")](){return`RequestCookies ${JSON.stringify(Object.fromEntries(this._parsed))}`}toString(){return[...this._parsed.values()].map(e=>`${e.name}=${encodeURIComponent(e.value)}`).join("; ")}},c=class{constructor(e){var t,r,i;this._parsed=new Map,this._headers=e;let n=null!=(i=null!=(r=null==(t=e.getSetCookie)?void 0:t.call(e))?r:e.get("set-cookie"))?i:[];for(let e of Array.isArray(n)?n:function(e){if(!e)return[];var t,r,i,n,s,a=[],o=0;function l(){for(;o<e.length&&/\s/.test(e.charAt(o));)o+=1;return o<e.length}for(;o<e.length;){for(t=o,s=!1;l();)if(","===(r=e.charAt(o))){for(i=o,o+=1,l(),n=o;o<e.length&&"="!==(r=e.charAt(o))&&";"!==r&&","!==r;)o+=1;o<e.length&&"="===e.charAt(o)?(s=!0,o=n,a.push(e.substring(t,i)),t=o):o=i+1}else o+=1;(!s||o>=e.length)&&a.push(e.substring(t,e.length))}return a}(n)){let t=l(e);t&&this._parsed.set(t.name,t)}}get(...e){let t="string"==typeof e[0]?e[0]:e[0].name;return this._parsed.get(t)}getAll(...e){var t;let r=Array.from(this._parsed.values());if(!e.length)return r;let i="string"==typeof e[0]?e[0]:null==(t=e[0])?void 0:t.name;return r.filter(e=>e.name===i)}has(e){return this._parsed.has(e)}set(...e){let[t,r,i]=1===e.length?[e[0].name,e[0].value,e[0]]:e,n=this._parsed;return n.set(t,function(e={name:"",value:""}){return"number"==typeof e.expires&&(e.expires=new Date(e.expires)),e.maxAge&&(e.expires=new Date(Date.now()+1e3*e.maxAge)),(null===e.path||void 0===e.path)&&(e.path="/"),e}({name:t,value:r,...i})),function(e,t){for(let[,r]of(t.delete("set-cookie"),e)){let e=a(r);t.append("set-cookie",e)}}(n,this._headers),this}delete(...e){let[t,r,i]="string"==typeof e[0]?[e[0]]:[e[0].name,e[0].path,e[0].domain];return this.set({name:t,path:r,domain:i,value:"",expires:new Date(0)})}[Symbol.for("edge-runtime.inspect.custom")](){return`ResponseCookies ${JSON.stringify(Object.fromEntries(this._parsed))}`}toString(){return[...this._parsed.values()].map(a).join("; ")}}},"./dist/compiled/cookie/index.js":e=>{(()=>{"use strict";"undefined"!=typeof __nccwpck_require__&&(__nccwpck_require__.ab=__dirname+"/");var t={};(()=>{/*!
  * cookie
  * Copyright(c) 2012-2014 Roman Shtylman
  * Copyright(c) 2015 Douglas Christopher Wilson
Commit: 98eddda76cdcaa41aba0e83d381e153ed11e2ec5

ijjk avatar Jun 24 '24 15:06 ijjk

As we now also get typed env variables, will we also get typed page & layout params? (Kind of like NextPage type of the pages directory, but the params attribute is generated based on the path params)

Netail avatar Jun 24 '24 21:06 Netail

Users can benefit the autocomplete and type-safe environment variable experience.

What does "type-safe" mean here? When would type-checking now fail when it passed previously?

It generates from the loaded env files, except production specific files like .env.production.local.

Why focus on dev env variables?

eps1lon avatar Jul 08 '24 11:07 eps1lon

What does "type-safe" mean here? When would type-checking now fail when it passed previously?

type-safe -> *type-checking

Why focus on dev env variables?

It's more like not including production-specific envs as this feature is targetted to the Dev Server, which those like .env.produciton or .env.production.local will not be included.

The main benefit of this feature is autocompletion of the loaded envs during development. This do not include build-time, there for production-specific envs were not included. (It does include .env.local, .env, etc.)

devjiwonchoi avatar Jul 08 '24 12:07 devjiwonchoi

type-safe -> *type-checking

It's not clear to me what's type checked now, that wasn't before.

The main benefit of this feature is autocompletion of the loaded envs during development. This do not include build-time, there for production-specific envs were not included. (It does include .env.local, .env, etc.)

But the code is shipped to production as well. Why wouldn't we not want to offer autocomplete in those cases?

eps1lon avatar Jul 08 '24 15:07 eps1lon

@eps1lon

Type-checking allows autocompletion, and validate the env was loaded (no need assumption like as string or notNull!.

Screenshot 2024-07-09 at 12 43 56 AM

Updated the description as well.

But the code is shipped to production as well.

It is because the Dev Server does not load the production-specific env files (e.g. .env.production). I'm planning to add validation for prod on requiredEnv.

devjiwonchoi avatar Jul 08 '24 15:07 devjiwonchoi

I think we should rename it to type hint for development. Since the PR is not covering production build checking atm, where it could bail the build when types are not matched. Could do it in the following PR.

huozhi avatar Jul 08 '24 17:07 huozhi

But the code is shipped to production as well. Why wouldn't we not want to offer autocomplete in those cases?

For prod build it's a bit more complicated. One environment variable might not be existing during build time, but only provided when running the application (ENV=1 next start).

shuding avatar Jul 10 '24 10:07 shuding

Type-checking allows autocompletion, and validate the env was loaded (no need assumption like as string or notNull!.

That's not what people generally understand under type-checking. We don't check if the specified variable actually exists.

as string wasn't needed before. Every member of process.env is already a string | undefined: Playground Link.

For some reason, in Next.js, you already got a non-nullable string for arbitrary members of process.env.

eps1lon avatar Jul 10 '24 14:07 eps1lon

Since process.env members are string | undefined we needed an extra layer to use it if required argument's type was strictly a string.

const foo = (str: string) => str
foo(process.env.FOO)
    ^^^^^^^^^^^^^^^
// Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
//  Type 'undefined' is not assignable to type 'string'.ts(2345)

foo(process.env.FOO as string)
foo(process.env.FOO!)

I think the word type-check is causing confusion. Will come up with better one.

devjiwonchoi avatar Jul 10 '24 14:07 devjiwonchoi

ProcessEnv type should be string | undefined instead of readonly string, since nodejs native process.env could contain a key but value is undefined

huozhi avatar Jul 10 '24 14:07 huozhi