diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000..06e61ae --- /dev/null +++ b/.nycrc @@ -0,0 +1,7 @@ +{ + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 +} \ No newline at end of file diff --git a/README.md b/README.md index bdbcfdf..811816f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ const Determination = require('@vrbo/determination'); **Determination.create(options)** - `options` (_Object_) - an options object containing: - - `config` (_String_) - required path to a JSON configuration. + - `config` (_String_ | _Object_) - required, either a path to a JSON configuration file or an object. + - `basedir` (_String_) - optional path used for resolving relative imports within configs. If config is a file, it defaults to the config file's directory. If config is an object, it defaults to `process.cwd()`. - `criteria` (_Object_) - optional resolution criteria. See [confidence](https://github.com/hapijs/confidence). Minimally will always contain `process.env` under the key `env`. - `protocols` (_Object_) - optional mapping of protocols for [shortstop](https://github.com/krakenjs/shortstop). Protocols are bound with context `config`, where `config` is the configuration being resolved. Obviously this doesn't work with arrow functions. - `defaults` (_Object_ | _String_) - optional default pre-resolved configuration values. diff --git a/lib/index.js b/lib/index.js index 96ae1b7..15bfe96 100644 --- a/lib/index.js +++ b/lib/index.js @@ -31,7 +31,8 @@ const Resolver = require('./resolver'); const Store = require('./store'); const schema = Joi.object({ - config: Joi.string().required(), + config: Joi.alternatives(Joi.string(), Joi.object()).required(), + basedir: Joi.string(), criteria: Joi.object().default({}), protocols: Joi.object().default({}), defaults: Joi.alternatives(Joi.string(), Joi.object()).default({}), diff --git a/lib/resolver.js b/lib/resolver.js index 8ec18fc..6af5b56 100644 --- a/lib/resolver.js +++ b/lib/resolver.js @@ -69,10 +69,14 @@ const loadAndParseJson = function (file) { } }; -const resolver = async function ({ config, criteria, protocols, defaults, overrides }) { +const resolver = async function ({ config, basedir, criteria, protocols, defaults, overrides }) { - const basedir = Path.dirname(config); - const configobject = loadAndParseJson(config); + let configobject = config; + + if (typeof config === 'string') { + configobject = loadAndParseJson(config); + basedir = Path.dirname(config); + } if (typeof defaults === 'string') { defaults = loadAndParseJson(defaults); @@ -91,6 +95,10 @@ const resolver = async function ({ config, criteria, protocols, defaults, overri const importsResolved = await resolveProtocols(resolvedCriteria, { import(key) { + if (!basedir) { + console.log('@vrbo/determination: No basedir set, defaulting to "process.cwd()" for resolving relative json imports.'); + basedir = process.cwd(); + } return resolveCriteria(loadAndParseJson(Path.resolve(Path.join(basedir, key))), criteria); } }); @@ -112,6 +120,7 @@ const resolver = async function ({ config, criteria, protocols, defaults, overri result = result[prop]; } + /* istanbul ignore next */ return keys.length ? null : result; } }); diff --git a/package.json b/package.json index 7d11d01..b436d3e 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ }, "scripts": { "commit": "cz", - "cover": "nyc npm test", "lint": "eslint lib", - "test": "npm run lint && ENV_TEST=5678 tape test/*.js" + "unit": "npm run lint && ENV_TEST=5678 tape test/*.js", + "test": "nyc npm run unit" }, "config": { "commitizen": { diff --git a/test/test-determination.js b/test/test-determination.js index 55a6f21..a056f3e 100644 --- a/test/test-determination.js +++ b/test/test-determination.js @@ -45,6 +45,50 @@ Test('test determination', (t) => { }); + t.test('resolve import, no basedir', async (t) => { + t.plan(1); + + const criteria = { + pass: 'false' + }; + + const configObj = { + a: 'import:./test/fixtures/a.json' + }; + + try { + const config = await Determination.create({ config: configObj, criteria }).resolve(); + + t.equal(config.get('a.test.value'), false, 'criteria resolved.'); + } + catch (error) { + console.log(error); + } + + }); + + t.test('resolve with config as object', async (t) => { + t.plan(3); + + const configObj = { + test: 'foo', + copy: 'config:test', + array: ['config:test'] + }; + + try { + const config = await Determination.create({ config: configObj, basedir: '/' }).resolve(); + + t.equal(config.get('test'), 'foo', 'criteria resolved.'); + t.equal(config.get('copy'), 'foo', 'config resolved.'); + t.equal(config.get('array')[0], 'foo', 'array resolved protocol.'); + } + catch (error) { + console.log(error); + } + + }); + t.test('resolve with defaults', async (t) => { t.plan(3);