├── README.md ├── .gitattributes ├── package.json ├── index.js ├── src └── env-to-json.js ├── LICENSE ├── .gitignore └── tests ├── env-to-json.test.js └── env-to-json-callback-version.test.js /README.md: -------------------------------------------------------------------------------- 1 | # env-to-json 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "env_to_json", 3 | "version": "0.1.0", 4 | "description": "Creates a json object from environment variables", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node tests/env-to-json.test.js" 8 | }, 9 | "author": "mustafa ilhan ", 10 | "license": "MIT", 11 | "dependencies": { 12 | "dotenv": "^6.2.0", 13 | "lodash": "^4.17.11" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const envToJSON = require('./src/env-to-json') 2 | const _ = require('lodash') 3 | 4 | let config = { 5 | 'api': { 6 | 'port': 2999 7 | }, 8 | 'database': { 9 | 'host': '127.0.0.1', 10 | 'port': 3000 11 | } 12 | } 13 | 14 | config = _.merge(config, envToJSON()) 15 | 16 | console.log(config) 17 | 18 | /* 19 | Object.keys(val.parsed) 20 | .map(x => x.toLowerCase()) 21 | .filter(x => x.) 22 | */ 23 | -------------------------------------------------------------------------------- /src/env-to-json.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | function converToNumberIfItIsNumber (val) { 3 | return isNaN(val) ? val : Number(val) 4 | } 5 | 6 | let obj = {} 7 | const dotenv = require('dotenv') 8 | const envVars = dotenv.config().parsed 9 | if (envVars) { 10 | let keys = Object.keys(envVars) 11 | 12 | for (let i = 0, ii = keys.length; i !== ii; i++) { 13 | envVars[keys[i]] = converToNumberIfItIsNumber(envVars[keys[i]]) 14 | 15 | let lowerKey = keys[i].toLowerCase() 16 | let key = lowerKey.substring(0, lowerKey.indexOf('_')) 17 | let subKey = lowerKey.substring(lowerKey.indexOf('_') + 1) 18 | obj[key] = obj[key] || {} 19 | obj[key][subKey] = envVars[keys[i]] 20 | } 21 | } 22 | return obj 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mustafa İlhan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | 75 | # FuseBox cache 76 | .fusebox/ 77 | 78 | package-lock.json 79 | -------------------------------------------------------------------------------- /tests/env-to-json.test.js: -------------------------------------------------------------------------------- 1 | const envToJSON = require('../src/env-to-json') 2 | const _ = require('lodash') 3 | const fs = require('fs') 4 | const path = require('path') 5 | 6 | const envPath = path.join(__dirname, '../.env') 7 | const envBakPath = path.join(__dirname, '../.env.bak') 8 | const fsPromises = fs.promises 9 | 10 | async function renameExistingEnvFile () { 11 | // Check existing file 12 | try { 13 | await fsPromises.access(envPath, fs.constants.F_OK) 14 | // Exists, move 15 | await fsPromises.rename(envPath, envBakPath) 16 | } catch (e) { 17 | // Not exist 18 | } 19 | } 20 | 21 | async function rollbackChanges () { 22 | try { 23 | await fsPromises.access(envBakPath, fs.constants.F_OK) 24 | // Exists, move 25 | await fsPromises.rename(envBakPath, envPath) 26 | } catch (e) { 27 | // Not exist 28 | } 29 | } 30 | 31 | async function createTestFile (data) { 32 | await fsPromises.writeFile(envPath, data) 33 | } 34 | 35 | async function test (fileData) { 36 | await renameExistingEnvFile() 37 | await createTestFile(fileData) 38 | let result = envToJSON() 39 | await rollbackChanges() 40 | return result 41 | } 42 | 43 | async function runTests (tests) { 44 | for (let i = 0, ii = tests.length; i != ii; i++) { 45 | let response = await test(tests[i].fileData) 46 | if (!_.isEqual(response, tests[i].expectedData)) { 47 | console.log('Couldn\'t pass.', '\nResponse:\n', response, '\nExpected:\n', tests[i].expectedData) 48 | process.exit(1) 49 | } 50 | } 51 | console.log('Passed', tests.length, 'cases.') 52 | process.exit(0) 53 | } 54 | 55 | runTests([ 56 | { 57 | fileData: 'DATABASE_HOST=192.168.1.1\nDATABASE_PORT=33060\nAPI_PORT=443\n', 58 | expectedData: { 59 | 'api': { 60 | 'port': 443 61 | }, 62 | 'database': { 63 | 'host': '192.168.1.1', 64 | 'port': 33060 65 | } 66 | } 67 | }, 68 | { 69 | fileData: '', 70 | expectedData: {} 71 | } 72 | ]) 73 | -------------------------------------------------------------------------------- /tests/env-to-json-callback-version.test.js: -------------------------------------------------------------------------------- 1 | const envToJSON = require('../src/env-to-json') 2 | const _ = require('lodash') 3 | const fs = require('fs') 4 | const path = require('path') 5 | 6 | const envPath = path.join(__dirname, '../.env') 7 | const envBakPath = path.join(__dirname, '../.env.bak') 8 | 9 | function renameExistingEnvFileFn (callback) { 10 | fs.access(envPath, fs.constants.F_OK, (err) => { 11 | if (!err) { 12 | fs.rename(envPath, envBakPath, (err) => { 13 | if (err) throw err 14 | callback() 15 | }) 16 | } 17 | }) 18 | } 19 | 20 | function rollbackChangesFn (callback) { 21 | fs.access(envBakPath, fs.constants.F_OK, (err) => { 22 | if (!err) { 23 | fs.rename(envBakPath, envPath, (err) => { 24 | if (err) throw err 25 | callback() 26 | }) 27 | } 28 | }) 29 | } 30 | 31 | function createTestFileFn (data, callback) { 32 | fs.writeFile(envPath, data, (err) => { 33 | if (err) throw err 34 | callback() 35 | }) 36 | } 37 | 38 | const fileData = 'DATABASE_HOST=192.168.1.1\nDATABASE_PORT=33060\nAPI_PORT=443\n' 39 | const expectedData = { 40 | 'api': { 41 | 'port': 443 42 | }, 43 | 'database': { 44 | 'host': '192.168.1.1', 45 | 'port': 33060 46 | } 47 | } 48 | const notExpectedData = { 49 | 'api': { 50 | 'port': 443 51 | }, 52 | 'database': { 53 | 'host': '192.168.1.1', 54 | 'port': '33060' 55 | } 56 | } 57 | 58 | function runTestFn () { 59 | let response = envToJSON() 60 | let equal = _.isEqual(response, expectedData) 61 | equal = equal && !_.isEqual(response, notExpectedData) 62 | rollbackChangesFn(function () { 63 | if (equal) { 64 | console.log('Passed') 65 | process.exit(0) 66 | } else { 67 | console.log('Couldn\'t pass.', '\nResponse:\n', response, '\nExpected:\n', expectedData, '\nNot expected:\n', notExpectedData) 68 | process.exit(1) 69 | } 70 | }) 71 | } 72 | 73 | renameExistingEnvFileFn(function () { 74 | createTestFileFn(fileData, runTestFn) 75 | }) 76 | --------------------------------------------------------------------------------