diff-dom
Version:
A diff for DOM elements, as client-side JavaScript code. Gets all modifications, insertions and removals between two DOM fragments.
144 lines (141 loc) • 4.27 kB
text/typescript
import { checkElementType } from "./diffDOM/helpers"
/**
* Use TraceLogger to figure out function calls inside
* JS objects by wrapping an object with a TraceLogger
* instance.
*
* Pretty-prints the call trace (using unicode box code)
* when tracelogger.toString() is called.
*/
/**
* Wrap an object by calling new TraceLogger(obj)
*
* If you're familiar with Python decorators, this
* does roughly the same thing, adding pre/post
* call hook logging calls so that you can see
* what's going on.
*/
export class TraceLogger {
messages: string[]
pad: string
padding: string
tick: number
constructor(obj = {}) {
this.pad = "│ "
this.padding = ""
this.tick = 1
this.messages = []
const wrapkey = (obj: object, key: string) => {
// trace this function
const oldfn = obj[key]
obj[key] = (
...args: ((
...args: (
| string
| HTMLElement
| number
| boolean
| false
| (string | HTMLElement | number | boolean | false)[]
)[]
) => void)[]
) => {
this.fin(key, Array.prototype.slice.call(args))
const result = oldfn.apply(obj, args)
this.fout(key, result)
return result
}
}
// can't use Object.keys for prototype walking
for (let key in obj) {
if (typeof obj[key] === "function") {
wrapkey(obj, key)
}
}
this.log("┌ TRACELOG START")
}
// called when entering a function
fin(
fn: string,
args:
| string
| HTMLElement
| number
| boolean
| false
| (string | HTMLElement | number | boolean | false)[],
) {
this.padding += this.pad
this.log(`├─> entering ${fn}`, args)
}
// called when exiting a function
fout(
fn: string,
result:
| string
| HTMLElement
| number
| boolean
| false
| (string | HTMLElement | number | boolean | false)[],
) {
this.log("│<──┘ generated return value", result)
this.padding = this.padding.substring(
0,
this.padding.length - this.pad.length,
)
}
// log message formatting
format(s: string, tick: number) {
let nf = function (t: number) {
let tStr = `${t}`
while (tStr.length < 4) {
tStr = `0${t}`
}
return tStr
}
return `${nf(tick)}> ${this.padding}${s}`
}
// log a trace message
log(...args) {
const stringCollapse = function (
v:
| string
| HTMLElement
| number
| boolean
| false
| (string | HTMLElement | number | boolean | false)[],
) {
if (!v) {
return "<falsey>"
}
if (typeof v === "string") {
return v
}
if (checkElementType(v, "HTMLElement")) {
return (v as HTMLElement).outerHTML || "<empty>"
}
if (v instanceof Array) {
return `[${v.map(stringCollapse).join(",")}]`
}
return v.toString() || v.valueOf() || "<unknown>"
}
const s = args.map(stringCollapse).join(", ")
this.messages.push(this.format(s, this.tick++))
}
// turn the log into a structured string with
// unicode box codes to make it a sensible trace.
toString() {
let cap = "× "
let terminator = "└───"
while (terminator.length <= this.padding.length + this.pad.length) {
terminator += cap
}
let _ = this.padding
this.padding = ""
terminator = this.format(terminator, this.tick)
this.padding = _
return `${this.messages.join("\n")}\n${terminator}`
}
}