├── .gitignore ├── .eslintrc ├── package.json ├── index.js ├── test ├── plugins.js ├── api.js ├── program.js └── helpers.js ├── lib └── compile.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Vendor 2 | node_modules 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": [2, 2], 4 | "quotes": [2, "single"], 5 | "linebreak-style": [2, "unix"], 6 | "semi": [2, "always"] 7 | }, 8 | "env": { 9 | "es6": true, 10 | "node": true 11 | }, 12 | "extends": "eslint:recommended" 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jazzon", 3 | "version": "1.0.0", 4 | "description": "Add some jazz to your JSON files", 5 | "main": "index.js", 6 | "engines": { 7 | "node": "^4.0.0" 8 | }, 9 | "scripts": { 10 | "test": "tape test/**/*.js", 11 | "posttest": "npm run lint", 12 | "lint": "eslint ./" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/tornqvist/jazzon.git" 17 | }, 18 | "keywords": [ 19 | "json", 20 | "extend", 21 | "filter", 22 | "modify", 23 | "transform", 24 | "mock" 25 | ], 26 | "author": "Carl Törnqvist ", 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/tornqvist/jazzon/issues" 30 | }, 31 | "homepage": "https://github.com/tornqvist/jazzon#readme", 32 | "devDependencies": { 33 | "eslint": "^1.4.3", 34 | "tape": "^4.2.0" 35 | }, 36 | "dependencies": { 37 | "co": "^4.6.0", 38 | "is-generator": "^1.0.2", 39 | "lodash.clonedeep": "^3.0.2", 40 | "lodash.iserror": "^3.0.1", 41 | "lodash.isfunction": "^3.0.6", 42 | "lodash.isplainobject": "^3.2.0", 43 | "lodash.isstring": "^3.0.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const compile = require('./lib/compile'); 4 | 5 | /** 6 | * Factory for creating an instance 7 | * @param {Array} plugins List of helpers to "use" 8 | * @return {Object} An instance of jazzon 9 | */ 10 | 11 | function create(plugins) { 12 | plugins = Array.isArray(plugins) ? plugins : []; 13 | 14 | const jazzon = { 15 | 16 | /** 17 | * Allow access to a shallow copy of the plugins 18 | */ 19 | 20 | get plugins() { 21 | return plugins.slice(0); 22 | }, 23 | 24 | /** 25 | * But don't allow modifying it 26 | */ 27 | 28 | set plugins(value) { 29 | throw (new Error('Plugins is immutable')); 30 | } 31 | }; 32 | 33 | /** 34 | * Expose the factory function for modularity 35 | */ 36 | 37 | jazzon.create = create; 38 | 39 | /** 40 | * Set up a simple "use interface" 41 | */ 42 | 43 | jazzon.use = (fn) => { 44 | plugins.push(fn); 45 | return jazzon; 46 | }; 47 | 48 | /** 49 | * Proxy the compile function with the current set of plugins 50 | */ 51 | 52 | jazzon.compile = (json) => compile(json, plugins); 53 | 54 | return jazzon; 55 | } 56 | 57 | module.exports = create(); 58 | -------------------------------------------------------------------------------- /test/plugins.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let resolve = require('path').resolve; 4 | let test = require('tape'); 5 | let pkg = require('../package.json'); 6 | let jazzon = require(resolve(__dirname, '..', pkg.main)); 7 | 8 | test('plugin context is current tree level', assert => { 9 | let instance = jazzon.create(); 10 | let json = { 11 | one: { 12 | test: '@{ top }', 13 | two: { 14 | test: '@{ bottom }', 15 | bar: 'baz' 16 | } 17 | } 18 | }; 19 | 20 | instance 21 | .use(function (state, helper) { 22 | switch (helper) { 23 | case 'top': 24 | assert.deepLooseEqual(this, json.one, 'context is level one'); 25 | break; 26 | case 'bottom': 27 | assert.deepLooseEqual(this, json.one.two, 'context is level two'); 28 | break; 29 | default: 30 | assert.fail('should not fall through'); 31 | break; 32 | } 33 | 34 | return state; 35 | }) 36 | .compile(json) 37 | .then(() => assert.end(), assert.end); 38 | }); 39 | 40 | test('plugin arguments', assert => { 41 | let instance = jazzon.create(); 42 | let json = { foo: '@{ first(foo, bar) | second }' }; 43 | 44 | instance 45 | .use(function (state, helper, args) { 46 | switch (helper) { 47 | case 'first': 48 | assert.looseEqual(state, null, 'first helpers has no state'); 49 | assert.deepEqual(args, ['foo', 'bar'], 'gets helper arguments'); 50 | return Promise.resolve('foo'); 51 | case 'second': 52 | assert.equal(state, 'foo', 'state is output from last helper'); 53 | return Promise.resolve(state); 54 | default: 55 | assert.fail('should not fall through'); 56 | } 57 | }) 58 | .compile(json) 59 | .then(() => assert.end(), assert.end); 60 | }); 61 | -------------------------------------------------------------------------------- /test/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let resolve = require('path').resolve; 4 | let test = require('tape'); 5 | let pkg = require('../package.json'); 6 | let jazzon = require(resolve(__dirname, '..', pkg.main)); 7 | 8 | const noop = function * noop() {}; 9 | 10 | test('jazzon.create() returns a new instance', assert => { 11 | let instance = jazzon.create(); 12 | 13 | assert.notEqual(instance, jazzon, 'it is truly a new object'); 14 | 15 | for (let key in jazzon) { 16 | assert.ok(instance.hasOwnProperty(key), `it has "${ key }"`); 17 | assert.equal( 18 | instance[key].toString(), 19 | jazzon[key].toString(), 20 | `it.${ key } is the same as jazzon.${ key }`); 21 | } 22 | 23 | assert.end(); 24 | }); 25 | 26 | test('jazzon exposes a use method', assert => { 27 | let instance = jazzon.create(); 28 | 29 | assert.equal(typeof instance.use, 'function', 'it is a function'); 30 | assert.equal(typeof instance.use(noop).use, 'function', 'it is chainable'); 31 | assert.end(); 32 | }); 33 | 34 | test('jazzon exposes a compile method', assert => { 35 | let instance = jazzon.create(); 36 | 37 | assert.equal(typeof instance.compile, 'function', 'it is a function'); 38 | assert.ok(instance.compile({}) instanceof Promise, 'it returns a promise'); 39 | assert.end(); 40 | }); 41 | 42 | test('jazzon.plugins is immutable', assert => { 43 | let instance = jazzon.create(); 44 | 45 | assert.notEqual(instance.plugins, instance.plugins, 'accessing plugins returns a unique instance'); 46 | 47 | instance.plugins.push('foo'); 48 | 49 | assert.equal(instance.plugins.indexOf('foo'), -1, 'plugins.push() does not mutate'); 50 | 51 | assert.throws(() => instance.plugins = 'foo', 'trying to set plugins throws an error'); 52 | 53 | assert.end(); 54 | }); 55 | 56 | test('jazzon.compile() does not mutate', assert => { 57 | let instance = jazzon.create(); 58 | let json = { foo: 'bar' }; 59 | 60 | instance 61 | .compile(json) 62 | .then((result) => { 63 | assert.deepLooseEqual(result, json, 'it returns a similar object'); 64 | assert.notEqual(result, json, 'it is not the same object'); 65 | assert.end(); 66 | }, assert.end); 67 | }); 68 | -------------------------------------------------------------------------------- /test/program.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let resolve = require('path').resolve; 4 | let test = require('tape'); 5 | let pkg = require('../package.json'); 6 | let jazzon = require(resolve(__dirname, '..', pkg.main)); 7 | 8 | test('program is recursive', assert => { 9 | let instance = jazzon.create(); 10 | let json = { 11 | foo: '@{ foo }' 12 | }; 13 | let expected = { 14 | foo: { 15 | bar: 'foobar' 16 | } 17 | }; 18 | 19 | instance 20 | .use(function (state, helper) { 21 | return new Promise((resolve) => { 22 | switch (helper) { 23 | case 'foo': resolve({ bar: '@{ bar }' }); break; 24 | case 'bar': resolve('foobar'); break; 25 | default: assert.fail('should not fall through'); break; 26 | } 27 | }); 28 | }) 29 | .compile(json) 30 | .then((result) => { 31 | assert.deepLooseEqual(result, expected); 32 | assert.end(); 33 | }, assert.end); 34 | }); 35 | 36 | test('helper names may be dot notaded', assert => { 37 | let instance = jazzon.create(); 38 | let json = { test: '@{ much.helper | other.helper.very.nested(with, args) }' }; 39 | 40 | instance 41 | .use(function (state, name, args) { 42 | switch (name) { 43 | case 'much.helper': 44 | assert.pass('dot notaded helper identified'); 45 | break; 46 | case 'other.helper.very.nested': 47 | assert.pass('deep nested helper identified'); 48 | assert.deepLooseEqual(args, ['with', 'args'], 'nested helper does not affect args'); 49 | break; 50 | default: 51 | assert.fail('should not fall through'); 52 | break; 53 | } 54 | }) 55 | .compile(json) 56 | .then(() => assert.end(), assert.end); 57 | }); 58 | 59 | test('helper arguments are unwrapped from quotes', assert => { 60 | let instance = jazzon.create(); 61 | let json = { 62 | test: '@{ helper("") | helper(" ") | helper("foo") | helper(\'bar\', "baz") | helper(\' \') }' 63 | }; 64 | 65 | instance 66 | .use(function (state, name, args) { 67 | return ((state || '') + args.join('')); 68 | }) 69 | .compile(json) 70 | .then(result => { 71 | assert.equal(result.test, ' foobarbaz '); 72 | assert.end(); 73 | }, assert.end); 74 | }); 75 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let resolve = require('path').resolve; 4 | let test = require('tape'); 5 | let pkg = require('../package.json'); 6 | let jazzon = require(resolve(__dirname, '..', pkg.main)); 7 | 8 | test('helpers are chained', assert => { 9 | let instance = jazzon.create(); 10 | let json = { foo: '@{ foo | bar }' }; 11 | 12 | instance 13 | .use(function (state, helper) { 14 | return new Promise((resolve) => { 15 | switch (helper) { 16 | case 'foo': resolve('foo'); break; 17 | case 'bar': resolve(state + 'bar'); break; 18 | default: assert.fail('should not fall through'); break; 19 | } 20 | }); 21 | }) 22 | .compile(json) 23 | .then((result) => { 24 | assert.equal(result.foo, 'foobar', 'helper output have been concatinated'); 25 | assert.end(); 26 | }, assert.end); 27 | }); 28 | 29 | test('helper basic return values', assert => { 30 | let instance = jazzon.create(); 31 | let fixtures = ['string', 'promise', 'function', 'generator', 'generatorFunction']; 32 | let json = { test: `@{ ${ fixtures.join('|') } }` }; 33 | 34 | instance 35 | .use(function (state, helper) { 36 | state = (state || ''); 37 | 38 | switch (helper) { 39 | case 'string': 40 | return state + 'string'; 41 | case 'promise': 42 | return Promise.resolve(state + 'promise'); 43 | case 'function': 44 | return function () { return state + 'function'; }; 45 | case 'generator': 46 | return (function * () { return state + 'generator'; }()); 47 | case 'generatorFunction': 48 | return function * () { return state + 'generatorFunction'; }; 49 | default: 50 | assert.fail('should not fall through'); 51 | break; 52 | } 53 | }) 54 | .compile(json) 55 | .then((result) => { 56 | assert.equal( 57 | result.test, 58 | fixtures.join(''), 59 | `works with return types: ${ fixtures.join(', ') }`); 60 | assert.end(); 61 | }, assert.end); 62 | }); 63 | 64 | test('helper can return an object', assert => { 65 | let instance = jazzon.create(); 66 | let json = { test: '@{ obj }' }; 67 | 68 | instance 69 | .use(function (state, helper) { 70 | switch (helper) { 71 | case 'obj': 72 | return { 73 | string: 'foo', 74 | promise: Promise.resolve('bar') 75 | }; 76 | default: 77 | assert.fail('should not fall through'); 78 | break; 79 | } 80 | }) 81 | .compile(json) 82 | .then((result) => { 83 | assert.deepLooseEqual( 84 | result.test, 85 | { string: 'foo', promise: 'bar' }, 86 | 'object with nestled promise resolves'); 87 | assert.end(); 88 | }, assert.end); 89 | }); 90 | 91 | test('helper can return an array', assert => { 92 | let instance = jazzon.create(); 93 | let json = { test: '@{ arr }' }; 94 | 95 | instance 96 | .use(function (state, helper) { 97 | switch (helper) { 98 | case 'arr': 99 | return [ 100 | 'foo', 101 | Promise.resolve('bar') 102 | ]; 103 | default: 104 | assert.fail('should not fall through'); 105 | break; 106 | } 107 | }) 108 | .compile(json) 109 | .then((result) => { 110 | assert.deepLooseEqual( 111 | result.test, 112 | ['foo', 'bar'], 113 | 'array with nestled promise resolves'); 114 | assert.end(); 115 | }, assert.end); 116 | }); 117 | -------------------------------------------------------------------------------- /lib/compile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const co = require('co'); 4 | const clone = require('lodash.clonedeep'); 5 | const isObject = require('lodash.isplainobject'); 6 | const isString = require('lodash.isstring'); 7 | const isFunction = require('lodash.isfunction'); 8 | const isGenerator = require('is-generator'); 9 | const isError = require('lodash.iserror'); 10 | 11 | // Matches individual helper declaration 12 | const PLUGIN = '([\\w\\.]+)(?:\\(([^\\|}]+)\\))?'; 13 | 14 | // Matches the helper syntax (required curly wrappers and pipe separator) 15 | const SYNTAX = `(?:(?:(?:{|\\|)\\s*)(?:${ PLUGIN })(?=\\s*(?:\\||})))`; 16 | 17 | // Matches all helpers 18 | const PLUGIN_ALL = new RegExp(SYNTAX, 'g'); 19 | 20 | // Matches individual helper 21 | const PLUGIN_ONE = new RegExp(PLUGIN); 22 | 23 | // Qualifier for helper declarations 24 | const IDENTIFIER = /^\@{.+}$/; 25 | 26 | // Arguments separator 27 | const ARG_SPLIT = /\s*,\s*/; 28 | 29 | // Arguments citation wrappers 30 | const ARG_WRAPPER = /^("|')(.*)\1/; 31 | 32 | /** 33 | * Compile given json object 34 | * @param {Mixed} json JSON object to transform 35 | * @param {Array} plugins List of plugins to apply to all helpers 36 | * @return {Promise} Resolves to a transformed copy of the given JSON 37 | */ 38 | 39 | function compile(json, plugins) { 40 | /** 41 | * Ensure that the input is not modified 42 | */ 43 | 44 | const source = clone(json); 45 | 46 | const transform = co.wrap(function * (value, ctx) { 47 | let output = null; 48 | const helpers = value.match(PLUGIN_ALL); 49 | 50 | /** 51 | * Iterate over all identified helpers 52 | */ 53 | 54 | for (let helper of helpers) { 55 | const match = helper.match(PLUGIN_ONE); 56 | 57 | /** 58 | * Reject any malformed helper declaration 59 | */ 60 | 61 | if (!match) { 62 | return Promise.reject(new Error(`Invalid helper syntax at "${ value }"`)); 63 | } 64 | 65 | /** 66 | * Seperate helper name form arguments 67 | */ 68 | 69 | const name = match[1]; 70 | const args = match[2] && match[2].trim().split(ARG_SPLIT).map(unwrap); 71 | 72 | /** 73 | * Iterate over all the plugins letting them each have a go at the helper 74 | */ 75 | 76 | for (let plugin of plugins) { 77 | output = yield promisify(plugin.call(ctx, output, name, args), ctx); 78 | } 79 | } 80 | 81 | /** 82 | * Recursively call the parse method 83 | */ 84 | 85 | return co(function * () { 86 | return yield parse(output, transform); 87 | }); 88 | }); 89 | 90 | /** 91 | * Run the source through our parser supplying a transform callback 92 | */ 93 | 94 | return co(function * () { 95 | return yield parse(source, transform); 96 | }); 97 | } 98 | 99 | /** 100 | * Unwrap quoted strings 101 | * @param {String} str String to unwrap 102 | * @return {String} Unwrapped string or original unwrapped string 103 | */ 104 | 105 | function unwrap(string) { 106 | const match = string.match(ARG_WRAPPER); 107 | 108 | if (match) { 109 | return string.substring(1, (string.length - 1)); 110 | } else { 111 | return string; 112 | } 113 | } 114 | 115 | /** 116 | * Replaces all tempalte-ish strings with a co-promise 117 | */ 118 | 119 | function parse(ctx, transform) { 120 | let keys; 121 | const isArray = Array.isArray(ctx); 122 | 123 | /** 124 | * Immediately resolve anything we cannot iterate over 125 | */ 126 | 127 | if (!isObject(ctx) && !isArray) { return Promise.resolve(ctx); } 128 | 129 | /** 130 | * Normalize a list of keys depending on wether it is an Array or an Object 131 | */ 132 | 133 | if (isArray) { 134 | keys = ctx.map((val, index) => index); 135 | } else { 136 | keys = Object.keys(ctx); 137 | } 138 | 139 | /** 140 | * Loop through the object calling the transform on any qualifying strings 141 | */ 142 | 143 | for (let i = 0, l = keys.length; i < l; i += 1) { 144 | const key = keys[i]; 145 | const value = ctx[key]; 146 | 147 | if (isString(value) && IDENTIFIER.test(value)) { 148 | ctx[key] = transform(value, ctx); 149 | } else if (isFunction(value)) { 150 | ctx[key] = promisify(value, ctx); 151 | } else { 152 | parse(value, transform); 153 | } 154 | } 155 | 156 | return ctx; 157 | } 158 | 159 | /** 160 | * Wraps non co-compliant values in a Promise 161 | * @param {Mixed} val Object to turn in to a promise 162 | * @return {Mixed} Value (potentially) wrapped in a Promise 163 | */ 164 | 165 | function promisify(val, ctx) { 166 | const isPromise = (val instanceof Promise); 167 | 168 | /** 169 | * Don't wrap co-compliant values 170 | */ 171 | 172 | if (isPromise || isGenerator(val) || Array.isArray(val) || isObject(val)) { 173 | return val; 174 | } 175 | 176 | /** 177 | * Execute functions to expose their inners 178 | */ 179 | 180 | if (isFunction(val)) { 181 | return promisify(val.call(ctx)); 182 | } 183 | 184 | /** 185 | * Make a best guess as to the status of the Promise 186 | */ 187 | 188 | if (isError(val)) { 189 | return Promise.reject(val); 190 | } else { 191 | return Promise.resolve(val); 192 | } 193 | } 194 | 195 | module.exports = compile; 196 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jazzon 2 | 3 | Working with static JSON files for mocking data or whatever other purposes can be a real bore. Jazzon is a convenience utility for generating, concatenating and streamlining the handling of static JSON files. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | $ npm install --save jazzon 9 | ``` 10 | 11 | ## Usage 12 | 13 | In and of it self jazzon does nothing, *really*. It's sole purpose is to call registered plugins with the current state and identified helpers. Helpers can be chained passing the current state from one to the next. 14 | 15 | To illustrate a most basic scenario, this is how one might use jazzon together with [jazzon-uuid](https://github.com/tornqvist/jazzon-uuid) 16 | 17 | ```javascript 18 | const jazzon = require('jazzon'); 19 | const uuid = require('jazzon-uuid'); 20 | 21 | let data = { id: "@{ uuid }" }; 22 | 23 | jazzon.use(uuid()); 24 | 25 | jazzon 26 | .compile(data) 27 | .then(result => console.log(result)); // => {id: "6c84fb90-12c4-11e1-840d-7b25c5ee775a"} 28 | ``` 29 | 30 | In this scenario, jazzon encounters the helper `uuid` and calls each registered plugin (in this case `jazzon-uuid`) on it. 31 | 32 | Helpers can also be chained using the pipe (`|`) symbol. Each chained helper gets the output (state) of the previous helper to operate on. To illustrate a more complex scenario, take these two models: 33 | 34 | ```javascript 35 | // user.json 36 | 37 | { 38 | "id": "@{ uuid }", 39 | "name": "@{ name.findName }", 40 | "email": "@{ internet.email }", 41 | "username": "@{ internet.userName }" 42 | } 43 | ``` 44 | 45 | ```javascript 46 | // users.json 47 | 48 | { 49 | "total": 3, 50 | "users": "@{ import(user.json) | pick(id, username) | repeat(3) }" 51 | } 52 | ``` 53 | 54 | Running `users.json` through jazzon would produce something like this: 55 | 56 | ```javascript 57 | { 58 | "total": 3, 59 | "users": [{ 60 | "id": "a76f535f-cbc6-4c09-8151-573e200c1dbf", 61 | "username": "Doug.Simonis28" 62 | }, { 63 | "id": "0a512648-c418-40a6-90ac-1bb5ef1e7fab", 64 | "username": "Virgil_Kunze" 65 | }, { 66 | "id": "88d6903f-d13b-4d16-877e-f906461c69aa", 67 | "username": "Grady.Koelpin" 68 | }] 69 | } 70 | ``` 71 | 72 | ## Syntax 73 | 74 | The syntax of helpers are very similar to [JavaScript template strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings) to clearly illustrate their purpose. But do not confuse them as the pipe separator is not a valid JavaScript operator. 75 | 76 | The string must start with `@{` and end with `}`. Each helper is separated with a `|` symbol. Encountering an invalid template-ish string will throw an error. 77 | 78 | Use [this regexr](http://regexr.com/3bsnl) to experiment with the template strings. 79 | 80 | ## Plugins 81 | 82 | Plugins should export a function that get's called once for every helper encountered by jazzon. The convention is to export a factory function that returns the plugin. 83 | 84 | A plugin really is just a reducer that jazzon uses to process all the helpers. Therefore a plugin should *always* return a state, even if it does not manipulate the state. A switch statement does the job as so: 85 | 86 | ```javascript 87 | // myplugin.js 88 | 89 | module.exports = function (otions) { 90 | return function (state, helper, args) { 91 | switch (helper) { 92 | case 'name': 93 | return args[0] || options.default; 94 | case 'wrap': 95 | return `Hello ${ state }!`; 96 | default: 97 | return state; 98 | } 99 | } 100 | }; 101 | ``` 102 | 103 | ```javascript 104 | // myprogram.js 105 | 106 | jazzon 107 | .use(myplugin({ 108 | default: 'world' 109 | })) 110 | .compile({ 111 | "first": "@{ name | wrap }", 112 | "second": "@{ name(Joe) | wrap }" 113 | }) 114 | .then(result => console.log(result)); // => {"first": "Hello world!", "second": "Hello Joe!"} 115 | ``` 116 | 117 | Jazzon also supports async plugins. Under the hood, jazzon is using [co](https://github.com/tj/co) so anything that co can handle, jazzon can handle. As so, other than just plain strings, a plugin may return a Promise, generator, generator function, function, object or array. Due to the awesome nature of co, objects and arrays may contain nestled objects/arrays containing Promises or any of the other supported types. See some of the plugins for examples of how this is achieved. 118 | 119 | ### Availible plugins 120 | 121 | - [jazzon-uuid](https://github.com/tornqvist/jazzon-uuid) *Generates a UUID* 122 | - [jazzon-import](https://github.com/tornqvist/jazzon-import) *Import other files in place* 123 | - [jazzon-faker](https://github.com/tornqvist/jazzon-faker) *Generate fake data using faker* 124 | - [jazzon-lodash](https://github.com/tornqvist/jazzon-lodash) *Use some lodash goodness with jazzon* 125 | - [jazzon-repeat](https://github.com/tornqvist/jazzon-repeat) *Repeat given value n number of times* 126 | - [jazzon-format](https://github.com/tornqvist/jazzon-format) *Format strings using `util.format`* 127 | 128 | To add your own plugin, add it to the list and make a pull request. 129 | 130 | ## API 131 | 132 | - `jazon.create(/*plugins*/)` Creates a new instance of jazzon with optional list of plugins. 133 | - *Returns jazzon* 134 | - `jazzon.use(plugin)` Adds a plugin to be used when transforming template strings. 135 | - *Returns jazzon* 136 | - `jazzon.compile(object)` Iterates over the object looking for template strings to transform. 137 | - *Returns a Promise* 138 | - `jazzon.plugins` An list of registered helpers on this instance. 139 | - *Returns an Array* 140 | - *Is immutable* 141 | 142 | ## TODO 143 | 144 | - [x] Add documentation 145 | - [x] Add wrapper for non-Promises returned from plugins 146 | - [x] Add test for plugins (generator/non-generator) 147 | - [x] Better error handling 148 | - [ ] Add CLI 149 | --------------------------------------------------------------------------------