@adonisjs/env 4.0.0-2 → 4.0.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,21 +25,22 @@ import { EnvLoader } from '@adonisjs/env'
25
25
  const lookupPath = new URL('./', import.meta.url)
26
26
  const loader = new EnvLoader(lookupPath)
27
27
 
28
- const { envContents, currentEnvContents } = await loader.load()
28
+ const envFiles = await loader.load()
29
29
  ```
30
30
 
31
- ### `envContents`
31
+ The return value is an array of objects with following properties.
32
32
 
33
- - The `envContents` is read from the `.env` file from the root of the `lookupPath`.
34
- - No exceptions are raised if the `.env` file is missing. In brief, it is optional to have a `.env` file.
35
- - If the `ENV_PATH` environment variable is set, the loader will use that instead of the `.env` file. This allows overwriting the location of the dot-env file.
33
+ - `path`: The path to the loaded dot-env file.
34
+ - `contents`: The contents of the file.
36
35
 
36
+ Following is the list of loaded files. The array is ordered by the priority of the files. The first file has the highest priority and must override the variables from the last file.
37
37
 
38
- ### `currentEnvContents`
39
-
40
- - The `currentEnvContents` contents are read from the `.env.[NODE_ENV]` file.
41
- - If the current `NODE_ENV = 'development'`, then the contents of this variable will be from the `.env.development` file and so on.
42
- - The contents of this file should take precendence over the `.env` file.
38
+ | Priority | File name | Environment | Should I `.gitignore` it | Notes |
39
+ |----------|-----------|-------------|--------------------------|-------|
40
+ | 1st | `.env.[NODE_ENV].local` | Current environment | Yes | Loaded when `NODE_ENV` is set |
41
+ | 2nd | `.env.local` | All | Yes | Loaded in all the environments except `test` or `testing` environments |
42
+ | 3rd | `.env.[NODE_ENV]` | Current environment | No | Loaded when `NODE_ENV` is set |
43
+ | 4th | `.env` | All | Depends | Loaded in all the environments. You should `.gitignore` it when storing secrets in this file |
43
44
 
44
45
  ## EnvParser
45
46
  The `EnvParser` class is responsible for parsing the contents of the `.env` file(s) and converting them into an object.
@@ -49,21 +50,22 @@ import { EnvLoader, EnvParser } from '@adonisjs/env'
49
50
 
50
51
  const lookupPath = new URL('./', import.meta.url)
51
52
  const loader = new EnvLoader(lookupPath)
52
- const { envContents, currentEnvContents } = await loader.load()
53
+ const envFiles = await loader.load()
53
54
 
54
- const envParser = new EnvParser(envContents)
55
- const currentEnvParser = new EnvParser(currentEnvContents)
55
+ const envParser = new EnvParser(`
56
+ PORT=3000
57
+ HOST=localhost
58
+ `)
56
59
 
57
- console.log(envParser.parse()) // { key: value }
58
- console.log(currentEnvParser.parse()) // { key: value }
60
+ console.log(envParser.parse()) // { PORT: '3000', HOST: 'localhost' }
59
61
  ```
60
62
 
61
63
  The return value of `parser.parse` is an object with key-value pair. The parser also has support for interpolation.
62
64
 
63
- You can also instruct the parser to prefer existing `process.env` values when they exist. When `preferProcessEnv` is set to `true`, the value from the env contents will be discarded in favor of existing `process.env` value.
65
+ By default, the parser prefers existing `process.env` values when they exist. However, you can instruct the parser to ignore existing `process.env` files as follows.
64
66
 
65
67
  ```ts
66
- new EnvParser(envContents, { preferProcessEnv: true })
68
+ new EnvParser(envContents, { ignoreProcessEnv: true })
67
69
  ```
68
70
 
69
71
  ## Validating environment variables
@@ -92,39 +94,35 @@ validate(process.env)
92
94
 
93
95
  Following is a complete example of using the `EnvLoader`, `EnvParser`, and the validator to set up environment variables.
94
96
 
