diff --git a/__tests__/malformed/config.json b/__tests__/malformed/config.json new file mode 100644 index 0000000..98232c6 --- /dev/null +++ b/__tests__/malformed/config.json @@ -0,0 +1 @@ +{ diff --git a/src/Factory.ts b/src/Factory.ts index 1f24301..24bb21c 100644 --- a/src/Factory.ts +++ b/src/Factory.ts @@ -2,7 +2,7 @@ import path from 'path'; import caller from 'caller'; -import { BaseConfitType, ConfitOptions, IntermediateConfigValue, ShortstopHandler } from './types'; +import { BaseConfitType, ConfitOptions, IntermediateConfigValue } from './types'; import { Config } from './Config'; import { isAbsolutePath, loadJsonc, merge } from './common'; import { argv, convenience, environmentVariables } from './provider'; @@ -10,7 +10,7 @@ import { resolveConfig, resolveCustom, resolveImport } from './handlers'; export class Factory { private basedir: string; - private protocols: Record; + private protocols: ConfitOptions['protocols']; private promise: Promise; constructor(options: ConfitOptions) { diff --git a/src/handlers.ts b/src/handlers.ts index 193959f..e9924f5 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -60,9 +60,7 @@ export async function resolveCustom( for (const protocol of Object.keys(protocols)) { const impls = protocols[protocol]; if (Array.isArray(impls)) { - for (const impl of impls) { - shorty.use(protocol, impl); - } + impls.forEach((impl) => shorty.use(protocol, impl)); } else { shorty.use(protocol, impls); } diff --git a/src/index.spec.ts b/src/index.spec.ts index 512055b..5e1574f 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -299,198 +299,131 @@ describe('confit', () => { expect(config.get('tic:tac')).toBe('toe'); expect(config.get('blue')).toBe(true); }); -}); - -/* - t.test('protocols', function (t) { - var basedir, options; - - process.env.NODE_ENV = 'dev'; - basedir = path.join(__dirname, 'fixtures', 'defaults'); - options = { - basedir: basedir, - protocols: { - path: function (value) { - return path.join(basedir, value); - } - } - }; - - confit(options).create(function (err, config) { - t.error(err); - // Ensure handler was run correctly on default file - t.equal(config.get('misc'), path.join(basedir, 'config.json')); - t.equal(config.get('path'), path.join(basedir, 'development.json')); - - config.use({ path: __filename }); - t.equal(config.get('path'), __filename); - t.end(); - }); - }); - - - t.test('protocols (array)', function (t) { - var basedir, options; - - process.env.NODE_ENV = 'dev'; - basedir = path.join(__dirname, 'fixtures', 'defaults'); - options = { - basedir: basedir, - protocols: { - path: [ - function (value) { - return path.join(basedir, value); - }, - function (value) { - return value + '!'; - } - ] - } - }; - - confit(options).create(function (err, config) { - t.error(err); - // Ensure handler was run correctly on default file - t.equal(config.get('misc'), path.join(basedir, 'config.json!')); - t.equal(config.get('path'), path.join(basedir, 'development.json!')); - - config.use({ path: __filename }); - t.equal(config.get('path'), __filename); - t.end(); - }); - }); - - - t.test('error', function (t) { - var basedir, options; - - process.env.NODE_ENV = 'dev'; - basedir = path.join(__dirname, 'fixtures', 'defaults'); - options = { - basedir: basedir, - protocols: { - path: function (value) { - throw new Error('path'); - } - } - }; - - confit(options).create(function (err, config) { - t.ok(err); - t.notOk(config); - t.end(); - }); - }); - - - t.test('malformed', function (t) { - var basedir = path.join(__dirname, 'fixtures', 'malformed'); - confit(basedir).create(function (err, config) { - t.ok(err); - t.notOk(config); - t.end(); - }); - }); - - - t.test('addOverride', function (t) { - var basedir, factory; - - process.env.NODE_ENV = 'test'; - basedir = path.join(__dirname, 'fixtures', 'defaults'); - - factory = confit(basedir); - factory.addOverride('development.json'); - factory.addOverride(path.join(basedir, 'supplemental.json')); - factory.create(function (err, config) { - t.error(err); - t.ok(config); - t.equal(config.get('default'), 'config'); - t.equal(config.get('override'), 'supplemental'); - t.end(); - }); - }); + test('protocols', async () => { + process.env.NODE_ENV = 'dev'; + const basedir = path.join(__dirname, '..', '__tests__', 'defaults'); + const config = await confit<{ + misc: string; + path: string; + }>({ + basedir, + protocols: { + path: (value: string) => path.join(basedir, value), + }, + }).create(); + expect(config.get('misc')).toBe(path.join(basedir, 'config.json')); + expect(config.get('path')).toBe(path.join(basedir, 'development.json')); + + config.use({ path: __filename }); + expect(config.get('path')).toBe(__filename); + }); - t.test('addOverride error', function (t) { - var basedir; + test('protocols (array)', async () => { + process.env.NODE_ENV = 'dev'; + const basedir = path.join(__dirname, '..', '__tests__', 'defaults'); + const options = { + basedir, + protocols: { + path: [(value: string) => path.join(basedir, value), (value: string) => value + '!'], + }, + }; + const config = await confit<{ + misc: string; + path: string; + }>(options).create(); + expect(config.get('misc')).toBe(path.join(basedir, 'config.json!')); + expect(config.get('path')).toBe(path.join(basedir, 'development.json!')); + + config.use({ path: __filename }); + expect(config.get('path')).toBe(__filename); + }); - t.throws(function () { - confit(path.join(__dirname, 'fixtures', 'defaults')) - .addOverride('nonexistent.json'); - }); + test('error', async () => { + process.env.NODE_ENV = 'dev'; + const basedir = path.join(__dirname, '..', '__tests__', 'defaults'); + const options = { + basedir, + protocols: { + path: () => { + throw new Error('path'); + }, + }, + }; + await expect(confit(options).create()).rejects.toThrow(); + }); - t.throws(function () { - confit(path.join(__dirname, 'fixtures', 'defaults')) - .addOverride('malformed.json'); - }); + test('malformed', async () => { + const basedir = path.join(__dirname, '..', '__tests__', 'malformed'); + await expect(confit({ basedir }).create()).rejects.toThrow(); + }); - t.end(); - }); + test('addOverride', async () => { + process.env.NODE_ENV = 'test'; + const basedir = path.join(__dirname, '..', '__tests__', 'defaults'); + const config = await confit<{ + default: string; + override: string; + }>({ basedir }) + .addOverride('development.json') + .addOverride(path.join(basedir, 'supplemental.json')) + .create(); - t.test('import: with merging objects in imported files', function(t) { + expect(config.get('default')).toBe('config'); + expect(config.get('override')).toBe('supplemental'); + }); - var basedir = path.join(__dirname, 'fixtures', 'import'); - var factory = confit(basedir); - factory.addDefault('override.json'); + test('addOverride error', () => { + const basedir = path.join(__dirname, '..', '__tests__', 'defaults'); + expect(() => confit({ basedir }).addOverride('nonexistent.json').create()).rejects.toThrow(); + expect(() => confit({ basedir }).addOverride('malformed.json').create()).rejects.toThrow(); + }); - factory.create(function(err, config) { - t.error(err); - t.ok(config); - t.equal(config.get('child:grandchild:secret'), 'santa'); - t.equal(config.get('child:grandchild:name'), 'grandchild'); - t.equal(config.get('child:grandchild:another'), 'claus'); - t.end(); - }); - }); + test('import: with merging objects in imported files', async () => { + const basedir = path.join(__dirname, '..', '__tests__', 'import'); + const config = await confit({ basedir }).addDefault('override.json').create(); - t.test('precedence', function (t) { - var factory; - var argv = process.argv; - var env = process.env; + expect(config.getUntypedValue('child:grandchild:secret')).toBe('santa'); + expect(config.getUntypedValue('child:grandchild:name')).toBe('grandchild'); + expect(config.getUntypedValue('child:grandchild:another')).toBe('claus'); + }); - process.argv = [ 'node', __filename, '--override=argv']; - process.env = { - NODE_ENV: 'development', - override: 'env', - misc: 'env' - }; + test('precedence', async () => { + const argv = process.argv; + const env = process.env; + process.argv = ['node', __filename, '--override=argv']; + process.env = { + NODE_ENV: 'development', + override: 'env', + misc: 'env', + }; - factory = confit(path.join(__dirname, 'fixtures', 'defaults')); - factory.create(function (err, config) { - t.error(err); - t.ok(config); - t.equal(config.get('override'), 'argv'); - t.equal(config.get('misc'), 'env'); - process.argv = argv; - process.env = env; - t.end(); - }); - }); - t.test('env ignore', function (t) { - var basedir, options; - - var env = process.env = { - NODE_ENV: 'development', - fromlocal: 'config:local', - local: 'motion', - ignoreme: 'file:./path/to/mindyourbusiness' - }; - basedir = path.join(__dirname, 'fixtures', 'defaults'); - options = { - basedir: basedir, - envignore: ['ignoreme'] - }; + const basedir = path.join(__dirname, '..', '__tests__', 'defaults'); + const factory = confit<{ override: string; misc: string }>({ basedir }); + const config = await factory.create(); + expect(config.get('override')).toBe('argv'); + expect(config.get('misc')).toBe('env'); + process.argv = argv; + process.env = env; + }); - confit(options).create(function (err, config) { - t.error(err); - // Ensure env is read except for the desired ignored property - t.equal(config.get('fromlocal'), env.local); - t.equal(config.get('ignoreme'), undefined); - t.end(); - }); + test('env ignore', async () => { + const env = (process.env = { + NODE_ENV: 'development', + fromlocal: 'config:local', + local: 'motion', + ignoreme: 'file:./path/to/mindyourbusiness', }); - - t.end(); -});*/ + const basedir = path.join(__dirname, '..', '__tests__', 'defaults'); + const config = await confit<{ + fromlocal: string; + ignoreme?: string; + }>({ + basedir, + excludeEnvVariables: ['ignoreme'], + }).create(); + expect(config.get('fromlocal')).toBe(env.local); + expect(config.get('ignoreme')).toBeUndefined(); + }); +}); diff --git a/src/shortstop/create.ts b/src/shortstop/create.ts index feaa890..e681f49 100644 --- a/src/shortstop/create.ts +++ b/src/shortstop/create.ts @@ -70,6 +70,7 @@ class ShortstopHandlers { } handler.stack.push(implementation as ShortstopHandler); + let removed = false; return function unuse() { let idx; @@ -109,6 +110,7 @@ class ShortstopHandlers { if (!handler) { return data; } + const stack = this.getStack(handler.protocol); if (!stack) { return data; diff --git a/src/types.ts b/src/types.ts index 486a2f6..ed3b393 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,7 +6,10 @@ export type ShortstopHandler = ( export interface ConfitOptions { defaults?: string; basedir?: string; - protocols?: Record; + protocols?: Record< + string, + ShortstopHandler | ShortstopHandler[] | ShortstopHandler | ShortstopHandler[] + >; excludeEnvVariables?: string[]; } @@ -20,4 +23,11 @@ export interface BaseConfitType { }; } -export type IntermediateConfigValue = object | string | number | boolean | null | undefined | IntermediateConfigValue[]; \ No newline at end of file +export type IntermediateConfigValue = + | object + | string + | number + | boolean + | null + | undefined + | IntermediateConfigValue[];