aboutsummaryrefslogtreecommitdiff
path: root/node_modules/concordance/lib/primitiveValues
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/concordance/lib/primitiveValues')
-rw-r--r--node_modules/concordance/lib/primitiveValues/boolean.js40
-rw-r--r--node_modules/concordance/lib/primitiveValues/null.js32
-rw-r--r--node_modules/concordance/lib/primitiveValues/number.js41
-rw-r--r--node_modules/concordance/lib/primitiveValues/string.js306
-rw-r--r--node_modules/concordance/lib/primitiveValues/symbol.js114
-rw-r--r--node_modules/concordance/lib/primitiveValues/undefined.js32
6 files changed, 565 insertions, 0 deletions
diff --git a/node_modules/concordance/lib/primitiveValues/boolean.js b/node_modules/concordance/lib/primitiveValues/boolean.js
new file mode 100644
index 000000000..7bad50408
--- /dev/null
+++ b/node_modules/concordance/lib/primitiveValues/boolean.js
@@ -0,0 +1,40 @@
+'use strict'
+
+const constants = require('../constants')
+const formatUtils = require('../formatUtils')
+const lineBuilder = require('../lineBuilder')
+
+const DEEP_EQUAL = constants.DEEP_EQUAL
+const UNEQUAL = constants.UNEQUAL
+
+function describe (value) {
+ return new BooleanValue(value)
+}
+exports.describe = describe
+
+exports.deserialize = describe
+
+const tag = Symbol('BooleanValue')
+exports.tag = tag
+
+class BooleanValue {
+ constructor (value) {
+ this.value = value
+ }
+
+ compare (expected) {
+ return this.tag === expected.tag && this.value === expected.value
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ formatDeep (theme) {
+ return lineBuilder.single(formatUtils.wrap(theme.boolean, this.value === true ? 'true' : 'false'))
+ }
+
+ serialize () {
+ return this.value
+ }
+}
+Object.defineProperty(BooleanValue.prototype, 'isPrimitive', { value: true })
+Object.defineProperty(BooleanValue.prototype, 'tag', { value: tag })
diff --git a/node_modules/concordance/lib/primitiveValues/null.js b/node_modules/concordance/lib/primitiveValues/null.js
new file mode 100644
index 000000000..9436ed9a1
--- /dev/null
+++ b/node_modules/concordance/lib/primitiveValues/null.js
@@ -0,0 +1,32 @@
+'use strict'
+
+const constants = require('../constants')
+const formatUtils = require('../formatUtils')
+const lineBuilder = require('../lineBuilder')
+
+const DEEP_EQUAL = constants.DEEP_EQUAL
+const UNEQUAL = constants.UNEQUAL
+
+function describe () {
+ return new NullValue()
+}
+exports.describe = describe
+
+exports.deserialize = describe
+
+const tag = Symbol('NullValue')
+exports.tag = tag
+
+class NullValue {
+ compare (expected) {
+ return expected.tag === tag
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ formatDeep (theme) {
+ return lineBuilder.single(formatUtils.wrap(theme.null, 'null'))
+ }
+}
+Object.defineProperty(NullValue.prototype, 'isPrimitive', { value: true })
+Object.defineProperty(NullValue.prototype, 'tag', { value: tag })
diff --git a/node_modules/concordance/lib/primitiveValues/number.js b/node_modules/concordance/lib/primitiveValues/number.js
new file mode 100644
index 000000000..d1dec8edb
--- /dev/null
+++ b/node_modules/concordance/lib/primitiveValues/number.js
@@ -0,0 +1,41 @@
+'use strict'
+
+const constants = require('../constants')
+const formatUtils = require('../formatUtils')
+const lineBuilder = require('../lineBuilder')
+
+const DEEP_EQUAL = constants.DEEP_EQUAL
+const UNEQUAL = constants.UNEQUAL
+
+function describe (value) {
+ return new NumberValue(value)
+}
+exports.describe = describe
+
+exports.deserialize = describe
+
+const tag = Symbol('NumberValue')
+exports.tag = tag
+
+class NumberValue {
+ constructor (value) {
+ this.value = value
+ }
+
+ compare (expected) {
+ return expected.tag === tag && Object.is(this.value, expected.value)
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ formatDeep (theme) {
+ const string = Object.is(this.value, -0) ? '-0' : String(this.value)
+ return lineBuilder.single(formatUtils.wrap(theme.number, string))
+ }
+
+ serialize () {
+ return this.value
+ }
+}
+Object.defineProperty(NumberValue.prototype, 'isPrimitive', { value: true })
+Object.defineProperty(NumberValue.prototype, 'tag', { value: tag })
diff --git a/node_modules/concordance/lib/primitiveValues/string.js b/node_modules/concordance/lib/primitiveValues/string.js
new file mode 100644
index 000000000..af120022f
--- /dev/null
+++ b/node_modules/concordance/lib/primitiveValues/string.js
@@ -0,0 +1,306 @@
+'use strict'
+
+const fastDiff = require('fast-diff')
+const keyword = require('esutils').keyword
+
+const constants = require('../constants')
+const formatUtils = require('../formatUtils')
+const lineBuilder = require('../lineBuilder')
+
+const DEEP_EQUAL = constants.DEEP_EQUAL
+const UNEQUAL = constants.UNEQUAL
+
+function describe (value) {
+ return new StringValue(value)
+}
+exports.describe = describe
+
+exports.deserialize = describe
+
+const tag = Symbol('StringValue')
+exports.tag = tag
+
+// TODO: Escape invisible characters (e.g. zero-width joiner, non-breaking space),
+// ambiguous characters (other kinds of spaces, combining characters). Use
+// http://graphemica.com/blocks/control-pictures where applicable.
+function basicEscape (string) {
+ return string.replace(/\\/g, '\\\\')
+}
+
+const CRLF_CONTROL_PICTURE = '\u240D\u240A'
+const LF_CONTROL_PICTURE = '\u240A'
+const CR_CONTROL_PICTURE = '\u240D'
+
+const MATCH_CONTROL_PICTURES = new RegExp(`${CR_CONTROL_PICTURE}|${LF_CONTROL_PICTURE}|${CR_CONTROL_PICTURE}`, 'g')
+
+function escapeLinebreak (string) {
+ if (string === '\r\n') return CRLF_CONTROL_PICTURE
+ if (string === '\n') return LF_CONTROL_PICTURE
+ if (string === '\r') return CR_CONTROL_PICTURE
+ return string
+}
+
+function themeControlPictures (theme, resetWrap, str) {
+ return str.replace(MATCH_CONTROL_PICTURES, picture => {
+ return resetWrap.close + formatUtils.wrap(theme.string.controlPicture, picture) + resetWrap.open
+ })
+}
+
+const MATCH_SINGLE_QUOTE = /'/g
+const MATCH_DOUBLE_QUOTE = /"/g
+const MATCH_BACKTICKS = /`/g
+function escapeQuotes (line, string) {
+ const quote = line.escapeQuote
+ if (quote === '\'') return string.replace(MATCH_SINGLE_QUOTE, "\\'")
+ if (quote === '"') return string.replace(MATCH_DOUBLE_QUOTE, '\\"')
+ if (quote === '`') return string.replace(MATCH_BACKTICKS, '\\`')
+ return string
+}
+
+function includesLinebreaks (string) {
+ return string.includes('\r') || string.includes('\n')
+}
+
+function diffLine (theme, actual, expected) {
+ const outcome = fastDiff(actual, expected)
+
+ // TODO: Compute when line is mostly unequal (80%? 90%?) and treat it as being
+ // completely unequal.
+ const isPartiallyEqual = !(
+ (outcome.length === 2 && outcome[0][1] === actual && outcome[1][1] === expected) ||
+ // Discount line ending control pictures, which will be equal even when the
+ // rest of the line isn't.
+ (
+ outcome.length === 3 &&
+ outcome[2][0] === fastDiff.EQUAL &&
+ MATCH_CONTROL_PICTURES.test(outcome[2][1]) &&
+ outcome[0][1] + outcome[2][1] === actual &&
+ outcome[1][1] + outcome[2][1] === expected
+ )
+ )
+
+ let stringActual = ''
+ let stringExpected = ''
+
+ const noopWrap = { open: '', close: '' }
+ const deleteWrap = isPartiallyEqual ? theme.string.diff.delete : noopWrap
+ const insertWrap = isPartiallyEqual ? theme.string.diff.insert : noopWrap
+ const equalWrap = isPartiallyEqual ? theme.string.diff.equal : noopWrap
+ for (const diff of outcome) {
+ if (diff[0] === fastDiff.DELETE) {
+ stringActual += formatUtils.wrap(deleteWrap, diff[1])
+ } else if (diff[0] === fastDiff.INSERT) {
+ stringExpected += formatUtils.wrap(insertWrap, diff[1])
+ } else {
+ const string = formatUtils.wrap(equalWrap, themeControlPictures(theme, equalWrap, diff[1]))
+ stringActual += string
+ stringExpected += string
+ }
+ }
+
+ if (!isPartiallyEqual) {
+ stringActual = formatUtils.wrap(theme.string.diff.deleteLine, stringActual)
+ stringExpected = formatUtils.wrap(theme.string.diff.insertLine, stringExpected)
+ }
+
+ return [stringActual, stringExpected]
+}
+
+const LINEBREAKS = /\r\n|\r|\n/g
+
+function gatherLines (string) {
+ const lines = []
+ let prevIndex = 0
+ for (let match; (match = LINEBREAKS.exec(string)); prevIndex = match.index + match[0].length) {
+ lines.push(string.slice(prevIndex, match.index) + escapeLinebreak(match[0]))
+ }
+ lines.push(string.slice(prevIndex))
+ return lines
+}
+
+class StringValue {
+ constructor (value) {
+ this.value = value
+ }
+
+ compare (expected) {
+ return expected.tag === tag && this.value === expected.value
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ get includesLinebreaks () {
+ return includesLinebreaks(this.value)
+ }
+
+ formatDeep (theme, indent) {
+ // Escape backslashes
+ let escaped = basicEscape(this.value)
+
+ if (!this.includesLinebreaks) {
+ escaped = escapeQuotes(theme.string.line, escaped)
+ return lineBuilder.single(formatUtils.wrap(theme.string.line, formatUtils.wrap(theme.string, escaped)))
+ }
+
+ escaped = escapeQuotes(theme.string.multiline, escaped)
+ const lineStrings = gatherLines(escaped).map(string => {
+ return formatUtils.wrap(theme.string, themeControlPictures(theme, theme.string, string))
+ })
+ const lastIndex = lineStrings.length - 1
+ const indentation = indent
+ return lineBuilder.buffer()
+ .append(
+ lineStrings.map((string, index) => {
+ if (index === 0) return lineBuilder.first(theme.string.multiline.start + string)
+ if (index === lastIndex) return lineBuilder.last(indentation + string + theme.string.multiline.end)
+ return lineBuilder.line(indentation + string)
+ }))
+ }
+
+ formatAsKey (theme) {
+ const key = this.value
+ if (keyword.isIdentifierNameES6(key, true) || String(parseInt(key, 10)) === key) {
+ return key
+ }
+
+ const escaped = basicEscape(key)
+ .replace(/\n/g, '\\n')
+ .replace(/\r/g, '\\r')
+ .replace(/'/g, "\\'")
+ return formatUtils.wrap(theme.string.line, formatUtils.wrap(theme.string, escaped))
+ }
+
+ diffDeep (expected, theme, indent) {
+ if (expected.tag !== tag) return null
+
+ const escapedActual = basicEscape(this.value)
+ const escapedExpected = basicEscape(expected.value)
+
+ if (!includesLinebreaks(escapedActual) && !includesLinebreaks(escapedExpected)) {
+ const result = diffLine(theme,
+ escapeQuotes(theme.string.line, escapedActual),
+ escapeQuotes(theme.string.line, escapedExpected))
+
+ return lineBuilder.actual.single(formatUtils.wrap(theme.string.line, result[0]))
+ .concat(lineBuilder.expected.single(formatUtils.wrap(theme.string.line, result[1])))
+ }
+
+ const actualLines = gatherLines(escapeQuotes(theme.string.multiline, escapedActual))
+ const expectedLines = gatherLines(escapeQuotes(theme.string.multiline, escapedExpected))
+
+ const indentation = indent
+ const lines = lineBuilder.buffer()
+ const lastActualIndex = actualLines.length - 1
+ const lastExpectedIndex = expectedLines.length - 1
+
+ let actualBuffer = []
+ let expectedBuffer = []
+ let mustOpenNextExpected = false
+ for (let actualIndex = 0, expectedIndex = 0, extraneousOffset = 0; actualIndex < actualLines.length;) {
+ if (actualLines[actualIndex] === expectedLines[expectedIndex]) {
+ lines.append(actualBuffer)
+ lines.append(expectedBuffer)
+ actualBuffer = []
+ expectedBuffer = []
+
+ let string = actualLines[actualIndex]
+ string = themeControlPictures(theme, theme.string.diff.equal, string)
+ string = formatUtils.wrap(theme.string.diff.equal, string)
+
+ if (actualIndex === 0) {
+ lines.append(lineBuilder.first(theme.string.multiline.start + string))
+ } else if (actualIndex === lastActualIndex && expectedIndex === lastExpectedIndex) {
+ lines.append(lineBuilder.last(indentation + string + theme.string.multiline.end))
+ } else {
+ lines.append(lineBuilder.line(indentation + string))
+ }
+
+ actualIndex++
+ expectedIndex++
+ continue
+ }
+
+ let expectedIsMissing = false
+ {
+ const compare = actualLines[actualIndex]
+ for (let index = expectedIndex; !expectedIsMissing && index < expectedLines.length; index++) {
+ expectedIsMissing = compare === expectedLines[index]
+ }
+ }
+
+ let actualIsExtraneous = (actualIndex - extraneousOffset) > lastExpectedIndex
+ if (!actualIsExtraneous) {
+ const compare = expectedLines[expectedIndex]
+ for (let index = actualIndex; !actualIsExtraneous && index < actualLines.length; index++) {
+ actualIsExtraneous = compare === actualLines[index]
+ }
+
+ if (!actualIsExtraneous && (actualIndex - extraneousOffset) === lastExpectedIndex && actualIndex < lastActualIndex) {
+ actualIsExtraneous = true
+ }
+ }
+
+ if (actualIsExtraneous && !expectedIsMissing) {
+ const string = formatUtils.wrap(theme.string.diff.deleteLine, actualLines[actualIndex])
+
+ if (actualIndex === 0) {
+ actualBuffer.push(lineBuilder.actual.first(theme.string.multiline.start + string))
+ mustOpenNextExpected = true
+ } else if (actualIndex === lastActualIndex) {
+ actualBuffer.push(lineBuilder.actual.last(indentation + string + theme.string.multiline.end))
+ } else {
+ actualBuffer.push(lineBuilder.actual.line(indentation + string))
+ }
+
+ actualIndex++
+ extraneousOffset++
+ } else if (expectedIsMissing && !actualIsExtraneous) {
+ const string = formatUtils.wrap(theme.string.diff.insertLine, expectedLines[expectedIndex])
+
+ if (mustOpenNextExpected) {
+ expectedBuffer.push(lineBuilder.expected.first(theme.string.multiline.start + string))
+ mustOpenNextExpected = false
+ } else if (expectedIndex === lastExpectedIndex) {
+ expectedBuffer.push(lineBuilder.expected.last(indentation + string + theme.string.multiline.end))
+ } else {
+ expectedBuffer.push(lineBuilder.expected.line(indentation + string))
+ }
+
+ expectedIndex++
+ } else {
+ const result = diffLine(theme, actualLines[actualIndex], expectedLines[expectedIndex])
+
+ if (actualIndex === 0) {
+ actualBuffer.push(lineBuilder.actual.first(theme.string.multiline.start + result[0]))
+ mustOpenNextExpected = true
+ } else if (actualIndex === lastActualIndex) {
+ actualBuffer.push(lineBuilder.actual.last(indentation + result[0] + theme.string.multiline.end))
+ } else {
+ actualBuffer.push(lineBuilder.actual.line(indentation + result[0]))
+ }
+
+ if (mustOpenNextExpected) {
+ expectedBuffer.push(lineBuilder.expected.first(theme.string.multiline.start + result[1]))
+ mustOpenNextExpected = false
+ } else if (expectedIndex === lastExpectedIndex) {
+ expectedBuffer.push(lineBuilder.expected.last(indentation + result[1] + theme.string.multiline.end))
+ } else {
+ expectedBuffer.push(lineBuilder.expected.line(indentation + result[1]))
+ }
+
+ actualIndex++
+ expectedIndex++
+ }
+ }
+
+ lines.append(actualBuffer)
+ lines.append(expectedBuffer)
+ return lines
+ }
+
+ serialize () {
+ return this.value
+ }
+}
+Object.defineProperty(StringValue.prototype, 'isPrimitive', { value: true })
+Object.defineProperty(StringValue.prototype, 'tag', { value: tag })
diff --git a/node_modules/concordance/lib/primitiveValues/symbol.js b/node_modules/concordance/lib/primitiveValues/symbol.js
new file mode 100644
index 000000000..3778b4118
--- /dev/null
+++ b/node_modules/concordance/lib/primitiveValues/symbol.js
@@ -0,0 +1,114 @@
+'use strict'
+
+const stringEscape = require('js-string-escape')
+const wellKnownSymbols = require('well-known-symbols')
+
+const constants = require('../constants')
+const formatUtils = require('../formatUtils')
+const lineBuilder = require('../lineBuilder')
+
+const DEEP_EQUAL = constants.DEEP_EQUAL
+const UNEQUAL = constants.UNEQUAL
+
+function describe (value) {
+ let stringCompare = null
+
+ const key = Symbol.keyFor(value)
+ if (key !== undefined) {
+ stringCompare = `Symbol.for(${stringEscape(key)})`
+ } else if (wellKnownSymbols.isWellKnown(value)) {
+ stringCompare = wellKnownSymbols.getLabel(value)
+ }
+
+ return new SymbolValue({
+ stringCompare,
+ value
+ })
+}
+exports.describe = describe
+
+function deserialize (state) {
+ const stringCompare = state[0]
+ const string = state[1] || state[0]
+
+ return new DeserializedSymbolValue({
+ string,
+ stringCompare,
+ value: null
+ })
+}
+exports.deserialize = deserialize
+
+const tag = Symbol('SymbolValue')
+exports.tag = tag
+
+class SymbolValue {
+ constructor (props) {
+ this.stringCompare = props.stringCompare
+ this.value = props.value
+ }
+
+ compare (expected) {
+ if (expected.tag !== tag) return UNEQUAL
+
+ if (this.stringCompare !== null) {
+ return this.stringCompare === expected.stringCompare
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ return this.value === expected.value
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ formatString () {
+ if (this.stringCompare !== null) return this.stringCompare
+ return stringEscape(this.value.toString())
+ }
+
+ formatDeep (theme) {
+ return lineBuilder.single(formatUtils.wrap(theme.symbol, this.formatString()))
+ }
+
+ formatAsKey (theme) {
+ return formatUtils.wrap(theme.property.keyBracket, formatUtils.wrap(theme.symbol, this.formatString()))
+ }
+
+ serialize () {
+ const string = this.formatString()
+ return this.stringCompare === string
+ ? [this.stringCompare]
+ : [this.stringCompare, string]
+ }
+}
+Object.defineProperty(SymbolValue.prototype, 'isPrimitive', { value: true })
+Object.defineProperty(SymbolValue.prototype, 'tag', { value: tag })
+
+class DeserializedSymbolValue extends SymbolValue {
+ constructor (props) {
+ super(props)
+ this.string = props.string
+ }
+
+ compare (expected) {
+ if (expected.tag !== tag) return UNEQUAL
+
+ if (this.stringCompare !== null) {
+ return this.stringCompare === expected.stringCompare
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ // Symbols that are not in the global symbol registry, and are not
+ // well-known, cannot be compared when deserialized. Treat symbols
+ // as equal if they are formatted the same.
+ return this.string === expected.formatString()
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ formatString () {
+ return this.string
+ }
+}
diff --git a/node_modules/concordance/lib/primitiveValues/undefined.js b/node_modules/concordance/lib/primitiveValues/undefined.js
new file mode 100644
index 000000000..507556e61
--- /dev/null
+++ b/node_modules/concordance/lib/primitiveValues/undefined.js
@@ -0,0 +1,32 @@
+'use strict'
+
+const constants = require('../constants')
+const formatUtils = require('../formatUtils')
+const lineBuilder = require('../lineBuilder')
+
+const DEEP_EQUAL = constants.DEEP_EQUAL
+const UNEQUAL = constants.UNEQUAL
+
+function describe () {
+ return new UndefinedValue()
+}
+exports.describe = describe
+
+exports.deserialize = describe
+
+const tag = Symbol('UndefinedValue')
+exports.tag = tag
+
+class UndefinedValue {
+ compare (expected) {
+ return expected.tag === tag
+ ? DEEP_EQUAL
+ : UNEQUAL
+ }
+
+ formatDeep (theme) {
+ return lineBuilder.single(formatUtils.wrap(theme.undefined, 'undefined'))
+ }
+}
+Object.defineProperty(UndefinedValue.prototype, 'isPrimitive', { value: true })
+Object.defineProperty(UndefinedValue.prototype, 'tag', { value: tag })