Skip to content
Permalink
9bfb9ba527
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
Andrew Eisenberg Fix dependabot issues
Latest commit 531c6ba Oct 21, 2021 History
0 contributors

Users who have contributed to this file

254 lines (208 sloc) 7.63 KB
'use strict'
const constants = require('../constants')
const formatUtils = require('../formatUtils')
const recursorUtils = require('../recursorUtils')
const DEEP_EQUAL = constants.DEEP_EQUAL
const UNEQUAL = constants.UNEQUAL
function describeComplex (index, value) {
return new ComplexItem(index, value)
}
exports.describeComplex = describeComplex
function deserializeComplex (index, recursor) {
const value = recursor()
return new ComplexItem(index, value)
}
exports.deserializeComplex = deserializeComplex
function describePrimitive (index, value) {
return new PrimitiveItem(index, value)
}
exports.describePrimitive = describePrimitive
function deserializePrimitive (state) {
const index = state[0]
const value = state[1]
return new PrimitiveItem(index, value)
}
exports.deserializePrimitive = deserializePrimitive
const complexTag = Symbol('ComplexItem')
exports.complexTag = complexTag
const primitiveTag = Symbol('PrimitiveItem')
exports.primitiveTag = primitiveTag
class ComplexItem {
constructor (index, value) {
this.index = index
this.value = value
}
createRecursor () {
return recursorUtils.singleValue(this.value)
}
compare (expected) {
return expected.tag === complexTag && this.index === expected.index
? this.value.compare(expected.value)
: UNEQUAL
}
formatShallow (theme, indent) {
const increaseValueIndent = theme.item.increaseValueIndent === true
return new formatUtils.SingleValueFormatter(theme, value => {
if (typeof theme.item.customFormat === 'function') {
return theme.item.customFormat(theme, indent, value)
}
return value.withLastPostfixed(theme.item.after)
}, increaseValueIndent)
}
prepareDiff (expected, lhsRecursor, rhsRecursor, compareComplexShape, isCircular) {
// Circular values cannot be compared. They must be treated as being unequal when diffing.
if (isCircular(this.value) || isCircular(expected.value)) return { compareResult: UNEQUAL }
// Try to line up this or remaining items with the expected items.
const lhsFork = recursorUtils.fork(lhsRecursor)
const rhsFork = recursorUtils.fork(rhsRecursor)
const initialExpected = expected
let expectedIsMissing = false
while (!expectedIsMissing && expected !== null && expected.isItem === true) {
if (expected.tag === complexTag) {
expectedIsMissing = compareComplexShape(this.value, expected.value) !== UNEQUAL
}
expected = rhsFork.shared()
}
let actualIsExtraneous = false
if (initialExpected.tag === complexTag) {
let actual = this
while (!actualIsExtraneous && actual !== null && actual.isItem === true) {
if (actual.tag === complexTag) {
actualIsExtraneous = compareComplexShape(actual.value, initialExpected.value) !== UNEQUAL
}
actual = lhsFork.shared()
}
} else if (initialExpected.tag === primitiveTag) {
let actual = this
while (!actualIsExtraneous && actual !== null && actual.isItem === true) {
if (actual.tag === primitiveTag) {
actualIsExtraneous = initialExpected.value.compare(actual.value) === DEEP_EQUAL
}
actual = lhsFork.shared()
}
}
if (actualIsExtraneous && !expectedIsMissing) {
return {
actualIsExtraneous: true,
lhsRecursor: lhsFork.recursor,
rhsRecursor: recursorUtils.map(
recursorUtils.unshift(rhsFork.recursor, initialExpected),
next => {
if (next.isItem !== true) return next
next.index++
return next
}),
}
}
if (expectedIsMissing && !actualIsExtraneous) {
return {
expectedIsMissing: true,
lhsRecursor: recursorUtils.map(
recursorUtils.unshift(lhsFork.recursor, this),
next => {
if (next.isItem !== true) return next
next.index++
return next
}),
rhsRecursor: rhsFork.recursor,
}
}
const mustRecurse = this.tag === complexTag && initialExpected.tag === complexTag &&
this.value.compare(initialExpected.value) !== UNEQUAL
return {
mustRecurse,
isUnequal: !mustRecurse,
lhsRecursor: lhsFork.recursor,
rhsRecursor: rhsFork.recursor,
}
}
serialize () {
return this.index
}
}
Object.defineProperty(ComplexItem.prototype, 'isItem', { value: true })
Object.defineProperty(ComplexItem.prototype, 'tag', { value: complexTag })
class PrimitiveItem {
constructor (index, value) {
this.index = index
this.value = value
}
compare (expected) {
return expected.tag === primitiveTag && this.index === expected.index
? this.value.compare(expected.value)
: UNEQUAL
}
formatDeep (theme, indent) {
const increaseValueIndent = theme.item.increaseValueIndent === true
const valueIndent = increaseValueIndent ? indent.increase() : indent
// Since the value is formatted directly, modifiers are not applied. Apply
// modifiers to the item descriptor instead.
const formatted = this.value.formatDeep(theme, valueIndent)
if (typeof theme.item.customFormat === 'function') {
return theme.item.customFormat(theme, indent, formatted)
}
return formatted.withLastPostfixed(theme.item.after)
}
prepareDiff (expected, lhsRecursor, rhsRecursor, compareComplexShape, isCircular) {
const compareResult = this.compare(expected)
// Short-circuit when values are deeply equal.
if (compareResult === DEEP_EQUAL) return { compareResult }
// Short-circut when values can be diffed directly.
if (
expected.tag === primitiveTag &&
this.value.tag === expected.value.tag && typeof this.value.diffDeep === 'function'
) {
return { compareResult }
}
// Try to line up this or remaining items with the expected items.
const rhsFork = recursorUtils.fork(rhsRecursor)
const initialExpected = expected
do {
if (expected === null || expected.isItem !== true) {
return {
actualIsExtraneous: true,
rhsRecursor: recursorUtils.map(
recursorUtils.unshift(rhsFork.recursor, initialExpected),
next => {
if (next.isItem !== true) return next
next.index++
return next
}),
}
}
if (this.value.compare(expected.value) === DEEP_EQUAL) {
return {
expectedIsMissing: true,
lhsRecursor: recursorUtils.map(
recursorUtils.unshift(lhsRecursor, this),
next => {
if (next.isItem !== true) return next
next.index++
return next
}),
rhsRecursor: rhsFork.recursor,
}
}
expected = rhsFork.shared()
} while (true)
}
diffDeep (expected, theme, indent, invert) {
// Verify a diff can be returned.
if (this.tag !== expected.tag || typeof this.value.diffDeep !== 'function') return null
const increaseValueIndent = theme.property.increaseValueIndent === true
const valueIndent = increaseValueIndent ? indent.increase() : indent
// Since the value is diffed directly, modifiers are not applied. Apply
// modifiers to the item descriptor instead.
const diff = this.value.diffDeep(expected.value, theme, valueIndent, invert)
if (diff === null) return null
if (typeof theme.item.customFormat === 'function') {
return theme.item.customFormat(theme, indent, diff)
}
return diff.withLastPostfixed(theme.item.after)
}
serialize () {
return [this.index, this.value]
}
}
Object.defineProperty(PrimitiveItem.prototype, 'isItem', { value: true })
Object.defineProperty(PrimitiveItem.prototype, 'tag', { value: primitiveTag })