Comparing version 1.2.1 to 1.3.0
109
index.js
'use strict' | ||
const internalsSymbol = Symbol('Lawn config') | ||
const validatorsSymbol = Symbol('Lawn validators') | ||
const transformationsSymbol = Symbol('Lawn transformations') | ||
const cloneSymbol = Symbol('Lawn create') | ||
const { internalsSymbol } = require('./symbols') | ||
const Lawn = { | ||
[internalsSymbol]: {}, | ||
[validatorsSymbol]: [], | ||
[transformationsSymbol]: [], | ||
[cloneSymbol] (changes, validator, converter) { | ||
const obj = Object.create(Lawn) | ||
obj[internalsSymbol] = this[internalsSymbol] | ||
obj[validatorsSymbol] = this[validatorsSymbol] | ||
obj[transformationsSymbol] = this[transformationsSymbol] | ||
return obj | ||
use (name, BaseClass) { | ||
Object.defineProperty(this, name, { | ||
configurable: true, | ||
enumerable: true, | ||
get () { | ||
return new BaseClass() | ||
} | ||
}) | ||
}, | ||
props (props) { | ||
const obj = this[cloneSymbol]() | ||
obj[internalsSymbol] = Object.assign({}, this[internalsSymbol], props) | ||
return obj | ||
}, | ||
validation (validation) { | ||
const obj = this[cloneSymbol]() | ||
obj[validatorsSymbol] = [...this[validatorsSymbol], validation] | ||
return obj | ||
}, | ||
transformation (transformation) { | ||
const obj = this[cloneSymbol]() | ||
obj[transformationsSymbol] = [...this[transformationsSymbol], transformation] | ||
return obj | ||
}, | ||
get string () { | ||
return this.props({ type: 'string' }) | ||
}, | ||
get number () { | ||
return this.props({ type: 'number' }) | ||
.transformation(v => parseInt(v, 10)) | ||
.validation({ message: 'is not a number', fn: v => !Number.isNaN(v) }) | ||
}, | ||
get bool () { | ||
return this.props({ type: 'bool' }) | ||
.transformation(v => { | ||
switch (v.toLowerCase()) { | ||
case 'true': | ||
case 'yes': | ||
case 'y': | ||
case '1': | ||
return true | ||
default: | ||
return false | ||
} | ||
}) | ||
}, | ||
desc (description) { | ||
return this.props({ description }) | ||
}, | ||
example (example) { | ||
return this.props({ example }) | ||
}, | ||
default (def) { | ||
return this.props({ default: def }) | ||
}, | ||
get optional () { | ||
return this.props({ optional: true }) | ||
}, | ||
regex (re, message) { | ||
if (message === undefined) { | ||
message = 'must match the regex ' + re.toString() | ||
} | ||
return this.validation({ message, fn: v => re.test(v) }) | ||
}, | ||
validate (config, properties) { | ||
@@ -95,5 +24,4 @@ if (!properties) { | ||
for (const key of Object.keys(config)) { | ||
const obj = config[key] | ||
const cfg = config[key][internalsSymbol] | ||
const transformations = config[key][transformationsSymbol] | ||
const validators = config[key][validatorsSymbol] | ||
const originalVal = properties[key] | ||
@@ -111,11 +39,9 @@ | ||
} else { | ||
val = transformations.reduce((val, transformation) => transformation(val), originalVal) | ||
val = obj._transform(originalVal) | ||
} | ||
for (const validator of validators) { | ||
const message = validator.message | ||
const fn = validator.fn | ||
if (!fn(val)) { | ||
throw new Error(`${key} is invalid: '${originalVal}' ${message}`) | ||
} | ||
try { | ||
obj.validate(val, originalVal) | ||
} catch (err) { | ||
throw new Error(`${key} is invalid: '${originalVal}' ${err.message}`) | ||
} | ||
@@ -154,2 +80,7 @@ | ||
Lawn.use('string', require('./types/string')) | ||
Lawn.use('number', require('./types/number')) | ||
Lawn.use('bool', require('./types/boolean')) | ||
Lawn.use('url', require('./types/url')) | ||
module.exports = Lawn |
@@ -227,2 +227,18 @@ /* global describe it */ | ||
it('includes descriptions, if available (using description)', function () { | ||
const spec = { | ||
MYSQL_HOST: lawn.string.description('The MySQL host'), | ||
MYSQL_PORT: lawn.number.description('The port to run MySQL on') | ||
} | ||
const result = lawn.output(spec) | ||
assert.equal(result, stripIndent(` | ||
# The MySQL host | ||
MYSQL_HOST= | ||
# The port to run MySQL on | ||
MYSQL_PORT= | ||
`).trim()) | ||
}) | ||
it('includes examples', function () { | ||
@@ -229,0 +245,0 @@ const spec = { |
{ | ||
"name": "lawn", | ||
"version": "1.2.1", | ||
"version": "1.3.0", | ||
"description": "The environment is dangerous. Your lawn is nice. Stay in your lawn.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
113
README.md
@@ -141,29 +141,10 @@ # Lawn | ||
### .string | ||
### Common properties | ||
Declares that this property is a string. | ||
#### .default(v) | ||
### .number | ||
Declares this this property is an integer. | ||
### .bool | ||
Declare that this property is a boolean. | ||
Values that resolve to `true` are: | ||
- `"true"` (case-insensitive) | ||
- `"yes"` (case-insensitive) | ||
- `"t"` (case-insensitive) | ||
- `"1"` | ||
All other values resolve to `false`. | ||
### .default(v) | ||
The default value of the property. If no environment variable is set for this | ||
property, then use the default. | ||
### .description(d) | ||
#### .desc(d), .description(d) | ||
@@ -173,3 +154,3 @@ A description of the property. This is used when generating an example | ||
### .example(v) | ||
#### .example(v) | ||
@@ -179,3 +160,3 @@ An example value of the property. This is used when generating an example | ||
### .optional | ||
#### .optional | ||
@@ -186,4 +167,10 @@ Mark the property as optional. If marked as optional and the property does not | ||
### .regex(re, [description]) | ||
### String | ||
#### .string | ||
Declares that this property is a string. | ||
#### .regex(re, [description]) | ||
Validate that the string provided matches the given regex. If it does not, the | ||
@@ -203,1 +190,77 @@ optional description will be displayed as the error message. | ||
``` | ||
### Number | ||
#### .number | ||
Declares this this property is an integer. | ||
### Boolean | ||
#### .bool | ||
Declare that this property is a boolean. | ||
Values that resolve to `true` are: | ||
- `"true"` (case-insensitive) | ||
- `"yes"` (case-insensitive) | ||
- `"t"` (case-insensitive) | ||
- `"1"` | ||
All other values resolve to `false`. | ||
### URL | ||
#### .url | ||
Declares that this property is a URL. | ||
#### .protocol(str|regex) | ||
Validate that the URL provided matches the required protocol. | ||
```js | ||
const lawnSepc = { | ||
WEB_API: lawn.url.protocol(/http|https/) | ||
} | ||
lawn.validate(lawnSpec, { WEB_API: 'https://example.com/api' }) | ||
//=> { WEB_API: 'https://example.com/api' } | ||
lawn.validate(lawnSpec, { WEB_API: 'mysql://user:pass@host/database' }) | ||
//=> throws "WEB_API is invalid: 'mysql://user:pass@host/database' must have a protocol that matches /http|https/" | ||
``` | ||
#### .defaultQuery(name, val) | ||
Defaults a query string parameter to the given value. | ||
```js | ||
const lawnSepc = { | ||
MYSQL: lawn.url.defaultQuery('connectionLimit', '8') | ||
} | ||
lawn.validate(lawnSpec, { MYSQL: 'mysql://user:pass@host/database' }) | ||
//=> { MYSQL: 'mysql://user:pass@host/database?connectionLimit=8' } | ||
lawn.validate(lawnSpec, { MYSQL: 'mysql://user:pass@host/database?connectionLimit=2' }) | ||
//=> { MYSQL: 'mysql://user:pass@host/database?connectionLimit=2' } | ||
``` | ||
#### .overrideQuery(name, val) | ||
Forces a query string parameter to the given value. This is useful if a certain | ||
query string value needs to be set on the URL. | ||
```js | ||
const lawnSepc = { | ||
MYSQL: lawn.url.overrideQuery('multipleStatements', 'true') | ||
} | ||
lawn.validate(lawnSpec, { MYSQL: 'mysql://user:pass@host/database' }) | ||
//=> { MYSQL: 'mysql://user:pass@host/database?multipleStatements=true' } | ||
lawn.validate(lawnSpec, { MYSQL: 'mysql://user:pass@host/database?multipleStatements=false' }) | ||
//=> { MYSQL: 'mysql://user:pass@host/database?multipleStatements=true' } | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25815
14
572
262
3