Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: motdotla/dotenv
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.4.0
Choose a base ref
...
head repository: motdotla/dotenv
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.0.0
Choose a head ref

Commits on Jun 30, 2014

  1. Update README example for custom .env locations

    Previous code was using method for parsing a string, rather than method for read from FS and processing an entire file.
    James Butler committed Jun 30, 2014
    Copy the full SHA
    dbc11dd View commit details

Commits on Jul 1, 2014

  1. Update README.md with correct file load instructions

    Seva Rybakov committed Jul 1, 2014

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    99840ac View commit details
  2. Merge pull request #32 from CodingFu/patch-1

    Update README.md with correct file load instructions
    maxbeatty committed Jul 1, 2014
    Copy the full SHA
    9b9968d View commit details

Commits on Jul 2, 2014

  1. Merge pull request #31 from sandfox/patch-1

    Update README example for custom .env locations
    Scott Motte committed Jul 2, 2014
    Copy the full SHA
    fba888d View commit details

Commits on Aug 23, 2014

  1. Copy the full SHA
    b34b785 View commit details

Commits on Aug 24, 2014

  1. Merge pull request #33 from jonathanmv/master

    Removed './node_modules/.bin/' because tests won't run on windows.
    maxbeatty committed Aug 24, 2014
    Copy the full SHA
    d7e4ada View commit details

Commits on Oct 15, 2014

  1. Fix links in README

    motdotla committed Oct 15, 2014
    Copy the full SHA
    d430f5d View commit details
  2. Copy the full SHA
    0907e59 View commit details

Commits on Oct 31, 2014

  1. Update messaging

    motdotla committed Oct 31, 2014
    1
    Copy the full SHA
    50fd0ac View commit details
  2. Add temporary logo/icon

    motdotla committed Oct 31, 2014
    3
    Copy the full SHA
    8cd94a2 View commit details
  3. align-right

    motdotla committed Oct 31, 2014

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    61a836b View commit details

Commits on Nov 23, 2014

  1. Update gitignore file

    motdotla committed Nov 23, 2014

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    ddd120f View commit details

Commits on Dec 22, 2014

  1. Fix small typo in quotation

    srt32 committed Dec 22, 2014
    Copy the full SHA
    26e5176 View commit details

Commits on Dec 23, 2014

  1. Merge pull request #41 from srt32/master

    Fix small typo in quotation
    jcblw committed Dec 23, 2014
    Copy the full SHA
    6b98ef7 View commit details

Commits on Dec 28, 2014

  1. return false if load doesn't succeed

    Max Beatty committed Dec 28, 2014
    Copy the full SHA
    beb519c View commit details

Commits on Dec 29, 2014

  1. bump version, add documentation

    Max Beatty committed Dec 29, 2014
    Copy the full SHA
    695568c View commit details

Commits on Dec 30, 2014

  1. add editorconfig

    Max Beatty committed Dec 30, 2014
    Copy the full SHA
    c53cc2c View commit details
  2. Merge pull request #45 from motdotla/editorconfig

    add editorconfig
    jcblw committed Dec 30, 2014
    Copy the full SHA
    85c7553 View commit details

Commits on Dec 31, 2014

  1. initial 1.0 proposal

    Max Beatty committed Dec 31, 2014
    Copy the full SHA
    dc0aad6 View commit details
  2. document 1.0 changes

    Max Beatty committed Dec 31, 2014
    Copy the full SHA
    fd2d3d4 View commit details
  3. drop support for old versions of node

    Max Beatty committed Dec 31, 2014
    Copy the full SHA
    eb6b29e View commit details

Commits on Jan 27, 2015

  1. Merge pull request #44 from motdotla/rz-file-existece

    return false if load doesn't succeed
    maxbeatty committed Jan 27, 2015
    Copy the full SHA
    fe07320 View commit details

Commits on Jan 28, 2015

  1. set env vars even if environment env file is missing

    Max Beatty committed Jan 28, 2015
    Copy the full SHA
    3e243a3 View commit details
  2. simplify missing file return logic

    Max Beatty committed Jan 28, 2015
    Copy the full SHA
    daee45d View commit details
  3. Merge pull request #48 from motdotla/fix-extra-env-load

    set env vars even if environment env file is missing
    jcblw committed Jan 28, 2015
    Copy the full SHA
    09c8285 View commit details

Commits on Feb 13, 2015

  1. Add explanation env-specific variables to README

    Adam Neary committed Feb 13, 2015
    Copy the full SHA
    54cd775 View commit details
  2. Merge pull request #50 from adamrneary/patch-1

    Add explanation env-specific variables to README
    jcblw committed Feb 13, 2015
    Copy the full SHA
    d7af593 View commit details

Commits on Mar 1, 2015

  1. Add coveralls creds

    motdotla committed Mar 1, 2015
    Copy the full SHA
    880f461 View commit details
  2. Copy the full SHA
    1a98856 View commit details
  3. Defer to standard

    motdotla committed Mar 1, 2015
    Copy the full SHA
    9292ac1 View commit details
  4. Add iojs and remove .11

    motdotla committed Mar 1, 2015
    Copy the full SHA
    5266abc View commit details
  5. Copy the full SHA
    196c0c3 View commit details

Commits on Mar 13, 2015

  1. Merge pull request #53 from motdotla/sm-proposal

    Move to lab and use its built in test coverage.
    motdotla committed Mar 13, 2015
    Copy the full SHA
    16c7ac6 View commit details
  2. Copy the full SHA
    1fd9349 View commit details

Commits on Mar 14, 2015

  1. Update README to show library first. Yes, it is typical to touch .env…

    … first, but I think someone new to a library is looking for code to paste first, and touch work to do after. I'd rather lead with the code to copy/paste
    motdotla committed Mar 14, 2015
    Copy the full SHA
    baf58c7 View commit details
  2. Copy the full SHA
    f252f61 View commit details
  3. Remove xavi

    motdotla committed Mar 14, 2015
    Copy the full SHA
    43bce52 View commit details
  4. single quotes

    motdotla committed Mar 14, 2015
    Copy the full SHA
    f095ee9 View commit details
  5. adjust to original example

    motdotla committed Mar 14, 2015
    Copy the full SHA
    5ede0b4 View commit details
  6. Clarify writing

    motdotla committed Mar 14, 2015
    Copy the full SHA
    a75af05 View commit details
Showing with 453 additions and 344 deletions.
  1. +13 −0 .editorconfig
  2. +0 −2 .env.development
  3. +0 −1 .env.production
  4. +0 −6 .env.staging
  5. +12 −1 .gitignore
  6. +2 −2 .travis.yml
  7. +25 −0 Contributing.md
  8. +122 −59 README.md
  9. BIN dotenv.png
  10. +79 −105 lib/main.js
  11. +10 −9 package.json
  12. +4 −4 { → test}/.env
  13. +186 −155 test/main.js
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
2 changes: 0 additions & 2 deletions .env.development

This file was deleted.

1 change: 0 additions & 1 deletion .env.production

This file was deleted.

6 changes: 0 additions & 6 deletions .env.staging

This file was deleted.

13 changes: 12 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
node_modules/
# Coverage directory used by tools like istanbul
coverage

# Dependency directory
# Commenting this out is preferred by some people, see
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules

# Users Environment Variables
.lock-wscript

.DS_Store
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: node_js

node_js:
- 0.6
- 0.8
- iojs
- 0.12
- 0.10
25 changes: 25 additions & 0 deletions Contributing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Contributing

1. Fork it
2. `npm install`
3. Create your feature branch (`git checkout -b my-new-feature`)
4. Commit your changes (`git commit -am 'Added some feature'`)
5. `npm test`
6. Push to the branch (`git push origin my-new-feature`)
7. Create new Pull Request

## Testing

We use [lab](https://github.com/hapijs/lab) and [should](https://github.com/shouldjs/should.js) to write BDD test. Run our test suite with this command:

```
npm test
```

## Code Style

We use [eslint](http://eslint.org) and [editorconfig](http://editorconfig.org) to maintain code style and best practices. Please make sure your PR adheres to the guides by running:

```
gulp lint
```
181 changes: 122 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,109 +1,172 @@
# dotenv

Dotenv loads environment variables from .env into ENV (process.env). It is a superior alternative to [nconf](https://github.com/flatiron/nconf) and other variants.
<img src="https://raw.githubusercontent.com/motdotla/dotenv/master/dotenv.png" alt="dotenv" align="right" />

[![BuildStatus](https://travis-ci.org/scottmotte/dotenv.png?branch=master)](https://travis-ci.org/scottmotte/dotenv)
Dotenv loads environment variables from `.env` into `ENV` (process.env).

[![BuildStatus](https://travis-ci.org/motdotla/dotenv.png?branch=master)](https://travis-ci.org/motdotla/dotenv)
[![NPM version](https://badge.fury.io/js/dotenv.png)](http://badge.fury.io/js/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.
>
> But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv load variables from a `.env` file into ENV when the environment is bootstrapped."
>
> "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."
>
> [Brandon Keepers' Dotenv in Ruby](https://github.com/bkeepers/dotenv)
## Installation

Add it to your package.json file.
## Install

```javascript
{
...
"dependencies": {
...
"dotenv": "0.4.0"
}
}
```bash
npm install dotenv --save
```

## Usage

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.

```
var dotenv = require('dotenv');
dotenv._getKeyAndValueFromLine('/custom/path/to/your/.env');
dotenv._setEnvs();
### 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'});
```

That's it. It ends up being just one extra line of code.
#### Encoding

### Dotenv.parse
Default: `utf8`

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.
You may specify the encoding of your file containing environment variables
using this option.

```javascript
```js
require('dotenv').config({encoding: 'base64'});
```

## 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.

```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' }
```

## Should I commit my .env file?
### Rules

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.
The parsing engine currently supports the following rules:

## Contributing
- `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

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
```
{MULTILINE: 'new
line'}
```
- inner quotes are maintained (think JSON) (`JSON={"foo": "bar"}` becomes `{JSON:"{\"foo\": \"bar\"}"`)

#### Expanding Variables

Basic variable expansion is supported.

```
BASIC=basic
TEST=$BASIC
```

## Running tests
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/scottmotte/kibble)
* [flossedtoday](https://github.com/scottmotte/flossedtoday)
* [github-streaker](https://github.com/scottmotte/github-streaker)
* [kibble](https://github.com/motdotla/kibble)
* [github-streaker](https://github.com/motdotla/github-streaker)

[Create a pull request](https://github.com/scottmotte/dotenv/pulls) and add yours to the list.
Binary file added dotenv.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
184 changes: 79 additions & 105 deletions lib/main.js
Original file line number Diff line number Diff line change
@@ -1,112 +1,86 @@
"use strict";

var package_json = require('./../package.json');
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");
},
_loadEnvDotEnvironment: function() {
return dotenv._getKeysAndValuesFromEnvFilePath(".env."+dotenv.environment());
},
_getKeyAndValueFromLine: function(line) {
var key_value_array = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/);

if (!key_value_array) return null;

var key = key_value_array[1];
var value = key_value_array[2];

if(typeof value === "undefined"){
value = "";
'use strict'

var fs = require('fs')

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 (options) {
if (options.path) {
path = options.path
}

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 [key, value];
},
_splitMultilineString: function ( lines ){
var content;
if(typeof lines !== 'string'){
return [];
if (options.encoding) {
encoding = options.encoding
}
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 ] || '';
}

try {
// specifying an encoding returns a string instead of a buffer
var parsedObj = this.parse(fs.readFileSync(path, { encoding: encoding }))

Object.keys(parsedObj).forEach(function (key) {
process.env[key] = process.env[key] || parsedObj[key]
})

return true
} catch(e) {
console.error(e)
return false
}
},

/*
* Parses a string or buffer into an object
* @param {String|Buffer} src - source to be parsed
* @returns {Object}
*/
parse: function (src) {
var obj = {}

// 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]

// default undefined or missing values to empty string
var value = keyValueArr[2] ? keyValueArr[2] : ''

// 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')
}

// remove any surrounding quotes and extra spaces
value = value.replace(/(^['"]|['"]$)/g, '').trim()

// 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)
}

obj[key] = value
}
// varaible can be escaped with a \$
if (value.substring(0,2) === "\\$") {
value = value.substring(1);
}

return value;
},
_getKeysAndValuesFromEnvFilePath: function(filepath) {
var data, content, lines;
var keys_and_values = {};
})

try {
data = fs.readFileSync(filepath);
} catch (e) {
return false;
}

keys_and_values = dotenv.parse( data );
for( var key in keys_and_values ) {
var value = keys_and_values[ key ];
value = dotenv._processForPotentialEnvVariable( value );

dotenv.keys_and_values[ key ] = value;
}
return obj
}

return true;
},
_setEnvs: function() {
Object.keys(dotenv.keys_and_values).forEach(function(key) {
var value = dotenv.keys_and_values[key];

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() {
dotenv._loadEnv();
dotenv._loadEnvDotEnvironment();
dotenv._setEnvs();

return true;
},
};

return dotenv;
}

module.exports = dotenv();
module.exports.load = module.exports.config
19 changes: 10 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
{
"name": "dotenv",
"version": "0.4.0",
"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": "./node_modules/.bin/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"
},
"keywords": [
"dotenv",
@@ -26,7 +23,11 @@
"author": "scottmotte",
"license": "BSD",
"devDependencies": {
"mocha": "",
"should": ""
"lab": "^5.3.0",
"should": "4.4.2",
"sinon": "1.12.2",
"standard": "^2.10.0"
},
"dependencies": {
}
}
8 changes: 4 additions & 4 deletions .env → test/.env
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
NODE_ENV=staging
NODE_ENV=development
BASIC=basic

AFTER_LINE=after_line
BASIC_EXPAND=$BASIC
MACHINE_EXPAND=$MACHINE
TEST_EXPAND=$TEST
UNDEFINED_EXPAND=$TOTALLY_UNDEFINED_ENV_KEY
ESCAPED_EXPAND=\$ESCAPED
EMPTY=
@@ -10,9 +12,7 @@ DOUBLE_QUOTES="double_quotes"
EXPAND_NEWLINES="expand\nnewlines"
DONT_EXPAND_NEWLINES_1=dontexpand\nnewlines
DONT_EXPAND_NEWLINES_2='dontexpand\nnewlines'
ENVIRONMENT_OVERRIDE=production
# COMMENTS=work
EQUAL_SIGNS=equals==
ZERO_WIDTH_CHARACTER=​user:pass@troup.mongohq.com:1004/dude
RETAIN_INNER_QUOTES={"foo": "bar"}
RETAIN_INNER_QUOTES_AS_STRING='{"foo": "bar"}'
341 changes: 186 additions & 155 deletions test/main.js
Original file line number Diff line number Diff line change
@@ -1,159 +1,190 @@
var assert = require('assert'),
should = require('should'),
fs = require('fs'),
dotenv = require('../lib/main');

var result;

describe('dotenv', function() {
before(function() {
process.env["MACHINE"] = "my_computer";
result = dotenv;
});

it('version should be set', function() {
result.version.should.eql("0.4.0");
});

describe('.load()', function() {
before(function() {
result.load();
});

it('sets the basic environment variables', function() {
process.env.BASIC.should.eql("basic");
});

it('expands environment variables', function() {
process.env.BASIC_EXPAND.should.eql("basic");
});

it('expands undefined variables to an empty string', function() {
process.env.UNDEFINED_EXPAND.should.eql("");
});

it('does not expand escaped variables', function() {
process.env.ESCAPED_EXPAND.should.equal("$ESCAPED");
});

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";
});

it('prioritizes local file set value', function() {
process.env.BASIC_EXPAND.should.eql("basic");
});

it('defers to the machine set value', function() {
process.env.MACHINE_EXPAND.should.eql("my_computer");
});
});

it('sets empty enviroment variable', function () {
process.env.EMPTY.should.eql("");
});

it('sets double quotes environment variables', function() {
process.env.DOUBLE_QUOTES.should.eql("double_quotes");
});

it('sets single quotes environment variables', function() {
process.env.SINGLE_QUOTES.should.eql("single_quotes");
});

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");
});

it('reads from .env.staging', function() {
process.env.FROM_STAGING_ENV.should.eql("from_staging_env");
});

it('overrides any values in .env with .env.environment', function() {
process.env.ENVIRONMENT_OVERRIDE.should.eql("staging");
});

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);
});

it('respects equals signs in values', function() {
process.env.EQUAL_SIGNS.should.eql("equals==");
});

it('should handle zero width unicode characters', function() {
process.env.ZERO_WIDTH_CHARACTER.should.eql("user:pass@troup.mongohq.com:1004/dude");
});

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"}');
});


});

describe('.load() after an ENV was already set on the machine', function() {
before(function() {
process.env.ENVIRONMENT_OVERRIDE = "set_on_machine";
result.load();
});

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
});
});

describe('.load() if NODE_ENV is set in .env', function() {
before(function() {
result.load();
});

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;
});
});

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('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;
});
});

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();
'use strict'

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 () {
beforeEach(function (done) {
s = sinon.sandbox.create()
done()
})

afterEach(function (done) {
s.restore()
done()
})

describe('config', function () {
var readFileSyncStub, parseStub

beforeEach(function (done) {
readFileSyncStub = s.stub(fs, 'readFileSync').returns('test=val')
parseStub = s.stub(dotenv, 'parse').returns({test: 'val'})
done()
})

it('takes option for path', function (done) {
var testPath = 'test/.env'
dotenv.config({path: testPath})

readFileSyncStub.args[0][0].should.eql(testPath)
done()
})

it('takes option for encoding', function (done) {
var testEncoding = 'base64'
dotenv.config({encoding: testEncoding})

readFileSyncStub.args[0][1].should.have.property('encoding', testEncoding)
done()
})

it('reads path with encoding, parsing output to process.env', function (done) {
dotenv.config()

readFileSyncStub.callCount.should.eql(1)
parseStub.callCount.should.eql(1)
done()
})

it('makes load a synonym of config', function (done) {
dotenv.load()

readFileSyncStub.callCount.should.eql(1)
parseStub.callCount.should.eql(1)
done()
})

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()

process.env.TEST.should.eql('test')
done()
})

it('catches any errors thrown from reading file or parsing', function (done) {
var errorStub = s.stub(console, 'error')
readFileSyncStub.throws()

dotenv.config().should.eql(false)
errorStub.callCount.should.eql(1)
done()
})

})

describe('parse', function () {
var parsed
before(function (done) {
process.env.TEST = 'test'
parsed = dotenv.parse(fs.readFileSync('test/.env', {encoding: 'utf8'}))
done()
})

it('should return an object', function (done) {
parsed.should.be.an.instanceOf(Object)
done()
})

it('should parse a buffer from a file into an object', function (done) {
var buffer = new Buffer('BASIC=basic')

var payload = dotenv.parse(buffer)
payload.should.have.property('BASIC', 'basic')
done()
})

it('sets basic environment variable', function (done) {
parsed.BASIC.should.eql('basic')
done()
})

it('reads after a skipped line', function (done) {
parsed.AFTER_LINE.should.eql('after_line')
done()
})

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()
})

it('prioritizes .env file value (if exists)', function (done) {
parsed.BASIC_EXPAND.should.eql('basic')
done()
})

it('defers to process.env', function (done) {
// from `before`
parsed.TEST_EXPAND.should.eql('test')
done()
})

it('defaults missing variables to an empty string', function (done) {
parsed.UNDEFINED_EXPAND.should.eql('')
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('does not expand escaped variables', function (done) {
parsed.ESCAPED_EXPAND.should.equal('$ESCAPED')
done()
})
})

it('defaults empty values to empty string', function (done) {
parsed.EMPTY.should.eql('')
done()
})

it('escapes double quoted values', function (done) {
parsed.DOUBLE_QUOTES.should.eql('double_quotes')
done()
})

it('escapes single quoted values', function (done) {
parsed.SINGLE_QUOTES.should.eql('single_quotes')
done()
})

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()
})
})
});
})