Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

confort

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

confort - npm Package Compare versions

Comparing version 0.1.4 to 0.2.0

deno/main.js

14

CHANGELOG.md

@@ -8,2 +8,15 @@ # Confort Changelog

## [v0.2.0] - 2021-01-18
### Added
- support for [Deno](https://deno.land/)
### Changed
- entire interface to a simple funcion (`confort(...layers)`)
### Removed
- reloading capabilities
- layer history
- support to CSON format
## [v0.1.4] - 2020-08-07

@@ -40,1 +53,2 @@

[v0.1.4]: https://gitlab.com/GCSBOSS/confort/-/tags/v0.1.4
[v0.2.0]: https://gitlab.com/GCSBOSS/confort/-/tags/v0.2.0

98

lib/main.js

@@ -1,83 +0,45 @@

const fs = require('fs');
const assert = require('assert');
const deepmerge = require('deepmerge');
const EventEmitter = require('events');
const loadConf = require('./conf-loader');
const fs = require('fs')
const path = require('path')
function createWatches(files, cb){
return files.map( f =>
fs.watch(f, { persistent: false }, op =>
op == 'change' && cb() ) );
}
function setupWatches(){
for(let w of this.watchers)
w.close();
this.watchers = [];
if(this.shouldReload)
this.watchers = createWatches(this.files, () => this.reload());
}
const isMergeableObject = val =>
val && typeof val === 'object' &&
(!val.constructor || ['Object', 'Array'].includes(val.constructor.name));
Boolean(val) && typeof val === 'object' &&
(!val.constructor || 'Object' === val.constructor.name)
module.exports = class Confort extends EventEmitter {
function deepMerge(...subjects){
constructor(firstLayer, type){
super();
this.object = {};
this.layers = [];
this.files = [];
this.watchers = [];
const root = {}
this.shouldReload = false;
for(const obj of subjects){
if(!isMergeableObject(obj))
throw new Error('Cannot merge non-object')
if(firstLayer)
this.addLayer(firstLayer, type);
for(const k in obj)
root[k] = isMergeableObject(root[k]) && isMergeableObject(obj[k])
? deepMerge(root[k], obj[k])
: root[k] = obj[k]
}
addLayer(pathOrObject, fileType){
this.layers.push([pathOrObject, fileType]);
return root
}
if(typeof pathOrObject == 'string'){
this.files.push(pathOrObject);
pathOrObject = loadConf(pathOrObject, fileType);
}
const loaders = {
toml: conf => require('toml').parse(conf),
yaml: conf => require('yaml').parse(conf),
json: conf => JSON.parse(conf)
}
assert.strictEqual(typeof pathOrObject, 'object');
loaders.yml = loaders.yaml;
this.object = deepmerge(this.object, pathOrObject, {
arrayMerge: (a, b) => b,
isMergeableObject
});
function loadConf(conf){
const type = path.extname(conf).substr(1);
setupWatches.bind(this)();
if(typeof loaders[type] !== 'function')
throw new Error('Conf type not supported: ' + type);
this.emit('layer');
}
return loaders[type](fs.readFileSync(conf, 'utf-8'));
}
reload(){
let layers = this.layers;
this.clear();
layers.forEach(l => this.addLayer(...l));
this.emit('reload');
}
set liveReload(enabled){
enabled = Boolean(enabled);
if(enabled == this.shouldReload)
return;
this.shouldReload = enabled;
setupWatches.bind(this)();
}
clear(){
this.object = {};
this.layers = [];
this.files = [];
}
module.exports = function confort(...subjects){
subjects = subjects.map(c => typeof c == 'string' ? loadConf(c) : c);
return deepMerge(...subjects);
}
{
"name": "confort",
"version": "0.1.4",
"version": "0.2.0",
"description": "A library for incrementally build config objects through layering config files in many formats.",

@@ -18,6 +18,3 @@ "main": "lib/main.js",

"layer",
"env",
"watch",
"live",
"reload"
"env"
],

@@ -33,10 +30,7 @@ "author": "Guilherme C. Souza",

},
"dependencies": {
"deepmerge": "^4.0.0"
},
"dependencies": {},
"devDependencies": {
"cson-parser": "^4.0.2",
"toml": "^3.0.0",
"yaml": "^1.7.0"
"yaml": "^1.10.0"
}
}

@@ -9,6 +9,6 @@ # [Confort](https://gitlab.com/GCSBOSS/confort)

- Merge configs chronologically
- Option to watch changes in loaded config files
- Supported formats so far: TOML (`toml`), JSON, CSON (`cson-parser`) and YAML (`yaml`)
- Available in the Deno ecosystem (see directory `/deno`)
## Get Started
## Get Started on NodeJS

@@ -22,27 +22,29 @@ Install with: `npm i -P confort`

```js
const Confort = require('confort');
const confort = require('confort');
// Constructor Forms
let conf = new Confort(); // Empty conf object
let conf = new Confort({ key: 'value', key2: 'value2' }); // Initial conf from objects
let conf = new Confort('./my-file.toml'); // Initial conf from file
let conf = confort(); // Empty conf object
let conf = confort({ key: 'value', key2: 'value2' }); // Initial conf from objects
let conf = confort('./my-file.toml'); // Initial conf from file
// Adding incremental config layer
conf.addLayer({ key: 'value' });
conf.object; // => { key: 'value' }
conf.addLayer({ key: 'newValue', otherKey: 'value' });
conf.object; // => { key: 'newValue', otherKey: 'value' }
conf.addLayer('./my-file.yml');
let c1 = confort(c1, { key: 'value' }); // => { key: 'value' }
c1 = confort(c1, { key: 'newValue', otherKey: 'value' }); // => { key: 'newValue', otherKey: 'value' }
c1 = confort(c1, './my-file.yml');
```
// Reset the configuration for reuse
conf.clear();
## Get Started on [Deno](https://deno.land/)
// Reset the conf and reapply all layers effectively reading changes in files
conf.reload();
```js
import confort from 'https://deno.land/x/confort@0.2.0/deno/main.js'
// Enable auto reload when changes in loaded conf files happen
conf.liveReload = true;
// Constructor Forms
let conf = confort(); // Empty conf object
let conf = confort({ key: 'value', key2: 'value2' }); // Initial conf from objects
let conf = confort('./my-file.toml'); // Initial conf from file
// Disables auto reload
conf.liveReload = false;
// Adding incremental config layer
let c1 = confort(c1, { key: 'value' }); // => { key: 'value' }
c1 = confort(c1, { key: 'newValue', otherKey: 'value' }); // => { key: 'newValue', otherKey: 'value' }
c1 = confort(c1, './my-file.yml');
```

@@ -49,0 +51,0 @@

const fs = require('fs');
const assert = require('assert');
describe('Conf Loader', () => {
const loadConf = require('../lib/conf-loader');
const confort = require('../lib/main.js');
it('Should fail if no conf file is specified', () => {
assert.throws( () => loadConf() );
describe('confort', () => {
it('Should fail merging non-object', () => {
assert.throws(() => confort(234));
});
it('Should fail if conf file is not found', () => {
assert.throws( () => loadConf('./bla.json', /couldn't open/) );
it('Should add layers if specified', () => {
let conf = confort({ key: 'value' });
assert.strictEqual(conf.key, 'value');
});
it('Should fail if given conf type is not supported', () => {
assert.throws( () => loadConf('./test/res/conf.xml'), /type not supported/ );
it('Should add object layer', () => {
let conf = confort({});
conf = confort(conf, { key: 'value' });
assert.strictEqual(conf.key, 'value');
});
it('Should properly load a TOML file and generate an object', () => {
let obj = loadConf('./test/res/conf.toml');
assert.strictEqual(obj.key, 'value');
it('Should override layer values also present in previous state', () => {
let conf = confort({ key: 'value' }, { key: 'valu3' });
assert.strictEqual(conf.key, 'valu3');
});
it('Should properly load an YAML file and generate an object', () => {
let obj = loadConf('./test/res/conf.yaml');
assert.strictEqual(obj.key, 'value');
it('Should preserve values not present in new layer', () => {
let conf = confort({ key: 'value' });
conf = confort(conf, { newKey: 'valu3' });
assert.strictEqual(conf.key, 'value');
});
it('Should properly load an JSON file and generate an object', () => {
let obj = loadConf('./test/res/conf.json');
assert.strictEqual(obj.key, 'value');
it('Should merge objects instead of just replacing', () => {
let conf = confort({ key: { a: 'b', e: [ 0 ] } });
conf = confort(conf, { key: { c: 'd', e: [ 1 ] } });
assert.strictEqual(conf.key.a, 'b');
assert.strictEqual(conf.key.c, 'd');
assert.strictEqual(conf.key.e[0], 1);
});
it('Should properly load an CSON file and generate an object', () => {
let obj = loadConf('./test/res/conf.cson');
assert.strictEqual(obj.key, 'value');
it('Should merge objects with null prototype', () => {
let o = Object.create(null);
o.b = 2;
let conf = confort(o);
conf = confort(conf, { a: 1 });
assert.strictEqual(conf.a, 1);
assert.strictEqual(conf.b, 2);
});
});
describe('Confort', function(){
const Confort = require('../lib/main.js');
it('Should replace objects with a class other than Object', () => {
class Foo{ constructor(){ this.a = 'foo' } }
let conf = confort({ key: { a: 'a', b: 'bar' } }, { key: new Foo() });
assert.strictEqual(conf.key.a, 'foo');
assert.strictEqual(typeof conf.key.b, 'undefined');
});
describe('constructor ( pathOrObject, [fileType] )', () => {
it('Should add layers if specified', () => {
let conf = new Confort({ key: 'value' });
assert.strictEqual(conf.object.key, 'value');
});
it('Should fail if given conf type is not supported', () => {
assert.throws(() => confort('./conf.xml'));
});
describe('::addLayer ( pathOrObject, [fileType] )', () => {
it('Should add object layer', () => {
let conf = new Confort();
conf.addLayer({ key: 'value' });
assert.strictEqual(conf.object.key, 'value');
});
it('Should override layer values also present in previous state', () => {
let conf = new Confort({ key: 'value' });
conf.addLayer({ key: 'valu3' });
assert.strictEqual(conf.object.key, 'valu3');
});
it('Should preserve values not present in new layer', () => {
let conf = new Confort({ key: 'value' });
conf.addLayer({ newKey: 'valu3' });
assert.strictEqual(conf.object.key, 'value');
});
it('Should merge objects instead of just replacing', () => {
let conf = new Confort({ key: { a: 'b', e: [ 0 ] } });
conf.addLayer({ key: { c: 'd', e: [ 1 ] } });
assert.strictEqual(conf.object.key.a, 'b');
assert.strictEqual(conf.object.key.c, 'd');
assert.strictEqual(conf.object.key.e[0], 1);
});
it('Should merge objects with null prototype', () => {
let o = Object.create(null);
o.b = 2;
let conf = new Confort(o);
conf.addLayer({ a: 1 });
assert.strictEqual(conf.object.a, 1);
assert.strictEqual(conf.object.b, 2);
});
it('Should replace objects with a class other than Object', () => {
class Foo{ constructor(){ this.a = 'foo'; } }
let conf = new Confort({ key: { a: 'a', b: 'bar' } });
conf.addLayer({ key: new Foo() });
assert.strictEqual(conf.object.key.a, 'foo');
assert.strictEqual(typeof conf.object.key.b, 'undefined');
});
it('Should load conf file when path is specified', () => {
let conf = new Confort('./test/res/conf.toml');
assert.strictEqual(conf.object.key, 'value');
});
it('Should record layers info', () => {
let conf = new Confort('./test/res/conf.toml');
conf.addLayer({ newKey: 'valu3' });
assert.strictEqual(conf.layers[0][0], './test/res/conf.toml');
assert.strictEqual(conf.layers[1][0].newKey, 'valu3');
assert.strictEqual(conf.files[0], './test/res/conf.toml');
});
it('Should emit layer event', done => {
let conf = new Confort('./test/res/conf.toml');
conf.on('layer', done);
conf.addLayer({ newKey: 'valu3' });
});
it('Should add new files to watch list if watching', function(done){
fs.copyFileSync('./test/res/conf.toml', './node_modules/conf.toml');
let conf = new Confort();
conf.liveReload = true;
conf.addLayer('./node_modules/conf.toml');
conf.on('reload', () => {
conf.liveReload = false;
done();
});
fs.writeFileSync('./node_modules/conf.toml', 'key = \'value2\'');
});
it('Should fail if conf file is not found', () => {
assert.throws(() => confort('./bla.json'));
});
describe('::clear ( )', () => {
it('Should remove all records and clear state', () => {
let conf = new Confort('./test/res/conf.toml');
conf.addLayer({ newKey: 'valu3' });
conf.clear();
assert.strictEqual(conf.layers.length, 0);
assert.strictEqual(conf.files.length, 0);
assert.strictEqual(Object.keys(conf.object).length, 0);
});
it('Should properly load a TOML file and generate an object', () => {
fs.writeFileSync(__dirname + '/a.toml', 'key = "value"', 'utf-8');
let conf = confort(__dirname + '/a.toml');
assert.strictEqual(conf.key, 'value');
fs.unlink(__dirname + '/a.toml', Function.prototype);
});
describe('::reload ( )', () => {
it('Should emit reload event', done => {
let conf = new Confort('./test/res/conf.toml');
conf.on('reload', done);
conf.reload();
});
it('Should maintain state unchanged when no file changed', () => {
let conf = new Confort('./test/res/conf.toml');
conf.addLayer({ newKey: 'valu3' });
conf.reload();
assert.strictEqual(conf.layers[0][0], './test/res/conf.toml');
assert.strictEqual(conf.layers[1][0].newKey, 'valu3');
assert.strictEqual(conf.files[0], './test/res/conf.toml');
assert.strictEqual(conf.object.key, 'value');
});
it('Should load any values changed in recorded files', () => {
fs.copyFileSync('./test/res/conf.toml', './node_modules/conf.toml');
let conf = new Confort('./node_modules/conf.toml');
conf.addLayer({ newKey: 'valu3' });
fs.writeFileSync('./node_modules/conf.toml', 'key = \'value2\'');
conf.reload();
assert.strictEqual(conf.object.key, 'value2');
});
it('Should properly load an YAML file and generate an object', () => {
fs.writeFileSync(__dirname + '/a.yml', 'key: value', 'utf-8');
let conf = confort(__dirname + '/a.yml');
assert.strictEqual(conf.key, 'value');
fs.unlink(__dirname + '/a.yml', Function.prototype);
});
describe('::liveReload ( enabled )', () => {
it('Should reload on conf file changes', function(done){
fs.copyFileSync('./test/res/conf.toml', './node_modules/conf.toml');
let conf = new Confort('./node_modules/conf.toml');
conf.liveReload = true;
conf.liveReload = true;
fs.writeFileSync('./node_modules/conf.toml', 'key = \'value2\'');
conf.on('reload', () => {
assert.strictEqual(conf.object.key, 'value2');
conf.liveReload = false;
done();
});
});
it('Should noop when enabling already enabled', function(){
let conf = new Confort();
conf.liveReload = true;
conf.liveReload = true;
conf.liveReload = false;
});
it('Should properly load an JSON file and generate an object', () => {
fs.writeFileSync(__dirname + '/a.json', '{"key": "value"}', 'utf-8');
let conf = confort(__dirname + '/a.json');
assert.strictEqual(conf.key, 'value');
fs.unlink(__dirname + '/a.json', Function.prototype);
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc