feat: type check for `env`
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
Tests Passed
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
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)
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?
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.)
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
Type-checking allows autocompletion, and validate the env was loaded (no need assumption like as string or notNull!.
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.
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.
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).
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.
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.
ProcessEnv type should be string | undefined instead of readonly string, since nodejs native process.env could contain a key but value is undefined