├── .eslintrc.js ├── .github └── workflows │ └── pr-workflow.yaml ├── .gitignore ├── LICENSE ├── README.md ├── examples └── normal.conf ├── lib ├── NodeBuilder.js ├── Queryable.js ├── QueryableNodeBuilder.js ├── nginx-config-parser.js ├── parse.js └── stringify.js ├── package-lock.json ├── package.json ├── test-nginx.js └── test ├── main.test.js ├── parse.test.js ├── query.test.js ├── stringify.test.js └── utils └── getConf.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "commonjs": true, 4 | "es6": false, 5 | "node": true, 6 | jest: true 7 | }, 8 | "extends": "eslint:recommended", 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 5 15 | }, 16 | "rules": { 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /.github/workflows/pr-workflow.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Validate PR 3 | on: 4 | - pull_request 5 | jobs: 6 | test: 7 | name: test 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-node@v1 12 | with: 13 | node-version: 10.x 14 | - run: npm ci 15 | - run: npm run lint 16 | - run: npm test 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /*.conf 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-present Sean McCollum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nginx config parser 2 | ================ 3 | 4 | Should parse and stringify nginx configs so they can be modified programmatically. 5 | 6 | Installation 7 | 8 | npm install nginx-config-parser 9 | 10 | Example use: 11 | 12 | var ncp = require('nginx-config-parser'), 13 | fs = require('fs'); 14 | 15 | var config = ncp.queryFromString( fs.readFileSync(NGINX_LOCATION, 'utf-8') ); 16 | 17 | config.find('http', 'server') 18 | .where('server_name').match(/notcool\.com/) 19 | .remove() 20 | 21 | .parent() 22 | 23 | .find('http', 'server') 24 | .where('server_name').match(/corslocation\.com/) 25 | .find(/location.*) 26 | .createNewNode('if $request_method = "OPTIONS"') 27 | .addDirective('add_header', 'Access-Allow-Origin:', '*') 28 | .addToQuery(); 29 | 30 | fs.writeFileSync( NGINX_LOCATION, config.stringify() ); 31 | 32 | That should get all servers with server name that matches "notcool.com" and remove them. 33 | Then it'll find all the servers with server name "corslocation.com", and add an if node/block with the add_header Access-Allow-Origin: *; in it. 34 | 35 | It's very chainable. It's definitely not amazing, but I also definitely spent more time on it than I should have... 36 | 37 | Things I'm aware it can't do 38 | ------- 39 | - add a directive (which is something that ends in a semicolon) 40 | 41 | It's late. I'm not a huge fan of documenting. You know how this goes... 42 | 43 | 44 | The issues for sure 45 | ------- 46 | 47 | ### The parser 48 | The parser is all one big stateful thing. I've never written a parser before, and this seemed like a good chance for one. 49 | With that disclaimer stated... 50 | 51 | The parser adds _isNodeArray and _isDirectiveArray to the respective arrays so that stringify can pump them out. 52 | This logic is repeated in the NodeBuilder. That bugs me. Really there should be the one NodeBuilder that takes a string or a parsed 53 | object and can be modified, then call it's build method for parse. 54 | 55 | ...but That's not what I did. I didn't build the NodeBuilder until last. 56 | -------------------------------------------------------------------------------- /examples/normal.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | events { 3 | worker_connections 1024; 4 | } 5 | http { 6 | include mime.types; 7 | server { 8 | server_name something.com; 9 | location / { 10 | root something_com; 11 | } 12 | } 13 | 14 | server { 15 | server_name something-special.com; 16 | location ~ another { 17 | root something_special_com; 18 | } 19 | } 20 | } 21 | 22 | http { 23 | server { 24 | server_name another.server; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/NodeBuilder.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | var nodeBuilderProto = { 4 | _isNodeBuilder: true, 5 | 6 | addNode: function ( nodeName ) { 7 | if (!this.nodes) { 8 | this.nodes = []; 9 | } 10 | var newNode = createNodeBuilder(nodeName, this); 11 | this.nodes.push( newNode ); 12 | 13 | return newNode; 14 | }, 15 | addDirective: function (name) { 16 | if (!this.directives) { 17 | this.directives = []; 18 | } 19 | 20 | this.directives.push({ 21 | name: name, 22 | values: Array.prototype.slice.call(arguments, 1) 23 | }); 24 | 25 | return this; 26 | }, 27 | 28 | parent: function () { 29 | if (this.parent) { 30 | return this.parent; 31 | } 32 | return this; 33 | }, 34 | 35 | root: function () { 36 | if (this.parent) { 37 | return this.parent.root(); 38 | } 39 | return this; 40 | }, 41 | 42 | // makes it a real parsed config 43 | // Assumes the thing using this will extract it's name 44 | build: function () { 45 | 46 | var nodeObj = {}; 47 | _.each(this.directives, function (d) { 48 | if (!nodeObj[d.name]) { 49 | nodeObj[d.name] = []; 50 | nodeObj[d.name]._isDirectiveArray = true; 51 | } 52 | nodeObj[ d.name ].push(d.values); 53 | }); 54 | 55 | _.each(this.nodes, function (n) { 56 | if (!nodeObj[n.name]) { 57 | nodeObj[n.name] = []; 58 | nodeObj[n.name]._isNodeArray = true; 59 | } 60 | nodeObj[ n.name ].push( n.build() ); 61 | }); 62 | 63 | return nodeObj; 64 | } 65 | }; 66 | var createNodeBuilder = function (nodeName, parent) { 67 | var nodeBuilder = Object.create(nodeBuilderProto); 68 | 69 | _.extend(nodeBuilder, { 70 | name: nodeName, 71 | nodeName: nodeName, 72 | 73 | parent: parent 74 | }); 75 | 76 | return nodeBuilder; 77 | }; 78 | 79 | module.exports = { 80 | create: createNodeBuilder 81 | }; 82 | -------------------------------------------------------------------------------- /lib/Queryable.js: -------------------------------------------------------------------------------- 1 | var parse = require('./parse'), 2 | stringify = require('./stringify'), 3 | QueryableNodeBuilder = require('./QueryableNodeBuilder'), 4 | _ = require("underscore"); 5 | 6 | var DEBUG_MODE = false; 7 | var dLog = function () { 8 | if (DEBUG_MODE) { 9 | console.log.apply(console, arguments); 10 | } 11 | }; 12 | 13 | 14 | // Might not be a regex 15 | var resetRegex = function (regex) { 16 | if (regex && regex.exec && regex.compile && regex.lastIndex !== undefined) { 17 | regex.lastIndex = 0; 18 | } 19 | }; 20 | 21 | var stringOrRegexMatch = function (matchee, matcher) { 22 | var isMatch = false; 23 | if (typeof matcher == "string") { 24 | isMatch = (matchee == matcher); 25 | } else { 26 | isMatch = !!String(matchee).match(matcher); 27 | resetRegex(matcher); 28 | } 29 | return isMatch; 30 | }; 31 | 32 | var matcherProto = Object.create({}); 33 | _.extend(matcherProto, { 34 | init: function (attributeName, queryable) { 35 | 36 | // attributeName is what i will be matching each of the 37 | // queryable's keys against 38 | // It can be a string or a regex 39 | this.attributeName = attributeName; 40 | 41 | // queryable is the queryable I will be filtering 42 | this.queryable = queryable; 43 | 44 | dLog('===== matcher init, queryable ====', queryable); 45 | }, 46 | 47 | /* 48 | * This will return a new queryable, 49 | * whose parent is the original queryable 50 | * where the base queryable's attributeName match this attributeValue 51 | * 52 | * This means that any element of the queryable that does not HAVE the 53 | * attributeName key will be discarded 54 | */ 55 | match: function (attributeValue) { 56 | var newData = [], 57 | matcher = this; 58 | 59 | dLog('match called'); 60 | 61 | _.each(this.queryable, function (datum) { 62 | dLog("queryable element ", datum); 63 | 64 | datumChecker: for (var key in datum) { 65 | if (!Object.prototype.hasOwnProperty.call(datum, key)) { 66 | continue; 67 | } 68 | 69 | if (key == "parentArray") { 70 | continue; 71 | 72 | } 73 | dLog('--- key', key); 74 | 75 | if (stringOrRegexMatch(key, matcher.attributeName)) { 76 | dLog('--- key matches ' + matcher.attributeName); 77 | 78 | // Each of these keys have an array associated with them 79 | // so to match it, I'll need to check all of the values associated with this key 80 | // I'll return it if ANY match 81 | 82 | var values = datum[key]; 83 | for (var i = 0; i < values.length; i++) { 84 | var value = values[i]; 85 | dLog('--- checking ', value, ' if it matches', attributeValue); 86 | 87 | if (stringOrRegexMatch(value, attributeValue)) { 88 | newData.push(datum); 89 | break datumChecker; 90 | } 91 | } 92 | } 93 | } 94 | }); 95 | 96 | dLog("match data", newData); 97 | return createQueryable(newData, this.queryable); 98 | }, 99 | all: function() { 100 | // convenience method 101 | return this.match(/./); 102 | } 103 | }); 104 | 105 | var createMatcher = function (attributeName, queryable) { 106 | var matcher = Object.create(matcherProto); 107 | matcher.init(attributeName, queryable); 108 | return matcher; 109 | }; 110 | 111 | 112 | // Extending from an array, 113 | var queryableProto = Object.create([]); 114 | _.extend(queryableProto, { 115 | /* 116 | * @returns Queryable 117 | */ 118 | init: function (data, parent) { 119 | if (this === queryableProto) { 120 | throw new Error("Do not directly init the prototype."); 121 | } 122 | this.parent = parent; 123 | this.clear(); 124 | 125 | 126 | if ( !(data instanceof Array) ) { 127 | data = [data]; 128 | } 129 | 130 | var q = this; 131 | data.forEach(function (value) { 132 | q.push(value); 133 | }); 134 | }, 135 | 136 | 137 | /* 138 | * Each argument is treated as an incremental search 139 | * each item can be a string or a regex 140 | * @example 141 | * q.find('http', 'server', /location/) 142 | * will return all the /location/ blocks in all the 'server' blocks in all the 'http' blocks 143 | * 144 | * @returns A new Queryable 145 | */ 146 | find: function () { 147 | 148 | dLog("========= in find =========="); 149 | 150 | var keys = Array.prototype.slice.call(arguments), 151 | key; 152 | 153 | if (!keys.length) { 154 | throw new Error("Must provide arguments to find."); 155 | } 156 | 157 | var currentData = this, // This will become the data for the new queryable 158 | bufferData; //These get remade each key 159 | 160 | dLog("---- keys", keys); 161 | 162 | while ( (key = keys.shift()) ) { 163 | bufferData = []; 164 | 165 | dLog('--- looking for ', key); 166 | 167 | currentData.forEach(function (configDataPoint) { 168 | 169 | dLog('--- data point', configDataPoint); 170 | 171 | for (var dataPointKey in configDataPoint) { 172 | dLog('------ data point key', dataPointKey); 173 | 174 | if (stringOrRegexMatch(dataPointKey, key)) { 175 | dLog('--------- data point key matches'); 176 | 177 | var configDataArray = configDataPoint[dataPointKey]; 178 | 179 | // Point all of the elements of this array back to the original 180 | // so i can modify the array in place later on. 181 | _.each(configDataArray, function (obj) { 182 | obj.parentArray = configDataArray; 183 | bufferData.push( obj ); 184 | }); 185 | } 186 | } 187 | }); 188 | 189 | currentData = bufferData; 190 | } 191 | 192 | dLog('--- reutrning queryable based on this data', currentData); 193 | var q = createQueryable(currentData, this); 194 | 195 | dLog('--- returning queryable', q); 196 | return q; 197 | }, 198 | 199 | /* 200 | * Returns new queryable with only that index (or empty) 201 | */ 202 | eq: function (index) { 203 | return createQueryable( this[index] || [], this); 204 | }, 205 | 206 | /* 207 | * Returns a matcher 208 | * The idea is that you'll do something like this: 209 | * @example 210 | * queryable.find('server').where('server_name').match(/awe/).find(/location/) 211 | * 212 | * So "where" returns a matcher whose "match" method you call, which will return a filtered queryable 213 | */ 214 | where: function (attributeName) { 215 | return createMatcher(attributeName, this); 216 | }, 217 | 218 | 219 | 220 | 221 | _getDataPointIndex: function (datum) { 222 | var parentArray = datum.parentArray; 223 | if (!parentArray) { 224 | throw new Error("data point has no parent array!"); 225 | 226 | } else { 227 | for (var i=0; i < parentArray.length; i++) { 228 | if (parentArray[i] == datum) { 229 | return i; 230 | } 231 | } 232 | throw new Error("Could not find data point inside array"); 233 | } 234 | }, 235 | 236 | 237 | remove: function () { 238 | var queryable = this; 239 | _.each(this, function (datum) { 240 | var dataPointIndex = queryable._getDataPointIndex(datum); 241 | datum.parentArray.splice(dataPointIndex, 1); 242 | }); 243 | 244 | return this; 245 | }, 246 | 247 | 248 | _parseNewNode: function (newNode) { 249 | var isBuilder = newNode._isNodeBuilder; 250 | if (isBuilder) { 251 | newNode = newNode.build(); 252 | } 253 | return newNode; 254 | }, 255 | 256 | addNode: function (key, newNode) { 257 | newNode = this._parseNewNode( newNode ); 258 | 259 | _.each(this, function (datum) { 260 | if (!datum[key]) { 261 | datum[key] = []; 262 | datum[key]._isNodeArray = true; 263 | } 264 | datum[key].push(newNode); 265 | }); 266 | 267 | return this; 268 | }, 269 | replaceWith: function ( newNode ) { 270 | var queryable = this; 271 | newNode = this._parseNewNode( newNode ); 272 | 273 | _.each(this, function (datum) { 274 | var dataPointIndex = queryable._getDataPointIndex(datum); 275 | datum.parentArray.splice(dataPointIndex, 1, newNode); 276 | }); 277 | 278 | return this; 279 | }, 280 | 281 | 282 | /* 283 | * Allows NodeBuilder to be accessed implicitly 284 | */ 285 | createNewNode: function (name) { 286 | return QueryableNodeBuilder.create(this, name); 287 | }, 288 | alterNodes: function () { 289 | return QueryableNodeBuilder.create(this, this); 290 | }, 291 | 292 | parent: function () { 293 | return this.parent; 294 | }, 295 | root: function () { 296 | if (this.parent) { 297 | return this.parent.root(); 298 | } 299 | return this; //only root doesn't have a parent 300 | }, 301 | 302 | // output whatever this is currently matching 303 | stringify: function () { 304 | var stringifieds = []; 305 | 306 | _.each(this, function (datum) { 307 | stringifieds.push( stringify(datum) ); 308 | }); 309 | 310 | return stringifieds.join("\n"); 311 | }, 312 | 313 | 314 | clear: function () { 315 | this.splice(0, this.length); 316 | } 317 | }); 318 | 319 | var createQueryable = function ( data, parent ) { 320 | var q = Object.create(queryableProto); 321 | 322 | if (typeof data == "string") { 323 | data = parse(data); 324 | } 325 | 326 | q.init(data, parent); 327 | return q; 328 | }; 329 | 330 | 331 | module.exports = { 332 | create: createQueryable 333 | }; 334 | -------------------------------------------------------------------------------- /lib/QueryableNodeBuilder.js: -------------------------------------------------------------------------------- 1 | var NodeBuilder = require('./NodeBuilder'), 2 | _ = require('underscore'); 3 | 4 | 5 | var queryableNodeBuilderProto = { 6 | addToQuery: function () { 7 | return this.queryable.addNode( this.name, this ); 8 | }, 9 | replaceQuery: function () { 10 | return this.queryable.replaceWith( this ); 11 | } 12 | }; 13 | 14 | var createQueryableNodeBuilder = function (queryable) { 15 | var qnBuilder = Object.create(queryableNodeBuilderProto); 16 | _.extend(qnBuilder, { 17 | queryable: queryable 18 | }); 19 | 20 | var args = Array.prototype.slice.call(arguments, 1); 21 | var nodeBuilder = NodeBuilder.create.apply(this, args); 22 | 23 | // Add the extra functions 24 | _.extend(nodeBuilder, qnBuilder); 25 | 26 | return nodeBuilder; 27 | }; 28 | 29 | module.exports = { 30 | create: createQueryableNodeBuilder 31 | } -------------------------------------------------------------------------------- /lib/nginx-config-parser.js: -------------------------------------------------------------------------------- 1 | var Queryable = require('./Queryable'), 2 | parse = require('./parse'); 3 | 4 | // 5 | // There are some weird things that happen 6 | // with parsing from files in other scripts 7 | // with relative loactions, 8 | // so I'm not including it anymore. 9 | // 10 | 11 | var parseFromString = function (data) { 12 | return parse(data); 13 | }; 14 | 15 | var queryFromString = function (data) { 16 | return Queryable.create( parseFromString(data) ); 17 | }; 18 | 19 | module.exports = { 20 | parseFromString: parseFromString, 21 | queryFromString: queryFromString 22 | }; -------------------------------------------------------------------------------- /lib/parse.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Will create a JSON representation of the conf it's fed 3 | */ 4 | 5 | module.exports = function (confFile) { 6 | // "global" variables used to maintain the current state 7 | var obj = {}; 8 | var nodeChain = [obj]; 9 | var currentBuffer = ""; 10 | var token; 11 | var currentObject; 12 | 13 | // helper methods 14 | var getCurrentNode = function () { 15 | return nodeChain[ nodeChain.length - 1 ]; 16 | }; 17 | var strip = function (str) { 18 | return str.replace(/^\s*/, '').replace(/\s*$/, ''); 19 | }; 20 | 21 | 22 | // a correct token has been found 23 | // The goal is to split the string into "tokens", which are basically split by 24 | // space..but then there's quotes... 25 | var prepareBuffer = function () { 26 | currentBuffer = currentBuffer.substring(0, currentBuffer.length-1); 27 | 28 | var split = []; 29 | var newBuffer = ""; 30 | for (var i = 0; i < currentBuffer.length; i++) { 31 | var character = currentBuffer.substring(i, i+1); 32 | newBuffer += character; 33 | 34 | // Don't split if it's a quote 35 | if (quoteHandler.handling) { 36 | quoteHandler.handle(character); 37 | 38 | }else{ 39 | if (character.match(/'|"/)) { 40 | quoteHandler.init(character); 41 | 42 | } else if (character.match(/\s/)) { 43 | newBuffer = strip(newBuffer); 44 | // could be "" at this point. 45 | if (newBuffer) { 46 | split.push(newBuffer); 47 | } 48 | newBuffer = ""; 49 | } 50 | 51 | } 52 | } 53 | 54 | // Any remaining...like the tail end of the string. 55 | if (newBuffer) { 56 | split.push(newBuffer); 57 | } 58 | 59 | // reset the buffer. 60 | currentBuffer = ""; 61 | return split; 62 | }; 63 | 64 | var endObject = function () { 65 | prepareBuffer(); 66 | nodeChain.pop(); 67 | }; 68 | 69 | var newObject = function () { 70 | var strings = prepareBuffer(), 71 | key = strings.join(" "), 72 | currentObject = getCurrentNode(), 73 | currentKey = currentObject[key]; 74 | 75 | // This key already exists on this object. Sooo we'll make the current value part of an array related to this key. 76 | if (!currentKey) { 77 | currentObject[key] = []; 78 | currentObject[key]._isNodeArray = true; 79 | } 80 | 81 | // And create the actual new obj 82 | var newObj = {}; 83 | 84 | currentObject[key].push(newObj); 85 | 86 | nodeChain.push(newObj); 87 | }; 88 | 89 | 90 | var quoteHandler = { 91 | 92 | init: function (token) { 93 | this.handling = true; 94 | this.quoteOpeners = [token]; 95 | }, 96 | handle: function (token) { 97 | // sample 'theres this "cool \'whatupppp' super" thing' that 98 | 99 | if (token == "\\") { 100 | this.nextIsEscaped = true; 101 | 102 | // Shouldn't count if it was escaped 103 | } else if (this.nextIsEscaped) { 104 | this.nextIsEscaped = false; 105 | 106 | } 107 | 108 | // might be the last quote... 109 | if (!this.nextIsEscaped 110 | && token == this.quoteOpeners[ this.quoteOpeners.length-1 ]) { 111 | this.quoteOpeners.pop(); 112 | 113 | // And it's totes done?? 114 | if (!this.quoteOpeners.length) { 115 | this.complete(); 116 | } 117 | 118 | // a nested quote 119 | // ..and i do push it if it's escaped. Assuming that the closing quote is also going to be escaped 120 | } else if (token.match(/'|"/)) { 121 | this.quoteOpeners.push(token); 122 | 123 | } 124 | }, 125 | complete: function () { 126 | // This doesn't modify the buffer. The buffer should be used as is 127 | this.handling = false; 128 | } 129 | }; 130 | 131 | var commentHandler = { 132 | init: function () { 133 | this.handling = true; 134 | if (!currentObject.comments) { 135 | currentObject.comments = []; 136 | } 137 | }, 138 | handle: function (token) { 139 | if (token.match(/\n|\r/)) { 140 | this.complete(); 141 | } 142 | }, 143 | complete: function () { 144 | // Do this manually for comments, because there shouldn't be any parsing 145 | currentBuffer = strip(currentBuffer); 146 | currentObject.comments.push( currentBuffer ); 147 | currentBuffer = ""; 148 | 149 | this.handling = false; 150 | } 151 | }; 152 | 153 | 154 | for (var i = 0; i < confFile.length; i++) { 155 | token = confFile.substring(i, i+1); 156 | currentBuffer += token; 157 | 158 | currentObject = getCurrentNode(); 159 | 160 | if (commentHandler.handling) { 161 | commentHandler.handle(token); 162 | continue; 163 | 164 | } else if (quoteHandler.handling) { 165 | quoteHandler.handle(token); 166 | continue; 167 | 168 | // Normal processing 169 | } else { 170 | // An ending brace means the object is complete 171 | if (token == "}") { 172 | endObject(); 173 | continue; 174 | 175 | // an opening brace means a new object is starting 176 | } else if (token == "{") { 177 | newObject(); 178 | continue; 179 | 180 | // most other things need to end in a semicolon 181 | } else if (token == ";") { 182 | var strings = prepareBuffer(); 183 | var name = strings[0]; 184 | 185 | if (!currentObject[ name ]) { 186 | currentObject[ name ] = []; 187 | currentObject[ name ]._isDirectiveArray = true; 188 | } 189 | currentObject[ name ].push( strings.slice(1) ); 190 | continue; 191 | 192 | // a comment. this line doesn't have to end in a semicolon 193 | } else if (token == "#") { 194 | commentHandler.init(token); 195 | 196 | } else if (token.match(/"|'/)) { 197 | quoteHandler.init(token); 198 | } 199 | } 200 | } 201 | 202 | //console.log( JSON.stringify(obj, null, 4)); 203 | return obj; 204 | }; 205 | -------------------------------------------------------------------------------- /lib/stringify.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function ( parsedNode, DEBUG_MODE ) { 3 | 4 | var dLog = function () { 5 | if (DEBUG_MODE) { 6 | console.log.apply(console, arguments); 7 | } 8 | }; 9 | 10 | 11 | // var stringFromComments = function (comments, indent) { 12 | // var s = ""; 13 | // for (var i=0; i < comments.length; i++) { 14 | // s += indent + "#" + comments[i] + "\n"; 15 | // } 16 | // return s; 17 | // }; 18 | 19 | var stringFromNodes = function (key, nodes, indent) { 20 | var s = ""; 21 | 22 | for (var i=0; i < nodes.length; i++) { 23 | var node = nodes[i]; 24 | s += "" 25 | + indent + key + " {" + "\n" 26 | + indent + stringFromNode(node, indent.length + 4) + "\n" 27 | + indent + "}" + "\n"; 28 | } 29 | 30 | return s; 31 | }; 32 | 33 | var stringFromDirectives = function (key, directives, indent) { 34 | var s = ""; 35 | for (var i = 0; i < directives.length; i++) { 36 | s += indent + key + " " + directives[i].join(" ") + ";" + " \n"; 37 | } 38 | return s; 39 | }; 40 | 41 | var stringFromNode = function (node, indentAmount) { 42 | 43 | dLog("=== in stringFromNode ==="); 44 | 45 | indentAmount = indentAmount || 0; 46 | var indent = ""; 47 | for (var i = 0; i < indentAmount; i++) { 48 | indent += " "; 49 | } 50 | 51 | var nodeString = ""; 52 | for (var key in node) { 53 | var v = node[key]; 54 | 55 | dLog("key = ", key); 56 | 57 | // comments array 58 | if (key == "comments") { 59 | //nodeString += stringFromComments(v, indent) + "\n"; 60 | 61 | // Queryable puts a parentArray key on node arrays so that it can delete them. 62 | } else if (key == "parentArray") { 63 | continue; 64 | 65 | } else if (v._isNodeArray) { 66 | dLog("--- is node"); 67 | nodeString += stringFromNodes(key, v, indent); 68 | 69 | } else if (v._isDirectiveArray) { 70 | dLog("--- is directive"); 71 | nodeString += stringFromDirectives(key, v, indent); 72 | } 73 | 74 | } 75 | 76 | return nodeString; 77 | }; 78 | 79 | return stringFromNode(parsedNode); 80 | 81 | }; 82 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nginx-config-parser", 3 | "version": "0.1.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.8.3", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", 10 | "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.8.3" 14 | } 15 | }, 16 | "@babel/highlight": { 17 | "version": "7.8.3", 18 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", 19 | "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", 20 | "dev": true, 21 | "requires": { 22 | "chalk": "^2.0.0", 23 | "esutils": "^2.0.2", 24 | "js-tokens": "^4.0.0" 25 | } 26 | }, 27 | "acorn": { 28 | "version": "7.1.1", 29 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", 30 | "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", 31 | "dev": true 32 | }, 33 | "acorn-jsx": { 34 | "version": "5.1.0", 35 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", 36 | "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", 37 | "dev": true 38 | }, 39 | "ajv": { 40 | "version": "6.11.0", 41 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", 42 | "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", 43 | "dev": true, 44 | "requires": { 45 | "fast-deep-equal": "^3.1.1", 46 | "fast-json-stable-stringify": "^2.0.0", 47 | "json-schema-traverse": "^0.4.1", 48 | "uri-js": "^4.2.2" 49 | } 50 | }, 51 | "ansi-escapes": { 52 | "version": "4.3.0", 53 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", 54 | "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", 55 | "dev": true, 56 | "requires": { 57 | "type-fest": "^0.8.1" 58 | } 59 | }, 60 | "ansi-regex": { 61 | "version": "5.0.0", 62 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 63 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 64 | "dev": true 65 | }, 66 | "ansi-styles": { 67 | "version": "3.2.1", 68 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 69 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 70 | "dev": true, 71 | "requires": { 72 | "color-convert": "^1.9.0" 73 | } 74 | }, 75 | "anymatch": { 76 | "version": "3.1.1", 77 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 78 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 79 | "dev": true, 80 | "requires": { 81 | "normalize-path": "^3.0.0", 82 | "picomatch": "^2.0.4" 83 | } 84 | }, 85 | "argparse": { 86 | "version": "1.0.10", 87 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 88 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 89 | "dev": true, 90 | "requires": { 91 | "sprintf-js": "~1.0.2" 92 | } 93 | }, 94 | "astral-regex": { 95 | "version": "1.0.0", 96 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", 97 | "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", 98 | "dev": true 99 | }, 100 | "balanced-match": { 101 | "version": "1.0.0", 102 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 103 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 104 | "dev": true 105 | }, 106 | "binary-extensions": { 107 | "version": "2.0.0", 108 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", 109 | "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", 110 | "dev": true 111 | }, 112 | "brace-expansion": { 113 | "version": "1.1.11", 114 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 115 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 116 | "dev": true, 117 | "requires": { 118 | "balanced-match": "^1.0.0", 119 | "concat-map": "0.0.1" 120 | } 121 | }, 122 | "braces": { 123 | "version": "3.0.2", 124 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 125 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 126 | "dev": true, 127 | "requires": { 128 | "fill-range": "^7.0.1" 129 | } 130 | }, 131 | "buffer-from": { 132 | "version": "1.1.1", 133 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 134 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 135 | "dev": true 136 | }, 137 | "callsites": { 138 | "version": "3.1.0", 139 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 140 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 141 | "dev": true 142 | }, 143 | "chalk": { 144 | "version": "2.4.2", 145 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 146 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 147 | "dev": true, 148 | "requires": { 149 | "ansi-styles": "^3.2.1", 150 | "escape-string-regexp": "^1.0.5", 151 | "supports-color": "^5.3.0" 152 | } 153 | }, 154 | "chardet": { 155 | "version": "0.7.0", 156 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", 157 | "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", 158 | "dev": true 159 | }, 160 | "chokidar": { 161 | "version": "3.3.1", 162 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", 163 | "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", 164 | "dev": true, 165 | "requires": { 166 | "anymatch": "~3.1.1", 167 | "braces": "~3.0.2", 168 | "fsevents": "~2.1.2", 169 | "glob-parent": "~5.1.0", 170 | "is-binary-path": "~2.1.0", 171 | "is-glob": "~4.0.1", 172 | "normalize-path": "~3.0.0", 173 | "readdirp": "~3.3.0" 174 | } 175 | }, 176 | "cli-cursor": { 177 | "version": "3.1.0", 178 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", 179 | "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", 180 | "dev": true, 181 | "requires": { 182 | "restore-cursor": "^3.1.0" 183 | } 184 | }, 185 | "cli-width": { 186 | "version": "2.2.0", 187 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 188 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", 189 | "dev": true 190 | }, 191 | "color-convert": { 192 | "version": "1.9.3", 193 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 194 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 195 | "dev": true, 196 | "requires": { 197 | "color-name": "1.1.3" 198 | } 199 | }, 200 | "color-name": { 201 | "version": "1.1.3", 202 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 203 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 204 | "dev": true 205 | }, 206 | "concat-map": { 207 | "version": "0.0.1", 208 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 209 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 210 | "dev": true 211 | }, 212 | "core-js": { 213 | "version": "3.6.4", 214 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", 215 | "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", 216 | "dev": true 217 | }, 218 | "cross-spawn": { 219 | "version": "6.0.5", 220 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 221 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 222 | "dev": true, 223 | "requires": { 224 | "nice-try": "^1.0.4", 225 | "path-key": "^2.0.1", 226 | "semver": "^5.5.0", 227 | "shebang-command": "^1.2.0", 228 | "which": "^1.2.9" 229 | }, 230 | "dependencies": { 231 | "semver": { 232 | "version": "5.7.1", 233 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 234 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 235 | "dev": true 236 | } 237 | } 238 | }, 239 | "debug": { 240 | "version": "4.1.1", 241 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 242 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 243 | "dev": true, 244 | "requires": { 245 | "ms": "^2.1.1" 246 | } 247 | }, 248 | "deep-is": { 249 | "version": "0.1.3", 250 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 251 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 252 | "dev": true 253 | }, 254 | "diff": { 255 | "version": "4.0.2", 256 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 257 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 258 | "dev": true 259 | }, 260 | "doctrine": { 261 | "version": "3.0.0", 262 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 263 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 264 | "dev": true, 265 | "requires": { 266 | "esutils": "^2.0.2" 267 | } 268 | }, 269 | "emoji-regex": { 270 | "version": "8.0.0", 271 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 272 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 273 | "dev": true 274 | }, 275 | "end-of-stream": { 276 | "version": "1.4.4", 277 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 278 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 279 | "dev": true, 280 | "requires": { 281 | "once": "^1.4.0" 282 | } 283 | }, 284 | "escape-string-regexp": { 285 | "version": "1.0.5", 286 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 287 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 288 | "dev": true 289 | }, 290 | "eslint": { 291 | "version": "6.8.0", 292 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", 293 | "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", 294 | "dev": true, 295 | "requires": { 296 | "@babel/code-frame": "^7.0.0", 297 | "ajv": "^6.10.0", 298 | "chalk": "^2.1.0", 299 | "cross-spawn": "^6.0.5", 300 | "debug": "^4.0.1", 301 | "doctrine": "^3.0.0", 302 | "eslint-scope": "^5.0.0", 303 | "eslint-utils": "^1.4.3", 304 | "eslint-visitor-keys": "^1.1.0", 305 | "espree": "^6.1.2", 306 | "esquery": "^1.0.1", 307 | "esutils": "^2.0.2", 308 | "file-entry-cache": "^5.0.1", 309 | "functional-red-black-tree": "^1.0.1", 310 | "glob-parent": "^5.0.0", 311 | "globals": "^12.1.0", 312 | "ignore": "^4.0.6", 313 | "import-fresh": "^3.0.0", 314 | "imurmurhash": "^0.1.4", 315 | "inquirer": "^7.0.0", 316 | "is-glob": "^4.0.0", 317 | "js-yaml": "^3.13.1", 318 | "json-stable-stringify-without-jsonify": "^1.0.1", 319 | "levn": "^0.3.0", 320 | "lodash": "^4.17.14", 321 | "minimatch": "^3.0.4", 322 | "mkdirp": "^0.5.1", 323 | "natural-compare": "^1.4.0", 324 | "optionator": "^0.8.3", 325 | "progress": "^2.0.0", 326 | "regexpp": "^2.0.1", 327 | "semver": "^6.1.2", 328 | "strip-ansi": "^5.2.0", 329 | "strip-json-comments": "^3.0.1", 330 | "table": "^5.2.3", 331 | "text-table": "^0.2.0", 332 | "v8-compile-cache": "^2.0.3" 333 | } 334 | }, 335 | "eslint-scope": { 336 | "version": "5.0.0", 337 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", 338 | "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", 339 | "dev": true, 340 | "requires": { 341 | "esrecurse": "^4.1.0", 342 | "estraverse": "^4.1.1" 343 | } 344 | }, 345 | "eslint-utils": { 346 | "version": "1.4.3", 347 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", 348 | "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", 349 | "dev": true, 350 | "requires": { 351 | "eslint-visitor-keys": "^1.1.0" 352 | } 353 | }, 354 | "eslint-visitor-keys": { 355 | "version": "1.1.0", 356 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", 357 | "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", 358 | "dev": true 359 | }, 360 | "eslint-watch": { 361 | "version": "6.0.1", 362 | "resolved": "https://registry.npmjs.org/eslint-watch/-/eslint-watch-6.0.1.tgz", 363 | "integrity": "sha512-tWT6gQQWzGVn4KMs6ZAdG+nkNNQHg+c6wGdw/mwhp+jWyA7OWEOLxQnRhrP34ddJjpgDafhHOHBNtpVOtXXv4g==", 364 | "dev": true, 365 | "requires": { 366 | "chokidar": "^3.1.1", 367 | "core-js": "^3.2.1", 368 | "debug": "^4.1.0", 369 | "execa": "^2.0.4", 370 | "keypress": "^0.2.1", 371 | "lodash.debounce": "^4.0.8", 372 | "lodash.isempty": "^4.4.0", 373 | "lodash.isequal": "^4.5.0", 374 | "lodash.kebabcase": "^4.1.1", 375 | "lodash.unionwith": "^4.6.0", 376 | "optionator": "^0.8.2", 377 | "source-map-support": "^0.5.13" 378 | } 379 | }, 380 | "espree": { 381 | "version": "6.1.2", 382 | "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", 383 | "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", 384 | "dev": true, 385 | "requires": { 386 | "acorn": "^7.1.0", 387 | "acorn-jsx": "^5.1.0", 388 | "eslint-visitor-keys": "^1.1.0" 389 | } 390 | }, 391 | "esprima": { 392 | "version": "4.0.1", 393 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 394 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 395 | "dev": true 396 | }, 397 | "esquery": { 398 | "version": "1.1.0", 399 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz", 400 | "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==", 401 | "dev": true, 402 | "requires": { 403 | "estraverse": "^4.0.0" 404 | } 405 | }, 406 | "esrecurse": { 407 | "version": "4.2.1", 408 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 409 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 410 | "dev": true, 411 | "requires": { 412 | "estraverse": "^4.1.0" 413 | } 414 | }, 415 | "estraverse": { 416 | "version": "4.3.0", 417 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 418 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 419 | "dev": true 420 | }, 421 | "esutils": { 422 | "version": "2.0.3", 423 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 424 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 425 | "dev": true 426 | }, 427 | "execa": { 428 | "version": "2.1.0", 429 | "resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz", 430 | "integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==", 431 | "dev": true, 432 | "requires": { 433 | "cross-spawn": "^7.0.0", 434 | "get-stream": "^5.0.0", 435 | "is-stream": "^2.0.0", 436 | "merge-stream": "^2.0.0", 437 | "npm-run-path": "^3.0.0", 438 | "onetime": "^5.1.0", 439 | "p-finally": "^2.0.0", 440 | "signal-exit": "^3.0.2", 441 | "strip-final-newline": "^2.0.0" 442 | }, 443 | "dependencies": { 444 | "cross-spawn": { 445 | "version": "7.0.1", 446 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", 447 | "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", 448 | "dev": true, 449 | "requires": { 450 | "path-key": "^3.1.0", 451 | "shebang-command": "^2.0.0", 452 | "which": "^2.0.1" 453 | } 454 | }, 455 | "path-key": { 456 | "version": "3.1.1", 457 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 458 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 459 | "dev": true 460 | }, 461 | "shebang-command": { 462 | "version": "2.0.0", 463 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 464 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 465 | "dev": true, 466 | "requires": { 467 | "shebang-regex": "^3.0.0" 468 | } 469 | }, 470 | "shebang-regex": { 471 | "version": "3.0.0", 472 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 473 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 474 | "dev": true 475 | }, 476 | "which": { 477 | "version": "2.0.2", 478 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 479 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 480 | "dev": true, 481 | "requires": { 482 | "isexe": "^2.0.0" 483 | } 484 | } 485 | } 486 | }, 487 | "external-editor": { 488 | "version": "3.1.0", 489 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", 490 | "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", 491 | "dev": true, 492 | "requires": { 493 | "chardet": "^0.7.0", 494 | "iconv-lite": "^0.4.24", 495 | "tmp": "^0.0.33" 496 | } 497 | }, 498 | "eyes": { 499 | "version": "0.1.8", 500 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 501 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", 502 | "dev": true 503 | }, 504 | "fast-deep-equal": { 505 | "version": "3.1.1", 506 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", 507 | "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", 508 | "dev": true 509 | }, 510 | "fast-json-stable-stringify": { 511 | "version": "2.1.0", 512 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 513 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 514 | "dev": true 515 | }, 516 | "fast-levenshtein": { 517 | "version": "2.0.6", 518 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 519 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 520 | "dev": true 521 | }, 522 | "figures": { 523 | "version": "3.2.0", 524 | "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", 525 | "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", 526 | "dev": true, 527 | "requires": { 528 | "escape-string-regexp": "^1.0.5" 529 | } 530 | }, 531 | "file-entry-cache": { 532 | "version": "5.0.1", 533 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", 534 | "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", 535 | "dev": true, 536 | "requires": { 537 | "flat-cache": "^2.0.1" 538 | } 539 | }, 540 | "fill-range": { 541 | "version": "7.0.1", 542 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 543 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 544 | "dev": true, 545 | "requires": { 546 | "to-regex-range": "^5.0.1" 547 | } 548 | }, 549 | "flat-cache": { 550 | "version": "2.0.1", 551 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", 552 | "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", 553 | "dev": true, 554 | "requires": { 555 | "flatted": "^2.0.0", 556 | "rimraf": "2.6.3", 557 | "write": "1.0.3" 558 | } 559 | }, 560 | "flatted": { 561 | "version": "2.0.1", 562 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", 563 | "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", 564 | "dev": true 565 | }, 566 | "fs.realpath": { 567 | "version": "1.0.0", 568 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 569 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 570 | "dev": true 571 | }, 572 | "fsevents": { 573 | "version": "2.1.2", 574 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", 575 | "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", 576 | "dev": true, 577 | "optional": true 578 | }, 579 | "functional-red-black-tree": { 580 | "version": "1.0.1", 581 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 582 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 583 | "dev": true 584 | }, 585 | "get-stream": { 586 | "version": "5.1.0", 587 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", 588 | "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", 589 | "dev": true, 590 | "requires": { 591 | "pump": "^3.0.0" 592 | } 593 | }, 594 | "glob": { 595 | "version": "7.1.6", 596 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 597 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 598 | "dev": true, 599 | "requires": { 600 | "fs.realpath": "^1.0.0", 601 | "inflight": "^1.0.4", 602 | "inherits": "2", 603 | "minimatch": "^3.0.4", 604 | "once": "^1.3.0", 605 | "path-is-absolute": "^1.0.0" 606 | } 607 | }, 608 | "glob-parent": { 609 | "version": "5.1.0", 610 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", 611 | "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", 612 | "dev": true, 613 | "requires": { 614 | "is-glob": "^4.0.1" 615 | } 616 | }, 617 | "globals": { 618 | "version": "12.3.0", 619 | "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", 620 | "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", 621 | "dev": true, 622 | "requires": { 623 | "type-fest": "^0.8.1" 624 | } 625 | }, 626 | "has-flag": { 627 | "version": "3.0.0", 628 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 629 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 630 | "dev": true 631 | }, 632 | "iconv-lite": { 633 | "version": "0.4.24", 634 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 635 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 636 | "dev": true, 637 | "requires": { 638 | "safer-buffer": ">= 2.1.2 < 3" 639 | } 640 | }, 641 | "ignore": { 642 | "version": "4.0.6", 643 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 644 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 645 | "dev": true 646 | }, 647 | "import-fresh": { 648 | "version": "3.2.1", 649 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", 650 | "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", 651 | "dev": true, 652 | "requires": { 653 | "parent-module": "^1.0.0", 654 | "resolve-from": "^4.0.0" 655 | } 656 | }, 657 | "imurmurhash": { 658 | "version": "0.1.4", 659 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 660 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 661 | "dev": true 662 | }, 663 | "inflight": { 664 | "version": "1.0.6", 665 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 666 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 667 | "dev": true, 668 | "requires": { 669 | "once": "^1.3.0", 670 | "wrappy": "1" 671 | } 672 | }, 673 | "inherits": { 674 | "version": "2.0.4", 675 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 676 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 677 | "dev": true 678 | }, 679 | "inquirer": { 680 | "version": "7.0.4", 681 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.4.tgz", 682 | "integrity": "sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ==", 683 | "dev": true, 684 | "requires": { 685 | "ansi-escapes": "^4.2.1", 686 | "chalk": "^2.4.2", 687 | "cli-cursor": "^3.1.0", 688 | "cli-width": "^2.0.0", 689 | "external-editor": "^3.0.3", 690 | "figures": "^3.0.0", 691 | "lodash": "^4.17.15", 692 | "mute-stream": "0.0.8", 693 | "run-async": "^2.2.0", 694 | "rxjs": "^6.5.3", 695 | "string-width": "^4.1.0", 696 | "strip-ansi": "^5.1.0", 697 | "through": "^2.3.6" 698 | } 699 | }, 700 | "is-binary-path": { 701 | "version": "2.1.0", 702 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 703 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 704 | "dev": true, 705 | "requires": { 706 | "binary-extensions": "^2.0.0" 707 | } 708 | }, 709 | "is-extglob": { 710 | "version": "2.1.1", 711 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 712 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 713 | "dev": true 714 | }, 715 | "is-fullwidth-code-point": { 716 | "version": "3.0.0", 717 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 718 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 719 | "dev": true 720 | }, 721 | "is-glob": { 722 | "version": "4.0.1", 723 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 724 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 725 | "dev": true, 726 | "requires": { 727 | "is-extglob": "^2.1.1" 728 | } 729 | }, 730 | "is-number": { 731 | "version": "7.0.0", 732 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 733 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 734 | "dev": true 735 | }, 736 | "is-promise": { 737 | "version": "2.1.0", 738 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 739 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", 740 | "dev": true 741 | }, 742 | "is-stream": { 743 | "version": "2.0.0", 744 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", 745 | "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", 746 | "dev": true 747 | }, 748 | "isexe": { 749 | "version": "2.0.0", 750 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 751 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 752 | "dev": true 753 | }, 754 | "js-tokens": { 755 | "version": "4.0.0", 756 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 757 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 758 | "dev": true 759 | }, 760 | "js-yaml": { 761 | "version": "3.13.1", 762 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 763 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 764 | "dev": true, 765 | "requires": { 766 | "argparse": "^1.0.7", 767 | "esprima": "^4.0.0" 768 | } 769 | }, 770 | "json-schema-traverse": { 771 | "version": "0.4.1", 772 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 773 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 774 | "dev": true 775 | }, 776 | "json-stable-stringify-without-jsonify": { 777 | "version": "1.0.1", 778 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 779 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 780 | "dev": true 781 | }, 782 | "keypress": { 783 | "version": "0.2.1", 784 | "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.2.1.tgz", 785 | "integrity": "sha1-HoBFQlABjbrUw/6USX1uZ7YmnHc=", 786 | "dev": true 787 | }, 788 | "levn": { 789 | "version": "0.3.0", 790 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 791 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 792 | "dev": true, 793 | "requires": { 794 | "prelude-ls": "~1.1.2", 795 | "type-check": "~0.3.2" 796 | } 797 | }, 798 | "lodash": { 799 | "version": "4.17.21", 800 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 801 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 802 | "dev": true 803 | }, 804 | "lodash.debounce": { 805 | "version": "4.0.8", 806 | "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", 807 | "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", 808 | "dev": true 809 | }, 810 | "lodash.isempty": { 811 | "version": "4.4.0", 812 | "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", 813 | "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=", 814 | "dev": true 815 | }, 816 | "lodash.isequal": { 817 | "version": "4.5.0", 818 | "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", 819 | "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", 820 | "dev": true 821 | }, 822 | "lodash.kebabcase": { 823 | "version": "4.1.1", 824 | "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", 825 | "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", 826 | "dev": true 827 | }, 828 | "lodash.unionwith": { 829 | "version": "4.6.0", 830 | "resolved": "https://registry.npmjs.org/lodash.unionwith/-/lodash.unionwith-4.6.0.tgz", 831 | "integrity": "sha1-dNFAtcqBRubGQ8NyT1FSU42awfA=", 832 | "dev": true 833 | }, 834 | "merge-stream": { 835 | "version": "2.0.0", 836 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 837 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 838 | "dev": true 839 | }, 840 | "mimic-fn": { 841 | "version": "2.1.0", 842 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 843 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 844 | "dev": true 845 | }, 846 | "minimatch": { 847 | "version": "3.0.4", 848 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 849 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 850 | "dev": true, 851 | "requires": { 852 | "brace-expansion": "^1.1.7" 853 | } 854 | }, 855 | "minimist": { 856 | "version": "0.0.8", 857 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 858 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 859 | "dev": true 860 | }, 861 | "mkdirp": { 862 | "version": "0.5.1", 863 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 864 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 865 | "dev": true, 866 | "requires": { 867 | "minimist": "0.0.8" 868 | } 869 | }, 870 | "ms": { 871 | "version": "2.1.2", 872 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 873 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 874 | "dev": true 875 | }, 876 | "mute-stream": { 877 | "version": "0.0.8", 878 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", 879 | "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", 880 | "dev": true 881 | }, 882 | "natural-compare": { 883 | "version": "1.4.0", 884 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 885 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 886 | "dev": true 887 | }, 888 | "nice-try": { 889 | "version": "1.0.5", 890 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 891 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 892 | "dev": true 893 | }, 894 | "normalize-path": { 895 | "version": "3.0.0", 896 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 897 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 898 | "dev": true 899 | }, 900 | "npm-run-path": { 901 | "version": "3.1.0", 902 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", 903 | "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", 904 | "dev": true, 905 | "requires": { 906 | "path-key": "^3.0.0" 907 | }, 908 | "dependencies": { 909 | "path-key": { 910 | "version": "3.1.1", 911 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 912 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 913 | "dev": true 914 | } 915 | } 916 | }, 917 | "once": { 918 | "version": "1.4.0", 919 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 920 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 921 | "dev": true, 922 | "requires": { 923 | "wrappy": "1" 924 | } 925 | }, 926 | "onetime": { 927 | "version": "5.1.0", 928 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", 929 | "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", 930 | "dev": true, 931 | "requires": { 932 | "mimic-fn": "^2.1.0" 933 | } 934 | }, 935 | "optionator": { 936 | "version": "0.8.3", 937 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", 938 | "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", 939 | "dev": true, 940 | "requires": { 941 | "deep-is": "~0.1.3", 942 | "fast-levenshtein": "~2.0.6", 943 | "levn": "~0.3.0", 944 | "prelude-ls": "~1.1.2", 945 | "type-check": "~0.3.2", 946 | "word-wrap": "~1.2.3" 947 | } 948 | }, 949 | "os-tmpdir": { 950 | "version": "1.0.2", 951 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 952 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 953 | "dev": true 954 | }, 955 | "p-finally": { 956 | "version": "2.0.1", 957 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", 958 | "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", 959 | "dev": true 960 | }, 961 | "parent-module": { 962 | "version": "1.0.1", 963 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 964 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 965 | "dev": true, 966 | "requires": { 967 | "callsites": "^3.0.0" 968 | } 969 | }, 970 | "path-is-absolute": { 971 | "version": "1.0.1", 972 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 973 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 974 | "dev": true 975 | }, 976 | "path-key": { 977 | "version": "2.0.1", 978 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 979 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 980 | "dev": true 981 | }, 982 | "picomatch": { 983 | "version": "2.2.1", 984 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", 985 | "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", 986 | "dev": true 987 | }, 988 | "prelude-ls": { 989 | "version": "1.1.2", 990 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 991 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 992 | "dev": true 993 | }, 994 | "progress": { 995 | "version": "2.0.3", 996 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 997 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 998 | "dev": true 999 | }, 1000 | "pump": { 1001 | "version": "3.0.0", 1002 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 1003 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 1004 | "dev": true, 1005 | "requires": { 1006 | "end-of-stream": "^1.1.0", 1007 | "once": "^1.3.1" 1008 | } 1009 | }, 1010 | "punycode": { 1011 | "version": "2.1.1", 1012 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1013 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1014 | "dev": true 1015 | }, 1016 | "readdirp": { 1017 | "version": "3.3.0", 1018 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", 1019 | "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", 1020 | "dev": true, 1021 | "requires": { 1022 | "picomatch": "^2.0.7" 1023 | } 1024 | }, 1025 | "regexpp": { 1026 | "version": "2.0.1", 1027 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", 1028 | "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", 1029 | "dev": true 1030 | }, 1031 | "resolve-from": { 1032 | "version": "4.0.0", 1033 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1034 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1035 | "dev": true 1036 | }, 1037 | "restore-cursor": { 1038 | "version": "3.1.0", 1039 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", 1040 | "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", 1041 | "dev": true, 1042 | "requires": { 1043 | "onetime": "^5.1.0", 1044 | "signal-exit": "^3.0.2" 1045 | } 1046 | }, 1047 | "rimraf": { 1048 | "version": "2.6.3", 1049 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 1050 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 1051 | "dev": true, 1052 | "requires": { 1053 | "glob": "^7.1.3" 1054 | } 1055 | }, 1056 | "run-async": { 1057 | "version": "2.3.0", 1058 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", 1059 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", 1060 | "dev": true, 1061 | "requires": { 1062 | "is-promise": "^2.1.0" 1063 | } 1064 | }, 1065 | "rxjs": { 1066 | "version": "6.5.4", 1067 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", 1068 | "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", 1069 | "dev": true, 1070 | "requires": { 1071 | "tslib": "^1.9.0" 1072 | } 1073 | }, 1074 | "safer-buffer": { 1075 | "version": "2.1.2", 1076 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1077 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1078 | "dev": true 1079 | }, 1080 | "semver": { 1081 | "version": "6.3.0", 1082 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1083 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1084 | "dev": true 1085 | }, 1086 | "shebang-command": { 1087 | "version": "1.2.0", 1088 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1089 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1090 | "dev": true, 1091 | "requires": { 1092 | "shebang-regex": "^1.0.0" 1093 | } 1094 | }, 1095 | "shebang-regex": { 1096 | "version": "1.0.0", 1097 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1098 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 1099 | "dev": true 1100 | }, 1101 | "signal-exit": { 1102 | "version": "3.0.2", 1103 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1104 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 1105 | "dev": true 1106 | }, 1107 | "slice-ansi": { 1108 | "version": "2.1.0", 1109 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", 1110 | "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", 1111 | "dev": true, 1112 | "requires": { 1113 | "ansi-styles": "^3.2.0", 1114 | "astral-regex": "^1.0.0", 1115 | "is-fullwidth-code-point": "^2.0.0" 1116 | }, 1117 | "dependencies": { 1118 | "is-fullwidth-code-point": { 1119 | "version": "2.0.0", 1120 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1121 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1122 | "dev": true 1123 | } 1124 | } 1125 | }, 1126 | "source-map": { 1127 | "version": "0.6.1", 1128 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1129 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1130 | "dev": true 1131 | }, 1132 | "source-map-support": { 1133 | "version": "0.5.16", 1134 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", 1135 | "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", 1136 | "dev": true, 1137 | "requires": { 1138 | "buffer-from": "^1.0.0", 1139 | "source-map": "^0.6.0" 1140 | } 1141 | }, 1142 | "sprintf-js": { 1143 | "version": "1.0.3", 1144 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1145 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1146 | "dev": true 1147 | }, 1148 | "string-width": { 1149 | "version": "4.2.0", 1150 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 1151 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 1152 | "dev": true, 1153 | "requires": { 1154 | "emoji-regex": "^8.0.0", 1155 | "is-fullwidth-code-point": "^3.0.0", 1156 | "strip-ansi": "^6.0.0" 1157 | }, 1158 | "dependencies": { 1159 | "strip-ansi": { 1160 | "version": "6.0.0", 1161 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1162 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1163 | "dev": true, 1164 | "requires": { 1165 | "ansi-regex": "^5.0.0" 1166 | } 1167 | } 1168 | } 1169 | }, 1170 | "strip-ansi": { 1171 | "version": "5.2.0", 1172 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1173 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1174 | "dev": true, 1175 | "requires": { 1176 | "ansi-regex": "^4.1.0" 1177 | }, 1178 | "dependencies": { 1179 | "ansi-regex": { 1180 | "version": "4.1.0", 1181 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1182 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1183 | "dev": true 1184 | } 1185 | } 1186 | }, 1187 | "strip-final-newline": { 1188 | "version": "2.0.0", 1189 | "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", 1190 | "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", 1191 | "dev": true 1192 | }, 1193 | "strip-json-comments": { 1194 | "version": "3.0.1", 1195 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", 1196 | "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", 1197 | "dev": true 1198 | }, 1199 | "supports-color": { 1200 | "version": "5.5.0", 1201 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1202 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1203 | "dev": true, 1204 | "requires": { 1205 | "has-flag": "^3.0.0" 1206 | } 1207 | }, 1208 | "table": { 1209 | "version": "5.4.6", 1210 | "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", 1211 | "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", 1212 | "dev": true, 1213 | "requires": { 1214 | "ajv": "^6.10.2", 1215 | "lodash": "^4.17.14", 1216 | "slice-ansi": "^2.1.0", 1217 | "string-width": "^3.0.0" 1218 | }, 1219 | "dependencies": { 1220 | "emoji-regex": { 1221 | "version": "7.0.3", 1222 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 1223 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 1224 | "dev": true 1225 | }, 1226 | "is-fullwidth-code-point": { 1227 | "version": "2.0.0", 1228 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1229 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1230 | "dev": true 1231 | }, 1232 | "string-width": { 1233 | "version": "3.1.0", 1234 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1235 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1236 | "dev": true, 1237 | "requires": { 1238 | "emoji-regex": "^7.0.1", 1239 | "is-fullwidth-code-point": "^2.0.0", 1240 | "strip-ansi": "^5.1.0" 1241 | } 1242 | } 1243 | } 1244 | }, 1245 | "text-table": { 1246 | "version": "0.2.0", 1247 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1248 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1249 | "dev": true 1250 | }, 1251 | "through": { 1252 | "version": "2.3.8", 1253 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1254 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1255 | "dev": true 1256 | }, 1257 | "tmp": { 1258 | "version": "0.0.33", 1259 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 1260 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 1261 | "dev": true, 1262 | "requires": { 1263 | "os-tmpdir": "~1.0.2" 1264 | } 1265 | }, 1266 | "to-regex-range": { 1267 | "version": "5.0.1", 1268 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1269 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1270 | "dev": true, 1271 | "requires": { 1272 | "is-number": "^7.0.0" 1273 | } 1274 | }, 1275 | "tslib": { 1276 | "version": "1.10.0", 1277 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", 1278 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", 1279 | "dev": true 1280 | }, 1281 | "type-check": { 1282 | "version": "0.3.2", 1283 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1284 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1285 | "dev": true, 1286 | "requires": { 1287 | "prelude-ls": "~1.1.2" 1288 | } 1289 | }, 1290 | "type-fest": { 1291 | "version": "0.8.1", 1292 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 1293 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 1294 | "dev": true 1295 | }, 1296 | "underscore": { 1297 | "version": "1.5.2", 1298 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.5.2.tgz", 1299 | "integrity": "sha1-EzXF5PXm0zu7SwBrqMhqAPVW3gg=" 1300 | }, 1301 | "uri-js": { 1302 | "version": "4.2.2", 1303 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1304 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1305 | "dev": true, 1306 | "requires": { 1307 | "punycode": "^2.1.0" 1308 | } 1309 | }, 1310 | "v8-compile-cache": { 1311 | "version": "2.1.0", 1312 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", 1313 | "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", 1314 | "dev": true 1315 | }, 1316 | "vows": { 1317 | "version": "0.8.3", 1318 | "resolved": "https://registry.npmjs.org/vows/-/vows-0.8.3.tgz", 1319 | "integrity": "sha512-PVIxa/ovXhrw5gA3mz6M+ZF3PHlqX4tutR2p/y9NWPAaFVKcWBE8b2ktfr0opQM/qFmcOVWKjSCJVjnYOvjXhw==", 1320 | "dev": true, 1321 | "requires": { 1322 | "diff": "^4.0.1", 1323 | "eyes": "~0.1.6", 1324 | "glob": "^7.1.2" 1325 | } 1326 | }, 1327 | "which": { 1328 | "version": "1.3.1", 1329 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1330 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1331 | "dev": true, 1332 | "requires": { 1333 | "isexe": "^2.0.0" 1334 | } 1335 | }, 1336 | "word-wrap": { 1337 | "version": "1.2.3", 1338 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1339 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 1340 | "dev": true 1341 | }, 1342 | "wrappy": { 1343 | "version": "1.0.2", 1344 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1345 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1346 | "dev": true 1347 | }, 1348 | "write": { 1349 | "version": "1.0.3", 1350 | "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", 1351 | "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", 1352 | "dev": true, 1353 | "requires": { 1354 | "mkdirp": "^0.5.1" 1355 | } 1356 | } 1357 | } 1358 | } 1359 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nginx-config-parser", 3 | "description": "Parses nginx configs and converts back to strings", 4 | "author": "Sean McCollum", 5 | "scripts": { 6 | "lint": "eslint lib", 7 | "test": "vows", 8 | "prepublish": "npm run lint" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/adiktofsugar/node-nginx-config.git" 13 | }, 14 | "main": "lib/nginx-config-parser", 15 | "version": "0.1.1", 16 | "dependencies": { 17 | "underscore": "~1.5.2" 18 | }, 19 | "devDependencies": { 20 | "eslint": "^6.8.0", 21 | "eslint-watch": "^6.0.1", 22 | "vows": "^0.8.3" 23 | }, 24 | "license": "MIT" 25 | } 26 | -------------------------------------------------------------------------------- /test-nginx.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var WITH_NGINX_TEST = true; 4 | 5 | var nginx_config_parser = require('./lib/nginx_config_parser'), 6 | fs = require('fs'), 7 | spawn = require('child_process').spawn, 8 | path = require('path'); 9 | 10 | 11 | 12 | var nginxConfLocation = { 13 | "test": './test-conf.conf', 14 | "real": '/usr/local/nginx/conf/nginx.conf' 15 | }[ WITH_NGINX_TEST ? "real" : "test" ]; 16 | var confDir = '/usr/local/nginx/conf/'; 17 | 18 | var conf = nginx_config_parser.parse( fs.readFileSync(nginxConfLocation, 'utf-8') ); 19 | var sameThing = nginx_config_parser.stringify(conf); 20 | 21 | console.log(JSON.stringify(conf, null, 4)); 22 | return; 23 | 24 | if (WITH_NGINX_TEST) { 25 | 26 | fs.writeFileSync( path.join(confDir, 'my-nginx.conf'), sameThing); 27 | 28 | 29 | var nginxConfTest = spawn('sudo', ['nginx', '-c', path.join(confDir, 'my-nginx.conf')]); 30 | 31 | nginxConfTest.stdout.on('data', function (data) { 32 | console.log('nginx stdout: ' + data); 33 | }); 34 | 35 | nginxConfTest.stderr.on('data', function (data) { 36 | console.log('nginx stderr: ' + data); 37 | }); 38 | 39 | nginxConfTest.on('close', function (code) { 40 | console.log('nginx conf test exited with code ' + code); 41 | }); 42 | 43 | 44 | 45 | } else { 46 | console.log( sameThing ); 47 | } 48 | -------------------------------------------------------------------------------- /test/main.test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | ncp = require('../lib/nginx-config-parser'), 4 | getConf = require('./utils/getConf'); 5 | 6 | 7 | vows.describe('main module') 8 | .addBatch({ 9 | 'query': { 10 | topic: ncp.queryFromString( getConf() ), 11 | 'finding all servers': { 12 | topic: function (q) { 13 | return q.find('http', 'server'); 14 | }, 15 | 'should have all the servers in the https': function (q) { 16 | assert.equal(q.length, 3); 17 | } 18 | }, 19 | 'finding all certain locations': { 20 | topic: function (q) { 21 | return q.find('http', 'server', /location/); 22 | }, 23 | 'should contain all locations in all servers in all https': function (t) { 24 | assert.equal(t.length, 2); 25 | } 26 | } 27 | } 28 | }) 29 | .export(module); 30 | -------------------------------------------------------------------------------- /test/parse.test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | parse = require('../lib/parse'), 4 | getConf = require('./utils/getConf'); 5 | 6 | 7 | 8 | vows.describe('parse') 9 | .addBatch({ 10 | 'parses': { 11 | topic: parse( getConf() ), 12 | 'should get two https': function (t) { 13 | assert(t.http.length, 2); 14 | } 15 | } 16 | }) 17 | .export(module); 18 | -------------------------------------------------------------------------------- /test/query.test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | Queryable = require('../lib/Queryable'), 4 | getConf = require('./utils/getConf'); 5 | 6 | 7 | vows.describe('Queries') 8 | .addBatch({ 9 | 'init': { 10 | topic: Queryable.create( getConf() ), 11 | 'should have have two https in actual object': function (q) { 12 | assert(q[0].http.length, 2); 13 | } 14 | }, 15 | 'find': { 16 | topic: Queryable.create( getConf() ), 17 | 'should have find method': function (t) { 18 | assert.isFunction(t.find); 19 | }, 20 | 'should error if no selector is given': function (t) { 21 | assert.throws(function () { t.find(); }); 22 | }, 23 | 'should return a filtered queryable': { 24 | topic: function (topic) { 25 | return topic.find('http'); 26 | }, 27 | 'should contain http only': function (t) { 28 | assert.equal(t.length, 2); 29 | } 30 | }, 31 | 'should work with multiple selectrors': { 32 | topic: function (topic) { 33 | return topic.find('http', 'server'); 34 | }, 35 | 'should contain the server instances in all of the https': function (t) { 36 | assert.equal(t.length, 3); 37 | } 38 | } 39 | }, 40 | 'where': { 41 | topic: Queryable.create( getConf() ).find('http', 'server').where('server_name'), 42 | 43 | 'should return a matcher': function (t) { 44 | assert.isFunction(t.match); 45 | }, 46 | 'works with regex': { 47 | topic: Queryable.create( getConf() ).find('http', 'server') 48 | .where(/location.*?another/).all(), 49 | 'should have an array of matching elements': function (t) { 50 | assert.equal(t.length, 1); 51 | assert.isFunction(t.find); // is a queryable 52 | } 53 | }, 54 | 55 | 'match': { 56 | 'when it exists': { 57 | topic: function (topic) { 58 | return topic.match('another.server'); 59 | }, 60 | 'should have an array of matching elements': function (t) { 61 | assert.equal(t.length, 1); 62 | assert.isFunction(t.find); // is a queryable 63 | } 64 | }, 65 | 'when it doesn\'t exist': { 66 | topic: function (topic) { 67 | return topic.match('something else'); 68 | }, 69 | 'should have an array of matching elements': function (t) { 70 | assert.equal(t.length, 0); 71 | assert.isFunction(t.find); // is a queryable 72 | } 73 | }, 74 | 'works with regex': { 75 | topic: function (topic) { 76 | return topic.match(/another/); 77 | }, 78 | 'should have an array of matching elements': function (t) { 79 | assert.equal(t.length, 1); 80 | assert.isFunction(t.find); // is a queryable 81 | } 82 | } 83 | } 84 | }, 85 | 'stringify': { 86 | topic: Queryable.create( getConf() ), 87 | 'should be able to stringify entire configuration': function (t) { 88 | var s = function () { t.stringify(); }; 89 | assert.doesNotThrow(s); 90 | }, 91 | 'should be able to stringify part of configuration': function (t) { 92 | t = t.find('http'); 93 | var s = function () { t.stringify(); }; 94 | assert.doesNotThrow(s); 95 | } 96 | } 97 | }) 98 | 99 | .addBatch({ 100 | 'remove': { 101 | topic: Queryable.create( getConf() ), 102 | 'cannot remove root node': function (t) { 103 | var s = function () { 104 | t.remove(); 105 | }; 106 | assert.throws(s); 107 | }, 108 | 'should remove current matched nodes': function (t) { 109 | t.find('http', 'server').remove(); 110 | var s = t.stringify(); 111 | var matches = s.match(/\s+server\s+\{/); 112 | 113 | // there is no doesNotMatch 114 | assert.equal(matches, null); 115 | } 116 | }, 117 | 'createNewNode/replaceQuery': { 118 | topic: Queryable.create( getConf() ).find('http', 'server') 119 | .where('server_name') 120 | .match(/another/), 121 | 122 | 'creates new node with NodeBuilder and replaces': function (t) { 123 | t.createNewNode('server') 124 | .addDirective('expires', 'on') 125 | .replaceQuery(); 126 | 127 | var newQuery = t.root().find('http', 'server').where('expires').match('on'); 128 | 129 | assert.equal(newQuery.length, 1); 130 | } 131 | }, 132 | 'createNewNode/addToQuery': { 133 | topic: Queryable.create( getConf() ), 134 | 'creates new node with NodeBuilder and adds': function (t) { 135 | var newQuery; 136 | 137 | newQuery = t.find('http', 'server'); 138 | assert.equal(newQuery.length, 3); 139 | 140 | // add new one 141 | t.find('http').eq(0) 142 | .createNewNode('server') 143 | .addDirective('whoop', 'dee', 'doo') 144 | .addToQuery(); 145 | 146 | //console.log( t.stringify() ); 147 | 148 | newQuery = t.find('http', 'server'); 149 | assert.equal(newQuery.length, 4); 150 | } 151 | } 152 | }) 153 | .export(module); 154 | -------------------------------------------------------------------------------- /test/stringify.test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'), 2 | assert = require('assert'), 3 | 4 | stringify = require('../lib/stringify'), 5 | parse = require('../lib/parse'), 6 | Queryable = require('../lib/Queryable'), 7 | getConf = require('./utils/getConf'); 8 | 9 | 10 | vows.describe('stringify') 11 | .addBatch({ 12 | 'from parsed': { 13 | topic: parse( getConf() ), 14 | 15 | 'entire config': function (t) { 16 | var s = function () { stringify(t); }; 17 | assert.doesNotThrow(s); 18 | }, 19 | 'single node': function (t) { 20 | var node = t.http[0]; 21 | var s = function () { 22 | stringify(node); 23 | }; 24 | assert.doesNotThrow(s); 25 | } 26 | }, 27 | 'from query': { 28 | topic: Queryable.create( getConf() ), 29 | 'entire config': function (t) { 30 | var s = function () { 31 | stringify( t[0] ); 32 | }; 33 | assert.doesNotThrow(s); 34 | }, 35 | 'single node': function (t) { 36 | var node = t.find('http')[0]; 37 | var s = function () { 38 | stringify( node ); 39 | }; 40 | assert.doesNotThrow(s); 41 | } 42 | } 43 | 44 | }) 45 | .export(module); 46 | -------------------------------------------------------------------------------- /test/utils/getConf.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | module.exports = function () { 4 | return fs.readFileSync(path.resolve(__dirname, '../../examples/normal.conf'), 'utf-8'); 5 | } 6 | --------------------------------------------------------------------------------