@adonisjs/env 4.0.0-1 → 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 +46 -42
- package/build/src/loader.d.ts +3 -3
- package/build/src/loader.js +36 -7
- package/build/src/parser.d.ts +3 -1
- package/build/src/parser.js +10 -3
- package/build/src/validator.js +4 -4
- package/package.json +1 -1
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
|
|
28
|
+
const envFiles = await loader.load()
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
The return value is an array of objects with following properties.
|
|
32
32
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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,17 +50,24 @@ 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
|
|
53
|
+
const envFiles = await loader.load()
|
|
53
54
|
|
|
54
|
-
const envParser = new EnvParser(
|
|
55
|
-
|
|
55
|
+
const envParser = new EnvParser(`
|
|
56
|
+
PORT=3000
|
|
57
|
+
HOST=localhost
|
|
58
|
+
`)
|
|
56
59
|
|
|
57
|
-
console.log(envParser.parse()) // {
|
|
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
|
|
|
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.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
new EnvParser(envContents, { ignoreProcessEnv: true })
|
|
69
|
+
```
|
|
70
|
+
|
|
63
71
|
## Validating environment variables
|
|
64
72
|
Once you have the parsed objects, you can optionally validate them against a pre-defined schema. We recommend validation for the following reasons.
|
|
65
73
|
|
|
@@ -86,39 +94,35 @@ validate(process.env)
|
|
|
86
94
|
|
|
87
95
|
Following is a complete example of using the `EnvLoader`, `EnvParser`, and the validator to set up environment variables.
|
|
88
96
|
|
|
97
|
+
> **Note**: Existing `process.env` variables have the top most priority over the variables defined in any of the files.
|
|
98
|
+
|
|
89
99
|
```ts
|
|
90
100
|
import { EnvLoader, EnvParser, Env } from '@adonisjs/env'
|
|
91
101
|
|
|
92
102
|
const lookupPath = new URL('./', import.meta.url)
|
|
93
103
|
const loader = new EnvLoader(lookupPath)
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
* them on "process.env"
|
|
102
|
-
*
|
|
103
|
-
* However, if the value already exists on "process.env", then
|
|
104
|
-
* do not overwrite it, instead update the envValues
|
|
105
|
-
* object.
|
|
106
|
-
*/
|
|
107
|
-
Object.keys(envValues).forEach((key) => {
|
|
108
|
-
if (process.env[key]) {
|
|
109
|
-
envValues[key] = process.env[key]
|
|
110
|
-
} else {
|
|
111
|
-
process.env[key] = envValues[key]
|
|
104
|
+
const envFiles = await loader.load()
|
|
105
|
+
|
|
106
|
+
let envValues = {}
|
|
107
|
+
|
|
108
|
+
envFiles.forEach(({ contents }) => {
|
|
109
|
+
if (!contents.trim()) {
|
|
110
|
+
return
|
|
112
111
|
}
|
|
113
|
-
})
|
|
114
112
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
+
})
|
|
122
126
|
})
|
|
123
127
|
|
|
124
128
|
// Now perform the validation
|
|
@@ -127,7 +131,7 @@ const validate = Env.rules({
|
|
|
127
131
|
HOST: Env.schema.string({ format: 'host' })
|
|
128
132
|
})
|
|
129
133
|
|
|
130
|
-
const validated = validate(
|
|
134
|
+
const validated = validate(envValues)
|
|
131
135
|
const env = new Env(validated)
|
|
132
136
|
|
|
133
137
|
env.get('PORT') // is a number
|
package/build/src/loader.d.ts
CHANGED
package/build/src/loader.js
CHANGED
|
@@ -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 =
|
|
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
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
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
|
}
|
package/build/src/parser.d.ts
CHANGED
package/build/src/parser.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import dotenv from 'dotenv';
|
|
2
2
|
export class EnvParser {
|
|
3
3
|
#envContents;
|
|
4
|
-
|
|
4
|
+
#preferProcessEnv = true;
|
|
5
|
+
constructor(envContents, options) {
|
|
6
|
+
if (options?.ignoreProcessEnv) {
|
|
7
|
+
this.#preferProcessEnv = false;
|
|
8
|
+
}
|
|
5
9
|
this.#envContents = envContents;
|
|
6
10
|
}
|
|
7
11
|
#getValue(key, parsed) {
|
|
12
|
+
if (this.#preferProcessEnv && process.env[key]) {
|
|
13
|
+
return process.env[key];
|
|
14
|
+
}
|
|
8
15
|
if (parsed[key]) {
|
|
9
16
|
return this.#interpolate(parsed[key], parsed);
|
|
10
17
|
}
|
|
11
|
-
return '';
|
|
18
|
+
return process.env[key] || '';
|
|
12
19
|
}
|
|
13
20
|
#interpolateMustache(token, parsed) {
|
|
14
21
|
const closingBrace = token.indexOf('}');
|
|
@@ -53,7 +60,7 @@ export class EnvParser {
|
|
|
53
60
|
parse() {
|
|
54
61
|
const envCollection = dotenv.parse(this.#envContents.trim());
|
|
55
62
|
return Object.keys(envCollection).reduce((result, key) => {
|
|
56
|
-
result[key] = this.#
|
|
63
|
+
result[key] = this.#getValue(key, envCollection);
|
|
57
64
|
return result;
|
|
58
65
|
}, {});
|
|
59
66
|
}
|
package/build/src/validator.js
CHANGED
|
@@ -7,18 +7,18 @@ export class EnvValidator {
|
|
|
7
7
|
this.#error = new InvalidEnvVariablesException();
|
|
8
8
|
}
|
|
9
9
|
validate(values) {
|
|
10
|
-
const
|
|
10
|
+
const cause = [];
|
|
11
11
|
const validated = Object.keys(this.#schema).reduce((result, key) => {
|
|
12
12
|
try {
|
|
13
13
|
result[key] = this.#schema[key](key, values[key]);
|
|
14
14
|
}
|
|
15
15
|
catch (error) {
|
|
16
|
-
|
|
16
|
+
cause.push(`- ${error.message}`);
|
|
17
17
|
}
|
|
18
18
|
return result;
|
|
19
19
|
}, { ...values });
|
|
20
|
-
if (
|
|
21
|
-
this.#error.
|
|
20
|
+
if (cause.length) {
|
|
21
|
+
this.#error.cause = cause.join('\n');
|
|
22
22
|
throw this.#error;
|
|
23
23
|
}
|
|
24
24
|
return validated;
|