@adonisjs/env 6.0.0 → 6.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
@@ -12,8 +12,6 @@ Install the package from the npm packages registry as follows.
12
12
 
13
13
  ```sh
14
14
  npm i @adonisjs/env
15
-
16
- yarn add @adonisjs/env
17
15
  ```
18
16
 
19
17
  ## EnvLoader
@@ -52,7 +50,7 @@ const envParser = new EnvParser(`
52
50
  HOST=localhost
53
51
  `)
54
52
 
55
- console.log(envParser.parse()) // { PORT: '3000', HOST: 'localhost' }
53
+ console.log(await envParser.parse()) // { PORT: '3000', HOST: 'localhost' }
56
54
  ```
57
55
 
58
56
  The return value of `parser.parse` is an object with key-value pair. The parser also has support for interpolation.
@@ -63,6 +61,27 @@ By default, the parser prefers existing `process.env` values when they exist. Ho
63
61
  new EnvParser(envContents, { ignoreProcessEnv: true })
64
62
  ```
65
63
 
64
+ ### Identifier
65
+
66
+ You can define an "identifier" to be used for interpolation. The identifier is a string that prefix the environment variable value and let you customize the value resolution.
67
+
68
+ ```ts
69
+ import { readFile } from 'node:fs/promises'
70
+ import { EnvParser } from '@adonisjs/env'
71
+
72
+ EnvParser.identifier('file', (value) => {
73
+ return readFile(value, 'utf-8')
74
+ })
75
+
76
+ const envParser = new EnvParser(`
77
+ DB_PASSWORD=file:/run/secret/db_password
78
+ `)
79
+
80
+ console.log(await envParser.parse()) // { DB_PASSWORD: 'Value from file /run/secret/db_password' }
81
+ ```
82
+
83
+ This can be useful when you are using secrets manager like `Docker Secret`, `HashiCorp Vault`, `Google Secrets Manager` and others to manage your secrets.
84
+
66
85
  ## Validating environment variables
67
86
  Once you have the parsed objects, you can optionally validate them against a pre-defined schema. We recommend validation for the following reasons.
68
87
 
@@ -120,6 +139,18 @@ editor.add('HOST', 'localhost')
120
139
  await editor.save()
