Socket
Socket
Sign inDemoInstall

dotenv

Package Overview
Dependencies
Maintainers
3
Versions
86
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dotenv - npm Package Compare versions

Comparing version 0.5.1 to 1.0.0

Contributing.md

165

lib/main.js

@@ -1,113 +0,86 @@

"use strict";
'use strict'
var package_json = require('./../package.json');
var fs = require('fs');
var fs = require('fs')
function dotenv() {
dotenv = {
version: package_json.version,
keys_and_values: {},
environment: function() {
return process.env.NODE_ENV || dotenv.keys_and_values["NODE_ENV"] || "development"
},
_loadEnv: function() {
return dotenv._getKeysAndValuesFromEnvFilePath(".env", true);
},
_loadEnvDotEnvironment: function() {
return dotenv._getKeysAndValuesFromEnvFilePath(".env."+dotenv.environment());
},
_getKeyAndValueFromLine: function(line) {
var key_value_array = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/);
module.exports = {
/*
* Main entry point into dotenv. Allows configuration before loading .env and .env.$NODE_ENV
* @param {Object} options - valid options: path ('.env'), encoding ('utf8')
* @returns {Boolean}
*/
config: function (options) {
var path = '.env'
var encoding = 'utf8'
if (!key_value_array) return null;
if (options) {
if (options.path) {
path = options.path
}
if (options.encoding) {
encoding = options.encoding
}
}
var key = key_value_array[1];
var value = key_value_array[2];
try {
// specifying an encoding returns a string instead of a buffer
var parsedObj = this.parse(fs.readFileSync(path, { encoding: encoding }))
if(typeof value === "undefined"){
value = "";
}
Object.keys(parsedObj).forEach(function (key) {
process.env[key] = process.env[key] || parsedObj[key]
})
if (value.charAt(0) === '"' && value.charAt(value.length-1) == '"') {
value = value.replace(/\\n/gm, "\n");
}
value = value.replace(/(^['"]|['"]$)/g, ''); // replace first and last quotes only
return true
} catch(e) {
console.error(e)
return false
}
},
return [key, value];
},
_splitMultilineString: function ( lines ){
var content;
if(typeof lines !== 'string'){
return [];
}
lines = lines.trim().split('\n');
return lines.filter(function(line) { return line.trim().length; }); // remove any empty lines
},
_processForPotentialEnvVariable: function ( value ){
// variable in value starts with a $
if (value.charAt(0) === '$') {
var substringed_value = value.substring(1);
value = dotenv.keys_and_values[ substringed_value ] || process.env[ substringed_value ] || '';
}
// varaible can be escaped with a \$
if (value.substring(0,2) === "\\$") {
value = value.substring(1);
}
/*
* Parses a string or buffer into an object
* @param {String|Buffer} src - source to be parsed
* @returns {Object}
*/
parse: function (src) {
var obj = {}
return value;
},
_getKeysAndValuesFromEnvFilePath: function(filepath, required) {
var data, content, lines, keys_and_values = {};
// convert Buffers before splitting into lines and processing
src.toString().split('\n').forEach(function (line) {
// matching "KEY' and 'VAL' in 'KEY=VAL'
var keyValueArr = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/)
// matched?
if (keyValueArr != null) {
var key = keyValueArr[1]
try {
data = fs.readFileSync(filepath);
} catch (e) {
// return false if no such file and it was required
return !(e.code === 'ENOENT' && required);
}
// default undefined or missing values to empty string
var value = keyValueArr[2] ? keyValueArr[2] : ''
keys_and_values = dotenv.parse( data );
for( var key in keys_and_values ) {
var value = keys_and_values[ key ];
value = dotenv._processForPotentialEnvVariable( value );
// expand newlines in quoted values
var len = value ? value.length : 0
if (len > 0 && value.charAt(0) === '\"' && value.charAt(len - 1) === '\"') {
value = value.replace(/\\n/gm, '\n')
}
dotenv.keys_and_values[ key ] = value;
}
// remove any surrounding quotes and extra spaces
value = value.replace(/(^['"]|['"]$)/g, '').trim()
return true;
},
_setEnvs: function() {
Object.keys(dotenv.keys_and_values).forEach(function(key) {
var value = dotenv.keys_and_values[key];
// is this value a variable?
if (value.charAt(0) === '$') {
var possibleVar = value.substring(1)
value = obj[possibleVar] || process.env[possibleVar] || ''
}
// varaible can be escaped with a \$
if (value.substring(0, 2) === '\\$') {
value = value.substring(1)
}
process.env[key] = process.env[key] || value;
});
},
parse : function(data) {
var keys_and_values;
var payload = {};
var lines = dotenv._splitMultilineString( data.toString() );
keys_and_values = lines.map(dotenv._getKeyAndValueFromLine)
.filter(Array.isArray);
keys_and_values.forEach(function(pair) {
var key = pair[0];
var value = pair[1];
payload[key] = value.trim();
});
return payload;
},
load: function() {
if (dotenv._loadEnv() && dotenv._loadEnvDotEnvironment()) {
dotenv._setEnvs();
return true;
} else {
return false;
obj[key] = value
}
},
};
})
return dotenv;
return obj
}
}
module.exports = dotenv();
module.exports.load = module.exports.config
{
"name": "dotenv",
"version": "0.5.1",
"description": "Loads environment variables from .env",
"version": "1.0.0",
"description": "Loads environment variables from .env file",
"main": "lib/main.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "mocha test/*.js"
"test": "lab test/* --coverage && standard"
},
"repository": {
"type": "git",
"url": "git://github.com/scottmotte/dotenv.git"
"url": "git://github.com/motdotla/dotenv.git"
},

@@ -29,5 +26,9 @@ "keywords": [

"devDependencies": {
"mocha": "",
"should": ""
"lab": "^5.3.0",
"should": "4.4.2",
"sinon": "1.12.2",
"standard": "^2.10.0"
},
"dependencies": {
}
}

@@ -10,20 +10,19 @@ # dotenv

> "Storing [configuration in the environment](http://www.12factor.net/config) is one of the tenets of a [twelve-factor app](http://www.12factor.net/). Anything that is likely to change between deployment environments–such as resource handles for databases or credentials for external services–should be extracted from the code into environment variables.
> "Storing [configuration in the environment](http://www.12factor.net/config)
> is one of the tenets of a [twelve-factor app](http://www.12factor.net/).
> Anything that is likely to change between deployment environments–such as
> resource handles for databases or credentials for external services–should be
> extracted from the code into environment variables.
>
> But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv loads variables from a `.env` file into ENV when the environment is bootstrapped."
> But it is not always practical to set environment variables on development
> machines or continuous integration servers where multiple projects are run.
> Dotenv loads variables from a `.env` file into ENV when the environment is
> bootstrapped."
>
> [Brandon Keepers' Dotenv in Ruby](https://github.com/bkeepers/dotenv)
## Installation
## Install
Add it to your package.json file.
```javascript
{
...
"dependencies": {
...
"dotenv": "0.5.1"
}
}
```bash
npm install dotenv --save
```

@@ -33,85 +32,143 @@

As early as possible in your application require dotenv and load the `.env` variables:
As early as possible in your application, require and load dotenv.
```javascript
var dotenv = require('dotenv');
dotenv.load();
require('dotenv').load();
```
Then, create a `.env` file in the root directory of your project. Add the application configuration you want. For example:
Create a `.env` file in the root directory of your project. Add
environment-specific variables on new lines in the form of `NAME=VALUE`.
For example:
```
S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
SENDGRID_USERNAME=YOURSENDGRIDUSERNAME
SENDGRID_PASSWORD=YOURSENDGRIDPASSWORDGOESHERE
DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3
```
Whenever your application loads, these variables will be available in `process.env`:
That's it.
`process.env` now has the keys and values you defined in your `.env` file.
```javascript
var sendgrid_username = process.env.SENDGRID_USERNAME;
var secret_key = process.env.SECRET_KEY;
db.connect({
host: process.env.DB_HOST,
username: process.env.DB_USER,
password: process.env.DB_PASS
});
```
That's it. You're done.
## Config
### Custom .env location path
`config` will read your .env file, parse the contents, and assign it to
`process.env` - just like `load` does. You can additionally, pass options to
`config`.
The generally accepted standard is to keep your .env file in the root of your project directory. But you might find yourself wanting to place it elsewhere on your server. Here is how to do that.
Note: `config` and `load` are synonyms. You can pass options to either.
### Options
#### Path
Default: `.env`
You can specify a custom path if your file containing environment variables is
named or located differently.
```js
require('dotenv').config({path: '/custom/path/to/your/env/vars'});
```
var dotenv = require('dotenv');
dotenv._getKeysAndValuesFromEnvFilePath('/custom/path/to/your/.env');
dotenv._setEnvs();
#### Encoding
Default: `utf8`
You may specify the encoding of your file containing environment variables
using this option.
```js
require('dotenv').config({encoding: 'base64'});
```
That's it. It ends up being just one extra line of code.
## Parse
### Dotenv.parse
The engine which parses the contents of your file containing environment
variables is available to use. It accepts a String or Buffer and will return
an Object with the parsed keys and values.
Also added in `0.2.6` the method `parse` has been exposed. This is how `dotenv` internally parses multiline buffers or strings into an object to place into the `process.env` object.
```javascript
```js
var dotenv = require('dotenv');
var file = fs.readFileSync('./config/staging');
var config = dotenv.parse(file); // passing in a buffer
console.log( typeof config, config ) // object { API : 'http://this.is.a/example' }
var buf = new Buffer('BASIC=basic');
var config = dotenv.parse(buf); // will return an object
console.log(typeof config, config) // object { BASIC : 'basic' }
```
### Dotenv.load
### Rules
Added in `0.5.0`, the method `load` returns a boolean to indicate your .env file has been loaded without error. The most likely cause of error is not finding or reading your .env file(s).
The parsing engine currently supports the following rules:
## Should I commit my .env file?
- `BASIC=basic` becomes `{BASIC: 'basic'}`
- empty lines are skipped
- lines beginning with `#` are treated as comments
- empty values become empty strings (`EMPTY=` becomes `{EMPTY: ''}`)
- single and double quoted values are escaped (`SINGLE_QUOTE='quoted'` becomes `{SINGLE_QUOTE: "quoted"}`)
- new lines are expanded if in double quotes (`MULTILINE='new\nline'` becomes
Try not to commit your .env file to version control. It is best to keep it local to your machine and local on any machine you deploy to. Keep production credential .envs on your production machines, and keep development .envs on your local machine.
```
{MULTILINE: 'new
line'}
```
- inner quotes are maintained (think JSON) (`JSON={"foo": "bar"}` becomes `{JSON:"{\"foo\": \"bar\"}"`)
## Contributing
#### Expanding Variables
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Added some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
Basic variable expansion is supported.
## Running tests
```
BASIC=basic
TEST=$BASIC
```
Parsing that would result in `{BASIC: 'basic', TEST: 'basic'}`. You can escape
variables by quoting or beginning with `\` (e.g. `TEST=\$BASIC`). If the
variable is not found in the file, `process.env` is checked. Missing variables
result in an empty string.
```
BASIC=basic
TEST=$TEST
DNE=$DNE
```
```bash
npm install
npm test
TEST=example node -e 'require("dotenv").config();'
```
- `process.env.BASIC` would equal `basic`
- `process.env.TEST` would equal `example`
- `process.env.DNE` would equal `""`
## FAQ
### Should I commit my .env file?
No. We **strongly** recommend against committing your .env file to version
control. It should only include environment-specific values such as database
passwords or API keys. Your production database should have a different
password than your development database.
## Contributing
See [Contributing Guide](Contributing.md)
## Who's using dotenv
Here's a list of apps/sites/libraries using dotenv. It's in no way a complete list.
Here's just a few of many repositories using dotenv:
* [npm](https://github.com/npm/newww)
* [sendgrid-nodejs](https://github.com/sendgrid/sendgrid-nodejs)
* [handshake.js](https://github.com/handshakejs/handshakejs-api)
* [xavi](http://xavi.io/)
* [google-oauth2-service-account](https://github.com/jacoblwe20/google-oauth2-service-account)
* [kibble](https://github.com/motdotla/kibble)
* [flossedtoday](https://github.com/motdotla/flossedtoday)
* [github-streaker](https://github.com/motdotla/github-streaker)
[Create a pull request](https://github.com/motdotla/dotenv/pulls) and add yours to the list.

@@ -1,175 +0,190 @@

var assert = require('assert'),
should = require('should'),
fs = require('fs'),
dotenv = require('../lib/main');
'use strict'
var result;
require('should')
var sinon = require('sinon')
var Lab = require('lab')
var lab = exports.lab = Lab.script()
var describe = lab.experiment
var before = lab.before
var beforeEach = lab.beforeEach
var afterEach = lab.afterEach
var it = lab.test
var fs = require('fs')
var dotenv = require('../lib/main')
var s
describe('dotenv', function() {
before(function() {
process.env["MACHINE"] = "my_computer";
result = dotenv;
});
describe('dotenv', function () {
beforeEach(function (done) {
s = sinon.sandbox.create()
done()
})
it('version should be set', function() {
result.version.should.eql("0.5.1");
});
afterEach(function (done) {
s.restore()
done()
})
describe('.load()', function() {
before(function() {
result.load();
});
describe('config', function () {
var readFileSyncStub, parseStub
it('sets the basic environment variables', function() {
process.env.BASIC.should.eql("basic");
});
beforeEach(function (done) {
readFileSyncStub = s.stub(fs, 'readFileSync').returns('test=val')
parseStub = s.stub(dotenv, 'parse').returns({test: 'val'})
done()
})
it('expands environment variables', function() {
process.env.BASIC_EXPAND.should.eql("basic");
});
it('takes option for path', function (done) {
var testPath = 'test/.env'
dotenv.config({path: testPath})
it('expands undefined variables to an empty string', function() {
process.env.UNDEFINED_EXPAND.should.eql("");
});
readFileSyncStub.args[0][0].should.eql(testPath)
done()
})
it('does not expand escaped variables', function() {
process.env.ESCAPED_EXPAND.should.equal("$ESCAPED");
});
it('takes option for encoding', function (done) {
var testEncoding = 'base64'
dotenv.config({encoding: testEncoding})
describe('machine environment variables are already set', function() {
before(function() {
process.env.MACHINE="my_computer";
process.env.BASIC="should_not_be_chosen_because_exists_in_local_env";
});
readFileSyncStub.args[0][1].should.have.property('encoding', testEncoding)
done()
})
it('prioritizes local file set value', function() {
process.env.BASIC_EXPAND.should.eql("basic");
});
it('reads path with encoding, parsing output to process.env', function (done) {
dotenv.config()
it('defers to the machine set value', function() {
process.env.MACHINE_EXPAND.should.eql("my_computer");
});
});
readFileSyncStub.callCount.should.eql(1)
parseStub.callCount.should.eql(1)
done()
})
it('sets empty enviroment variable', function () {
process.env.EMPTY.should.eql("");
});
it('makes load a synonym of config', function (done) {
dotenv.load()
it('sets double quotes environment variables', function() {
process.env.DOUBLE_QUOTES.should.eql("double_quotes");
});
readFileSyncStub.callCount.should.eql(1)
parseStub.callCount.should.eql(1)
done()
})
it('sets single quotes environment variables', function() {
process.env.SINGLE_QUOTES.should.eql("single_quotes");
});
it('does not write over keys already in process.env', function (done) {
process.env.TEST = 'test'
// 'val' returned as value in `beforeEach`. should keep this 'test'
dotenv.config()
it('expands newlines but only if double quoted', function() {
process.env.EXPAND_NEWLINES.should.eql("expand\nnewlines");
process.env.DONT_EXPAND_NEWLINES_1.should.eql("dontexpand\\nnewlines");
process.env.DONT_EXPAND_NEWLINES_2.should.eql("dontexpand\\nnewlines");
});
process.env.TEST.should.eql('test')
done()
})
it('reads from .env.staging', function() {
process.env.FROM_STAGING_ENV.should.eql("from_staging_env");
});
it('catches any errors thrown from reading file or parsing', function (done) {
var errorStub = s.stub(console, 'error')
readFileSyncStub.throws()
it('overrides any values in .env with .env.environment', function() {
process.env.ENVIRONMENT_OVERRIDE.should.eql("staging");
});
dotenv.config().should.eql(false)
errorStub.callCount.should.eql(1)
done()
})
it('reads from a skipped line in .env.development', function() {
process.env.AFTER_LINE.should.eql("after_line");
});
})
it('ignores commented lines', function() {
should.not.exist(process.env.COMMENTS);
});
describe('parse', function () {
var parsed
before(function (done) {
process.env.TEST = 'test'
parsed = dotenv.parse(fs.readFileSync('test/.env', {encoding: 'utf8'}))
done()
})
it('respects equals signs in values', function() {
process.env.EQUAL_SIGNS.should.eql("equals==");
});
it('should return an object', function (done) {
parsed.should.be.an.instanceOf(Object)
done()
})
it('should handle zero width unicode characters', function() {
process.env.ZERO_WIDTH_CHARACTER.should.eql("user:pass@troup.mongohq.com:1004/dude");
});
it('should parse a buffer from a file into an object', function (done) {
var buffer = new Buffer('BASIC=basic')
it ('retains inner quotes', function() {
process.env.RETAIN_INNER_QUOTES.should.eql('{"foo": "bar"}');
process.env.RETAIN_INNER_QUOTES_AS_STRING.should.eql('{"foo": "bar"}');
});
var payload = dotenv.parse(buffer)
payload.should.have.property('BASIC', 'basic')
done()
})
it('should return true if .env file exists', function() {
result.load().should.eql(true);
});
it('sets basic environment variable', function (done) {
parsed.BASIC.should.eql('basic')
done()
})
it('should return true if .env.ENVIRONMENT file does not exists', function() {
var tmp = process.env.NODE_ENV;
process.env.NODE_ENV = 'DNE';
result.load().should.eql(true);
process.env.NODE_ENV = tmp; // reset for future tests
});
it('reads after a skipped line', function (done) {
parsed.AFTER_LINE.should.eql('after_line')
done()
})
it('should return false if .env file does not exist', function() {
fs.renameSync('.env', '.tmpenv');
result.load().should.eql(false);
fs.renameSync('.tmpenv', '.env');
});
describe('expanding variables', function () {
before(function (done) {
process.env.BASIC = 'should_not_be_chosen_because_exists_in_local_env'
done()
})
});
it('expands environment variables like $BASIC', function (done) {
parsed.BASIC_EXPAND.should.eql('basic')
done()
})
describe('.load() after an ENV was already set on the machine', function() {
before(function() {
process.env.ENVIRONMENT_OVERRIDE = "set_on_machine";
result.load();
});
it('prioritizes .env file value (if exists)', function (done) {
parsed.BASIC_EXPAND.should.eql('basic')
done()
})
it('sets using the value set on the machine', function() {
process.env.ENVIRONMENT_OVERRIDE.should.eql("set_on_machine");
delete process.env.ENVIRONMENT_OVERRIDE; //clean up for other tests
});
});
it('defers to process.env', function (done) {
// from `before`
parsed.TEST_EXPAND.should.eql('test')
done()
})
describe('.load() if NODE_ENV is set in .env', function() {
before(function() {
result.load();
});
it('defaults missing variables to an empty string', function (done) {
parsed.UNDEFINED_EXPAND.should.eql('')
done()
})
it('ENVIRONMENT_OVERRIDE should equal the value set in the .env.staging', function() {
process.env.ENVIRONMENT_OVERRIDE.should.eql('staging');
delete process.env.NODE_ENV; //cleanup for other tests
delete process.env.ENVIRONMENT_OVERRIDE;
});
});
it('does not expand escaped variables', function (done) {
parsed.ESCAPED_EXPAND.should.equal('$ESCAPED')
done()
})
})
describe('.load() if NODE_ENV is set in .env but NODE_ENV is already set on machine', function() {
before(function() {
process.env.NODE_ENV = "development"
result.load();
});
it('defaults empty values to empty string', function (done) {
parsed.EMPTY.should.eql('')
done()
})
it('ENVIRONMENT_OVERRIDE should equal the value set in the .env.development because that is the environment being set by the machine. machine wins here.', function() {
process.env.ENVIRONMENT_OVERRIDE.should.eql('development');
delete process.env.NODE_ENV; //clean up for other tests
delete process.env.ENVIRONMENT_OVERRIDE;
});
});
it('escapes double quoted values', function (done) {
parsed.DOUBLE_QUOTES.should.eql('double_quotes')
done()
})
describe('.parse()', function(){
it('should return an object', function(){
dotenv.parse('').should.be.an.Object;
});
var buffer;
before(function(done){
fs.readFile('.env', function(err,res){
buffer = res;
done();
})
});
it('escapes single quoted values', function (done) {
parsed.SINGLE_QUOTES.should.eql('single_quotes')
done()
})
it('should parse a buffer from a file into an object', function(){
var payload = dotenv.parse( buffer );
payload.should.be.an.Object;
payload.should.have.property('BASIC', 'basic');
it('expands newlines but only if double quoted', function (done) {
parsed.EXPAND_NEWLINES.should.eql('expand\nnewlines')
parsed.DONT_EXPAND_NEWLINES_1.should.eql('dontexpand\\nnewlines')
parsed.DONT_EXPAND_NEWLINES_2.should.eql('dontexpand\\nnewlines')
done()
})
it('ignores commented lines', function (done) {
parsed.should.not.have.property('COMMENTS')
done()
})
it('respects equals signs in values', function (done) {
parsed.EQUAL_SIGNS.should.eql('equals==')
done()
})
it('retains inner quotes', function (done) {
parsed.RETAIN_INNER_QUOTES.should.eql('{\"foo\": \"bar\"}')
parsed.RETAIN_INNER_QUOTES_AS_STRING.should.eql('{\"foo\": \"bar\"}')
done()
})
})
});
})

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc