Skip to content
This repository has been archived by the owner on Aug 22, 2020. It is now read-only.

Commit

Permalink
feat(env): load environment variables from a file; added favicon supp…
Browse files Browse the repository at this point in the history
…ort to docs
  • Loading branch information
thealjey committed Mar 11, 2019
1 parent 3e758c0 commit a15015e
Show file tree
Hide file tree
Showing 35 changed files with 1,361 additions and 335 deletions.
2 changes: 0 additions & 2 deletions assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ const { AssertionError } = require('assert')

/**
* A subclass of {@link Error} that indicates the failure of an assertion.
* All errors thrown by the assert module will be instances of the
* `AssertionError` class.
*
* @external AssertionError
* @see {@link https://nodejs.org/api/assert.html#assert_class_assert_assertionerror|AssertionError}
Expand Down
11 changes: 4 additions & 7 deletions constants.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
/* @flow */

const get = require('lodash/get')
const has = require('lodash/has')
const g = require('global')

/** @namespace constants */

const g = typeof global === 'undefined'
? (typeof window === 'undefined' ? {} : window)
: global

/**
* truthy when running in Node.js
*
Expand All @@ -21,7 +18,7 @@ const g = typeof global === 'undefined'
* // not Node.js
* }
*/
const isNode = exports.isNode = get(g, 'process.versions.node')
const isNode = exports.isNode = has(g, 'process.versions.node')

/**
* truthy when running in a browser
Expand All @@ -36,7 +33,7 @@ const isNode = exports.isNode = get(g, 'process.versions.node')
* // not browser
* }
*/
exports.isBrowser = !isNode && get(g, 'document')
exports.isBrowser = !isNode && has(g, 'document')

/**
* contains `WebSocket` or `MozWebSocket` from the global scope if available
Expand Down
9 changes: 1 addition & 8 deletions cookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ const pick = require('lodash/pick')

/**
* The `req` object represents the HTTP request and has properties for the
* request query string, parameters, body, HTTP headers, and so on. In this
* documentation and by convention, the object is always referred to as `req`
* (and the HTTP response is `res`) but its actual name is determined by the
* parameters to the callback function in which you’re working.
* request query string, parameters, body, HTTP headers, and so on.
*
* @external Request
* @see {@link http://expressjs.com/en/4x/api.html#req|Request}
Expand All @@ -21,10 +18,6 @@ const pick = require('lodash/pick')
* The `res` object represents the HTTP response that an Express app sends when
* it gets an HTTP request.
*
* In this documentation and by convention, the object is always referred to as
* `res` (and the HTTP request is `req`) but its actual name is determined by
* the parameters to the callback function in which you’re working.
*
* @external Response
* @see {@link http://expressjs.com/en/4x/api.html#res|Response}
*/
Expand Down
Binary file added docs/favicon.ico
Binary file not shown.
70 changes: 38 additions & 32 deletions docs/index.html

Large diffs are not rendered by default.

19 changes: 16 additions & 3 deletions documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,25 @@ toc:
- assertString
- name: Misc
children:
- env
- cookie
- constants
- name: theme
description: |
a [documentation.js](http://documentation.js.org/) theme used to
generate this documentation
<br />
<br />
generate this documentation<br><br>
Extends the [@example](http://usejsdoc.org/tags-example.html) jsdoc
tag to support a language modifier (e.g. `@example(scss)`).<br>
If a modifier is omitted or is not supported the language defaults to
`'flow'`.<br>
Follow [this link](https://prismjs.com/#languages-list) to see the
full list of supported languages.
The `--favicon` option must be a path to an SVG file, which will be
rendered in the header bar as well as automatically compiled to an ICO
file and used as a favicon.
*Example usage*
1. install this package - `yarn add secondwheel`
Expand All @@ -50,6 +61,8 @@ toc:
<span class="token punctuation">}</span></code></pre>
- name: Types
children:
- EnvOptions
- EnvError
- AssertionError
- ReactElement
- ApolloLink
Expand Down
122 changes: 122 additions & 0 deletions env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* @flow */

const { parse } = require('dotenv')
const forOwn = require('lodash/forOwn')
const has = require('lodash/has')
const keys = require('lodash/keys')
const pull = require('lodash/pull')
const { readFileSync } = require('fs')
const { join } = require('path')

/*::
export type EnvOptions = {
path?: string;
example?: string;
debug?: boolean;
}
*/

/**
* @typedef {Object} EnvOptions
* @param {string} [path='<cwd>/.env'] - a custom path if your file containing environment variables is located elsewhere
* @param {string} [example='<cwd>/.env.example'] - path to example environment file
* @param {boolean} [debug=false] - turns on logging to help debug why certain keys or values are not being set as you expect
* @example
* import type { EnvOptions } from 'secondwheel/env'
*/

/**
* @typedef {Error} EnvError
* @property {Set<string>} missing - a list of variable names
*/

const pattern = /\${\s*(\w+)\s*}/g

const interpolate = (value, parsed, missing) =>
value.replace(pattern, (match, key) => {
if (!has(parsed, key) && !has(process.env, key)) {
missing.add(key)
}
return interpolate(process.env[key] || parsed[key] || '', parsed, missing)
})

const parseFile = (
file/*: string */,
options/*: EnvOptions */
) => {
try {
return parse(readFileSync(file), options)
} catch (error) {
return {}
}
}

class EnvError extends Error {
/*:: missing: Set<string>; */

constructor (missing) {
super(`missing environment variables: ${Array.from(missing).join(', ')}`)
Error.captureStackTrace(this, EnvError)
this.name = 'EnvError'
this.missing = missing
}
}

/**
* Loads environment variables from a file.
*
* Performs validation based on the existence of variables rather than their value.
* Hence, a variable with no value is considered declared.
*
* @throws {EnvError} validation failure
* @example(properties)
* # .env.example (every key defined here must also be present in the environment)
* FULL_NAME=John Doe
* PASSWORD=
* @example(properties)
* # .env (supports javascript-style interpolation)
* AGE=30
* LAST_NAME=Smith
* FULL_NAME=${FIRST_NAME} ${LAST_NAME}
* # EnvError: missing environment variables: FIRST_NAME, PASSWORD
* @example
* import env from 'secondwheel/env'
*
* env()
*
* // process.env is configured
*/
const env = (options/*: ?EnvOptions */) => {
const cwd = process.cwd()
const config = {
path: join(cwd, '.env'),
example: join(cwd, '.env.example'),
debug: false,
...options
}
const { path, example, debug } = config
const parsed = parseFile(path, config)
const missing = new Set(
pull(
keys(parseFile(example, config)),
...keys(parsed),
...keys(process.env)
)
)

forOwn(parsed, (value, key) => {
parsed[key] = interpolate(value, parsed, missing)
})

if (missing.size) throw new EnvError(missing)

forOwn(parsed, (value, key) => {
if (!has(process.env, key)) {
process.env[key] = value
} else if (debug) {
console.log(`"${key}" is already defined in process.env`)
}
})
}

module.exports = env
1 change: 1 addition & 0 deletions favicon.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion htmlToArray.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const transformElement = ({
return { type, props, children }
}

const transformElements = (elements = []) =>
const transformElements = elements =>
map(
reject(elements, ['type', 'comment']),
el => el.type === 'text' ? el.data : transformElement(el)
Expand Down
48 changes: 34 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"author": "Eugene Kuzmenko",
"license": "MIT",
"scripts": {
"test": "flow && standard *.js test/*.js && c8 --reporter=text --reporter=lcov mocha --recursive -b -R min --exit",
"docs": "documentation build *.js -f html -c documentation.yml --np --project-name secondwheel --project-homepage https://github.com/thealjey/secondwheel -t theme -o docs",
"test": "flow && standard *.js test/*.js && nyc mocha -t 10000 -b -R min --exit",
"docs": "documentation build *.js -f html -c documentation.yml --np --project-name secondwheel --project-homepage https://github.com/thealjey/secondwheel --favicon favicon.svg -t theme -o docs",
"semantic-release": "semantic-release",
"cz": "git-cz"
},
Expand All @@ -24,29 +24,32 @@
"dependencies": {
"apollo-cache-inmemory": "^1.5.1",
"apollo-client": "^2.5.1",
"apollo-link": "^1.2.8",
"apollo-link-batch-http": "^1.2.8",
"apollo-link-error": "^1.1.7",
"apollo-link-retry": "^2.2.10",
"apollo-link-ws": "^1.0.14",
"apollo-link": "^1.2.9",
"apollo-link-batch-http": "^1.2.9",
"apollo-link-error": "^1.1.8",
"apollo-link-retry": "^2.2.11",
"apollo-link-ws": "^1.0.15",
"apollo-utilities": "^1.2.1",
"cheerio": "^1.0.0-rc.2",
"documentation": "^9.3.0",
"dotenv": "^6.2.0",
"github-slugger": "^1.2.1",
"global": "^4.3.2",
"graphql": "^14.1.1",
"isomorphic-unfetch": "^3.0.0",
"lodash": "^4.17.11",
"marked": "^0.6.1",
"md5.js": "^1.3.5",
"prismjs": "^1.15.0",
"react-apollo": "^2.5.1",
"react-apollo": "^2.5.2",
"react-element-to-jsx-string": "^14.0.2",
"react-test-renderer": "^16.8.3",
"subscriptions-transport-ws": "^0.9.15",
"react-test-renderer": "^16.8.4",
"subscriptions-transport-ws": "^0.9.16",
"svg2png": "^4.1.1",
"to-ico": "^1.1.5",
"vinyl": "^2.2.0"
},
"devDependencies": {
"c8": "^3.4.0",
"classnames": "^2.2.6",
"commitizen": "^3.0.7",
"coveralls": "^3.0.3",
Expand All @@ -55,11 +58,12 @@
"husky": "^1.3.1",
"mocha": "^6.0.0",
"next": "^8.0.3",
"nyc": "^13.3.0",
"proxyquire": "^2.1.0",
"react": "^16.8.3",
"react-dom": "^16.8.3",
"react": "^16.8.4",
"react-dom": "^16.8.4",
"semantic-release": "^15.13.3",
"sinon": "^7.2.6",
"sinon": "^7.2.7",
"standard": "^12.0.1"
},
"version": "0.0.0-development",
Expand All @@ -72,5 +76,21 @@
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"nyc": {
"check-coverage": true,
"lines": 100,
"statements": 100,
"functions": 100,
"branches": 100,
"include": [
"*.js"
],
"reporter": [
"lcov",
"text"
],
"cache": true,
"all": true
}
}
4 changes: 2 additions & 2 deletions subscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const tail = require('lodash/tail')
/**
* {@link cookie} configuration options
* @typedef {Object} SubscriptionPayload
* @param {'CREATED' | 'UPDATED' | 'DELETED'} mutation
* @param {Object | Object[]} node
* @property {'CREATED' | 'UPDATED' | 'DELETED'} mutation
* @property {Object | Object[]} node
* @example
* import type { SubscriptionPayload } from 'secondwheel/subscription'
*/
Expand Down
4 changes: 4 additions & 0 deletions test/arrayToJSX.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ const { createElement: h, Fragment } = require('react')
const reactElementToJSXString = require('react-element-to-jsx-string')

describe('arrayToJSX', () => {
it('test default', () => {
strictEqual(arrayToJSX(h).length, 0)
})

it('should return predictable result', () => {
strictEqual(
/* @flowignore */
Expand Down
2 changes: 1 addition & 1 deletion test/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { strictEqual } = require('assert')

describe('assert', () => {
it('should not throw', () => {
assert(100, value => value < 1000)
assert(true)
})

it('should throw', () => {
Expand Down

0 comments on commit a15015e

Please sign in to comment.