1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-30 10:29:48 +02:00

Replace cheerio with parse5 (#934)

* Replace cheerio with parse5

* Convert to dependency injection for HTML parser
* Add options.domParser to HTML serializer
* Fallback to native DOMParser if present and no option provided
* Error if no DOM parser is available (option or native)
* Update tests to pass parse5 as config option

* Update test so it passes.
Cheerio interprets `<p><hr /></p>` as one `p` node with a child `hr`
node, but both parse5 and native DOMParser interpret it as 3 nodes: a
blank `p` node, `hr` node, and second blank `p` node. Update test
expectation to match new API.

* Remove cheerio-esque compatibility conversion.

* Use `application/xml` in native DOMParser
Using `text/html` causes it to wrap the fragment in html, body, etc

* Change error message to single line.
Was inserting an undesired newline char

* Add documentation for new `domParser` option to html serializer
Also boyscout missing documentation for `defaultBlockType` option

* Rename `domParser` option to `parseHtml`
Rename the option to make it clearer what it does, since it accepts a
function and not a `DOMParser` analogue object.
This commit is contained in:
Zach Schneider
2017-07-20 12:46:02 -04:00
committed by Ian Storm Taylor
parent cf85c6e3fb
commit 4bbf7487ea
26 changed files with 186 additions and 262 deletions

View File

@@ -12,6 +12,8 @@ For an example of the `Html` serializer in action, check out the [`paste-html` e
- [Example](#example)
- [Properties](#properties)
- [`rules`](#rules)
- [`defaultBlockType`](#defaultblocktype)
- [`parseHtml`](#parsehtml)
- [Methods](#methods)
- [`deserialize`](#deserialize)
- [`serialize`](#serialize)
@@ -42,6 +44,15 @@ new Html({
An array of rules to initialize the `Html` serializer with, defining your schema.
### `defaultBlockType`
`String|Object`
A default block type for blocks which do not match any rule. Can be a string such as `paragraph` or an object with a `type` attribute such as `{ type: 'paragraph' }`.
### `parseHtml`
`Function`
A function to parse an HTML string and return a DOM object. Defaults to using the native `DOMParser` in browser environments that support it. For older browsers or server-side rendering, you can include the [parse5](https://www.npmjs.com/package/parse5) package and pass `parse5.parseFragment` as the `parseHtml` option.
## Methods
@@ -75,9 +86,9 @@ Each rule must define two properties:
#### `rule.deserialize`
`rule.deserialize(el: CheerioElement, next: Function) => Object || Void`
`rule.deserialize(el: Element, next: Function) => Object || Void`
The `deserialize` function should return a plain Javascript object representing the deserialized state, or nothing if the rule in question doesn't know how to deserialize the object, in which case the next rule in the stack will be attempted.
The `deserialize` function receives a DOM element and should return a plain Javascript object representing the deserialized state, or nothing if the rule in question doesn't know how to deserialize the object, in which case the next rule in the stack will be attempted.
The returned object is almost exactly equivalent to the objects returned by the [`Raw`](./raw.md) serializer, except an extra `kind: 'mark'` is added to account for the ability to nest marks.

View File

@@ -58,7 +58,7 @@ const rules = [
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}
@@ -68,7 +68,7 @@ const rules = [
If you've worked with the [`Raw`](../reference/serializers/raw.md) serializer before, the return value of the `deserialize` should look familiar! It's just the same raw JSON format.
The `el` argument that the `deserialize` function receives is just a [`cheerio`](https://github.com/cheeriojs/cheerio) element object. And the `next` argument is a function that will deserialize any `cheerio` element(s) we pass it, which is how you recurse through each nodes children.
The `el` argument that the `deserialize` function receives is just a DOM element. And the `next` argument is a function that will deserialize any element(s) we pass it, which is how you recurse through each node's children.
Okay, that's `deserialize`, now let's define the `serialize` property of the paragraph rule as well:
@@ -80,7 +80,7 @@ const rules = [
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
},
@@ -119,7 +119,7 @@ const rules = [
return {
kind: 'block',
type: type,
nodes: next(el.children)
nodes: next(el.childNodes)
}
},
// Switch serialize to handle more blocks...
@@ -137,7 +137,7 @@ const rules = [
Now each of our block types is handled.
You'll notice that even though code blocks are nested in a `<pre>` and a `<code>` element, we don't need to specifically handle that case in our `deserialize` function, because the `Html` serializer will automatically recurse through `el.children` if no matching deserializer is found. This way, unknown tags will just be skipped over in the tree, instead of their contents omitted completely.
You'll notice that even though code blocks are nested in a `<pre>` and a `<code>` element, we don't need to specifically handle that case in our `deserialize` function, because the `Html` serializer will automatically recurse through `el.childNodes` if no matching deserializer is found. This way, unknown tags will just be skipped over in the tree, instead of their contents omitted completely.
Okay. So now our serializer can handle blocks, but we need to add our marks to it as well. Let's do that with a new rule...
@@ -164,7 +164,7 @@ const rules = [
return {
kind: 'block',
type: type,
nodes: next(el.children)
nodes: next(el.childNodes)
}
},
serialize(object, children) {
@@ -184,7 +184,7 @@ const rules = [
return {
kind: 'mark',
type: type,
nodes: next(el.children)
nodes: next(el.childNodes)
}
},
serialize(object, children) {

View File

@@ -85,7 +85,7 @@ const RULES = [
return {
kind: 'block',
type: block,
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
},
@@ -96,23 +96,23 @@ const RULES = [
return {
kind: 'mark',
type: mark,
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
},
{
// Special case for code blocks, which need to grab the nested children.
// Special case for code blocks, which need to grab the nested childNodes.
deserialize(el, next) {
if (el.tagName != 'pre') return
const code = el.children[0]
const children = code && code.tagName == 'code'
? code.children
: el.children
const code = el.childNodes[0]
const childNodes = code && code.tagName == 'code'
? code.childNodes
: el.childNodes
return {
kind: 'block',
type: 'code',
nodes: next(children)
nodes: next(childNodes)
}
}
},
@@ -123,9 +123,9 @@ const RULES = [
return {
kind: 'inline',
type: 'link',
nodes: next(el.children),
nodes: next(el.childNodes),
data: {
href: el.attribs.href
href: el.attrs.find(({ name }) => name == 'href').value
}
}
}

View File

@@ -6,7 +6,6 @@
"repository": "git://github.com/ianstormtaylor/slate.git",
"main": "./lib/index.js",
"dependencies": {
"cheerio": "^0.22.0",
"debug": "^2.3.2",
"direction": "^0.1.5",
"es6-map": "^0.1.4",
@@ -59,6 +58,7 @@
"mocha": "^2.5.3",
"np": "^2.9.0",
"npm-run-all": "^2.3.0",
"parse5": "^3.0.2",
"prismjs": "^1.5.1",
"react": "^15.4.2",
"react-addons-perf": "^15.4.2",

View File

@@ -2,7 +2,6 @@
import Raw from './raw'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import cheerio from 'cheerio'
import typeOf from 'type-of'
import { Record } from 'immutable'
@@ -34,12 +33,12 @@ const TEXT_RULE = {
}
}
if (el.type == 'text') {
if (el.data && el.data.match(/<!--.*?-->/)) return
if (el.nodeName == '#text') {
if (el.value && el.value.match(/<!--.*?-->/)) return
return {
kind: 'text',
text: el.data
text: el.value
}
}
},
@@ -71,8 +70,8 @@ class Html {
*
* @param {Object} options
* @property {Array} rules
* @property {String} defaultBlockType
* @property {String|Object} defaultBlockType
* @property {Function} parseHtml
*/
constructor(options = {}) {
@@ -82,6 +81,19 @@ class Html {
]
this.defaultBlockType = options.defaultBlockType || 'paragraph'
// Set DOM parser function or fallback to native DOMParser if present.
if (options.parseHtml !== null) {
this.parseHtml = options.parseHtml
} else if (typeof DOMParser !== 'undefined') {
this.parseHtml = (html) => {
return new DOMParser().parseFromString(html, 'application/xml')
}
} else {
throw new Error(
'Native DOMParser is not present in this environment; you must supply a parse function via options.parseHtml'
)
}
}
/**
@@ -94,8 +106,7 @@ class Html {
*/
deserialize = (html, options = {}) => {
const $ = cheerio.load(html).root()
const children = $.children().toArray()
const children = this.parseHtml(html).childNodes
let nodes = this.deserializeElements(children)
const { defaultBlockType } = this
@@ -151,7 +162,7 @@ class Html {
}
/**
* Deserialize an array of Cheerio `elements`.
* Deserialize an array of DOM elements.
*
* @param {Array} elements
* @return {Array}
@@ -160,7 +171,7 @@ class Html {
deserializeElements = (elements = []) => {
let nodes = []
elements.forEach((element) => {
elements.filter(this.cruftNewline).forEach((element) => {
const node = this.deserializeElement(element)
switch (typeOf(node)) {
case 'array':
@@ -176,7 +187,7 @@ class Html {
}
/**
* Deserialize a Cheerio `element`.
* Deserialize a DOM element.
*
* @param {Object} element
* @return {Any}
@@ -215,7 +226,7 @@ class Html {
break
}
return node || next(element.children)
return node || next(element.childNodes)
}
/**
@@ -226,7 +237,7 @@ class Html {
*/
deserializeMark = (mark) => {
const { type, data } = mark
const { type, value } = mark
const applyMark = (node) => {
if (node.kind == 'mark') {
@@ -237,7 +248,7 @@ class Html {
if (!node.ranges) node.ranges = [{ text: node.text }]
node.ranges = node.ranges.map((range) => {
range.marks = range.marks || []
range.marks.push({ type, data })
range.marks.push({ type, value })
return range
})
}
@@ -337,6 +348,17 @@ class Html {
}
}
/**
* Filter out cruft newline nodes inserted by the DOM parser.
*
* @param {Object} element
* @return {Boolean}
*/
cruftNewline = (element) => {
return !(element.nodeName == '#text' && element.value == '\n')
}
}
/**

50
test/helpers/clean.js Normal file
View File

@@ -0,0 +1,50 @@
import parse5 from 'parse5'
const UNWANTED_ATTRS = [
'data-key',
'data-offset-key'
]
const UNWANTED_TOP_LEVEL_ATTRS = [
'autocorrect',
'spellcheck',
'style',
'data-gramm'
]
/**
* Clean an element of unwanted attributes
*
* @param {Element} element
* @return {Element}
*/
function stripUnwantedAttrs(element) {
if(Array.isArray(element.attrs)) {
element.attrs = element.attrs.filter(({ name }) => { return !UNWANTED_ATTRS.includes(name) })
if(element.parentNode.nodeName === '#document-fragment') {
element.attrs = element.attrs.filter(({ name }) => { return !UNWANTED_TOP_LEVEL_ATTRS.includes(name) })
}
}
if(Array.isArray(element.childNodes)) {
element.childNodes.forEach(stripUnwantedAttrs)
}
if(element.nodeName === '#text') {
element.value = element.value.trim()
}
return element
}
/**
* Clean a renderer `html` string, removing dynamic attributes.
*
* @param {String} html
* @return {String}
*/
export default function clean(html) {
const $ = parse5.parseFragment(html)
$.childNodes.forEach(stripUnwantedAttrs)
return parse5.serialize($)
}

View File

@@ -2,11 +2,11 @@
import React from 'react'
import ReactDOM from 'react-dom/server'
import assert from 'assert'
import cheerio from 'cheerio'
import fs from 'fs-promise'
import readYaml from 'read-yaml-promise'
import { Editor, Raw } from '../..'
import { resolve } from 'path'
import clean from '../helpers/clean'
/**
* Tests.
@@ -29,9 +29,7 @@ describe('plugins', () => {
}
const string = ReactDOM.renderToStaticMarkup(<Editor {...props} />)
const expected = cheerio
.load(output)
.html()
const expected = output
.trim()
.replace(/\n/gm, '')
.replace(/>\s*</g, '><')
@@ -40,26 +38,3 @@ describe('plugins', () => {
})
}
})
/**
* Clean a renderer `html` string, removing dynamic attributes.
*
* @param {String} html
* @return {String}
*/
function clean(html) {
const $ = cheerio.load(html)
$('*').each((i, el) => {
$(el).removeAttr('data-key')
$(el).removeAttr('data-offset-key')
})
$.root().children().removeAttr('autocorrect')
$.root().children().removeAttr('spellcheck')
$.root().children().removeAttr('style')
$.root().children().removeAttr('data-gramm')
return $.html()
}

View File

@@ -2,11 +2,12 @@
import React from 'react'
import ReactDOM from 'react-dom/server'
import assert from 'assert'
import cheerio from 'cheerio'
import parse5 from 'parse5'
import fs from 'fs-promise'
import readYaml from 'read-yaml-promise'
import { Editor, Raw } from '../..'
import { resolve } from 'path'
import clean from '../helpers/clean'
/**
* Tests.
@@ -29,9 +30,7 @@ describe('rendering', () => {
}
const string = ReactDOM.renderToStaticMarkup(<Editor {...props} />)
const expected = cheerio
.load(output)
.html()
const expected = parse5.serialize(parse5.parseFragment(output))
.trim()
.replace(/\n/gm, '')
.replace(/>\s*</g, '><')
@@ -40,26 +39,3 @@ describe('rendering', () => {
})
}
})
/**
* Clean a renderer `html` string, removing dynamic attributes.
*
* @param {String} html
* @return {String}
*/
function clean(html) {
const $ = cheerio.load(html)
$('*').each((i, el) => {
$(el).removeAttr('data-key')
$(el).removeAttr('data-offset-key')
})
$.root().children().removeAttr('autocorrect')
$.root().children().removeAttr('spellcheck')
$.root().children().removeAttr('style')
$.root().children().removeAttr('data-gramm')
return $.html()
}

View File

@@ -8,14 +8,14 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'blockquote': {
return {
kind: 'block',
type: 'quote',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,14 +8,14 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'a': {
return {
kind: 'inline',
type: 'link',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -9,7 +9,7 @@ export default {
kind: 'block',
type: 'paragraph',
data: { key: 'value' },
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,7 +8,7 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,7 +8,7 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,7 +8,7 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,21 +8,21 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'a': {
return {
kind: 'inline',
type: 'link',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'b': {
return {
kind: 'inline',
type: 'hashtag',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,14 +8,14 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'a': {
return {
kind: 'inline',
type: 'link',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,16 +8,16 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'a': {
return {
kind: 'inline',
type: 'link',
nodes: next(el.children),
nodes: next(el.childNodes),
data: {
href: el.attribs.href
href: el.attrs.find(({ name }) => name == 'href').value
}
}
}

View File

@@ -8,7 +8,7 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'a': {

View File

@@ -8,14 +8,14 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'a': {
return {
kind: 'inline',
type: 'link',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,21 +8,21 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'em': {
return {
kind: 'mark',
type: 'italic',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'strong': {
return {
kind: 'mark',
type: 'bold',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,21 +8,21 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'em': {
return {
kind: 'mark',
type: 'italic',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'strong': {
return {
kind: 'mark',
type: 'bold',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -8,14 +8,14 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
case 'em': {
return {
kind: 'mark',
type: 'italic',
nodes: next(el.children)
nodes: next(el.childNodes)
}
}
}

View File

@@ -24,7 +24,7 @@ export default {
return {
kind: 'block',
type: 'paragraph',
nodes: next(el.children),
nodes: next(el.childNodes),
}
}
},

View File

@@ -5,10 +5,16 @@ nodes:
isVoid: false
data: {}
nodes:
- type: divider
isVoid: true
data: {}
nodes:
- characters:
- marks: []
text: " "
- characters: []
- type: divider
isVoid: true
data: {}
nodes:
- characters:
- text: " "
marks: []
- type: paragraph
isVoid: false
data: {}
nodes:
- characters: []

View File

@@ -7,6 +7,7 @@ import { Html, Plain, Raw } from '../..'
import { resolve } from 'path'
import React from 'react'
import { Iterable } from 'immutable'
import parse5 from 'parse5'
/**
* Tests.
@@ -22,7 +23,8 @@ describe('serializers', () => {
if (test[0] === '.') continue
it(test, async () => {
const innerDir = resolve(dir, test)
const html = new Html(require(innerDir).default)
const htmlOpts = Object.assign({}, require(innerDir).default, { parseHtml: parse5.parseFragment })
const html = new Html(htmlOpts)
const expected = await readYaml(resolve(innerDir, 'output.yaml'))
const input = fs.readFileSync(resolve(innerDir, 'input.html'), 'utf8')
const state = html.deserialize(input)
@@ -32,7 +34,9 @@ describe('serializers', () => {
}
it('optionally returns a raw representation', () => {
const html = new Html(require('./fixtures/html/deserialize/block').default)
const fixture = require('./fixtures/html/deserialize/block').default
const htmlOpts = Object.assign({}, fixture, { parseHtml: parse5.parseFragment })
const html = new Html(htmlOpts)
const input = fs.readFileSync(resolve(__dirname, './fixtures/html/deserialize/block/input.html'), 'utf8')
const serialized = html.deserialize(input, { toRaw: true })
assert.deepEqual(serialized, {
@@ -56,7 +60,9 @@ describe('serializers', () => {
})
it('optionally does not normalize', () => {
const html = new Html(require('./fixtures/html/deserialize/inline-with-is-void').default)
const fixture = require('./fixtures/html/deserialize/inline-with-is-void').default
const htmlOpts = Object.assign({}, fixture, { parseHtml: parse5.parseFragment })
const html = new Html(htmlOpts)
const input = fs.readFileSync(resolve(__dirname, './fixtures/html/deserialize/inline-with-is-void/input.html'), 'utf8')
const serialized = html.deserialize(input, { toRaw: true, normalize: false })
assert.deepEqual(serialized, {
@@ -89,7 +95,8 @@ describe('serializers', () => {
if (test[0] === '.') continue
it(test, async () => {
const innerDir = resolve(dir, test)
const html = new Html(require(innerDir).default)
const htmlOpts = Object.assign({}, require(innerDir).default, { parseHtml: parse5.parseFragment })
const html = new Html(htmlOpts)
const input = require(resolve(innerDir, 'input.js')).default
const expected = fs.readFileSync(resolve(innerDir, 'output.html'), 'utf8')
const serialized = html.serialize(input)
@@ -98,7 +105,9 @@ describe('serializers', () => {
}
it('optionally returns an iterable list of React elements', () => {
const html = new Html(require('./fixtures/html/serialize/block-nested').default)
const fixture = require('./fixtures/html/serialize/block-nested').default
const htmlOpts = Object.assign({}, fixture, { parseHtml: parse5.parseFragment })
const html = new Html(htmlOpts)
const input = require('./fixtures/html/serialize/block-nested/input.js').default
const serialized = html.serialize(input, { render: false })
assert(Iterable.isIterable(serialized), 'did not return an interable list')

145
yarn.lock
View File

@@ -2,6 +2,10 @@
# yarn lockfile v1
"@types/node@^6.0.46":
version "6.0.83"
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.83.tgz#dd022db01ac2c01c1057775e88ccffce96d1d6fe"
JSONStream@^1.0.3:
version "1.3.1"
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.1.tgz#707f761e01dae9e16f1bcf93703b78c70966579a"
@@ -1073,10 +1077,6 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.6"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
boolbase@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
boom@2.x.x:
version "2.10.1"
resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
@@ -1437,27 +1437,6 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
cheerio@^0.22.0:
version "0.22.0"
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
dependencies:
css-select "~1.2.0"
dom-serializer "~0.1.0"
entities "~1.1.1"
htmlparser2 "^3.9.1"
lodash.assignin "^4.0.9"
lodash.bind "^4.1.4"
lodash.defaults "^4.0.1"
lodash.filter "^4.4.0"
lodash.flatten "^4.2.0"
lodash.foreach "^4.3.0"
lodash.map "^4.4.0"
lodash.merge "^4.4.0"
lodash.pick "^4.2.1"
lodash.reduce "^4.4.0"
lodash.reject "^4.4.0"
lodash.some "^4.4.0"
chokidar@^1.0.0, chokidar@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2"
@@ -1775,19 +1754,6 @@ crypto-random-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
css-select@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
dependencies:
boolbase "~1.0.0"
css-what "2.1"
domutils "1.5.1"
nth-check "~1.0.1"
css-what@2.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0", "cssom@>= 0.3.2 < 0.4.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
@@ -1986,38 +1952,10 @@ doctrine@^2.0.0:
esutils "^2.0.2"
isarray "^1.0.0"
dom-serializer@0, dom-serializer@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
dependencies:
domelementtype "~1.1.1"
entities "~1.1.1"
domain-browser@~1.1.0:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
domelementtype@1, domelementtype@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
domelementtype@~1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
domhandler@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738"
dependencies:
domelementtype "1"
domutils@1.5.1, domutils@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
dependencies:
dom-serializer "0"
domelementtype "1"
dot-parts@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dot-parts/-/dot-parts-1.0.1.tgz#884bd7bcfc3082ffad2fe5db53e494d8f3e0743f"
@@ -2091,10 +2029,6 @@ encoding@^0.1.11:
dependencies:
iconv-lite "~0.4.13"
entities@^1.1.1, entities@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
envify@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/envify/-/envify-3.4.1.tgz#d7122329e8df1688ba771b12501917c9ce5cbce8"
@@ -3020,17 +2954,6 @@ htmlescape@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
htmlparser2@^3.9.1:
version "3.9.2"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
dependencies:
domelementtype "^1.3.0"
domhandler "^2.3.0"
domutils "^1.5.1"
entities "^1.1.1"
inherits "^2.0.1"
readable-stream "^2.0.2"
http-proxy@^1.8.1:
version "1.16.2"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742"
@@ -4005,14 +3928,6 @@ lodash.assign@^4.0.0, lodash.assign@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
lodash.assignin@^4.0.9:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
lodash.bind@^4.1.4:
version "4.2.1"
resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
lodash.clonedeep@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-3.0.2.tgz#a0a1e40d82a5ea89ff5b147b8444ed63d92827db"
@@ -4024,22 +3939,6 @@ lodash.cond@^4.3.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
lodash.defaults@^4.0.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
lodash.filter@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
lodash.flatten@^4.2.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
lodash.foreach@^4.3.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
lodash.isarguments@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
@@ -4056,38 +3955,14 @@ lodash.keys@^3.0.0:
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
lodash.map@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
lodash.memoize@~3.0.3:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
lodash.merge@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
lodash.pick@^4.2.1:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
lodash.pickby@^4.0.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff"
lodash.reduce@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
lodash.reject@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415"
lodash.some@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
lodash@^4.0.0, lodash@^4.14.0, lodash@^4.2.0, lodash@^4.3.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -4501,12 +4376,6 @@ npmlog@^4.0.2:
gauge "~2.7.1"
set-blocking "~2.0.0"
nth-check@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"
dependencies:
boolbase "~1.0.0"
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -4698,6 +4567,12 @@ parse5@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
parse5@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.2.tgz#05eff57f0ef4577fb144a79f8b9a967a6cc44510"
dependencies:
"@types/node" "^6.0.46"
patch-text@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/patch-text/-/patch-text-1.0.2.tgz#4bf36e65e51733d6e98f0cf62e09034daa0348ac"