Comparing version 0.2.0 to 0.3.0
@@ -11,5 +11,5 @@ { | ||
"no_throwing_strings": {"level": "error"}, | ||
"cyclomatic_complexity": {"level": "warn", "value": 5}, | ||
"cyclomatic_complexity": {"level": "warn", "value": 9}, | ||
"line_endings": {"level": "error", "value": "unix"}, | ||
"no_implicit_parens": {"level": "ignore"} | ||
} |
// Generated by CoffeeScript 1.3.3 | ||
(function() { | ||
var FileNotFoundError, Store, conifer, fs, handler, util, verifyArg; | ||
var FileNotFoundError, IMPORT_INDICATOR, IOError, Store, async, cleanImportString, conifer, fs, getFileHandlerForPath, handler, importStringRegexp, newFileNotFoundError, newFileReadError, newHandlerNotFoundError, parse, parseObjectImports, parseObjectImportsSync, parseSync, path, propertyIsImportMerge, removeImportMergeProperties, util, valueIsImportProperty, verifyArg, _ref, | ||
__hasProp = {}.hasOwnProperty; | ||
FileNotFoundError = require('er').FileNotFoundError; | ||
async = require('async'); | ||
_ref = require('er'), FileNotFoundError = _ref.FileNotFoundError, IOError = _ref.IOError; | ||
fs = require('fs'); | ||
@@ -11,2 +14,4 @@ | ||
path = require('path'); | ||
Store = require('./store').Store; | ||
@@ -24,33 +29,118 @@ | ||
conifer.parse = function(filePath, callback) { | ||
parse = function(filePath, callback, returnStore) { | ||
verifyArg.isUnemptyString('filePath', filePath); | ||
verifyArg.isFunction('callback', callback); | ||
return fs.stat(filePath, function(err, stats) { | ||
var fileExtension; | ||
if ((err != null) || !stats.isFile()) { | ||
return callback(null, new FileNotFoundError("Config file at " + filePath + " was not found, or is not a file")); | ||
} else { | ||
fileExtension = util.path.getFileExtension(filePath); | ||
handler = conifer.handler.getHandler(fileExtension); | ||
if (!(handler != null)) { | ||
callback(null, new conifer.handler.HandlerNotFoundError("Handler for '" + fileExtension + "' was not found")); | ||
return; | ||
handler = null; | ||
return async.waterfall([ | ||
function(done) { | ||
return fs.stat(filePath, function(error, stats) { | ||
if ((error != null) || !stats.isFile()) { | ||
return done(newFileNotFoundError(filePath)); | ||
} else { | ||
return done(); | ||
} | ||
}); | ||
}, function(done) { | ||
try { | ||
handler = getFileHandlerForPath(filePath); | ||
return done(null, handler); | ||
} catch (error) { | ||
return done(error); | ||
} | ||
return fs.readFile(filePath, 'utf8', function(err, data) { | ||
var parsedData; | ||
try { | ||
parsedData = handler(data); | ||
return callback(new conifer.Store(parsedData), null); | ||
} catch (error) { | ||
return callback(null, error); | ||
}, function(handler, done) { | ||
return fs.readFile(filePath, 'utf8', function(error, fileContent) { | ||
if (error != null) { | ||
return done(newFileReadError(filePath)); | ||
} else { | ||
return done(null, handler, fileContent); | ||
} | ||
}); | ||
}, function(handler, fileContent, done) { | ||
try { | ||
return done(null, handler(fileContent)); | ||
} catch (error) { | ||
return done(error); | ||
} | ||
}, function(parsedContent, done) { | ||
var queue; | ||
try { | ||
queue = async.queue((function(task, taskDone) { | ||
return task(taskDone, done); | ||
}), 3); | ||
queue.drain = function() { | ||
return done(null, parsedContent); | ||
}; | ||
parseObjectImports(parsedContent, path.dirname(filePath), queue); | ||
return queue.push(function(taskDone, parseDone) { | ||
return taskDone(); | ||
}); | ||
} catch (error) { | ||
return done(error); | ||
} | ||
} | ||
], function(error, result) { | ||
if (error) { | ||
return callback(null, error); | ||
} else if (returnStore) { | ||
return callback(new conifer.Store(result), null); | ||
} else { | ||
return callback(result, null); | ||
} | ||
}); | ||
}; | ||
conifer.parseSync = function(filePath) { | ||
var data, fileError, fileExtension, parsedData, stats; | ||
parseObjectImports = function(object, importBasePath, queue) { | ||
var property, value, _fn; | ||
_fn = function(property, value) { | ||
var importFilePath, _i, _len, _results; | ||
if (propertyIsImportMerge(property, value)) { | ||
_results = []; | ||
for (_i = 0, _len = value.length; _i < _len; _i++) { | ||
importFilePath = value[_i]; | ||
_results.push((function(importFilePath) { | ||
importFilePath = path.resolve(importBasePath + '/' + importFilePath); | ||
return queue.push(function(taskDone, parseDone) { | ||
return parse(importFilePath, function(importContent, error) { | ||
if (error) { | ||
return parseDone(error); | ||
} else { | ||
util.mergeObjects(object, importContent); | ||
return taskDone(); | ||
} | ||
}, false); | ||
}); | ||
})(importFilePath)); | ||
} | ||
return _results; | ||
} else if (valueIsImportProperty(value)) { | ||
importFilePath = path.resolve(importBasePath + '/' + cleanImportString(value)); | ||
return queue.push(function(taskDone, parseDone) { | ||
return parse(importFilePath, function(importContent, error) { | ||
if (error) { | ||
return parseDone(error); | ||
} else { | ||
object[property] = importContent; | ||
return taskDone(); | ||
} | ||
}, false); | ||
}); | ||
} else if (typeof value === 'object') { | ||
return parseObjectImports(value, importBasePath, queue); | ||
} | ||
}; | ||
for (property in object) { | ||
if (!__hasProp.call(object, property)) continue; | ||
value = object[property]; | ||
_fn(property, value); | ||
} | ||
return removeImportMergeProperties(object); | ||
}; | ||
conifer.parse = function(filePath, callback) { | ||
return parse(filePath, callback, true); | ||
}; | ||
parseSync = function(filePath, returnStore) { | ||
var fileContent, parsedContent, stats; | ||
verifyArg.isUnemptyString('filePath', filePath); | ||
fileError = new FileNotFoundError("Config file at " + filePath + " was not found, or is not a file"); | ||
try { | ||
@@ -62,15 +152,111 @@ stats = fs.statSync(filePath); | ||
} catch (error) { | ||
throw fileError; | ||
throw newFileNotFoundError(filePath); | ||
} | ||
handler = getFileHandlerForPath(filePath); | ||
fileContent = null; | ||
try { | ||
fileContent = fs.readFileSync(filePath, 'utf8'); | ||
} catch (error) { | ||
throw newFileReadError(filePath); | ||
} | ||
parsedContent = handler(fileContent); | ||
parseObjectImportsSync(parsedContent, path.dirname(filePath)); | ||
if (returnStore) { | ||
return new conifer.Store(parsedContent); | ||
} else { | ||
return parsedContent; | ||
} | ||
}; | ||
parseObjectImportsSync = function(object, importBasePath) { | ||
var importFilePath, property, value, _i, _len; | ||
for (property in object) { | ||
if (!__hasProp.call(object, property)) continue; | ||
value = object[property]; | ||
if (propertyIsImportMerge(property, value)) { | ||
for (_i = 0, _len = value.length; _i < _len; _i++) { | ||
importFilePath = value[_i]; | ||
importFilePath = path.resolve(importBasePath + '/' + importFilePath); | ||
util.mergeObjects(object, parseSync(importFilePath, false)); | ||
} | ||
} else if (valueIsImportProperty(value)) { | ||
importFilePath = path.resolve(importBasePath + '/' + cleanImportString(value)); | ||
object[property] = parseSync(importFilePath, false); | ||
} else if (typeof value === 'object') { | ||
parseObjectImportsSync(value, importBasePath); | ||
} | ||
} | ||
return removeImportMergeProperties(object); | ||
}; | ||
conifer.parseSync = function(filePath) { | ||
return parseSync(filePath, true); | ||
}; | ||
newFileNotFoundError = function(filePath) { | ||
return new FileNotFoundError("Config file at " + filePath + " was not found, or is not a file"); | ||
}; | ||
newHandlerNotFoundError = function(fileExtension) { | ||
return new conifer.handler.HandlerNotFoundError("Handler for '" + fileExtension + "' was not found"); | ||
}; | ||
newFileReadError = function(filePath) { | ||
return new IOError("Config file at " + filePath + " could not be read"); | ||
}; | ||
getFileHandlerForPath = function(filePath) { | ||
var fileExtension; | ||
fileExtension = util.path.getFileExtension(filePath); | ||
handler = conifer.handler.getHandler(fileExtension); | ||
if (!(handler != null)) { | ||
throw new conifer.handler.HandlerNotFoundError("Handler for '" + fileExtension + "' was not found"); | ||
return; | ||
throw newHandlerNotFoundError(fileExtension); | ||
} | ||
data = fs.readFileSync(filePath, 'utf8'); | ||
parsedData = handler(data); | ||
return new conifer.Store(parsedData); | ||
return handler; | ||
}; | ||
IMPORT_INDICATOR = '<<'; | ||
propertyIsImportMerge = function(property, value) { | ||
var item, _i, _len; | ||
if (property === IMPORT_INDICATOR && Array.isArray(value)) { | ||
for (_i = 0, _len = value.length; _i < _len; _i++) { | ||
item = value[_i]; | ||
if (typeof item !== 'string') { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}; | ||
removeImportMergeProperties = function(object) { | ||
var property, value, _results; | ||
_results = []; | ||
for (property in object) { | ||
if (!__hasProp.call(object, property)) continue; | ||
value = object[property]; | ||
if (propertyIsImportMerge(property, value)) { | ||
_results.push(delete object[property]); | ||
} else if (typeof value === 'object' && !Array.isArray(value)) { | ||
_results.push(removeImportMergeProperties(value)); | ||
} else { | ||
_results.push(void 0); | ||
} | ||
} | ||
return _results; | ||
}; | ||
importStringRegexp = new RegExp("^" + IMPORT_INDICATOR + "\\s+"); | ||
valueIsImportProperty = function(value) { | ||
return typeof value === 'string' && importStringRegexp.test(value); | ||
}; | ||
cleanImportString = function(importString) { | ||
return importString.replace(importStringRegexp, ''); | ||
}; | ||
}).call(this); |
// Generated by CoffeeScript 1.3.3 | ||
(function() { | ||
var ArgumentError, ArgumentMissingError, ArgumentTypeError, BadConstructionError, path, _ref; | ||
var ArgumentError, ArgumentMissingError, ArgumentTypeError, BadConstructionError, path, _ref, | ||
__hasProp = {}.hasOwnProperty; | ||
@@ -51,2 +52,14 @@ _ref = require('er'), ArgumentError = _ref.ArgumentError, ArgumentMissingError = _ref.ArgumentMissingError, ArgumentTypeError = _ref.ArgumentTypeError, BadConstructionError = _ref.BadConstructionError; | ||
exports.mergeObjects = function(object1, object2) { | ||
var property, value; | ||
exports.verifyArg.isObject('object1', object1); | ||
exports.verifyArg.isObject('object2', object2); | ||
for (property in object2) { | ||
if (!__hasProp.call(object2, property)) continue; | ||
value = object2[property]; | ||
object1[property] = value; | ||
} | ||
return object1; | ||
}; | ||
}).call(this); |
{ | ||
"name": "conifer", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "A multi-format, file-based configuration library for Node.", | ||
@@ -26,2 +26,3 @@ "keywords": [ | ||
"dependencies": { | ||
"async": "0.1.x", | ||
"cson": "1.2.x", | ||
@@ -28,0 +29,0 @@ "er": "0.2.x", |
@@ -16,2 +16,15 @@ | ||
Installing | ||
---------- | ||
Install Conifer through `npm`. Either with the command: | ||
```sh | ||
npm install conifer | ||
``` | ||
or by adding `conifer` to the dependencies in your project | ||
`package.json`. | ||
Basic Usage | ||
@@ -101,2 +114,86 @@ ----------- | ||
Configuration Importing | ||
----------------------- | ||
Your config files are able to import other configurations as | ||
properties, or by merging them into the current object. This | ||
allows for a single entry-point for your configuration, as well | ||
as a more managable and reusable set of config files. | ||
Examples below are mostly in JSON but this will work for all | ||
supported file types, as well as allowing for cross-file-type | ||
importing. | ||
### Import Properties | ||
Import properties allow you to import the contents of another | ||
config file into a property. They work on a property whose | ||
(string) value begins with `<< `. So if we have the following | ||
files: | ||
config/main.json: | ||
```json | ||
{ | ||
"name": "Hello World", | ||
"routes": "<< ./routes.json" | ||
} | ||
``` | ||
config/routes.json: | ||
```json | ||
{ | ||
"/": "controller/index", | ||
"/about": "controller/about", | ||
} | ||
``` | ||
Parsing `config/main.json` will result in the following | ||
structure: | ||
```json | ||
{ | ||
"name": "Hello World", | ||
"routes": { | ||
"/": "controller/index", | ||
"/about": "controller/about", | ||
} | ||
} | ||
``` | ||
### Import Merges | ||
Import merges allow you to merge the contents of another config | ||
file into the current object. Merges are indicated by the `<<` | ||
property of an object, which should be set to an array of file | ||
names. If we have the following files: | ||
config/main.json: | ||
```json | ||
{ | ||
"name": "Hello World", | ||
"outputErrors": true, | ||
"<<": [ | ||
"./production.json" | ||
] | ||
} | ||
``` | ||
config/production.json: | ||
```json | ||
{ | ||
"outputErrors": false, | ||
"logErrors": true | ||
} | ||
``` | ||
Parsing `config/main.json` will result in the following | ||
structure: | ||
```json | ||
{ | ||
"name": "Hello World", | ||
"outputErrors": false, | ||
"logErrors": true | ||
} | ||
``` | ||
Extending With File Handlers | ||
@@ -103,0 +200,0 @@ ---------------------------- |
@@ -18,23 +18,33 @@ | ||
Imports | ||
------- | ||
Property Replacement | ||
-------------------- | ||
I'd like to allow config files to include other config files. | ||
This is what I'm thinking right now: | ||
I may consider at some point adding the ability to insert the | ||
values of configurations in other configurations. Something like | ||
this: | ||
```json | ||
{ | ||
"routes": "<< ./routes.json", | ||
"<<": [ | ||
"./other-config.json", | ||
"./other-config.cson" | ||
] | ||
"name": "Hello World!", | ||
"version": "1.2.3", | ||
"author": { | ||
"name": "Foo Bar", | ||
"email": "foo@bar.com" | ||
}, | ||
"description": "{{name}} is an app which is currently at version {{version}}. It was written by {{author.name}}." | ||
} | ||
``` | ||
In the example above, the contents of `routes.json` would be | ||
parsed and set as the `routes` property in the current config | ||
file. The `other-config.*` files would be merged into the base | ||
object of the current config file. | ||
which would turn into this: | ||
Idea and syntax subject to change. Thoughts? | ||
```json | ||
{ | ||
"name": "Hello World!", | ||
"version": "1.2.3", | ||
"author": { | ||
"name": "Foo Bar", | ||
"email": "foo@bar.com" | ||
}, | ||
"description": "Hello World! is an app which is currently at version 1.2.3. It was written by Foo Bar." | ||
} | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
72072
32
433
291
0
4
+ Addedasync@0.1.x
+ Addedasync@0.1.22(transitive)