├── .gitignore ├── .jsdoc.json ├── README.md ├── example.js ├── package.json ├── src ├── Compiler.js ├── Highlighter.js ├── Lexer.js ├── builtin.js ├── config.json └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": ["jsdoc"] 5 | }, 6 | "source": { 7 | "include": ["src", "package.json", "README.md"], 8 | "includePattern": ".js$", 9 | "excludePattern": "(node_modules/|docs)" 10 | }, 11 | "plugins": [ 12 | "plugins/markdown" 13 | ], 14 | "templates": { 15 | "cleverLinks": false, 16 | "monospaceLinks": true 17 | }, 18 | "opts": { 19 | "destination": "./docs/", 20 | "encoding": "utf8", 21 | "private": true, 22 | "recurse": true, 23 | "template": "./node_modules/docdash" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## TagScript ## 2 | 3 | A better way to deal with the modern issues of tag parsing. 4 | 5 | It's a pretty simple system, you just put text in, maybe define some functions, 6 | and out comes parsed tags and such. 7 | 8 | There are three basic things you can pass as functions to the compiler. 9 | 10 | 1. Basic replacements. these are simple key/value strings. 11 | 2. Basic functions. These are simple __syncronous__ functions that take 12 | arguments and return a string, 13 | 3. Promise functions. These are __asyncronous__ functions that take arguments and resolve a string. __Note: any promise that rejects will be replaced with an empty string.__ 14 | 15 | You can see all of these as examples in `example.js` 16 | 17 | There are a number of builtin replacements and functions. 18 | They can be viewed in `builtin.js` 19 | 20 | Please note that two functions (`get` and `set`) are kept in the actual compiler due to internal variables they rely on. 21 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const TagScript = require('./src/index'); 2 | const superagent = require('superagent'); 3 | 4 | let compiler = new TagScript(); 5 | 6 | const functions = { 7 | // a simple replacement 8 | 'fight': '(ง\'̀-\'́)ง', 9 | 10 | // a simple function 11 | 'join': (joiner, ...args) => args.join(joiner), 12 | 13 | // an asyncronous example 14 | 'http': (method, url, body = {}) => { 15 | return new Promise((resolve, reject) => { 16 | if (!(method in superagent)) reject('invalid method'); 17 | superagent[method](url) 18 | .send(JSON.parse(`{${body}}`)) 19 | .end((err, res) => { 20 | if (err) reject(err); 21 | // if (json && json !== 'null') { 22 | // let props = json.split('.'); 23 | // let value = res.body; 24 | // for (const prop of props) value = value[prop]; 25 | // resolve(JSON.stringify(value)); 26 | // } 27 | resolve(res.text); 28 | }) 29 | }); 30 | } 31 | } 32 | 33 | let code = `{set;x;{math;1+1}} 34 | {note;this will not show up} 35 | yay x is {get;x} 36 | {choose;this;that;the other thing} 37 | {join;{fight};hello;how are you;are you good?} 38 | Search for One Punch Man: {object;{http;post;https://qeeqle.guscaplan.me;"query": "one punch man"};0.title} 39 | M{randstr;jT;1}{randstr;qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_-.;{range;55;60}} 40 | {{{a}}}`; 41 | 42 | compiler.highlight(code).then(console.log); 43 | compiler.compile(code, functions).then(console.log); 44 | 45 | // OR 46 | // if you want more control, you can lex, then pass the lexed stuff manually, possibly 47 | // modifying some things 48 | // 49 | // compiler.lex(code).then(lexed => { 50 | // compiler.compile(lexed.run, functions).then(console.log); 51 | // }) 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tagscript", 3 | "version": "2.0.0", 4 | "description": "", 5 | "main": "./src/index.js", 6 | "author": "Gus Caplan ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "asyncawait": "^1.0.6", 10 | "chalk": "^1.1.3", 11 | "chevrotain": "^0.22.0", 12 | "expr-eval": "^1.0.0" 13 | }, 14 | "devDependencies": { 15 | "docdash": "^0.4.0", 16 | "jsdoc": "^3.4.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Compiler.js: -------------------------------------------------------------------------------- 1 | const ohsync = require('asyncawait/async'); 2 | const ohwait = require('asyncawait/await'); 3 | 4 | module.exports = (run, functions = {}) => { 5 | let runtimeArgs = {}; 6 | const internal = { 7 | 'get': i => runtimeArgs[i], 8 | 'set': (i, x) => { 9 | runtimeArgs[i] = x; 10 | return; 11 | } 12 | } 13 | const builtin = Object.assign(require('./builtin.js'), internal) 14 | 15 | Object.keys(builtin).forEach(k => { 16 | if (Object.keys(functions).includes(k)) throw new Error(`"${k}" is a reserved function name`) 17 | }); 18 | 19 | functions = Object.assign(functions, builtin); 20 | 21 | return (ohsync(() => { 22 | run.forEach(i => { 23 | if (i.run.function === null) return; 24 | i.run.args = i.run.args.map(arg => { 25 | if (run.find(r => ('EX_' + r.ex) === arg)) { 26 | let replacement = run.find(r => 'EX_' + r.ex === arg).compiled; 27 | return replacement; 28 | } else { 29 | return arg; 30 | } 31 | }); 32 | if (!(i.run.function in functions)) { 33 | i.compiled = ''; 34 | return; 35 | } 36 | let compiled = functions[i.run.function]; 37 | if (typeof compiled === 'string') { 38 | i.compiled = compiled; 39 | } else if (typeof compiled === 'object') { 40 | i.compiled = JSON.stringify(compiled); 41 | } else { 42 | compiled = compiled(...i.run.args); 43 | if (compiled instanceof Promise) { 44 | try { 45 | i.compiled = ohwait(compiled); 46 | } catch (err) { 47 | i.compiled = ''; 48 | } 49 | } else { 50 | i.compiled = compiled; 51 | } 52 | } 53 | }); 54 | let compiled = run.filter(e => !e.called).map(e => e.compiled).join('').trim(); 55 | return compiled; 56 | })()); 57 | } 58 | -------------------------------------------------------------------------------- /src/Highlighter.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const colors = require('./config.json').colors; 3 | 4 | module.exports = (tokens, html = false) => { 5 | let highlighted = ''; 6 | let scope = []; 7 | 8 | const hilite = (text, color) => { 9 | if (html) { 10 | highlighted += `${text}`; 11 | } else { 12 | highlighted += chalk[color.ansi](text); 13 | } 14 | } 15 | 16 | const prepare = (scoped, ex, called = false) => { 17 | delete scoped.next; 18 | } 19 | 20 | const prepareBare = image => { 21 | scope.splice(scope.length - 1, 1); 22 | }; 23 | 24 | const scopeTemplate = next => { 25 | return {'function': null, next: next, args: []} 26 | } 27 | 28 | return new Promise((resolve, reject) => { 29 | tokens.forEach(token => { 30 | switch (token.constructor.name) { 31 | case 'FunctionOpen': 32 | if (!scope.last) scope.push(scopeTemplate); 33 | hilite(token.image, colors.FunctionBrace); 34 | switch (scope.last.next) { 35 | case 'args': 36 | scope.push(scopeTemplate('function')); 37 | break; 38 | default: 39 | scope.push(scopeTemplate('function')); 40 | break; 41 | } 42 | break; 43 | case 'FunctionClose': 44 | hilite(token.image, colors.FunctionBrace); 45 | prepare(scope.last, 0, (scope.length > 2)); 46 | scope.splice(scope.length - 1, 1); 47 | break; 48 | case 'ArgumentSeperator': 49 | hilite(token.image, colors.ArgumentSeperator); 50 | break; 51 | case 'Identifier': 52 | if (!scope.last) { 53 | hilite(token.image, colors.BareIdentifier); 54 | return prepareBare(token.image); 55 | } 56 | switch (scope.last.next) { 57 | case 'function': 58 | hilite(token.image, colors.FunctionName); 59 | scope.last.function = token.image; 60 | scope.last.next = 'args'; 61 | break; 62 | case 'args': 63 | hilite(token.image, colors.Identifier); 64 | scope.last.args.push(token.image); 65 | break; 66 | default: 67 | hilite(token.image, colors.BareIdentifier); 68 | prepareBare(token.image); 69 | break; 70 | } 71 | break; 72 | default: 73 | break; 74 | } 75 | }); 76 | resolve(highlighted); 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /src/Lexer.js: -------------------------------------------------------------------------------- 1 | const chevrotain = require('chevrotain'); 2 | 3 | if (!Object.values) { 4 | const reduce = Function.bind.call(Function.call, Array.prototype.reduce); 5 | const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable); 6 | const concat = Function.bind.call(Function.call, Array.prototype.concat); 7 | const keys = Reflect.ownKeys; 8 | Object.values = function values (O) { 9 | return reduce(keys(O), (v, k) => concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : []), []); 10 | }; 11 | } 12 | 13 | Object.defineProperty(Array.prototype, 'last', { // eslint-disable-line 14 | get () { 15 | return this[this.length - 1]; 16 | } 17 | }) 18 | 19 | const Lexer = chevrotain.Lexer; 20 | 21 | module.exports = (input, allTokens, highlight) => { 22 | return new Promise((resolve, reject) => { 23 | const SelectLexer = new Lexer(Object.values(allTokens), true); 24 | 25 | const tokenize = text => { 26 | var lexResult = SelectLexer.tokenize(text); 27 | 28 | if (lexResult.errors.length >= 1) { 29 | reject(lexResult.errors[0].message + ' ' + lexResult.errors[0].stack); 30 | } 31 | return lexResult; 32 | } 33 | 34 | let lexed = tokenize(input); 35 | let tokens = lexed.tokens; 36 | 37 | let scope = []; 38 | let run = []; 39 | let lastEX = 0; 40 | 41 | const prepare = (scoped, ex, called = false) => { 42 | delete scoped.next; 43 | let out = scoped; 44 | run.push({ex: ex, run: out, called: called, compiled: null}); 45 | } 46 | 47 | const prepareBare = image => { 48 | run.push({ex: lastEX++, run: {'function': null, args: []}, compiled: image}); 49 | scope.splice(scope.length - 1, 1); 50 | }; 51 | 52 | const scopeTemplate = next => { 53 | return {'function': null, next: next, args: []} 54 | } 55 | 56 | tokens.forEach(token => { 57 | switch (token.constructor.name) { 58 | case 'FunctionOpen': 59 | if (!scope.last) scope.push(scopeTemplate); 60 | switch (scope.last.next) { 61 | case 'args': 62 | scope.last.args.push(`EX_${lastEX}`); 63 | scope.push(scopeTemplate('function')); 64 | break; 65 | default: 66 | scope.push(scopeTemplate('function')); 67 | break; 68 | } 69 | break; 70 | case 'FunctionClose': 71 | prepare(scope.last, lastEX++, (scope.length > 2)); 72 | scope.splice(scope.length - 1, 1); 73 | break; 74 | case 'ArgumentSeperator': 75 | break; 76 | case 'Identifier': 77 | if (!scope.last) { 78 | return prepareBare(token.image); 79 | } 80 | switch (scope.last.next) { 81 | case 'function': 82 | scope.last.function = token.image; 83 | scope.last.next = 'args'; 84 | break; 85 | case 'args': 86 | scope.last.args.push(token.image); 87 | break; 88 | default: 89 | prepareBare(token.image); 90 | break; 91 | } 92 | break; 93 | default: 94 | break; 95 | } 96 | }); 97 | resolve({ 98 | run: run, 99 | raw: tokens 100 | }); 101 | }); 102 | } 103 | -------------------------------------------------------------------------------- /src/builtin.js: -------------------------------------------------------------------------------- 1 | const Parser = require('expr-eval').Parser; 2 | 3 | module.exports = { 4 | 'object': (obj, selector) => { 5 | if (typeof obj === 'string') obj = JSON.parse(json); 6 | let value = obj; 7 | for (const prop of selector.split('.')) value = value[prop]; 8 | return value; 9 | }, 10 | 'math': (...args) => Parser.evaluate(args.join('')), 11 | 'choose': (...args) => args[Math.floor(Math.random() * args.length)], 12 | 'range': (min, max) => Math.floor(Math.random() * parseInt(max)) + parseInt(min), 13 | 'randstr': (chars, length) => { 14 | let result = ''; 15 | for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]; 16 | return result; 17 | }, 18 | 'lower': t => t.toLowerCase(), 19 | 'upper': t => t.toUpperCase(), 20 | 'length': t => t.length, 21 | 'note': '', 22 | 'l': '{', 23 | 'r': '}', 24 | 'semi': ';' 25 | } 26 | -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors": { 3 | "FunctionBrace": { 4 | "ansi": "blue", 5 | "hex": "#268BD2" 6 | }, 7 | "FunctionName": { 8 | "ansi": "red", 9 | "hex": "#DC322F" 10 | }, 11 | "ArgumentSeperator": { 12 | "ansi": "yellow", 13 | "hex": "#E0BF45" 14 | }, 15 | "BareIdentifier": { 16 | "ansi": "white", 17 | "hex": "#FFF" 18 | }, 19 | "Identifier": { 20 | "ansi": "green", 21 | "hex": "#53893e" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const chevrotain = require('chevrotain'); 2 | const Compile = require('./Compiler'); 3 | const Lexer = require('./Lexer'); 4 | const Highlight = require('./Highlighter'); 5 | 6 | const extendToken = (name, find) => { 7 | let token = chevrotain.extendToken(name, find); 8 | token.STRING = token.PATTERN.toString().slice(1, -1); 9 | return token; 10 | }; 11 | 12 | /** 13 | * The Compiler. 14 | */ 15 | class Compiler { 16 | constructor () { 17 | this.tokens = { 18 | FunctionOpen: extendToken('FunctionOpen', /{/), 19 | FunctionClose: extendToken('FunctionClose', /}/), 20 | ArgumentSeperator: extendToken('ArgumentSeperator', /;/), 21 | Identifier: extendToken('Identifier', /([\u0020-\u003A]|[\u003C-\u007A]|[\u007E-\uFFFFF]|\u007C|[\r\n\v])+/i) 22 | }; 23 | } 24 | 25 | /** 26 | * Runs the lexer on the input and returns json output for the compiler 27 | * @param {string} input The string of TagScript to be lexed. 28 | * @returns {Object} output Object containing information for the compiler 29 | * @example 30 | * compiler.lex('{math;1+1}').then(console.log) // logs json data from the lexer 31 | */ 32 | lex (input) { 33 | return Lexer(input, this.tokens); 34 | } 35 | 36 | /** 37 | * Runs a highlighter on the input and returns an ANSI formatted string. 38 | * @param {string} input The string of TagScript to be highlighted. 39 | * @returns {string} output An ANSI formatted string. 40 | * @example 41 | * compiler.highlight('{math;1+2}').then(console.log) // logs ANSI highlighted 42 | */ 43 | highlight (input) { 44 | return new Promise((resolve, reject) => { 45 | this.lex(input).then(lexed => { 46 | Highlight(lexed.raw).then(resolve).catch(reject) 47 | }).catch(reject); 48 | }); 49 | } 50 | 51 | /** 52 | * Compiles a TagScript string or output from the Lexer. 53 | * @param {string|object} input Input in the form of a string or an object from the lexer. 54 | * @param {object} functions Functions to pass as executable items. 55 | * @returns {string} output The compiled TagScript in the form of a string. 56 | * @example 57 | * compiler.compile('{math;1+1}').then(console.log) // logs '2' 58 | */ 59 | compile (input, functions = {}) { 60 | if (typeof input === 'string') { 61 | return new Promise((resolve, reject) => { 62 | this.lex(input).then(lexed => { 63 | Compile(lexed.run, functions).then(resolve).catch(reject); 64 | }).catch(reject); 65 | }); 66 | } else { 67 | return Compile(input, functions); 68 | } 69 | } 70 | } 71 | 72 | module.exports = Compiler; 73 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | acorn-jsx@^3.0.0: 4 | version "3.0.1" 5 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" 6 | dependencies: 7 | acorn "^3.0.4" 8 | 9 | acorn@^3.0.4, acorn@^3.3.0: 10 | version "3.3.0" 11 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 12 | 13 | ansi-regex@^2.0.0: 14 | version "2.0.0" 15 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" 16 | 17 | ansi-styles@^2.2.1: 18 | version "2.2.1" 19 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 20 | 21 | asyncawait@^1.0.6: 22 | version "1.0.6" 23 | resolved "https://registry.yarnpkg.com/asyncawait/-/asyncawait-1.0.6.tgz#e446cf54e516a416d7423bbe35bf0b4e2b738b67" 24 | dependencies: 25 | bluebird "^3.1.1" 26 | fibers "^1.0.8" 27 | lodash v3.10.1 28 | 29 | bluebird@^3.1.1, bluebird@~3.4.6: 30 | version "3.4.6" 31 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" 32 | 33 | catharsis@~0.8.8: 34 | version "0.8.8" 35 | resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.8.tgz#693479f43aac549d806bd73e924cd0d944951a06" 36 | dependencies: 37 | underscore-contrib "~0.3.0" 38 | 39 | chalk@^1.1.3: 40 | version "1.1.3" 41 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 42 | dependencies: 43 | ansi-styles "^2.2.1" 44 | escape-string-regexp "^1.0.2" 45 | has-ansi "^2.0.0" 46 | strip-ansi "^3.0.0" 47 | supports-color "^2.0.0" 48 | 49 | chevrotain@^0.17.0: 50 | version "0.17.1" 51 | resolved "https://registry.yarnpkg.com/chevrotain/-/chevrotain-0.17.1.tgz#e97547d3c345b2caa60ec67a0f58c35d7390628e" 52 | 53 | docdash@^0.4.0: 54 | version "0.4.0" 55 | resolved "https://registry.yarnpkg.com/docdash/-/docdash-0.4.0.tgz#05c3a50d83189981699ee0c076d3a3950db7ec00" 56 | 57 | escape-string-regexp@^1.0.2, escape-string-regexp@~1.0.5: 58 | version "1.0.5" 59 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 60 | 61 | espree@~3.1.7: 62 | version "3.1.7" 63 | resolved "https://registry.yarnpkg.com/espree/-/espree-3.1.7.tgz#fd5deec76a97a5120a9cd3a7cb1177a0923b11d2" 64 | dependencies: 65 | acorn "^3.3.0" 66 | acorn-jsx "^3.0.0" 67 | 68 | expr-eval@^0.10.1: 69 | version "0.10.1" 70 | resolved "https://registry.yarnpkg.com/expr-eval/-/expr-eval-0.10.1.tgz#c009aee50bdc807f3d4437575b99a7761a8f723a" 71 | 72 | fibers@^1.0.8: 73 | version "1.0.15" 74 | resolved "https://registry.yarnpkg.com/fibers/-/fibers-1.0.15.tgz#22f039c8f18b856190fbbe4decf056154c1eae9c" 75 | 76 | has-ansi@^2.0.0: 77 | version "2.0.0" 78 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 79 | dependencies: 80 | ansi-regex "^2.0.0" 81 | 82 | js2xmlparser@~1.0.0: 83 | version "1.0.0" 84 | resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-1.0.0.tgz#5a170f2e8d6476ce45405e04823242513782fe30" 85 | 86 | jsdoc@^3.4.2: 87 | version "3.4.2" 88 | resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.4.2.tgz#b43ca03e54a575dfff4448a3825e11c4db8f87a1" 89 | dependencies: 90 | bluebird "~3.4.6" 91 | catharsis "~0.8.8" 92 | escape-string-regexp "~1.0.5" 93 | espree "~3.1.7" 94 | js2xmlparser "~1.0.0" 95 | klaw "~1.3.0" 96 | marked "~0.3.6" 97 | mkdirp "~0.5.1" 98 | requizzle "~0.2.1" 99 | strip-json-comments "~2.0.1" 100 | taffydb "2.6.2" 101 | underscore "~1.8.3" 102 | 103 | klaw@~1.3.0: 104 | version "1.3.0" 105 | resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.0.tgz#8857bfbc1d824badf13d3d0241d8bbe46fb12f73" 106 | 107 | lodash@v3.10.1: 108 | version "3.10.1" 109 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" 110 | 111 | marked@~0.3.6: 112 | version "0.3.6" 113 | resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7" 114 | 115 | minimist@0.0.8: 116 | version "0.0.8" 117 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 118 | 119 | mkdirp@~0.5.1: 120 | version "0.5.1" 121 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 122 | dependencies: 123 | minimist "0.0.8" 124 | 125 | requizzle@~0.2.1: 126 | version "0.2.1" 127 | resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.1.tgz#6943c3530c4d9a7e46f1cddd51c158fc670cdbde" 128 | dependencies: 129 | underscore "~1.6.0" 130 | 131 | strip-ansi@^3.0.0: 132 | version "3.0.1" 133 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 134 | dependencies: 135 | ansi-regex "^2.0.0" 136 | 137 | strip-json-comments@~2.0.1: 138 | version "2.0.1" 139 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 140 | 141 | supports-color@^2.0.0: 142 | version "2.0.0" 143 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 144 | 145 | taffydb@2.6.2: 146 | version "2.6.2" 147 | resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268" 148 | 149 | underscore-contrib@~0.3.0: 150 | version "0.3.0" 151 | resolved "https://registry.yarnpkg.com/underscore-contrib/-/underscore-contrib-0.3.0.tgz#665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7" 152 | dependencies: 153 | underscore "1.6.0" 154 | 155 | underscore@~1.6.0, underscore@1.6.0: 156 | version "1.6.0" 157 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" 158 | 159 | underscore@~1.8.3: 160 | version "1.8.3" 161 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" 162 | 163 | --------------------------------------------------------------------------------