Security First
No environment variables will be exposed to clients unless you define it in a .env.example file.
diff --git a/404.html b/404.html new file mode 100644 index 000000000..3a6aca817 --- /dev/null +++ b/404.html @@ -0,0 +1,26 @@ + + +
+ + +export interface PluginOptions {
+ /**
+ * The .env file path to load
+ *
+ * You can out-out this by passing an empty string
+ *
+ * @default ".env"
+ */
+ env?: string;
+
+ /**
+ * The public .env example file path to load
+ */
+ example: string;
+
+ /**
+ * Compile-time: statically replace `import.meta.env.KEY` with `"value"`
+ * Runtime: statically replace `import.meta.env` with a global accessor
+ *
+ * @default
+ * process.env.NODE_ENV === "production" ? "runtime" : "compile-time"
+ */
+ transformMode?: "compile-time" | "runtime";
+}
$ npx import-meta-env --help
Usage: import-meta-env [options]
+
+Populates your environment variables from the system or `.env` file.
+
+Options:
+ -V, --version output the version number
+ -e, --env <path> The .env file path to load. You can out-out this by
+ passing an empty string. (default: ".env")
+ -x, --example <path> The .env example file path to load
+ -o, --output <path...> [deprecated: use --path] The file/dir paths to inject
+ in-place (default:
+ ["dist/**/*",".next/**/*",".nuxt/**/*",".output/**/*","build/**/*"])
+ -p, --path <path...> The file/dir paths to inject in-place (default:
+ ["dist/**/*",".next/**/*",".nuxt/**/*",".output/**/*","build/**/*"])
+ --disposable Do not create backup files and restore from backup
+ files. In local development, disable this option to
+ avoid rebuilding the project when environment
+ variable changes, In production, enable this option
+ to avoid generating unnecessary backup files.
+ -h, --help display help for command
$ npx import-meta-env-flow --help
Usage: import-meta-env-flow [options]
+
+Generate flow type from .env.example
+
+Options:
+ -V, --version output the version number
+ -x, --example <path> The .env example file path to load
+ -o, --outDir <path> Specify an output folder for emitted file. (default:
+ ".")
+ -h, --help display help for command
$ npx import-meta-env-prepare --help
Usage: import-meta-env-prepare [options]
+
+Generate `.env` file from `.env.*` files.
+
+Options:
+ -V, --version output the version number
+ -e, --env <path> .env file path to write (default: ".env")
+ -x, --example <path> .env.example file path to read
+ -p, --path <path...> .env.* file paths to read (default:
+ [".env.local.defaults",".env.local"])
+ -u, --user-environment whether to load user environment variables (i.e.,
+ process.env.*) (default: false)
+ -h, --help display help for command
use serde;
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub struct Config {
+ /**
+ * The .env file path to load, related to current working directory.
+ *
+ * Defaults to ".env"
+ */
+ pub env: Option<String>,
+
+ /**
+ * The public .env example file path to load, related to current working directory.
+ */
+ pub example: String,
+
+ /**
+ * Compile-time: statically replace `import.meta.env.KEY` with `"value"`
+ * Runtime: statically replace `import.meta.env` with a global accessor
+ *
+ * Default:
+ *
+ * if `TransformPluginMetadataContextKind::Env` equals to `"production"`
+ * then `Some(TransformMode::Runtime)`
+ * otherwise `Some(TransformMode::CompileTime)`
+ */
+ #[serde(rename = "transformMode")]
+ pub transform_mode: Option<TransformMode>,
+}
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub enum TransformMode {
+ #[serde(rename = "compile-time")]
+ CompileTime,
+
+ #[serde(rename = "runtime")]
+ Runtime,
+}
$ npx import-meta-env-typescript --help
Usage: import-meta-env-typescript [options]
+
+Generate declaration file from .env.example
+
+Options:
+ -V, --version output the version number
+ -x, --example <path> The .env example file path to load
+ -o, --outDir <path> Specify an output folder for emitted file. (default:
+ ".")
+ -h, --help display help for command
export type Env = Record<string, string>;
+
+export interface PluginOptions {
+ /**
+ * The .env file path to load
+ *
+ * You can out-out this by passing an empty string
+ *
+ * @default ".env"
+ */
+ env?: string;
+
+ /**
+ * The public .env example file path to load
+ */
+ example: string;
+
+ /**
+ * Compile-time: statically replace `import.meta.env.KEY` with `"value"`
+ * Runtime: statically replace `import.meta.env` with a global accessor
+ *
+ * @default
+ *
+ * ```text
+ * vite: if mode is not `"production"` then `"compile-time"`, otherwise `"runtime"`
+ * webpack: if mode is `"development"` or `"none"` then `"compile-time"`, otherwise `"runtime"`
+ * rollup: if `NODE_ENV` is not `"production"` then `"compile-time"`, otherwise `"runtime"`
+ * esbuild: (needs to be set explicitly)
+ * ```
+ */
+ transformMode?: "compile-time" | "runtime";
+}
export interface PluginOptions {
+ /**
+ * The .env file path to load
+ *
+ * You can out-out this by passing an empty string
+ *
+ * @default ".env"
+ */
+ env?: string;
+
+ /**
+ * The public .env example file path to load
+ */
+ example: string;
+
+ /**
+ * Compile-time: statically replace \`import.meta.env.KEY\` with \`"value"\`
+ * Runtime: statically replace \`import.meta.env\` with a global accessor
+ *
+ * @default
+ * process.env.NODE_ENV === "production" ? "runtime" : "compile-time"
+ */
+ transformMode?: "compile-time" | "runtime";
+}
$ npx import-meta-env --help
Usage: import-meta-env [options]
+
+Populates your environment variables from the system or \`.env\` file.
+
+Options:
+ -V, --version output the version number
+ -e, --env <path> The .env file path to load. You can out-out this by
+ passing an empty string. (default: ".env")
+ -x, --example <path> The .env example file path to load
+ -o, --output <path...> [deprecated: use --path] The file/dir paths to inject
+ in-place (default:
+ ["dist/**/*",".next/**/*",".nuxt/**/*",".output/**/*","build/**/*"])
+ -p, --path <path...> The file/dir paths to inject in-place (default:
+ ["dist/**/*",".next/**/*",".nuxt/**/*",".output/**/*","build/**/*"])
+ --disposable Do not create backup files and restore from backup
+ files. In local development, disable this option to
+ avoid rebuilding the project when environment
+ variable changes, In production, enable this option
+ to avoid generating unnecessary backup files.
+ -h, --help display help for command
$ npx import-meta-env-flow --help
Usage: import-meta-env-flow [options]
+
+Generate flow type from .env.example
+
+Options:
+ -V, --version output the version number
+ -x, --example <path> The .env example file path to load
+ -o, --outDir <path> Specify an output folder for emitted file. (default:
+ ".")
+ -h, --help display help for command
$ npx import-meta-env-prepare --help
Usage: import-meta-env-prepare [options]
+
+Generate \`.env\` file from \`.env.*\` files.
+
+Options:
+ -V, --version output the version number
+ -e, --env <path> .env file path to write (default: ".env")
+ -x, --example <path> .env.example file path to read
+ -p, --path <path...> .env.* file paths to read (default:
+ [".env.local.defaults",".env.local"])
+ -u, --user-environment whether to load user environment variables (i.e.,
+ process.env.*) (default: false)
+ -h, --help display help for command
use serde;
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub struct Config {
+ /**
+ * The .env file path to load, related to current working directory.
+ *
+ * Defaults to ".env"
+ */
+ pub env: Option<String>,
+
+ /**
+ * The public .env example file path to load, related to current working directory.
+ */
+ pub example: String,
+
+ /**
+ * Compile-time: statically replace \`import.meta.env.KEY\` with \`"value"\`
+ * Runtime: statically replace \`import.meta.env\` with a global accessor
+ *
+ * Default:
+ *
+ * if \`TransformPluginMetadataContextKind::Env\` equals to \`"production"\`
+ * then \`Some(TransformMode::Runtime)\`
+ * otherwise \`Some(TransformMode::CompileTime)\`
+ */
+ #[serde(rename = "transformMode")]
+ pub transform_mode: Option<TransformMode>,
+}
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub enum TransformMode {
+ #[serde(rename = "compile-time")]
+ CompileTime,
+
+ #[serde(rename = "runtime")]
+ Runtime,
+}
$ npx import-meta-env-typescript --help
Usage: import-meta-env-typescript [options]
+
+Generate declaration file from .env.example
+
+Options:
+ -V, --version output the version number
+ -x, --example <path> The .env example file path to load
+ -o, --outDir <path> Specify an output folder for emitted file. (default:
+ ".")
+ -h, --help display help for command
export type Env = Record<string, string>;
+
+export interface PluginOptions {
+ /**
+ * The .env file path to load
+ *
+ * You can out-out this by passing an empty string
+ *
+ * @default ".env"
+ */
+ env?: string;
+
+ /**
+ * The public .env example file path to load
+ */
+ example: string;
+
+ /**
+ * Compile-time: statically replace \`import.meta.env.KEY\` with \`"value"\`
+ * Runtime: statically replace \`import.meta.env\` with a global accessor
+ *
+ * @default
+ *
+ * \`\`\`text
+ * vite: if mode is not \`"production"\` then \`"compile-time"\`, otherwise \`"runtime"\`
+ * webpack: if mode is \`"development"\` or \`"none"\` then \`"compile-time"\`, otherwise \`"runtime"\`
+ * rollup: if \`NODE_ENV\` is not \`"production"\` then \`"compile-time"\`, otherwise \`"runtime"\`
+ * esbuild: (needs to be set explicitly)
+ * \`\`\`
+ */
+ transformMode?: "compile-time" | "runtime";
+}
export interface PluginOptions {
+ /**
+ * The .env file path to load
+ *
+ * You can out-out this by passing an empty string
+ *
+ * @default ".env"
+ */
+ env?: string;
+
+ /**
+ * The public .env example file path to load
+ */
+ example: string;
+
+ /**
+ * Compile-time: statically replace \`import.meta.env.KEY\` with \`"value"\`
+ * Runtime: statically replace \`import.meta.env\` with a global accessor
+ *
+ * @default
+ * process.env.NODE_ENV === "production" ? "runtime" : "compile-time"
+ */
+ transformMode?: "compile-time" | "runtime";
+}
$ npx import-meta-env --help
Usage: import-meta-env [options]
+
+Populates your environment variables from the system or \`.env\` file.
+
+Options:
+ -V, --version output the version number
+ -e, --env <path> The .env file path to load. You can out-out this by
+ passing an empty string. (default: ".env")
+ -x, --example <path> The .env example file path to load
+ -o, --output <path...> [deprecated: use --path] The file/dir paths to inject
+ in-place (default:
+ ["dist/**/*",".next/**/*",".nuxt/**/*",".output/**/*","build/**/*"])
+ -p, --path <path...> The file/dir paths to inject in-place (default:
+ ["dist/**/*",".next/**/*",".nuxt/**/*",".output/**/*","build/**/*"])
+ --disposable Do not create backup files and restore from backup
+ files. In local development, disable this option to
+ avoid rebuilding the project when environment
+ variable changes, In production, enable this option
+ to avoid generating unnecessary backup files.
+ -h, --help display help for command
$ npx import-meta-env-flow --help
Usage: import-meta-env-flow [options]
+
+Generate flow type from .env.example
+
+Options:
+ -V, --version output the version number
+ -x, --example <path> The .env example file path to load
+ -o, --outDir <path> Specify an output folder for emitted file. (default:
+ ".")
+ -h, --help display help for command
$ npx import-meta-env-prepare --help
Usage: import-meta-env-prepare [options]
+
+Generate \`.env\` file from \`.env.*\` files.
+
+Options:
+ -V, --version output the version number
+ -e, --env <path> .env file path to write (default: ".env")
+ -x, --example <path> .env.example file path to read
+ -p, --path <path...> .env.* file paths to read (default:
+ [".env.local.defaults",".env.local"])
+ -u, --user-environment whether to load user environment variables (i.e.,
+ process.env.*) (default: false)
+ -h, --help display help for command
use serde;
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub struct Config {
+ /**
+ * The .env file path to load, related to current working directory.
+ *
+ * Defaults to ".env"
+ */
+ pub env: Option<String>,
+
+ /**
+ * The public .env example file path to load, related to current working directory.
+ */
+ pub example: String,
+
+ /**
+ * Compile-time: statically replace \`import.meta.env.KEY\` with \`"value"\`
+ * Runtime: statically replace \`import.meta.env\` with a global accessor
+ *
+ * Default:
+ *
+ * if \`TransformPluginMetadataContextKind::Env\` equals to \`"production"\`
+ * then \`Some(TransformMode::Runtime)\`
+ * otherwise \`Some(TransformMode::CompileTime)\`
+ */
+ #[serde(rename = "transformMode")]
+ pub transform_mode: Option<TransformMode>,
+}
+
+#[derive(serde::Serialize, serde::Deserialize)]
+pub enum TransformMode {
+ #[serde(rename = "compile-time")]
+ CompileTime,
+
+ #[serde(rename = "runtime")]
+ Runtime,
+}
$ npx import-meta-env-typescript --help
Usage: import-meta-env-typescript [options]
+
+Generate declaration file from .env.example
+
+Options:
+ -V, --version output the version number
+ -x, --example <path> The .env example file path to load
+ -o, --outDir <path> Specify an output folder for emitted file. (default:
+ ".")
+ -h, --help display help for command
export type Env = Record<string, string>;
+
+export interface PluginOptions {
+ /**
+ * The .env file path to load
+ *
+ * You can out-out this by passing an empty string
+ *
+ * @default ".env"
+ */
+ env?: string;
+
+ /**
+ * The public .env example file path to load
+ */
+ example: string;
+
+ /**
+ * Compile-time: statically replace \`import.meta.env.KEY\` with \`"value"\`
+ * Runtime: statically replace \`import.meta.env\` with a global accessor
+ *
+ * @default
+ *
+ * \`\`\`text
+ * vite: if mode is not \`"production"\` then \`"compile-time"\`, otherwise \`"runtime"\`
+ * webpack: if mode is \`"development"\` or \`"none"\` then \`"compile-time"\`, otherwise \`"runtime"\`
+ * rollup: if \`NODE_ENV\` is not \`"production"\` then \`"compile-time"\`, otherwise \`"runtime"\`
+ * esbuild: (needs to be set explicitly)
+ * \`\`\`
+ */
+ transformMode?: "compile-time" | "runtime";
+}
We sometimes want to store some structural data in an environment variable, in which case we can expose the environment variable in another global variable:
Define environment variables:
# .env.example
+FOO=
# .env
+FOO='["BAR","BAZ"]'
Validate, parse, and expose the environment variables:
// json-env.ts
+// you could use other tools you like:
+import { z } from "zod";
+// you could export instead of expose to global it:
+Object.defineProperty(globalThis, "jsonEnv", {
+ configurable: false,
+ writable: false,
+ value: {
+ FOO: z.array(z.string()).parse(import.meta.env.FOO),
+ },
+});
(Recommended) Import the json-env.ts
at the top of your application, then you can ensure that the rest of your application will have a valid JSON env:
// main.ts
+import "./json-env.ts";
Then you can use it anywhere:
console.log(jsonEnv.FOO[0]);
+// > "BAR"
We sometimes want to store some structural data in an environment variable, in which case we can expose the environment variable in another global variable:
Define environment variables:
# .env.example
+FOO=
# .env
+FOO='["BAR","BAZ"]'
Validate, parse, and expose the environment variables:
// json-env.ts
+// you could use other tools you like:
+import { z } from "zod";
+// you could export instead of expose to global it:
+Object.defineProperty(globalThis, "jsonEnv", {
+ configurable: false,
+ writable: false,
+ value: {
+ FOO: z.array(z.string()).parse(import.meta.env.FOO),
+ },
+});
(Recommended) Import the json-env.ts
at the top of your application, then you can ensure that the rest of your application will have a valid JSON env:
// main.ts
+import "./json-env.ts";
Then you can use it anywhere:
console.log(jsonEnv.FOO[0]);
+// > "BAR"
It's always a good idea to provide all team members with information about required environment variables, including sensitive ones.
To do this, you can create two separate example files and pass the public file to the example
option:
# .env.example.public
+NAME=
# .env.example.private
+SECRET_KEY=
You should use process.env
to access sensitive environment variables in your code, since it's server-side only:
const NAME = import.meta.env.NAME;
+const SECRET_KEY = process.env.SECRET_KEY;
It's always a good idea to provide all team members with information about required environment variables, including sensitive ones.
To do this, you can create two separate example files and pass the public file to the example
option:
# .env.example.public
+NAME=
# .env.example.private
+SECRET_KEY=
You should use process.env
to access sensitive environment variables in your code, since it's server-side only:
const NAME = import.meta.env.NAME;
+const SECRET_KEY = process.env.SECRET_KEY;
Since environment variables are statically injected into the client-side's globalThis.import_meta_env
, you'll need to use the client-side's hook (or similar technique) to obtain these environment variables.
For example, in NEXT.js you can use the useEffect
hook to access globalThis.import_meta_env
:
import { useEffect, useMemo, useState } from "react";
+
+const useImportMetaEnv = () => {
+ const [env, setEnv] = useState({ HELLO: "" });
+
+ useEffect(() => {
+ setEnv({ HELLO: import.meta.env.HELLO });
+ }, []);
+
+ return useMemo(() => env, [env]);
+};
+
+export default function Home() {
+ const env = useImportMetaEnv();
+
+ return <h1>Hello: {env.HELLO}</h1>;
+}
Since environment variables are statically injected into the client-side's globalThis.import_meta_env
, you'll need to use the client-side's hook (or similar technique) to obtain these environment variables.
For example, in NEXT.js you can use the useEffect
hook to access globalThis.import_meta_env
:
import { useEffect, useMemo, useState } from "react";
+
+const useImportMetaEnv = () => {
+ const [env, setEnv] = useState({ HELLO: "" });
+
+ useEffect(() => {
+ setEnv({ HELLO: import.meta.env.HELLO });
+ }, []);
+
+ return useMemo(() => env, [env]);
+};
+
+export default function Home() {
+ const env = useImportMetaEnv();
+
+ return <h1>Hello: {env.HELLO}</h1>;
+}
Environment variables are always strings.
If you need a boolean
value:
$ export DEBUG="1"
const debug = !!import.meta.env.DEBUG; // true
If you need a number
value:
$ export PORT="3000"
const port = parseInt(import.meta.env.PORT, 10); // 3000
Environment variables are always strings.
If you need a boolean
value:
$ export DEBUG="1"
const debug = !!import.meta.env.DEBUG; // true
If you need a number
value:
$ export PORT="3000"
const port = parseInt(import.meta.env.PORT, 10); // 3000
During production, the following environment variables are still statically replaced:
Built-in variables: MODE
, BASE_URL
, PROD
, DEV
, and SSR
.
@vitejs/plugin-legacy variable: LEGACY
.
envPrefix-ed variables.
During production, the following environment variables are still statically replaced:
Built-in variables: MODE
, BASE_URL
, PROD
, DEV
, and SSR
.
@vitejs/plugin-legacy variable: LEGACY
.
envPrefix-ed variables.
Please read the guide for how to use these plugins.
All compile-time transform plugins use the same options:
interface PluginOptions {
+ /**
+ * The .env file path to load
+ *
+ * You can out-out this by passing an empty string
+ *
+ * @default ".env"
+ */
+ env?: string;
+
+ /**
+ * The public .env example file path to load
+ */
+ example: string;
+
+ /**
+ * Compile-time: statically replace \`import.meta.env.KEY\` with \`"value"\`
+ * Runtime: statically replace \`import.meta.env\` with a global accessor
+ *
+ * @default
+ * Generally speaking, \`process.env.NODE_ENV === "production" ? "runtime" : "compile-time"\`
+ */
+ transformMode?: "compile-time" | "runtime";
+}
$ npm i -D @import-meta-env/babel
{
+ "plugins": [
+ [
+ "module:@import-meta-env/babel",
+ pluginOptions
+ ]
+ ]
+}
Related examples: babel, babel-loader, jest
$ npm i -D @import-meta-env/swc
.swcrc:
{
+ "$schema": "https://json.schemastore.org/swcrc",
+ "jsc": {
+ "experimental": {
+ "plugins": [
+ [
+ "@import-meta-env/swc",
+ pluginOptions
+ ]
+ ]
+ }
+ }
+}
Related examples: swc
$ npm i -D @import-meta-env/unplugin
// esbuild.config.js
+const { build } = require("esbuild");
+const importMetaEnv = require("@import-meta-env/unplugin");
+
+build({
+ plugins: [importMetaEnv.esbuild(pluginOptions)],
+});
Related examples: esbuild
// farm.config.ts
+import { defineConfig } from "@farmfe/core";
+import importMetaEnv from "@import-meta-env/unplugin";
+
+export default defineConfig({
+ plugins: [importMetaEnv.farm(pluginOptions)],
+});
Related examples: farm
// rollup.config.js
+import ImportMetaEnvPlugin from "@import-meta-env/unplugin";
+
+export default {
+ plugins: [ImportMetaEnvPlugin.rollup(pluginOptions)],
+};
Related examples: rollup
// vite.config.ts
+import ImportMetaEnvPlugin from "@import-meta-env/unplugin";
+
+export default {
+ plugins: [ImportMetaEnvPlugin.vite(pluginOptions)],
+};
Related examples: vite
// webpack.config.js
+const ImportMetaEnvPlugin = require("@import-meta-env/unplugin");
+
+module.exports = {
+ plugins: [ImportMetaEnvPlugin.webpack(pluginOptions)],
+};
Related examples: webpack
// rspack.config.js
+const ImportMetaEnvPlugin = require("@import-meta-env/unplugin");
+
+module.exports = {
+ plugins: [ImportMetaEnvPlugin.rspack(pluginOptions)],
+};
Related examples: rspack
Currently we support Babel plugin, SWC plugin and Unplugin (an unified plugin system for Vite, Rollup, Webpack, and more) transforms. If your toolchain is not supported, please feel free to file an issue on GitHub.
You can choose one of these or combine multiple plugins, for example if you are using Webpack 5 and Jest:
WARNING
There are some exceptions:
Please read the guide for how to use these plugins.
All compile-time transform plugins use the same options:
interface PluginOptions {
+ /**
+ * The .env file path to load
+ *
+ * You can out-out this by passing an empty string
+ *
+ * @default ".env"
+ */
+ env?: string;
+
+ /**
+ * The public .env example file path to load
+ */
+ example: string;
+
+ /**
+ * Compile-time: statically replace \`import.meta.env.KEY\` with \`"value"\`
+ * Runtime: statically replace \`import.meta.env\` with a global accessor
+ *
+ * @default
+ * Generally speaking, \`process.env.NODE_ENV === "production" ? "runtime" : "compile-time"\`
+ */
+ transformMode?: "compile-time" | "runtime";
+}
$ npm i -D @import-meta-env/babel
{
+ "plugins": [
+ [
+ "module:@import-meta-env/babel",
+ pluginOptions
+ ]
+ ]
+}
Related examples: babel, babel-loader, jest
$ npm i -D @import-meta-env/swc
.swcrc:
{
+ "$schema": "https://json.schemastore.org/swcrc",
+ "jsc": {
+ "experimental": {
+ "plugins": [
+ [
+ "@import-meta-env/swc",
+ pluginOptions
+ ]
+ ]
+ }
+ }
+}
Related examples: swc
$ npm i -D @import-meta-env/unplugin
// esbuild.config.js
+const { build } = require("esbuild");
+const importMetaEnv = require("@import-meta-env/unplugin");
+
+build({
+ plugins: [importMetaEnv.esbuild(pluginOptions)],
+});
Related examples: esbuild
// farm.config.ts
+import { defineConfig } from "@farmfe/core";
+import importMetaEnv from "@import-meta-env/unplugin";
+
+export default defineConfig({
+ plugins: [importMetaEnv.farm(pluginOptions)],
+});
Related examples: farm
// rollup.config.js
+import ImportMetaEnvPlugin from "@import-meta-env/unplugin";
+
+export default {
+ plugins: [ImportMetaEnvPlugin.rollup(pluginOptions)],
+};
Related examples: rollup
// vite.config.ts
+import ImportMetaEnvPlugin from "@import-meta-env/unplugin";
+
+export default {
+ plugins: [ImportMetaEnvPlugin.vite(pluginOptions)],
+};
Related examples: vite
// webpack.config.js
+const ImportMetaEnvPlugin = require("@import-meta-env/unplugin");
+
+module.exports = {
+ plugins: [ImportMetaEnvPlugin.webpack(pluginOptions)],
+};
Related examples: webpack
// rspack.config.js
+const ImportMetaEnvPlugin = require("@import-meta-env/unplugin");
+
+module.exports = {
+ plugins: [ImportMetaEnvPlugin.rspack(pluginOptions)],
+};
Related examples: rspack
Currently we support Babel plugin, SWC plugin and Unplugin (an unified plugin system for Vite, Rollup, Webpack, and more) transforms. If your toolchain is not supported, please feel free to file an issue on GitHub.
You can choose one of these or combine multiple plugins, for example if you are using Webpack 5 and Jest:
WARNING
There are some exceptions:
Import-meta-env
is a startup/runtime environment variable solution for JavaScript, it provides a set of transform plugins and tools designed to help you develop applications according to the 12-factor principle.
TIP
We are excited to introduce runtime-env, a framework-agnostic library that provides a runtime environment variable solution for JavaScript applications. It integrates effortlessly with any framework or build tool.
Discover it today and streamline your development workflow!
In this guide, we will use Webpack as an example, but you can also use other build tools. All supported build tools can be found on the compile-time transform plugins page.
To prevent accidentally leaking environment variables to the client, you need to list public environment variables in an example file (e.g., .env.example
):
# .env.example
+NAME=
See the .env.example file
section for details.
Obtain the environment variable:
// src/index.js
+- const name = process.env.NAME
++ const name = import.meta.env.NAME
+document.querySelector("body").innerHTML = \`<h1>Hello, \${name}</h1>\`;
See the syntax section for details.
Install dotenv in order for Import-meta-env packages to work:
$ npm i -D dotenv
During development and testing:
Define environment variables:
# .env
+NAME=development
See the .env
file section for details.
Install and setup compile-time transform plugins.
$ npm i -D @import-meta-env/unplugin
// webpack.config.js
+module.exports = {
+ plugins: [
++ require("@import-meta-env/unplugin").webpack({
++ example: ".env.example",
++ env: ".env",
++ transformMode: "compile-time",
++ }),
+ ],
+};
Transform it:
$ npx webpack --mode=development
// dist/main.js
+- const name = import.meta.env.NAME
++ const name = "developemnt"
+// ...
During production:
Install and setup compile-time transform plugins.
$ npm i -D @import-meta-env/unplugin
// webpack.config.js
+module.exports = {
+ plugins: [
+ require("@import-meta-env/unplugin").webpack({
+ example: ".env.example",
+ env: ".env",
+
+ // If you are using popular packagers such as Webpack and Vite,
+ // @import-meta-env/unplugin will automatically switch the \`transformMode\` for you, you don't have to explicitly define it:
+ // - for development mode, \`transformMode\` will be \`"compile-time"\`
+ // - for production mode, \`transformMode\` will be \`"runtime"\`
+ transformMode: undefined,
+
+ // Otherwise, you need to set \`transformMode\` according to your needs, for example:
+ transformMode: process.env.NODE_ENV === "development" ? "compile-time" : "runtime",
+
+- transformMode: "compile-time",
++ transformMode: "runtime",
+ }),
+ ],
+};
Transform it:
$ npx webpack --mode=production
// dist/main.js
+- const name = import.meta.env.NAME
++ const name = globalThis.import_meta_env.NAME
+// ...
In order to obtain environment variables from globalThis.import_meta_env
, you also need to add a special expression before your entry:
<!-- public/index.html -->
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
++ <script>
++ globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
++ </script>
+ </head>
+</html>
See the special expression section for details.
Define environment variables:
In the real world, we might define environment variables in the cloud, for example: Google Cloud Run environment variables.
Here, we simulate this by defining our environment variables in the system:
export NAME=production
Install runtime transform tool.
$ npm i -D @import-meta-env/cli
Transform it again:
npx import-meta-env -x .env.example -p dist/index.html
<!-- dist/index.html -->
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <script>
+- globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
++ globalThis.import_meta_env = JSON.parse('{"NAME":"production"}');
+ </script>
+ <script defer="defer" src="main.js"></script>
+ </head>
+</html>
See the transform section for details.
You can find the corresponding working example here, and all examples here.
By default, Import-meta-env
will load environment variables from your system and the .env
file (you can change or disable this using env
option):
$ export NAME=world
# .env
+NAME=world
WARNING
You should gitignore this file because these environment variables are usually used for local development purposes, while your production environment variables are usually stored in your cloud, such as GCP or AWS, and will be loaded from the system.
TIP
It is common to temporarily change environment variables for debugging or other purposes. You can use @import-meta-env/prepare to automatically generate the final .env
file from .env.local
, .env.local.defaults
, etc. files.
To prevent accidentally leaking environment variables to the client, only keys listed in an example file (e.g., .env.example
) are exposed.
For example, if you have these config:
$ export NAME=world
+$ export SECRET_KEY=****
# .env.example
+NAME=this-value-can-be-anything
then only NAME
will be exposed to your client source code, but SECRET_KEY
will not.
INFO
For security reason, the example
option does not have default value, you have to explicitly define it.
WARNING
Since any environment variables exposed to your source code will end up in your client bundle, the keys listed in .env.example
should not contain any sensitive information.
Import-meta-env
exposes your environment variables on special environment variable expressions:
import.meta.env.FOO;
+import.meta.env.BAR;
+// ...
INFO
Since these environment variables expression are statically transformed. It is therefore necessary to always reference them using the full static string.
In other words, the following are invalid:
import.meta.env
import.meta.env["FOO"]
Import-meta-env
provides two sets of transform:
In development and testing, you use the compile-time transform plugins to replace the environment variable expressions with environment variable strings.
In production, you need to use the compile-time transform plugins to replace the environment variable expressions with global accessors (in order to access to a special object), and then use runtime transform tool to inject environment variables to the special expression.
Since compile-time transform plugins will be used in two scenarios, it have two transform modes:
compile-time
: statically replace import.meta.env.KEY
with "value"
runtime
: statically replace import.meta.env
with a global accessorUsually, you don't need to define it explicitly, because Import-meta-env
determines it automatically based on your environment variables (e.g., process.env.NODE_ENV
). See API for details.
In order to inject environment variables in production, you also need to add a special expression in your app:
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
This expression will be replaced with environment variables by using runtime transform tool as follows:
globalThis.import_meta_env = JSON.parse('{"NAME":"world"}');
We encourage you to put this special expression in your index.html
because the Cache-Control
header is usually set to no-cache
when requesting index.html
:
<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ ...
+ </head>
+ <body>
+ ...
+ <script>
+ globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
+ </script>
+ <script src="src/index.js"></script>
+ </body>
+</html>
WARNING
The value of the expression will be statically replaced, the following will not work:
globalThis.import_meta_env = JSON.parse(
+ '"import_meta_env_placeholder"'
+);
+
+globalThis.import_meta_env = JSON.parse('"import_meta_env" + "_placeholder"');
+
+const placeholder = '"import_meta_env_placeholder"';
+globalThis.import_meta_env = JSON.parse(placeholder);
INFO
This expression should be placed before or at the top of your entry script, otherwise your code will end up with a TypeError
:
Uncaught TypeError: Cannot read properties of undefined (reading 'NAME')
+ at ...
Import-meta-env
is a startup/runtime environment variable solution for JavaScript, it provides a set of transform plugins and tools designed to help you develop applications according to the 12-factor principle.
TIP
We are excited to introduce runtime-env, a framework-agnostic library that provides a runtime environment variable solution for JavaScript applications. It integrates effortlessly with any framework or build tool.
Discover it today and streamline your development workflow!
In this guide, we will use Webpack as an example, but you can also use other build tools. All supported build tools can be found on the compile-time transform plugins page.
To prevent accidentally leaking environment variables to the client, you need to list public environment variables in an example file (e.g., .env.example
):
# .env.example
+NAME=
See the .env.example file
section for details.
Obtain the environment variable:
// src/index.js
+- const name = process.env.NAME
++ const name = import.meta.env.NAME
+document.querySelector("body").innerHTML = \`<h1>Hello, \${name}</h1>\`;
See the syntax section for details.
Install dotenv in order for Import-meta-env packages to work:
$ npm i -D dotenv
During development and testing:
Define environment variables:
# .env
+NAME=development
See the .env
file section for details.
Install and setup compile-time transform plugins.
$ npm i -D @import-meta-env/unplugin
// webpack.config.js
+module.exports = {
+ plugins: [
++ require("@import-meta-env/unplugin").webpack({
++ example: ".env.example",
++ env: ".env",
++ transformMode: "compile-time",
++ }),
+ ],
+};
Transform it:
$ npx webpack --mode=development
// dist/main.js
+- const name = import.meta.env.NAME
++ const name = "developemnt"
+// ...
During production:
Install and setup compile-time transform plugins.
$ npm i -D @import-meta-env/unplugin
// webpack.config.js
+module.exports = {
+ plugins: [
+ require("@import-meta-env/unplugin").webpack({
+ example: ".env.example",
+ env: ".env",
+
+ // If you are using popular packagers such as Webpack and Vite,
+ // @import-meta-env/unplugin will automatically switch the \`transformMode\` for you, you don't have to explicitly define it:
+ // - for development mode, \`transformMode\` will be \`"compile-time"\`
+ // - for production mode, \`transformMode\` will be \`"runtime"\`
+ transformMode: undefined,
+
+ // Otherwise, you need to set \`transformMode\` according to your needs, for example:
+ transformMode: process.env.NODE_ENV === "development" ? "compile-time" : "runtime",
+
+- transformMode: "compile-time",
++ transformMode: "runtime",
+ }),
+ ],
+};
Transform it:
$ npx webpack --mode=production
// dist/main.js
+- const name = import.meta.env.NAME
++ const name = globalThis.import_meta_env.NAME
+// ...
In order to obtain environment variables from globalThis.import_meta_env
, you also need to add a special expression before your entry:
<!-- public/index.html -->
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
++ <script>
++ globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
++ </script>
+ </head>
+</html>
See the special expression section for details.
Define environment variables:
In the real world, we might define environment variables in the cloud, for example: Google Cloud Run environment variables.
Here, we simulate this by defining our environment variables in the system:
export NAME=production
Install runtime transform tool.
$ npm i -D @import-meta-env/cli
Transform it again:
npx import-meta-env -x .env.example -p dist/index.html
<!-- dist/index.html -->
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <script>
+- globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
++ globalThis.import_meta_env = JSON.parse('{"NAME":"production"}');
+ </script>
+ <script defer="defer" src="main.js"></script>
+ </head>
+</html>
See the transform section for details.
You can find the corresponding working example here, and all examples here.
By default, Import-meta-env
will load environment variables from your system and the .env
file (you can change or disable this using env
option):
$ export NAME=world
# .env
+NAME=world
WARNING
You should gitignore this file because these environment variables are usually used for local development purposes, while your production environment variables are usually stored in your cloud, such as GCP or AWS, and will be loaded from the system.
TIP
It is common to temporarily change environment variables for debugging or other purposes. You can use @import-meta-env/prepare to automatically generate the final .env
file from .env.local
, .env.local.defaults
, etc. files.
To prevent accidentally leaking environment variables to the client, only keys listed in an example file (e.g., .env.example
) are exposed.
For example, if you have these config:
$ export NAME=world
+$ export SECRET_KEY=****
# .env.example
+NAME=this-value-can-be-anything
then only NAME
will be exposed to your client source code, but SECRET_KEY
will not.
INFO
For security reason, the example
option does not have default value, you have to explicitly define it.
WARNING
Since any environment variables exposed to your source code will end up in your client bundle, the keys listed in .env.example
should not contain any sensitive information.
Import-meta-env
exposes your environment variables on special environment variable expressions:
import.meta.env.FOO;
+import.meta.env.BAR;
+// ...
INFO
Since these environment variables expression are statically transformed. It is therefore necessary to always reference them using the full static string.
In other words, the following are invalid:
import.meta.env
import.meta.env["FOO"]
Import-meta-env
provides two sets of transform:
In development and testing, you use the compile-time transform plugins to replace the environment variable expressions with environment variable strings.
In production, you need to use the compile-time transform plugins to replace the environment variable expressions with global accessors (in order to access to a special object), and then use runtime transform tool to inject environment variables to the special expression.
Since compile-time transform plugins will be used in two scenarios, it have two transform modes:
compile-time
: statically replace import.meta.env.KEY
with "value"
runtime
: statically replace import.meta.env
with a global accessorUsually, you don't need to define it explicitly, because Import-meta-env
determines it automatically based on your environment variables (e.g., process.env.NODE_ENV
). See API for details.
In order to inject environment variables in production, you also need to add a special expression in your app:
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
This expression will be replaced with environment variables by using runtime transform tool as follows:
globalThis.import_meta_env = JSON.parse('{"NAME":"world"}');
We encourage you to put this special expression in your index.html
because the Cache-Control
header is usually set to no-cache
when requesting index.html
:
<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ ...
+ </head>
+ <body>
+ ...
+ <script>
+ globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
+ </script>
+ <script src="src/index.js"></script>
+ </body>
+</html>
WARNING
The value of the expression will be statically replaced, the following will not work:
globalThis.import_meta_env = JSON.parse(
+ '"import_meta_env_placeholder"'
+);
+
+globalThis.import_meta_env = JSON.parse('"import_meta_env" + "_placeholder"');
+
+const placeholder = '"import_meta_env_placeholder"';
+globalThis.import_meta_env = JSON.parse(placeholder);
INFO
This expression should be placed before or at the top of your entry script, otherwise your code will end up with a TypeError
:
Uncaught TypeError: Cannot read properties of undefined (reading 'NAME')
+ at ...
Please read the guide for how to use this tool.
$ npm i -D @import-meta-env/cli
$ npx import-meta-env -x .env.example
Related examples: docker
TIP
By default, import-meta-env will automatically replace all files in the common bundle output directories
You can override this behavior by passing the -p, --path <path...>
option.
For more information, see API.
The Single executable applications feature allows the distribution of a Node.js application conveniently to a system that does not have Node.js installed, you could use it to package the import-meta-env
script into a node
binary.
Please read the guide for how to use this tool.
$ npm i -D @import-meta-env/cli
$ npx import-meta-env -x .env.example
Related examples: docker
TIP
By default, import-meta-env will automatically replace all files in the common bundle output directories
You can override this behavior by passing the -p, --path <path...>
option.
For more information, see API.
The Single executable applications feature allows the distribution of a Node.js application conveniently to a system that does not have Node.js installed, you could use it to package the import-meta-env
script into a node
binary.
Generate Flow type from .env.example
.
$ npm i -D @import-meta-env/flow
$ npx import-meta-env-flow -x .env.example
By default, when running above command, the CLI will create an import-meta-env.js
file in your project root:
// import-meta-env.js
+// Generated by '@import-meta-env/flow'
+
+declare type Import$Meta = {
+ +env: $ReadOnly<{
+ NAME: string,
+ }>,
+};
Generate Flow type from .env.example
.
$ npm i -D @import-meta-env/flow
$ npx import-meta-env-flow -x .env.example
By default, when running above command, the CLI will create an import-meta-env.js
file in your project root:
// import-meta-env.js
+// Generated by '@import-meta-env/flow'
+
+declare type Import$Meta = {
+ +env: $ReadOnly<{
+ NAME: string,
+ }>,
+};
Generate .env
file from .env.*
files.
$ npm i -D @import-meta-env/prepare
$ npx import-meta-env-prepare -x .env.example
By default, when running above command, the CLI will load environment variables from .env.local.defaults
and .env.local
, merge both, then create an .env
file in your project root:
# .env
+# Generated by '@import-meta-env/prepare'
+
+NAME=world
Generate .env
file from .env.*
files.
$ npm i -D @import-meta-env/prepare
$ npx import-meta-env-prepare -x .env.example
By default, when running above command, the CLI will load environment variables from .env.local.defaults
and .env.local
, merge both, then create an .env
file in your project root:
# .env
+# Generated by '@import-meta-env/prepare'
+
+NAME=world
Generate TypeScript .d.ts from .env.example
.
$ npm i -D @import-meta-env/typescript
$ npx import-meta-env-typescript -x .env.example
By default, when running above command, the CLI will create an import-meta-env.d.ts
file in your project root:
// import-meta-env.d.ts
+// Generated by '@import-meta-env/typescript'
+
+interface ImportMetaEnv {
+ readonly NAME: string;
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv;
+}
Generate TypeScript .d.ts from .env.example
.
$ npm i -D @import-meta-env/typescript
$ npx import-meta-env-typescript -x .env.example
By default, when running above command, the CLI will create an import-meta-env.d.ts
file in your project root:
// import-meta-env.d.ts
+// Generated by '@import-meta-env/typescript'
+
+interface ImportMetaEnv {
+ readonly NAME: string;
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv;
+}
In order to support programmatically injecting script, the placeholder is changed:
x
<script id="import_meta_env"></script>
✅
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
The entire environment variables accessing are dropped, use dedicated environment variables instead:
x
const env = import.meta.env;
+env.HELLO;
✅
import.meta.env.HELLO;
The computed environment variables are dropped, use static environment variables instead:
x
import.meta.env["HELLO"];
✅
import.meta.env.HELLO;
In order to inject environment variables at runtime, you need to add a special script tag to your index.html
:
<script id="import_meta_env"></script>
In order to support programmatically injecting script, the placeholder is changed:
x
<script id="import_meta_env"></script>
✅
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
The entire environment variables accessing are dropped, use dedicated environment variables instead:
x
const env = import.meta.env;
+env.HELLO;
✅
import.meta.env.HELLO;
The computed environment variables are dropped, use static environment variables instead:
x
import.meta.env["HELLO"];
✅
import.meta.env.HELLO;
In order to inject environment variables at runtime, you need to add a special script tag to your index.html
:
<script id="import_meta_env"></script>
# .env.example
+HELLO=
# .env
+HELLO=world
// src/index.js
+const name = import.meta.env.NAME;
+document.querySelector("body").innerHTML = \`<h1>Hello, \${name}</h1>\`;
<!-- public/index.html -->
+<script>
+ globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
+</script>
$ npx webpack --mode=development
$ npx webpack --mode=production
$ npx import-meta-env -x .env.example -p dist/index.html
# .env.example
+HELLO=
# .env
+HELLO=world
// src/index.js
+const name = import.meta.env.NAME;
+document.querySelector("body").innerHTML = \`<h1>Hello, \${name}</h1>\`;
<!-- public/index.html -->
+<script>
+ globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
+</script>
$ npx webpack --mode=development
$ npx webpack --mode=production
$ npx import-meta-env -x .env.example -p dist/index.html
Startup/Runtime environment variables solution for JavaScript
Build once, deploy anywhere. Import-meta-env helps to developing applications following the 12-factor principles.
In order to support programmatically injecting script, the placeholder is changed:
x
<script id="import_meta_env"></script>
✅
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
The entire environment variables accessing are dropped, use dedicated environment variables instead:
x
const env = import.meta.env;
+env.HELLO;
✅
import.meta.env.HELLO;
The computed environment variables are dropped, use static environment variables instead:
x
import.meta.env["HELLO"];
✅
import.meta.env.HELLO;
In order to inject environment variables at runtime, you need to add a special script tag to your index.html
:
<script id="import_meta_env"></script>
# .env.example
+HELLO=
# .env
+HELLO=world
// src/index.js
+const name = import.meta.env.NAME;
+document.querySelector("body").innerHTML = `<h1>Hello, ${name}</h1>`;
<!-- public/index.html -->
+<script>
+ globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
+</script>
$ npx webpack --mode=development
$ npx webpack --mode=production
$ npx import-meta-env -x .env.example -p dist/index.html