Comparing version 0.1.0 to 0.2.0-alpha
{ | ||
"name": "xsalt", | ||
"main": "xsalt.js", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"homepage": "https://github.com/chocolatetoothpaste/xsalt", | ||
@@ -6,0 +6,0 @@ "authors": [ |
{ | ||
"name": "xsalt", | ||
"version": "0.1.0", | ||
"version": "0.2.0-alpha", | ||
"description": "A different kind of template engine", | ||
@@ -15,5 +15,31 @@ "main": "xsalt.js", | ||
"homepage": "https://github.com/chocolatetoothpaste/xsalt", | ||
"dependencies": { | ||
"jsdom": "^2.0.0" | ||
} | ||
"eslintConfig": { | ||
"parserOptions": { | ||
"ecmaVersion": 6 | ||
}, | ||
"rules": { | ||
"indent": 0, | ||
"quotes": [ | ||
"error", | ||
"single", | ||
{ | ||
"allowTemplateLiterals": true | ||
} | ||
], | ||
"linebreak-style": [ | ||
2, | ||
"unix" | ||
], | ||
"semi": [ | ||
2, | ||
"always" | ||
], | ||
"no-undef": 0 | ||
}, | ||
"extends": "eslint:recommended", | ||
"env": { | ||
"browser": true | ||
} | ||
} | ||
} |
156
README.md
# xsalt | ||
A different kind of template engine | ||
Why another templating engine? I'm sure that question was asked before mustache or handlebars were started. Why not, I say? | ||
***WARNING*** | ||
This is highly experimental and has known, major security risks. | ||
For educational purposes only. | ||
Bug reports and pull requests welcome. | ||
* I don't like `{{value}}` style templating | ||
* I hate the phrases "syntactic sugar" and "sugaring", so salt... | ||
* xsalt is a play on "XSLT", old school xml templating | ||
* once your templates are xsalt-ed, they are exalted | ||
What a template could look like: | ||
xsalt uses native document node manipulation to work with templates. There are three (very simple) regular expressions in the entire codebase. The syntax is meant to blend in with regular HTML, and the API is meant to feel like you are working with the native DOM. Because of that, nodes can be wrapped in jQuery (or other) and manipulated in a very natural way that blends with your code. See the examples for more. | ||
<html> | ||
<head> | ||
<link rel="icon" href="data:;base64,iVBORw0KGgo="> | ||
<link rel="stylesheet" type="text/css" href="style.css"> | ||
<script src="xsalt.js"></script> | ||
<script src="car.js"></script> | ||
</head> | ||
<body> | ||
### Unstable | ||
xsalt is brand new. The API is beginning to stabilize but might change radically from one version to the next. File a bug if you find one please and take a look at the open issues. | ||
<ul xs-ctrl="CarsCtrl"> | ||
<template> | ||
<li xs-each="cars" id="car_${_id}" xs-class="styles(${state})"> | ||
<span>${state}</span> | ||
<span xs-class="smiles(${state})">${plate}</span> | ||
<span xs-if="${state === 'AK'}"> | ||
The last frontier | ||
</span> | ||
<button xs-click="save(${_id})">Save</button> | ||
<button xs-click="delete(${_id})">Delete</button> | ||
</li> | ||
</template> | ||
</ul> | ||
I like to jump into examples before API docs, so... | ||
</body> | ||
</html> | ||
### Examples | ||
One quick disclaimer: tags with restrictions on their child elements (select, table, etc...) are not handling xs:* prefixed children, they are stripping them out. Experiment a bit to make your template work (start with xs:option and append it to a select, rather than include the select in your template, for example) | ||
Also, the examples previously have examples where a lot of closing tags were omitted. These examples would work, but it is probably best to include closing tags to ensure you get the structure you are expecting. | ||
As with HTML in general, it is best if you close tags (that require closing) to ensure the output is what you expect. | ||
What a controller might look like: | ||
// Basic usage: prefix elements to process with "xs:" | ||
// Supported attributes are val, html, and each | ||
// val populates the value attribute | ||
// html sets innerHTML | ||
// each iterates over a collection of properties and executes val or html | ||
// val and html essentially correspond to properties in the data object you pass in | ||
var Cars = xsalt.ctrl('CarsCtrl', ($ctrl) => { | ||
var eat = { | ||
food: { | ||
vegetables: [ | ||
{ name: "Corn" }, | ||
{ name: "Peas" }, | ||
{ name: "Carrots" } | ||
], | ||
} | ||
}; | ||
$ctrl.styles = function(data) { | ||
if ( data.state === 'UT' ) { | ||
return 'beehive cheddar'; | ||
} | ||
// output: <form method="POST" action="process.php"><input type="text" name="folder" value="Corn"><input type="text" name="folder" value="Peas"><input type="text" name="folder" value="Carrots"></form> | ||
console.log(xsalt('<form method="POST" action="process.php"><xs:input type="text" each="food.vegetables" val="name" name="folder">').compile(eat)); | ||
else if( data.state === 'AK' ) { | ||
return 'tlf'; | ||
} | ||
else { | ||
return ''; | ||
} | ||
}; | ||
var data = { | ||
users: [ | ||
{ username: 'ross', type: 1 }, | ||
{ username: 'steve', type: 1 }, | ||
{ username: 'bob', type: 2 }, | ||
{ username: 'dan', type: 1 }, | ||
{ username: 'trudy', type: 2 }, | ||
{ username: 'lucy', type: 1 } | ||
] | ||
}; | ||
$ctrl.smiles = function(data) { | ||
if ( data.state === 'MT' || data.state === 'AK' ) { | ||
return 'mountain'; | ||
} | ||
var list = xsalt('<ul class="items users"><xs:li each="users" html="username" data-action="some action"></xs:li></ul>'); | ||
else { | ||
return ''; | ||
} | ||
}; | ||
// passing in a callback, you can work with the DOM node. Wrap $(node) and | ||
// you can do anything you want to it. or use native methods | ||
$ctrl.save = function(id) { | ||
console.log('saving: ', $ctrl.cars[id] ); | ||
}; | ||
// output: <ul class="items users"><li data-action="some action">ross</li><li data-action="some action">steve</li><li data-action="some action" class="edit">bob</li><li data-action="some action">dan</li><li data-action="some action" class="edit">trudy</li><li data-action="some action">lucy</li></ul> | ||
console.log(list.compile(data, function(node, data) { | ||
if( data.type == 2 ) { | ||
node.className += "edit"; | ||
// jQuery version: $(node).addClass("edit"); | ||
} | ||
})); | ||
$ctrl.cars = { | ||
1: { _id: 1, state: 'UT', plate: '234 ASD' }, | ||
2: { _id: 2, state: 'MT', plate: '234 MDT' }, | ||
3: { _id: 3, state: 'WA', plate: '234 WER' }, | ||
4: { _id: 4, state: 'AK', plate: '999 TLF' } | ||
}; | ||
var consoles = [ | ||
{ text: "PS4", value: 1 }, | ||
{ text: "XBOX One", value: 2 }, | ||
{ text: "Wii U", value: 3 } | ||
]; | ||
}); | ||
// each="." will use the root value of the data passed into compile | ||
var options = xsalt('<select class="selectbox consoles"><xs:option each="." html="text" val="value"></xs:option></select>'); | ||
// output: <option value="1">PS4</option><option value="2">XBOX One</option><option value="3">Wii U</option> | ||
console.log(options.compile(consoles)); | ||
// "wrapping" attributes | ||
var data = { | ||
users: [ | ||
{username: "bob", type: 1, id: 42}, | ||
{username: "lucy", type: 2, id: 98}, | ||
{username: "sandy", type: 1, id: 39}, | ||
{username: "jerry", type: 1, id: 74} | ||
] | ||
Cars.delete = function del(id) { | ||
delete Cars.cars[id]; | ||
}; | ||
// output: <input type="text" value="bob" id="user_42"><input type="text" value="lucy" id="user_98"><input type="text" value="sandy" id="user_39"><input type="text" value="jerry" id="user_74"> | ||
var test = xsalt('<xs:input type="text" val="username" each="users">').compile(data, function( node, data ) { | ||
node.setAttribute("id", "user_" + data.id); | ||
// jQuery version: $(node).attr("id", "user_" + data.id); | ||
}); | ||
// Default values: 'default' attribute was added (v0.1.0) | ||
// the function for mapping nested objects was updated and will now return | ||
// and empty object if not matching key is found. this may cause unexpected | ||
// results, please test/verify your template output | ||
// output: <input type="text" value="stewie"> | ||
// no data passed to compile to force defalut value to be used | ||
var out = xsalt('<xs:input type="text" val="username" default="stewie">').compile(); | ||
// defalut innerHTML | ||
// output: <button type="button">Click me!</button> | ||
var button = xsalt('<xs:button type="button" html="fakester.nested.value">Click me!</xs:button>').compile(); | ||
### API | ||
Coming eventually. For now see the examples. |
337
xsalt.js
@@ -1,227 +0,240 @@ | ||
(function(document) { | ||
(function() { | ||
"use strict"; | ||
function xsalt(tmpl) { | ||
// unfortunately HTML strips out certain tags if the parent tag has | ||
// restrictions on child elements (like select boxes). This will prefeix ALL | ||
// tags with "xs!:" that are not already prefixed with "xs:" | ||
// totally sucks, but must be done until a better solution arises | ||
tmpl = tmpl.replace(/<(\/?)(?!\/?xs:)/g, '<$1xs!:'); | ||
function XSalt() { | ||
this.controller = {}; | ||
this.fn = {}; | ||
this.ev = {}; | ||
} | ||
// creating a sandbox for moving elements around | ||
var temp = document.createElement('div'); | ||
temp.innerHTML = tmpl; | ||
this.template = temp; | ||
}; | ||
XSalt.prototype.ctrl = function ctrl(ctrl, fn) { | ||
var xsctrl = `[xs-ctrl=${ctrl}]`; | ||
var handler = { | ||
set: (obj, prop, val) => { | ||
obj[prop] = (function watch(v) { | ||
var t = typeof val; | ||
if( Object(val) === val || val instanceof Array ) { | ||
for( let i in v ) { | ||
if( Object(v[i]) === v[i] || v[i] instanceof Array ) | ||
v[i] = watch(new Proxy(v[i], handler)); | ||
} | ||
/** | ||
* Factory | ||
*/ | ||
v = new Proxy(v, handler); | ||
} | ||
function xs(tmpl) { | ||
return new xsalt(tmpl); | ||
} | ||
return v; | ||
})(val); | ||
if( typeof val !== 'function') | ||
this.compile(document.querySelectorAll(xsctrl)); | ||
}, | ||
deleteProperty: (obj, prop) => { | ||
delete obj[prop]; | ||
/** | ||
* Maps an array of "keys" to a JSON object and returns the set | ||
*/ | ||
if( typeof val !== 'function') | ||
this.compile(document.querySelectorAll(xsctrl)); | ||
} | ||
}; | ||
xsalt.prototype.map = function( keys, data ) { | ||
// assign the controller | ||
this.controller[ctrl] = new Proxy({}, handler); | ||
return keys.reduce(function (data, key) { | ||
// make sure data and data[key] exist | ||
if( typeof data !== "undefined" && typeof data[key] !== "undefined" ) | ||
return data[key]; | ||
if( document.readyState === 'complete' ) { | ||
fn.call(null, this.controller[ctrl]); | ||
} | ||
else { | ||
let load = (e) => { | ||
e.target.removeEventListener(e.type, load); | ||
fn.call(null, this.controller[ctrl]); | ||
}; | ||
window.addEventListener( 'load', load, true ); | ||
} | ||
// return empty object...? maybe a bad practice, needs exploration | ||
// it *might* be better to throw an error here | ||
}, data) || {}; | ||
return this.controller[ctrl]; | ||
}; | ||
/** | ||
* Attribute parsers | ||
*/ | ||
XSalt.prototype.compile = function compile(nodes) { | ||
[].forEach.call(nodes, (node) => { | ||
// grab the name of the controller | ||
var ctrl = node.getAttribute('xs-ctrl'); | ||
xsalt.prototype.parse = { | ||
'each': function( node, data, callback ) { | ||
var that = this; | ||
// clone the template contents | ||
var content = document.importNode(node.childNodes[1].content, true); | ||
node.removeAttribute('each'); | ||
var sel = [ | ||
'[xs-click]', | ||
'[xs-submit]', | ||
'[xs-change]', | ||
'[xs-keyup]', | ||
'[xs-each]' | ||
]; | ||
data.forEach(function(i) { | ||
// var n = that.clone(node); | ||
var n = node.cloneNode(true); | ||
// grab all the nodes that "do something" | ||
var children = content.querySelectorAll(sel.join(', ')); | ||
Array.prototype.forEach.call(node.attributes, function(v) { | ||
if( typeof that.parse[v.nodeName] === "function" ) { | ||
that.parse[v.nodeName].call(that, n, v.value, i); | ||
var xsattr = ['xs-each', 'xs-click']; | ||
if( typeof callback === "function" ) { | ||
callback.call(that, n, i); | ||
} | ||
[].forEach.call(children, (child) => { | ||
xsattr.forEach( val => { | ||
if( child.hasAttribute(val) ) { | ||
this.parse[val].call(this, node, child, ctrl, child.getAttribute(val)); | ||
} | ||
}); | ||
node.parentNode.insertBefore(n, node); | ||
}); | ||
}); | ||
}; | ||
node.parentNode.removeChild(node); | ||
}, | ||
'html': function( node, key, data ) { | ||
if( typeof data[key] !== "undefined" ) { | ||
node.innerHTML = data[key]; | ||
} | ||
XSalt.prototype.parse = { | ||
'xs-click': function(node, child, ctrl, attr) { | ||
node.removeAttribute('html'); | ||
}, | ||
if( typeof this.ev['click'] === 'undefined' ) { | ||
this.ev['click'] = attr; | ||
'val': function( node, key, data ) { | ||
var val = ''; | ||
var fn = (e) => { | ||
if( ! e.target.hasAttribute('xs-click') ) { | ||
return false | ||
} | ||
var cb = e.target | ||
.getAttribute('xs-click') | ||
.replace(/[\'\"]|\)$/g, '') | ||
.split('(') | ||
if( typeof data[key] !== "undefined" ) { | ||
val = data[key]; | ||
} | ||
var args = cb[1].split(',').map((v) => { | ||
return v.trim(); | ||
}); | ||
else if( node.hasAttribute('default') ) { | ||
val = node.getAttribute('default'); | ||
node.removeAttribute('default'); | ||
this.controller[ctrl][cb.shift()].apply(null, args); | ||
}; | ||
document.removeEventListener('click', fn); | ||
document.addEventListener('click', fn); | ||
} | ||
}, | ||
node.setAttribute('value', val); | ||
'xs-each': function(node, child, ctrl, attr) { | ||
var data = this.controller[ctrl][attr]; | ||
node.removeAttribute('val'); | ||
} | ||
}; | ||
if( typeof data !== 'undefined' ) { | ||
var frag = ' '; | ||
[].forEach.call(node.children, (tmpl) => { | ||
if( tmpl.tagName.toLowerCase() === 'template' ) | ||
frag += tmpl.outerHTML; | ||
}); | ||
/** | ||
* The native methods for node cloning and replacement are working since tag | ||
* names are not sanitized until the final output, so these are not needed right | ||
* now. they are left here for future reference | ||
frag += this.transmogrify.each.call(this, ctrl, child, data); | ||
* Clones a node and it's attributes, trimming "xs:" from the tag name | ||
node.innerHTML = frag; | ||
} | ||
} | ||
}; | ||
xsalt.prototype.clone = function(node) { | ||
// don't replace tag name for now, all "xs:" prefixes get scrubbed after | ||
// compile is done, before returning result | ||
var tag = node.tagName.toLowerCase(); | ||
var n = document.createElement(tag); | ||
XSalt.prototype.transmogrify = { | ||
xscall: function xscall( ctrl, stmt, args, data ) { | ||
var ret = false; | ||
n.innerHTML = node.innerHTML; | ||
// var xstag = function xstag(strings, ...values) { | ||
// console.log(strings[1], values[1]); | ||
// }; | ||
Array.prototype.forEach.call(node.attributes, function(v) { | ||
n.setAttribute(v.nodeName, v.value); | ||
}); | ||
// if( args !== null ) { | ||
// args.push('xstag'); | ||
// } | ||
return n; | ||
}; | ||
if( /^\$/.test(stmt) ) { | ||
// var b = Function.apply(null, args.concat('return xstag`' + stmt + '`;')) | ||
var b = Function.apply(null, args.concat('return `' + stmt + '`;')) | ||
.apply(null, args.map( (v) => { | ||
return data[v]; | ||
// return ( v === 'xstag' ? xstag : data[v] ); | ||
})); | ||
ret = ( b === 'true' ); | ||
} | ||
/** | ||
* Replaces one node with another | ||
else if( /\(.*\)/.test(stmt) ) { | ||
stmt = stmt.replace(/[\'\"]|\)$/g, '').split('('); | ||
stmt.pop(); | ||
stmt.push(data); | ||
xsalt.prototype.replace = function(node, n) { | ||
node.parentNode.insertBefore(n, node); | ||
node.parentNode.removeChild(node); | ||
}; | ||
ret = this.controller[ctrl][stmt.shift()].apply( null, stmt ); | ||
} | ||
**/ | ||
return ret; | ||
}, | ||
each: function parse_each( ctrl, tmpl, data ) { | ||
var frag = '', | ||
re = /\$\{([\w]+)\}/g, | ||
v, | ||
args = [], | ||
attr_list = [ | ||
'[xs-if]', | ||
'[xs-class]' | ||
].join(', '); | ||
/** | ||
* Compiles template and data together and returns a HTML string | ||
*/ | ||
while( ( v = re.exec(tmpl.outerHTML) ) !== null ) { | ||
if( args.indexOf(v[1]) === -1 ) | ||
args.push(v[1]); | ||
} | ||
xsalt.prototype.compile = function( data, node, callback ) { | ||
if( typeof node === "function" ) { | ||
callback = node; | ||
node = this.template.children; | ||
} | ||
var nodes = tmpl.querySelectorAll(attr_list); | ||
else { | ||
node = node || this.template.children; | ||
} | ||
for( let d in data ) { | ||
var clone = tmpl.cloneNode(true); | ||
for( var ii = 0, l = node.length; ii < l; ++ii ) { | ||
var tag = node[ii].tagName.toLowerCase(); | ||
if( /^xs:/g.test( tag ) ) { | ||
// collections have to be handled a little differently | ||
// the template node does not get replaced here since it has to be | ||
// "cloned" multiple times | ||
// iterative directives must handle this internally | ||
if( node[ii].hasAttribute('each') ) { | ||
var attr = node[ii].getAttribute('each'); | ||
// not necessary to separate the last key | ||
// each data set will be available in the callback individually | ||
var set = ( attr === '.' | ||
? data | ||
: this.map( attr.split('.'), data ) ); | ||
this.parse.each.call(this, node[ii], set, callback); | ||
if( clone.hasAttribute('xs-class') ) { | ||
var f = clone.getAttribute('xs-class'); | ||
clone.className += this.transmogrify.xscall.call(this, ctrl, f, null, data[d]) | ||
} | ||
else { | ||
// hot swap the node to replace the "xs:" tag with the real one | ||
// this.replace(node[ii], this.clone(node[ii])); | ||
var n = node[ii].cloneNode(true); | ||
node[ii].parentNode.replaceChild(n, node[ii]); | ||
[].forEach.call(nodes, (n) => { | ||
if( n.hasAttribute('xs-if') && ! this.transmogrify.xscall.apply(this, [ | ||
ctrl, n.getAttribute('xs-if'), args, data[d] | ||
] ) ) { | ||
var sel = "[xs-if=\"" + n.getAttribute('xs-if') + "\"]"; | ||
clone.querySelectorAll(sel).forEach(c => { c.innerText = '' }); | ||
} | ||
// it is probably quicker (and easier to check) to iterate over | ||
// the available parsers than to iterate a nodes attributes | ||
for( var p in this.parse ) { | ||
if( node[ii].hasAttribute( p ) ) { | ||
var path = node[ii].getAttribute(p).split('.'); | ||
if( n.hasAttribute('xs-class') ) { | ||
n.className += ' ' + this.transmogrify.xscall.apply( this, [ | ||
ctrl, n.getAttribute('xs-class'), null, data[d] | ||
]) | ||
} | ||
}); | ||
// the final part of they key is separated from the map | ||
// so the entire data set can be passed around for use | ||
// in the callback | ||
var key = path.pop(); | ||
var html = clone.outerHTML; | ||
// make nested var selection possible by splitting the | ||
// var path and mapping it to the data object | ||
var set = this.map(path, data); | ||
if( typeof this.fn[html] === 'undefined' ) { | ||
var str = 'return `' + html + '`;'; | ||
this.fn[html] = Function.apply( null, args.concat(str) ); | ||
} | ||
this.parse[p].call(this, node[ii], key, set); | ||
var val = []; | ||
if( typeof callback === "function" ) { | ||
callback.call(this, node[ii], set); | ||
} | ||
} | ||
} | ||
for( let i = 0, len = args.length; i < len; ++i ) { | ||
val.push(data[d][args[i]] || ''); | ||
} | ||
frag += this.fn[html].apply(null, val); | ||
} | ||
if( node[ii] && node[ii].children ) { | ||
this.compile(data, node[ii].children, callback); | ||
} | ||
return frag; | ||
} | ||
// these two lines make me sad. shouldn't have to do this but since "xs!:" | ||
// prefixes have to be used temporarily self-closing tags end up with | ||
// closing tags and the unit tests fail... can't wait to delete this lines | ||
var out = document.createElement('div'); | ||
}; | ||
// clean up all remaing "xs:" and variant prefixes | ||
out.innerHTML = this.template.innerHTML.replace(/<(\/?)xs!?:/g, '<$1'); | ||
return out.innerHTML; | ||
}; | ||
if( typeof module !== 'undefined' && module.exports ) { | ||
module.exports = xs; | ||
if( typeof module !== "undefined" && module.exports ) { | ||
module.exports = new XSalt(); | ||
} | ||
else { | ||
window.xsalt = xs; | ||
window.xsalt = new XSalt(); | ||
} | ||
// hopefully xsalt can switch to xmldom and DOMParser in the future | ||
// jsdom is a lot chunkier and more complicated than is needed | ||
// it would clean up this execution line too | ||
})( typeof window === "undefined" ? require('jsdom').jsdom().parentWindow.document : document ); | ||
})(); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1462932
0
14
64185
82
1
- Removedjsdom@^2.0.0
- Removedajv@6.12.6(transitive)
- Removedasn1@0.2.6(transitive)
- Removedassert-plus@1.0.0(transitive)
- Removedasynckit@0.4.0(transitive)
- Removedaws-sign2@0.7.0(transitive)
- Removedaws4@1.13.2(transitive)
- Removedbcrypt-pbkdf@1.0.2(transitive)
- Removedbindings@1.5.0(transitive)
- Removedbrowser-request@0.3.3(transitive)
- Removedcaseless@0.12.0(transitive)
- Removedcombined-stream@1.0.8(transitive)
- Removedcontextify@0.1.15(transitive)
- Removedcore-util-is@1.0.2(transitive)
- Removedcssom@0.3.8(transitive)
- Removedcssstyle@0.2.37(transitive)
- Removeddashdash@1.14.1(transitive)
- Removeddelayed-stream@1.0.0(transitive)
- Removeddom-serializer@0.2.2(transitive)
- Removeddomelementtype@1.3.12.3.0(transitive)
- Removeddomhandler@2.4.2(transitive)
- Removeddomutils@1.7.0(transitive)
- Removedecc-jsbn@0.1.2(transitive)
- Removedentities@1.1.22.2.0(transitive)
- Removedextend@3.0.2(transitive)
- Removedextsprintf@1.3.0(transitive)
- Removedfast-deep-equal@3.1.3(transitive)
- Removedfast-json-stable-stringify@2.1.0(transitive)
- Removedfile-uri-to-path@1.0.0(transitive)
- Removedforever-agent@0.6.1(transitive)
- Removedform-data@2.3.3(transitive)
- Removedgetpass@0.1.7(transitive)
- Removedhar-schema@2.0.0(transitive)
- Removedhar-validator@5.1.5(transitive)
- Removedhtmlparser2@3.10.1(transitive)
- Removedhttp-signature@1.2.0(transitive)
- Removedinherits@2.0.4(transitive)
- Removedis-typedarray@1.0.0(transitive)
- Removedisstream@0.1.2(transitive)
- Removedjsbn@0.1.1(transitive)
- Removedjsdom@2.0.0(transitive)
- Removedjson-schema@0.4.0(transitive)
- Removedjson-schema-traverse@0.4.1(transitive)
- Removedjson-stringify-safe@5.0.1(transitive)
- Removedjsprim@1.4.2(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removednan@2.22.0(transitive)
- Removednwmatcher@1.4.4(transitive)
- Removedoauth-sign@0.9.0(transitive)
- Removedparse5@1.5.1(transitive)
- Removedperformance-now@2.1.0(transitive)
- Removedpsl@1.15.0(transitive)
- Removedpunycode@2.3.1(transitive)
- Removedqs@6.5.3(transitive)
- Removedreadable-stream@3.6.2(transitive)
- Removedrequest@2.88.2(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedsshpk@1.18.0(transitive)
- Removedstring_decoder@1.3.0(transitive)
- Removedtough-cookie@2.5.0(transitive)
- Removedtunnel-agent@0.6.0(transitive)
- Removedtweetnacl@0.14.5(transitive)
- Removeduri-js@4.4.1(transitive)
- Removedutil-deprecate@1.0.2(transitive)
- Removeduuid@3.4.0(transitive)
- Removedverror@1.10.0(transitive)
- Removedxmlhttprequest@1.8.0(transitive)