121
140
  ```
122
141
 
142
+ You can also insert an empty value for the `.env.example` file by setting the last argument to `true`.
143
+
144
+ ```ts
145
+ editor.add('SECRET_VARIABLE', 'secret-value', true)
146
+ ```
147
+
148
+ This will add the following line to the `.env.example` file.
149
+
150
+ ```env
151
+ SECRET_VARIABLE=
152
+ ```
153
+
123
154
  ## Known Exceptions
124
155
 
125
156
  ### E_INVALID_ENV_VARIABLES
@@ -14,8 +14,10 @@ export declare class EnvEditor {
14
14
  load(): Promise<void>;
15
15
  /**
16
16
  * Add key-value pair to the dot-env files.
17
+ * If `withEmptyExampleValue` is true then the key will be added with an empty value
18
+ * to the `.env.example` file.
17
19
  */
18
- add(key: string, value: string | number | boolean): void;
20
+ add(key: string, value: string | number | boolean, withEmptyExampleValue?: boolean): void;
19
21
  toJSON(): {
20
22
  contents: string[];
21
23
  path: string;
@@ -40,12 +40,18 @@ var EnvEditor = class _EnvEditor {
40
40
  }
41
41
  /**
42
42
  * Add key-value pair to the dot-env files.
43
+ * If `withEmptyExampleValue` is true then the key will be added with an empty value
44
+ * to the `.env.example` file.
43
45
  */
44
- add(key, value) {
46
+ add(key, value, withEmptyExampleValue = false) {
45
47
  this.#files.forEach((file) => {
46
48
  let entryIndex = file.contents.findIndex((line) => line.startsWith(`${key}=`));
47
49
  entryIndex = entryIndex === -1 ? file.contents.length : entryIndex;
48
- lodash.set(file.contents, entryIndex, `${key}=${String(value)}`);
50
+ if (withEmptyExampleValue && file.path.endsWith(".env.example")) {
51
+ lodash.set(file.contents, entryIndex, `${key}=`);
52
+ } else {
53
+ lodash.set(file.contents, entryIndex, `${key}=${value}`);
54
+ }
49
55
  });
50
56
  }
51
57
  toJSON() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/editor.ts"],"sourcesContent":["/*\n * @adonisjs/env\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport splitLines from 'split-lines'\nimport lodash from '@poppinss/utils/lodash'\nimport { writeFile } from 'node:fs/promises'\n\nimport { EnvLoader } from './loader.js'\n\nexport class EnvEditor {\n #appRoot: URL\n #loader: EnvLoader\n #files: { contents: string[]; path: string }[] = []\n\n /**\n * Creates an instance of env editor and loads .env files\n * contents.\n */\n static async create(appRoot: URL) {\n const editor = new EnvEditor(appRoot)\n await editor.load()\n\n return editor\n }\n\n constructor(appRoot: URL) {\n this.#appRoot = appRoot\n this.#loader = new EnvLoader(this.#appRoot, true)\n }\n\n /**\n * Loads .env files for editing. Only \".env\" and \".env.example\"\n * files are picked for editing.\n */\n async load() {\n const envFiles = await this.#loader.load()\n\n this.#files = envFiles\n .filter(\n (envFile) =>\n envFile.fileExists &&\n (envFile.path.endsWith('.env') || envFile.path.endsWith('.env.example'))\n )\n .map((envFile) => {\n return {\n contents: splitLines(envFile.contents.trim()),\n path: envFile.path,\n }\n })\n }\n\n /**\n * Add key-value pair to the dot-env files.\n */\n add(key: string, value: string | number | boolean) {\n this.#files.forEach((file) => {\n let entryIndex = file.contents.findIndex((line) => line.startsWith(`${key}=`))\n\n entryIndex = entryIndex === -1 ? file.contents.length : entryIndex\n lodash.set(file.contents, entryIndex, `${key}=${String(value)}`)\n })\n }\n\n toJSON() {\n return this.#files\n }\n\n /**\n * Save changes to the disk\n */\n async save() {\n await Promise.all(\n this.#files.map((file) => {\n return writeFile(file.path, file.contents.join('\\n'))\n })\n )\n }\n}\n"],"mappings":";;;;;AASA,OAAO,gBAAgB;AACvB,OAAO,YAAY;AACnB,SAAS,iBAAiB;AAInB,IAAM,YAAN,MAAM,WAAU;AAAA,EACrB;AAAA,EACA;AAAA,EACA,SAAiD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMlD,aAAa,OAAO,SAAc;AAChC,UAAM,SAAS,IAAI,WAAU,OAAO;AACpC,UAAM,OAAO,KAAK;AAElB,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,SAAc;AACxB,SAAK,WAAW;AAChB,SAAK,UAAU,IAAI,UAAU,KAAK,UAAU,IAAI;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO;AACX,UAAM,WAAW,MAAM,KAAK,QAAQ,KAAK;AAEzC,SAAK,SAAS,SACX;AAAA,MACC,CAAC,YACC,QAAQ,eACP,QAAQ,KAAK,SAAS,MAAM,KAAK,QAAQ,KAAK,SAAS,cAAc;AAAA,IAC1E,EACC,IAAI,CAAC,YAAY;AAChB,aAAO;AAAA,QACL,UAAU,WAAW,QAAQ,SAAS,KAAK,CAAC;AAAA,QAC5C,MAAM,QAAQ;AAAA,MAChB;AAAA,IACF,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,KAAa,OAAkC;AACjD,SAAK,OAAO,QAAQ,CAAC,SAAS;AAC5B,UAAI,aAAa,KAAK,SAAS,UAAU,CAAC,SAAS,KAAK,WAAW,GAAG,GAAG,GAAG,CAAC;AAE7E,mBAAa,eAAe,KAAK,KAAK,SAAS,SAAS;AACxD,aAAO,IAAI,KAAK,UAAU,YAAY,GAAG,GAAG,IAAI,OAAO,KAAK,CAAC,EAAE;AAAA,IACjE,CAAC;AAAA,EACH;AAAA,EAEA,SAAS;AACP,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO;AACX,UAAM,QAAQ;AAAA,MACZ,KAAK,OAAO,IAAI,CAAC,SAAS;AACxB,eAAO,UAAU,KAAK,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,MACtD,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/editor.ts"],"sourcesContent":["/*\n * @adonisjs/env\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport splitLines from 'split-lines'\nimport lodash from '@poppinss/utils/lodash'\nimport { writeFile } from 'node:fs/promises'\n\nimport { EnvLoader } from './loader.js'\n\nexport class EnvEditor {\n #appRoot: URL\n #loader: EnvLoader\n #files: { contents: string[]; path: string }[] = []\n\n /**\n * Creates an instance of env editor and loads .env files\n * contents.\n */\n static async create(appRoot: URL) {\n const editor = new EnvEditor(appRoot)\n await editor.load()\n\n return editor\n }\n\n constructor(appRoot: URL) {\n this.#appRoot = appRoot\n this.#loader = new EnvLoader(this.#appRoot, true)\n }\n\n /**\n * Loads .env files for editing. Only \".env\" and \".env.example\"\n * files are picked for editing.\n */\n async load() {\n const envFiles = await this.#loader.load()\n\n this.#files = envFiles\n .filter(\n (envFile) =>\n envFile.fileExists &&\n (envFile.path.endsWith('.env') || envFile.path.endsWith('.env.example'))\n )\n .map((envFile) => {\n return {\n contents: splitLines(envFile.contents.trim()),\n path: envFile.path,\n }\n })\n }\n\n /**\n * Add key-value pair to the dot-env files.\n * If `withEmptyExampleValue` is true then the key will be added with an empty value\n * to the `.env.example` file.\n */\n add(key: string, value: string | number | boolean, withEmptyExampleValue = false) {\n this.#files.forEach((file) => {\n let entryIndex = file.contents.findIndex((line) => line.startsWith(`${key}=`))\n\n entryIndex = entryIndex === -1 ? file.contents.length : entryIndex\n\n if (withEmptyExampleValue && file.path.endsWith('.env.example')) {\n lodash.set(file.contents, entryIndex, `${key}=`)\n } else {\n lodash.set(file.contents, entryIndex, `${key}=${value}`)\n }\n })\n }\n\n toJSON() {\n return this.#files\n }\n\n /**\n * Save changes to the disk\n */\n async save() {\n await Promise.all(\n this.#files.map((file) => {\n return writeFile(file.path, file.contents.join('\\n'))\n })\n )\n }\n}\n"],"mappings":";;;;;AASA,OAAO,gBAAgB;AACvB,OAAO,YAAY;AACnB,SAAS,iBAAiB;AAInB,IAAM,YAAN,MAAM,WAAU;AAAA,EACrB;AAAA,EACA;AAAA,EACA,SAAiD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMlD,aAAa,OAAO,SAAc;AAChC,UAAM,SAAS,IAAI,WAAU,OAAO;AACpC,UAAM,OAAO,KAAK;AAElB,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,SAAc;AACxB,SAAK,WAAW;AAChB,SAAK,UAAU,IAAI,UAAU,KAAK,UAAU,IAAI;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO;AACX,UAAM,WAAW,MAAM,KAAK,QAAQ,KAAK;AAEzC,SAAK,SAAS,SACX;AAAA,MACC,CAAC,YACC,QAAQ,eACP,QAAQ,KAAK,SAAS,MAAM,KAAK,QAAQ,KAAK,SAAS,cAAc;AAAA,IAC1E,EACC,IAAI,CAAC,YAAY;AAChB,aAAO;AAAA,QACL,UAAU,WAAW,QAAQ,SAAS,KAAK,CAAC;AAAA,QAC5C,MAAM,QAAQ;AAAA,MAChB;AAAA,IACF,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,KAAa,OAAkC,wBAAwB,OAAO;AAChF,SAAK,OAAO,QAAQ,CAAC,SAAS;AAC5B,UAAI,aAAa,KAAK,SAAS,UAAU,CAAC,SAAS,KAAK,WAAW,GAAG,GAAG,GAAG,CAAC;AAE7E,mBAAa,eAAe,KAAK,KAAK,SAAS,SAAS;AAExD,UAAI,yBAAyB,KAAK,KAAK,SAAS,cAAc,GAAG;AAC/D,eAAO,IAAI,KAAK,UAAU,YAAY,GAAG,GAAG,GAAG;AAAA,MACjD,OAAO;AACL,eAAO,IAAI,KAAK,UAAU,YAAY,GAAG,GAAG,IAAI,KAAK,EAAE;AAAA,MACzD;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,SAAS;AACP,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO;AACX,UAAM,QAAQ;AAAA,MACZ,KAAK,OAAO,IAAI,CAAC,SAAS;AACxB,eAAO,UAAU,KAAK,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,MACtD,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonisjs/env",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "description": "Environment variable manager for Node.js",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "precompile": "npm run lint && npm run clean",
26
26
  "compile": "tsup-node && tsc --emitDeclarationOnly --declaration",
27
27
  "build": "npm run compile",
28
- "release": "np",
28
+ "release": "release-it",
29
29
  "version": "npm run build",
30
30
  "prepublishOnly": "npm run build",
31
31
  "lint": "eslint . --ext=.ts",
@@ -40,33 +40,33 @@
40
40
  "author": "virk,adonisjs",
41
41
  "license": "MIT",
42
42
  "devDependencies": {
43
- "@adonisjs/eslint-config": "^1.2.1",
44
- "@adonisjs/prettier-config": "^1.2.1",
45
- "@adonisjs/tsconfig": "^1.2.1",
46
- "@commitlint/cli": "^18.5.0",
47
- "@commitlint/config-conventional": "^18.5.0",
48
- "@japa/assert": "^2.1.0",
49
- "@japa/expect-type": "^2.0.1",
50
- "@japa/file-system": "^2.2.0",
51
- "@japa/runner": "^3.1.1",
52
- "@swc/core": "^1.3.105",
53
- "@types/node": "^20.11.5",
43
+ "@adonisjs/eslint-config": "^1.3.0",
44
+ "@adonisjs/prettier-config": "^1.3.0",
45
+ "@adonisjs/tsconfig": "^1.3.0",
46
+ "@commitlint/cli": "^19.2.2",
47
+ "@commitlint/config-conventional": "^19.2.2",
48
+ "@japa/assert": "^3.0.0",
49
+ "@japa/expect-type": "^2.0.2",
50
+ "@japa/file-system": "^2.3.0",
51
+ "@japa/runner": "^3.1.4",
52
+ "@swc/core": "^1.4.16",
53
+ "@types/node": "^20.12.7",
54
54
  "c8": "^9.1.0",
55
55
  "cross-env": "^7.0.3",
56
56
  "del-cli": "^5.1.0",
57
57
  "eslint": "^8.56.0",
58
58
  "github-label-sync": "^2.3.1",
59
- "husky": "^8.0.3",
60
- "np": "^9.2.0",
61
- "prettier": "^3.2.4",
59
+ "husky": "^9.0.11",
60
+ "prettier": "^3.2.5",
61
+ "release-it": "^17.2.0",
62
62
  "ts-node": "^10.9.2",
63
- "tsup": "^8.0.1",
64
- "typescript": "^5.3.3"
63
+ "tsup": "^8.0.2",
64
+ "typescript": "^5.4.5"
65
65
  },
66
66
  "dependencies": {
67
- "@poppinss/utils": "^6.7.1",
67
+ "@poppinss/utils": "^6.7.3",
68
68
  "@poppinss/validator-lite": "^1.0.3",
69
- "dotenv": "^16.3.2",
69
+ "dotenv": "^16.4.5",
70
70
  "split-lines": "^3.0.0"
71
71
  },
72
72
  "repository": {
@@ -86,11 +86,22 @@
86
86
  "access": "public",
87
87
  "tag": "latest"
88
88
  },
89
- "np": {
90
- "message": "chore(release): %s",
91
- "tag": "latest",
92
- "branch": "main",
93
- "anyBranch": false
89
+ "release-it": {
90
+ "git": {
91
+ "commitMessage": "chore(release): ${version}",
92
+ "tagAnnotation": "v${version}",
93
+ "tagName": "v${version}"
94
+ },
95
+ "hooks": {
96
+ "before:init": [
97
+ "npm test"
98
+ ]
99
+ },
100
+ "github": {
101
+ "release": true,
102
+ "releaseName": "v${version}",
103
+ "web": true
104
+ }
94
105
  },
95
106
  "c8": {
96
107
  "reporter": [