aboutsummaryrefslogtreecommitdiff
path: root/node_modules/@concordance
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2017-08-14 05:01:11 +0200
committerFlorian Dold <florian.dold@gmail.com>2017-08-14 05:02:09 +0200
commit363723fc84f7b8477592e0105aeb331ec9a017af (patch)
tree29f92724f34131bac64d6a318dd7e30612e631c7 /node_modules/@concordance
parent5634e77ad96bfe1818f6b6ee70b7379652e5487f (diff)
downloadwallet-core-363723fc84f7b8477592e0105aeb331ec9a017af.tar.xz
node_modules
Diffstat (limited to 'node_modules/@concordance')
-rw-r--r--node_modules/@concordance/react/LICENSE14
-rw-r--r--node_modules/@concordance/react/README.md20
-rw-r--r--node_modules/@concordance/react/index.js75
-rw-r--r--node_modules/@concordance/react/lib/diffShallow.js239
-rw-r--r--node_modules/@concordance/react/lib/elementFactory.js349
-rw-r--r--node_modules/@concordance/react/lib/escapeText.js10
-rw-r--r--node_modules/@concordance/react/lib/testJsonFactory.js59
-rw-r--r--node_modules/@concordance/react/package.json80
8 files changed, 846 insertions, 0 deletions
diff --git a/node_modules/@concordance/react/LICENSE b/node_modules/@concordance/react/LICENSE
new file mode 100644
index 000000000..a192d7dbf
--- /dev/null
+++ b/node_modules/@concordance/react/LICENSE
@@ -0,0 +1,14 @@
+ISC License (ISC)
+Copyright (c) 2017, Mark Wubben <mark@novemberborn.net> (novemberborn.net)
+
+Permission to use, copy, modify, and/or distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
diff --git a/node_modules/@concordance/react/README.md b/node_modules/@concordance/react/README.md
new file mode 100644
index 000000000..baf16efcf
--- /dev/null
+++ b/node_modules/@concordance/react/README.md
@@ -0,0 +1,20 @@
+# @concordance/react
+
+React plugin for [Concordance](https://github.com/concordancejs/concordance).
+
+Allows
+[`React.createElement()`](https://facebook.github.io/react/docs/react-api.html#createelement)
+objects to be compared, formatted, diffed and serialized. Also supports
+`toJSON()` renderings of
+[`react-test-renderer`](https://www.npmjs.com/package/react-test-renderer).
+These may be compared to `React.createElement()` objects.
+
+When comparing [React
+component](https://facebook.github.io/react/docs/components-and-props.html)
+elements, the element type is compared by identity. After deserialization the
+element types are compared by function name, taking into account whether
+serialization and / or comparison is done using Node.js 4, which has less
+reliable support for function names.
+
+Component elements are formatted with a &#x235F; character after the element
+name. Properties and children are formatted by [Concordance](https://github.com/concordancejs/concordance).
diff --git a/node_modules/@concordance/react/index.js b/node_modules/@concordance/react/index.js
new file mode 100644
index 000000000..47ec117fa
--- /dev/null
+++ b/node_modules/@concordance/react/index.js
@@ -0,0 +1,75 @@
+'use strict'
+
+const pkg = require('./package.json')
+const elementFactory = require('./lib/elementFactory')
+const testJsonFactory = require('./lib/testJsonFactory')
+
+// Must be unique across all registered plugins.
+exports.name = pkg.name
+
+// Expected API version to be passed to register().
+exports.apiVersion = 1
+
+// Expected minimal version of Concordance. Concordance will increment its API
+// version for breaking changes, this is useful if you rely on features or
+// patches that were introduced in a specific version of Concordance.
+exports.minimalConcordanceVersion = '1.0.0'
+
+// Plugin-specific version of its serialization output.
+exports.serializerVersion = 1
+
+exports.theme = {
+ react: {
+ functionType: '\u235F',
+ openTag: {
+ start: '<',
+ end: '>',
+ selfClose: '/',
+ selfCloseVoid: ' /'
+ },
+ closeTag: {
+ open: '</',
+ close: '>'
+ },
+ tagName: {open: '', close: ''},
+ attribute: {
+ separator: '=',
+ value: {
+ openBracket: '{',
+ closeBracket: '}',
+ string: {
+ line: {open: '"', close: '"', escapeQuote: '"'}
+ }
+ }
+ },
+ child: {
+ openBracket: '{',
+ closeBracket: '}',
+ string: {
+ line: {open: '', close: '', escapeQuote: ''},
+ multiline: {start: '', end: '', escapeQuote: ''}
+ }
+ }
+ }
+}
+
+const ELEMENT = Symbol.for('react.element')
+const TEST_JSON = Symbol.for('react.test.json')
+
+function register (api) {
+ const reactTags = new Set()
+ const element = elementFactory(api, reactTags)
+ const testJson = testJsonFactory(api, element)
+
+ api.addDescriptor(0x01, element.tag, element.deserialize)
+ api.addDescriptor(0x02, testJson.tag, testJson.deserialize)
+
+ reactTags.add(element.tag).add(testJson.tag)
+
+ return value => {
+ if (value.$$typeof === ELEMENT) return element.describe
+ if (value.$$typeof === TEST_JSON) return testJson.describe
+ return null
+ }
+}
+exports.register = register
diff --git a/node_modules/@concordance/react/lib/diffShallow.js b/node_modules/@concordance/react/lib/diffShallow.js
new file mode 100644
index 000000000..4eeb4d4d3
--- /dev/null
+++ b/node_modules/@concordance/react/lib/diffShallow.js
@@ -0,0 +1,239 @@
+'use strict'
+
+function diffShallow (api, actual, expected, theme, indent) {
+ const childBuffer = api.lineBuilder.buffer()
+ const propertyBuffer = api.lineBuilder.buffer()
+
+ return {
+ append (formatted, origin) {
+ if (origin.isItem === true) {
+ childBuffer.append(formatted)
+ } else {
+ propertyBuffer.append(formatted)
+ }
+ },
+
+ finalize: () => {
+ const namesAreEqual = actual.compareNames(expected)
+ const actualName = actual.formatName(theme)
+ const expectedName = expected.formatName(theme)
+
+ const openTag = theme.react.openTag
+ const innerIndentation = indent.increase()
+
+ const allChildren = childBuffer.withFirstPrefixed(innerIndentation)
+ const children = allChildren.decompose()
+
+ const allProperties = propertyBuffer.withFirstPrefixed(innerIndentation)
+ const properties = allProperties.decompose()
+ // If the first properties are also the last, and either side has no
+ // children, ensure the properties are treated as being last. This
+ // leads to a better balanced diff.
+ if (properties.remaining.isEmpty && (!actual.hasChildren || !expected.hasChildren)) {
+ properties.last = properties.first
+ properties.first = {actual: api.lineBuilder.buffer(), expected: api.lineBuilder.buffer()}
+ }
+
+ const result = api.lineBuilder.buffer()
+
+ // Create a custom diff that is as neat as possible. It's likely
+ // there's a generic algorithm that can be used, but for expediency's
+ // sake handles all possible diffs by brute force instead.
+ if (actual.hasProperties && expected.hasProperties) {
+ if (namesAreEqual) {
+ result
+ .append(api.lineBuilder.first(openTag.start + actualName))
+ .append(properties.first.actual.stripFlags())
+ .append(properties.first.expected.stripFlags())
+ } else {
+ result
+ .append(api.lineBuilder.actual.first(openTag.start + actualName))
+ .append(properties.first.actual.stripFlags())
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName))
+ .append(properties.first.expected.stripFlags())
+ }
+ result.append(properties.remaining.stripFlags())
+
+ if (actual.hasChildren && expected.hasChildren) {
+ result
+ .append(properties.last.actual.stripFlags())
+ .append(properties.last.expected.stripFlags())
+ .append(api.lineBuilder.line(indent + openTag.end))
+
+ if (namesAreEqual) {
+ result
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ } else {
+ result
+ .append(children.first.actual.stripFlags())
+ .append(children.first.expected.stripFlags())
+ .append(children.remaining.stripFlags())
+ .append(children.last.actual.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ .append(children.last.expected.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + api.wrapFromTheme(theme.react.closeTag, expectedName)))
+ }
+ } else if (actual.hasChildren) {
+ result
+ .append(properties.last.actual.stripFlags())
+ .append(api.lineBuilder.actual.line(indent + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ .append(properties.last.expected.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + openTag.selfClose + openTag.end))
+ } else if (expected.hasChildren) {
+ result
+ .append(properties.last.actual.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + openTag.selfClose + openTag.end))
+ .append(properties.last.expected.stripFlags())
+ .append(api.lineBuilder.expected.line(indent + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + api.wrapFromTheme(theme.react.closeTag, expectedName)))
+ } else {
+ result
+ .append(properties.last.actual.stripFlags())
+ .append(properties.last.expected.stripFlags())
+ .append(api.lineBuilder.last(indent + openTag.selfClose + openTag.end))
+ }
+ } else if (actual.hasProperties) {
+ result
+ .append(api.lineBuilder.actual.first(openTag.start + actualName))
+ .append(allProperties.stripFlags())
+
+ if (actual.hasChildren && expected.hasChildren) {
+ result
+ .append(api.lineBuilder.actual.line(indent + openTag.end))
+ .append(children.first.actual.stripFlags())
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName + openTag.end))
+ .append(children.first.expected.stripFlags())
+ .append(children.remaining.stripFlags())
+
+ if (namesAreEqual) {
+ result
+ .append(children.last.actual.stripFlags())
+ .append(children.last.expected.stripFlags())
+ .append(api.lineBuilder.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ } else {
+ result
+ .append(children.last.actual.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ .append(children.last.expected.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + api.wrapFromTheme(theme.react.closeTag, expectedName)))
+ }
+ } else if (actual.hasChildren) {
+ result
+ .append(api.lineBuilder.actual.last(indent + openTag.selfClose + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ .append(api.lineBuilder.expected.single(openTag.start + expectedName + openTag.selfCloseVoid + openTag.end))
+ } else if (expected.hasChildren) {
+ result
+ .append(api.lineBuilder.actual.last(indent + openTag.selfClose + openTag.end))
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + api.wrapFromTheme(theme.react.closeTag, expectedName)))
+ } else {
+ result
+ .append(api.lineBuilder.actual.last(indent + openTag.selfClose + openTag.end))
+ .append(api.lineBuilder.expected.single(openTag.start + expectedName + openTag.selfCloseVoid + openTag.end))
+ }
+ } else if (expected.hasProperties) {
+ if (actual.hasChildren && expected.hasChildren) {
+ result
+ .append(api.lineBuilder.actual.first(openTag.start + actualName + openTag.end))
+ .append(children.first.actual.stripFlags())
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName))
+ .append(allProperties.stripFlags())
+ .append(api.lineBuilder.expected.line(indent + openTag.end))
+ .append(children.first.expected.stripFlags())
+ .append(children.remaining.stripFlags())
+
+ if (namesAreEqual) {
+ result
+ .append(children.last.actual.stripFlags())
+ .append(children.last.expected.stripFlags())
+ .append(api.lineBuilder.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ } else {
+ result
+ .append(children.last.actual.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ .append(children.last.expected.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + api.wrapFromTheme(theme.react.closeTag, expectedName)))
+ }
+ } else if (actual.hasChildren) {
+ result
+ .append(api.lineBuilder.actual.first(openTag.start + actualName + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName))
+ .append(allProperties.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + openTag.selfClose + openTag.end))
+ } else if (expected.hasChildren) {
+ result
+ .append(api.lineBuilder.actual.single(openTag.start + actualName + openTag.selfCloseVoid + openTag.end))
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName))
+ .append(allProperties.stripFlags())
+ .append(api.lineBuilder.expected.line(indent + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + api.wrapFromTheme(theme.react.closeTag, expectedName)))
+ } else {
+ result
+ .append(api.lineBuilder.actual.single(openTag.start + actualName + openTag.selfCloseVoid + openTag.end))
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName))
+ .append(allProperties.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + openTag.selfCloseVoid + openTag.end))
+ }
+ } else {
+ if (actual.hasChildren && expected.hasChildren) {
+ if (namesAreEqual) {
+ result
+ .append(api.lineBuilder.first(openTag.start + actualName + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ } else {
+ result
+ .append(api.lineBuilder.actual.first(openTag.start + actualName + openTag.end))
+ .append(children.first.actual.stripFlags())
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName + openTag.end))
+ .append(children.first.expected.stripFlags())
+ .append(children.remaining.stripFlags())
+ .append(children.last.actual.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ .append(children.last.expected.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + api.wrapFromTheme(theme.react.closeTag, expectedName)))
+ }
+ } else if (actual.hasChildren) {
+ result
+ .append(api.lineBuilder.actual.first(openTag.start + actualName + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.actual.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ .append(api.lineBuilder.expected.single(openTag.start + expectedName + openTag.selfCloseVoid + openTag.end))
+ } else if (expected.hasChildren) {
+ result
+ .append(api.lineBuilder.actual.single(openTag.start + actualName + openTag.selfCloseVoid + openTag.end))
+ .append(api.lineBuilder.expected.first(openTag.start + expectedName + openTag.end))
+ .append(allChildren.stripFlags())
+ .append(api.lineBuilder.expected.last(indent + api.wrapFromTheme(theme.react.closeTag, actualName)))
+ } else {
+ if (namesAreEqual) {
+ result.append(api.lineBuilder.single(openTag.start + actualName + openTag.selfCloseVoid + openTag.end))
+ } else {
+ result
+ .append(api.lineBuilder.actual.single(openTag.start + actualName + openTag.selfCloseVoid + openTag.end))
+ .append(api.lineBuilder.expected.single(openTag.start + expectedName + openTag.selfCloseVoid + openTag.end))
+ }
+ }
+ }
+
+ return result
+ },
+
+ shouldFormat (subject) {
+ return subject.isItem === true || subject.isProperty === true
+ },
+
+ increaseIndent: true
+ }
+}
+module.exports = diffShallow
diff --git a/node_modules/@concordance/react/lib/elementFactory.js b/node_modules/@concordance/react/lib/elementFactory.js
new file mode 100644
index 000000000..9be125cef
--- /dev/null
+++ b/node_modules/@concordance/react/lib/elementFactory.js
@@ -0,0 +1,349 @@
+'use strict'
+
+const arrify = require('arrify')
+const diffShallow = require('./diffShallow')
+const escapeText = require('./escapeText')
+
+function factory (api, reactTags) {
+ const tag = Symbol('@concordance/react.ElementValue')
+
+ function customPropertyFormatter (theme, indent, key, value) {
+ const separator = theme.react.attribute.separator + theme.react.attribute.value.openBracket
+ if (value.isSingle) {
+ return value
+ .withFirstPrefixed(key.formatAsKey(theme) + separator)
+ .withLastPostfixed(theme.react.attribute.value.closeBracket)
+ }
+
+ return api.lineBuilder.first(key.formatAsKey(theme) + separator)
+ .concat(value.withFirstPrefixed(indent.increase()).stripFlags())
+ .append(api.lineBuilder.last(indent + theme.react.attribute.value.closeBracket))
+ }
+
+ function themeProperty (theme) {
+ theme.property.increaseValueIndent = true
+ theme.property.customFormat = customPropertyFormatter
+ }
+
+ function themeStringProperty (theme) {
+ theme.property.separator = theme.react.attribute.separator
+ theme.property.after = ''
+ Object.assign(theme.string.line, theme.react.attribute.value.string.line)
+ }
+
+ function customItemFormatter (theme, indent, value) {
+ if (value.isSingle) {
+ return value
+ .withFirstPrefixed(theme.react.child.openBracket)
+ .withLastPostfixed(theme.react.child.closeBracket)
+ }
+
+ return api.lineBuilder.first(theme.react.child.openBracket)
+ .concat(value.withFirstPrefixed(indent.increase()).stripFlags())
+ .append(api.lineBuilder.last(indent + theme.react.child.closeBracket))
+ }
+
+ function themeChild (theme) {
+ theme.item.increaseValueIndent = true
+ theme.item.customFormat = customItemFormatter
+ }
+
+ function themeReactChild (theme) {
+ theme.item.after = ''
+ }
+
+ function themeStringChild (theme) {
+ theme.item.after = ''
+ Object.assign(theme.string, theme.react.child.string)
+ }
+
+ function describe (props) {
+ const element = props.value
+
+ const type = element.type
+ const hasTypeFn = typeof type === 'function'
+ const typeFn = hasTypeFn ? type : null
+ const name = hasTypeFn ? type.displayName || type.name : type
+
+ const children = arrify(element.props.children)
+
+ const properties = Object.assign({}, element.props)
+ delete properties.children
+ if (element.key !== null) {
+ properties.key = element.key
+ }
+ const hasProperties = Object.keys(properties).length > 0
+
+ return new DescribedElementValue(Object.assign({
+ children,
+ hasProperties,
+ hasTypeFn,
+ name,
+ properties,
+ typeFn,
+ isList: children.length > 0
+ }, props))
+ }
+
+ function deserialize (state, recursor) {
+ return new DeserializedElementValue(state, recursor)
+ }
+
+ class ElementValue extends api.ObjectValue {
+ constructor (props) {
+ super(props)
+ this.name = props.name
+ this.hasProperties = props.hasProperties
+ this.hasTypeFn = props.hasTypeFn
+
+ this.hasChildren = this.isList
+ }
+
+ compare (expected) {
+ return this.tag === expected.tag && this.name === expected.name
+ ? api.SHALLOW_EQUAL
+ : api.UNEQUAL
+ }
+
+ formatName (theme) {
+ const formatted = api.wrapFromTheme(theme.react.tagName, this.name)
+ return this.hasTypeFn
+ ? formatted + theme.react.functionType
+ : formatted
+ }
+
+ compareNames (expected) {
+ return this.name === expected.name && this.hasTypeFn === expected.hasTypeFn
+ }
+
+ formatShallow (theme, indent) {
+ const childBuffer = api.lineBuilder.buffer()
+ const propertyBuffer = api.lineBuilder.buffer()
+
+ return {
+ append (formatted, origin) {
+ if (origin.isItem === true) {
+ childBuffer.append(formatted)
+ } else {
+ propertyBuffer.append(formatted)
+ }
+ },
+
+ finalize: () => {
+ const name = this.formatName(theme)
+ const openTag = theme.react.openTag
+
+ if (!this.hasChildren && !this.hasProperties) {
+ return api.lineBuilder.single(openTag.start + name + openTag.selfCloseVoid + openTag.end)
+ }
+
+ const innerIndentation = indent.increase()
+ const children = childBuffer.withFirstPrefixed(innerIndentation).stripFlags()
+ const properties = propertyBuffer.withFirstPrefixed(innerIndentation).stripFlags()
+
+ const result = api.lineBuilder.buffer()
+ if (this.hasProperties) {
+ result
+ .append(api.lineBuilder.first(openTag.start + name))
+ .append(properties)
+
+ if (this.hasChildren) {
+ result.append(api.lineBuilder.line(indent + openTag.end))
+ } else {
+ result.append(api.lineBuilder.last(indent + openTag.selfClose + openTag.end))
+ }
+ } else {
+ result.append(api.lineBuilder.first(openTag.start + name + openTag.end))
+ }
+
+ if (this.hasChildren) {
+ result
+ .append(children)
+ .append(api.lineBuilder.last(indent + api.wrapFromTheme(theme.react.closeTag, name)))
+ }
+
+ return result
+ },
+
+ maxDepth: () => {
+ const name = this.formatName(theme)
+ const openTag = theme.react.openTag
+
+ if (!this.hasChildren && !this.hasProperties) {
+ return api.lineBuilder.single(openTag.start + name + openTag.selfCloseVoid + openTag.end)
+ }
+
+ let str = openTag.start + name
+ if (this.hasProperties) {
+ str += theme.maxDepth
+ if (this.hasChildren) {
+ str += openTag.end
+ } else {
+ str += ' ' + openTag.selfClose + openTag.end
+ }
+ } else {
+ str += openTag.end
+ }
+
+ if (this.hasChildren) {
+ str += theme.maxDepth + api.wrapFromTheme(theme.react.closeTag, name)
+ }
+
+ return api.lineBuilder.single(str)
+ },
+
+ shouldFormat (subject) {
+ return subject.isItem === true || subject.isProperty === true
+ },
+
+ increaseIndent: true
+ }
+ }
+
+ prepareDiff (expected) {
+ return {
+ compareResult: this.tag === expected.tag
+ ? api.SHALLOW_EQUAL
+ : api.UNEQUAL
+ }
+ }
+
+ diffShallow (expected, theme, indent) {
+ return diffShallow(api, this, expected, theme, indent)
+ }
+
+ serialize () {
+ return [this.name, this.hasProperties, this.hasTypeFn, super.serialize()]
+ }
+ }
+ Object.defineProperty(ElementValue.prototype, 'tag', {value: tag})
+
+ function modifyThemes (recursor) {
+ return api.mapRecursor(recursor, next => {
+ let modifier
+ if (next.isItem === true) {
+ if (next.tag === api.descriptorTags.primitiveItem && next.value.tag === api.descriptorTags.string) {
+ modifier = themeStringChild
+ } else if (next.tag === api.descriptorTags.complexItem && reactTags.has(next.value.tag)) {
+ modifier = themeReactChild
+ } else {
+ modifier = themeChild
+ }
+ } else if (next.isProperty === true) {
+ if (
+ next.tag === api.descriptorTags.primitiveProperty &&
+ next.value.tag === api.descriptorTags.string &&
+ !next.value.includesLinebreaks
+ ) {
+ modifier = themeStringProperty
+ } else {
+ modifier = themeProperty
+ }
+ }
+
+ return modifier
+ ? api.modifyTheme(next, modifier)
+ : next
+ })
+ }
+
+ function DescribedMixin (base) {
+ return class extends api.DescribedMixin(base) {
+ constructor (props) {
+ super(props)
+ this.children = props.children
+ this.properties = props.properties
+ this.typeFn = props.typeFn
+ }
+
+ compare (expected) {
+ const result = super.compare(expected)
+ return result === api.SHALLOW_EQUAL && this.typeFn !== expected.typeFn
+ ? api.UNEQUAL
+ : result
+ }
+
+ compareNames (expected) {
+ return super.compareNames(expected) && this.typeFn === expected.typeFn
+ }
+
+ createPropertyRecursor () {
+ // Symbols are not valid property keys for React elements. This code
+ // also assumes that the keys can be formatted as JSX-like attribute
+ // names. Keys are not pre-escaped before being passed to Concordance's
+ // property descriptor.
+ const keys = Object.keys(this.properties).sort()
+ const size = keys.length
+
+ let index = 0
+ const next = () => {
+ if (index === size) return null
+
+ const key = keys[index++]
+ // Note that string values are not specifically escaped such that the
+ // output is valid JSX.
+ return this.describeProperty(key, this.describeAny(this.properties[key]))
+ }
+
+ return {size, next}
+ }
+
+ createListRecursor () {
+ if (!this.isList) return super.createListRecursor()
+
+ const size = this.children.length
+
+ let index = 0
+ const next = () => {
+ if (index === size) return null
+
+ const current = index++
+ const child = this.children[current]
+ const type = typeof child
+ let descriptor
+ if (type === 'string') {
+ descriptor = this.describeAny(escapeText(child))
+ } else {
+ descriptor = this.describeAny(child)
+ }
+
+ return this.describeItem(current, descriptor)
+ }
+
+ return {size, next}
+ }
+
+ createRecursor () {
+ return modifyThemes(super.createRecursor())
+ }
+ }
+ }
+
+ function DeserializedMixin (base) {
+ return class extends api.DeserializedMixin(base) {
+ constructor (state, recursor) {
+ super(state[3], recursor)
+ this.name = state[0]
+ this.hasProperties = state[1]
+ this.hasTypeFn = state[2]
+ }
+
+ createRecursor () {
+ return modifyThemes(super.createRecursor())
+ }
+ }
+ }
+
+ const DescribedElementValue = DescribedMixin(ElementValue)
+ const DeserializedElementValue = DeserializedMixin(ElementValue)
+
+ return {
+ DescribedMixin,
+ DeserializedMixin,
+ ElementValue,
+ describe,
+ deserialize,
+ tag
+ }
+}
+module.exports = factory
diff --git a/node_modules/@concordance/react/lib/escapeText.js b/node_modules/@concordance/react/lib/escapeText.js
new file mode 100644
index 000000000..52447b1b6
--- /dev/null
+++ b/node_modules/@concordance/react/lib/escapeText.js
@@ -0,0 +1,10 @@
+'use strict'
+
+function escapeText (text) {
+ return text
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ // TODO: Escape characters that Concordance would otherwise replace with \u
+ // sequences.
+}
+module.exports = escapeText
diff --git a/node_modules/@concordance/react/lib/testJsonFactory.js b/node_modules/@concordance/react/lib/testJsonFactory.js
new file mode 100644
index 000000000..c3e97a620
--- /dev/null
+++ b/node_modules/@concordance/react/lib/testJsonFactory.js
@@ -0,0 +1,59 @@
+'use strict'
+
+const arrify = require('arrify')
+
+function factory (api, element) {
+ const tag = Symbol('@concordance/react.TestJsonValue')
+
+ function describe (props) {
+ const obj = props.value
+
+ const name = obj.type
+ const children = arrify(obj.children)
+ const properties = Object.assign({}, obj.props)
+ const hasProperties = Object.keys(properties).length > 0
+
+ return new DescribedTestJsonValue(Object.assign({
+ children,
+ hasProperties,
+ hasTypeFn: false,
+ name,
+ properties,
+ typeFn: null,
+ isList: children.length > 0
+ }, props))
+ }
+
+ function deserialize (state, recursor) {
+ return new DeserializedTestJsonValue(state, recursor)
+ }
+
+ class TestJsonValue extends element.ElementValue {
+ compare (expected) {
+ // Allow expected value to be a React element.
+ return (this.tag === expected.tag || expected.tag === element.tag) && this.name === expected.name
+ ? api.SHALLOW_EQUAL
+ : api.UNEQUAL
+ }
+
+ prepareDiff (expected) {
+ return {
+ // Allow expected value to be a React element.
+ compareResult: this.tag === expected.tag || expected.tag === element.tag
+ ? api.SHALLOW_EQUAL
+ : api.UNEQUAL
+ }
+ }
+ }
+ Object.defineProperty(TestJsonValue.prototype, 'tag', {value: tag})
+
+ const DescribedTestJsonValue = element.DescribedMixin(TestJsonValue)
+ const DeserializedTestJsonValue = element.DeserializedMixin(TestJsonValue)
+
+ return {
+ describe,
+ deserialize,
+ tag
+ }
+}
+module.exports = factory
diff --git a/node_modules/@concordance/react/package.json b/node_modules/@concordance/react/package.json
new file mode 100644
index 000000000..7ba194481
--- /dev/null
+++ b/node_modules/@concordance/react/package.json
@@ -0,0 +1,80 @@
+{
+ "name": "@concordance/react",
+ "version": "1.0.0",
+ "description": "Compare, format, diff and serialize React trees with Concordance",
+ "main": "index.js",
+ "files": [
+ "lib",
+ "index.js"
+ ],
+ "engines": {
+ "node": ">=4.5"
+ },
+ "scripts": {
+ "build:fixtures": "babel --presets=react,@ava/stage-4 --out-dir=test/fixtures/react --extensions=.jsx test/fixtures/react",
+ "coverage": "nyc npm test",
+ "lint": "as-i-preach",
+ "pretest": "npm run build:fixtures",
+ "test": "ava",
+ "posttest": "as-i-preach"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/concordancejs/react.git"
+ },
+ "author": "Mark Wubben (https://novemberborn.net/)",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/concordancejs/react/issues"
+ },
+ "homepage": "https://github.com/concordancejs/react#readme",
+ "keywords": [
+ "concordance-plugin",
+ "concordance",
+ "react"
+ ],
+ "dependencies": {
+ "arrify": "^1.0.1"
+ },
+ "devDependencies": {
+ "@novemberborn/as-i-preach": "^10.0.1",
+ "ava": "^0.19.0",
+ "babel-cli": "^6.24.1",
+ "babel-core": "^6.25.0",
+ "babel-preset-react": "^6.24.1",
+ "codecov": "^2.1.0",
+ "concordance": "^1.0.0",
+ "nyc": "^11.0.1",
+ "react": "^15.6.1",
+ "react-test-renderer": "^15.6.1"
+ },
+ "as-i-preach": {
+ "allowDevDependencies": [
+ "test/**/*.js",
+ "test/**/*.jsx"
+ ],
+ "ignore": [
+ "test/fixtures/react/*.js"
+ ]
+ },
+ "ava": {
+ "source": [
+ "!test/**/*.js.snap"
+ ],
+ "babel": {
+ "presets": [
+ "@ava/stage-4",
+ "@ava/transform-test-files",
+ "react"
+ ]
+ }
+ },
+ "nyc": {
+ "reporter": [
+ "html",
+ "lcov",
+ "text"
+ ]
+ },
+ "standard-engine": "@novemberborn/as-i-preach"
+}