97
+ > **Note**: Existing `process.env` variables have the top most priority over the variables defined in any of the files.
98
+
95
99
  ```ts
96
100
  import { EnvLoader, EnvParser, Env } from '@adonisjs/env'
97
101
 
98
102
  const lookupPath = new URL('./', import.meta.url)
99
103
  const loader = new EnvLoader(lookupPath)
100
- const { envContents, currentEnvContents } = await loader.load()
101
-
102
- /**
103
- * Loop over all the current env parsed values and let
104
- * them take precedence over the existing process.env
105
- * values.
106
- */
107
- const currentEnvValues = new EnvParser(currentEnvContents).parse()
108
- Object.keys(currentEnvValues).forEach((key) => {
109
- process.env[key] = currentEnvValues[key]
110
- })
104
+ const envFiles = await loader.load()
105
+
106
+ let envValues = {}
111
107
 
112
- const envValues = new EnvParser(envContents, { preferProcessEnv: true }).parse()
113
-
114
- /**
115
- * Loop over all the parsed env values and set
116
- * them on "process.env"
117
- *
118
- * However, if the value already exists on "process.env", then
119
- * do not overwrite it, instead update the envValues
120
- * object.
121
- */
122
- Object.keys(envValues).forEach((key) => {
123
- if (process.env[key]) {
124
- envValues[key] = process.env[key]
125
- } else {
126
- process.env[key] = envValues[key]
108
+ envFiles.forEach(({ contents }) => {
109
+ if (!contents.trim()) {
110
+ return
127
111
  }
112
+
113
+ const values = new EnvParser(contents).parse()
114
+ Object.keys(values).forEach((key) => {
115
+ let value = process.env[key]
116
+
117
+ if (!value) {
118
+ value = values[key]
119
+ process.env[key] = values[key]
120
+ }
121
+
122
+ if (!envValues[key]) {
123
+ envValues[key] = value
124
+ }
125
+ })
128
126
  })
129
127
 
130
128
  // Now perform the validation
@@ -133,7 +131,7 @@ const validate = Env.rules({
133
131
  HOST: Env.schema.string({ format: 'host' })
134
132
  })
135
133
 
136
- const validated = validate({ ...envValues, ...currentEnvValues })
134
+ const validated = validate(envValues)
137
135
  const env = new Env(validated)
138
136
 
139
137
  env.get('PORT') // is a number
@@ -3,7 +3,7 @@ export declare class EnvLoader {
3
3
  #private;
4
4
  constructor(appRoot: string | URL);
5
5
  load(): Promise<{
6
- envContents: string;
7
- currentEnvContents: string;
8
- }>;
6
+ path: string;
7
+ contents: string;
8
+ }[]>;
9
9
  }
@@ -7,7 +7,7 @@ export class EnvLoader {
7
7
  constructor(appRoot) {
8
8
  this.#appRoot = typeof appRoot === 'string' ? appRoot : fileURLToPath(appRoot);
9
9
  }
10
- async #loadFile(filePath, optional = false) {
10
+ async #loadFile(filePath, optional = true) {
11
11
  try {
12
12
  return await readFile(filePath, 'utf-8');
13
13
  }
@@ -24,11 +24,40 @@ export class EnvLoader {
24
24
  }
25
25
  }
26
26
  async load() {
27
- const envFile = process.env.ENV_PATH || '.env';
28
- const envPath = isAbsolute(envFile) ? envFile : join(this.#appRoot, envFile);
29
- const envContents = await this.#loadFile(envPath, !process.env.ENV_PATH);
30
- const currentEnvPath = join(this.#appRoot, `.env.${process.env.NODE_ENV}`);
31
- const currentEnvContents = await this.#loadFile(currentEnvPath, true);
32
- return { envContents, currentEnvContents };
27
+ const ENV_PATH = process.env.ENV_PATH;
28
+ const NODE_ENV = process.env.NODE_ENV;
29
+ const envFiles = [];
30
+ const baseEnvPath = ENV_PATH
31
+ ? isAbsolute(ENV_PATH)
32
+ ? ENV_PATH
33
+ : join(this.#appRoot, ENV_PATH)
34
+ : this.#appRoot;
35
+ if (NODE_ENV) {
36
+ const nodeEnvLocalFile = join(baseEnvPath, `.env.${process.env.NODE_ENV}.local`);
37
+ envFiles.push({
38
+ path: nodeEnvLocalFile,
39
+ contents: await this.#loadFile(nodeEnvLocalFile),
40
+ });
41
+ }
42
+ if (!NODE_ENV || !['test', 'testing'].includes(NODE_ENV)) {
43
+ const envLocalFile = join(baseEnvPath, '.env.local');
44
+ envFiles.push({
45
+ path: envLocalFile,
46
+ contents: await this.#loadFile(envLocalFile),
47
+ });
48
+ }
49
+ if (NODE_ENV) {
50
+ const nodeEnvFile = join(baseEnvPath, `.env.${process.env.NODE_ENV}`);
51
+ envFiles.push({
52
+ path: nodeEnvFile,
53
+ contents: await this.#loadFile(nodeEnvFile),
54
+ });
55
+ }
56
+ const envFile = join(baseEnvPath, '.env');
57
+ envFiles.push({
58
+ path: envFile,
59
+ contents: await this.#loadFile(envFile),
60
+ });
61
+ return envFiles;
33
62
  }
34
63
  }
@@ -2,7 +2,7 @@ import { DotenvParseOutput } from 'dotenv';
2
2
  export declare class EnvParser {
3
3
  #private;
4
4
  constructor(envContents: string, options?: {
5
- preferProcessEnv: boolean;
5
+ ignoreProcessEnv: boolean;
6
6
  });
7
7
  parse(): DotenvParseOutput;
8
8
  }
@@ -1,10 +1,10 @@
1
1
  import dotenv from 'dotenv';
2
2
  export class EnvParser {
3
3
  #envContents;
4
- #preferProcessEnv = false;
4
+ #preferProcessEnv = true;
5
5
  constructor(envContents, options) {
6
- if (options?.preferProcessEnv) {
7
- this.#preferProcessEnv = true;
6
+ if (options?.ignoreProcessEnv) {
7
+ this.#preferProcessEnv = false;
8
8
  }
9
9
  this.#envContents = envContents;
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonisjs/env",
3
- "version": "4.0.0-2",
3
+ "version": "4.0.1-0",
4
4
  "description": "Environment variable manager for Node.js",
5
5
  "main": "build/index.js",
6
6
  "type": "module",