a post-component library for building user-interfaces on the web.

trim some bytes (#29)

authored by tombl.dev and committed by

GitHub 1499b1cc 8400e7c8

+40 -62
+40 -61
src/html.js
··· 20 20 /** @return {node is Text} */ 21 21 const isText = node => node.nodeType === /** @satisfies {typeof Node.TEXT_NODE} */ (3) 22 22 23 - /** @return {node is Comment} */ 24 - const isComment = node => node.nodeType === /** @satisfies {typeof Node.COMMENT_NODE} */ (8) 25 - 26 23 /** @return {node is DocumentFragment} */ 27 24 const isDocumentFragment = node => node.nodeType === /** @satisfies {typeof Node.DOCUMENT_FRAGMENT_NODE} */ (11) 28 - 29 - export const html = (statics, ...dynamics) => new BoundTemplateInstance(statics, dynamics) 30 - 31 - const singlePartTemplate = part => html`${part}` 32 25 33 26 /** @return {value is Renderable} */ 34 27 const isRenderable = value => typeof value === 'object' && value !== null && 'render' in value ··· 36 29 /** @return {value is Iterable<unknown>} */ 37 30 const isIterable = value => typeof value === 'object' && value !== null && Symbol.iterator in value 38 31 32 + export const html = (statics, ...dynamics) => new BoundTemplateInstance(statics, dynamics) 33 + 34 + const singlePartTemplate = part => html`${part}` 35 + 39 36 /** @return {asserts value} */ 40 37 const assert = (value, message = 'assertion failed') => { 41 38 if (!DEV) return ··· 45 42 /** @implements {SpanInstance} */ 46 43 class Span { 47 44 /** 48 - * @param {ParentNode} parentNode 49 - * @param {Node} start the first node in the span 50 - * @param {Node} end the last node in the span 45 + * @param {Node} node the only node in the span 51 46 */ 52 - constructor(parentNode, start, end) { 53 - DEV: { 54 - assert(parentNode != null) 55 - assert(start.parentNode === parentNode) 56 - assert(end.parentNode === parentNode) 57 - } 58 - 59 - this.parentNode = parentNode 60 - this._start = start 61 - this._end = end 47 + constructor(node) { 48 + DEV: assert(node.parentNode !== null) 49 + this._parentNode = node.parentNode 50 + this._start = this._end = node 62 51 } 63 52 64 53 _deleteContents() { 65 54 const marker = new Text() 66 - this.parentNode.insertBefore(marker, this._start) 55 + this._parentNode.insertBefore(marker, this._start) 67 56 68 - for (const node of this) this.parentNode.removeChild(node) 57 + for (const node of this) this._parentNode.removeChild(node) 69 58 70 59 this._start = this._end = marker 71 60 } ··· 74 63 _insertNode(node) { 75 64 const end = isDocumentFragment(node) ? node.lastChild : node 76 65 if (end === null) return // empty fragment 77 - this.parentNode.insertBefore(node, this._end.nextSibling) 66 + this._parentNode.insertBefore(node, this._end.nextSibling) 78 67 this._end = end 79 68 80 69 if (isText(this._start) && this._start.data === '') { 81 - assert(this._start.nextSibling) 82 70 const marker = this._start 71 + 72 + DEV: assert(this._start.nextSibling) 83 73 this._start = this._start.nextSibling 84 - this.parentNode.removeChild(marker) 74 + 75 + this._parentNode.removeChild(marker) 85 76 } 86 77 } 87 78 *[Symbol.iterator]() { 88 79 let node = this._start 89 80 for (;;) { 90 - const { nextSibling } = node 81 + const next = node.nextSibling 91 82 yield node 92 83 if (node === this._end) return 93 - assert(nextSibling, 'expected more siblings') 94 - node = nextSibling 84 + assert(next, 'expected more siblings') 85 + node = next 95 86 } 96 87 } 97 88 _extractContents() { 98 89 const marker = new Text() 99 - this.parentNode.insertBefore(marker, this._start) 90 + this._parentNode.insertBefore(marker, this._start) 100 91 101 92 const fragment = document.createDocumentFragment() 102 93 for (const node of this) fragment.appendChild(node) ··· 104 95 this._start = this._end = marker 105 96 return fragment 106 97 } 107 - get length() { 108 - let length = 0 109 - for (const _ of this) length++ 110 - return length 111 - } 112 98 } 113 99 114 100 if (DEV) { ··· 151 137 static appendInto(parent) { 152 138 const marker = new Text() 153 139 parent.appendChild(marker) 154 - return new Root(new Span(parent, marker, marker)) 140 + return new Root(new Span(marker)) 155 141 } 156 142 157 143 /** @param {Node} node */ 158 144 static insertAfter(node) { 159 - assert(node.parentNode, 'expected a parent node') 145 + DEV: assert(node.parentNode, 'expected a parent node') 160 146 const marker = new Text() 161 147 node.parentNode.insertBefore(marker, node.nextSibling) 162 - return new Root(new Span(node.parentNode, marker, marker)) 148 + return new Root(new Span(marker)) 163 149 } 164 150 165 151 /** @param {Node} node */ 166 152 static replace(node) { 167 - assert(node.parentNode, 'expected a parent node') 168 - return new Root(new Span(node.parentNode, node, node)) 153 + return new Root(new Span(node)) 169 154 } 170 155 171 156 render(value) { ··· 409 394 /** @type {Span | undefined} */ 410 395 #span 411 396 create(node, value) { 412 - assert(this.#childIndex !== undefined) 413 - 414 397 if (node instanceof Span) { 415 398 let child = node._start 416 - assert(child, 'expected a start node') 417 399 for (let i = 0; i < this.#childIndex; i++) { 418 - assert(child.nextSibling !== null, 'expected more siblings') 419 - assert(child.nextSibling !== node._end, 'ran out of siblings before the end') 400 + DEV: { 401 + assert(child.nextSibling !== null, 'expected more siblings') 402 + assert(child.nextSibling !== node._end, 'ran out of siblings before the end') 403 + } 420 404 child = child.nextSibling 421 405 } 422 - this.#span = new Span(node.parentNode, child, child) 406 + this.#span = new Span(child) 423 407 } else { 424 408 const child = node.childNodes[this.#childIndex] 425 - this.#span = new Span(node, child, child) 409 + this.#span = new Span(child) 426 410 } 427 - 428 - this.#childIndex = undefined // we only need this once. 429 411 430 412 this.update(value) 431 413 } ··· 461 443 462 444 /** @param {Displayable} value */ 463 445 update(value) { 464 - assert(this.#span) 446 + DEV: assert(this.#span) 465 447 const endsWereEqual = 466 - this.#span.parentNode === this.#parentSpan.parentNode && this.#span._end === this.#parentSpan._end 448 + this.#span._parentNode === this.#parentSpan.parentNode && this.#span._end === this.#parentSpan._end 467 449 468 450 if (isRenderable(value)) { 469 451 this.#switchRenderable(value) ··· 474 456 controllers.set(renderable, { 475 457 _invalidateQueued: null, 476 458 _invalidate: () => { 477 - assert(this.#renderable === renderable, 'could not invalidate an outdated renderable') 459 + DEV: assert(this.#renderable === renderable, 'could not invalidate an outdated renderable') 478 460 this.update(renderable) 479 461 }, 480 462 _unmountCallbacks: null, // will be upgraded to a Set if needed. 481 - _parentNode: this.#span.parentNode, 463 + _parentNode: this.#span._parentNode, 482 464 }) 483 465 484 466 value = renderable.render() ··· 507 489 508 490 // create or update a root for every item. 509 491 let i = 0 510 - let end = this.#span._end 511 492 for (const item of value) { 512 493 // @ts-expect-error -- WeakMap lookups of non-objects always return undefined, which is fine 513 494 const key = keys.get(item) ?? item 514 - let root = (this.#roots[i] ??= Root.insertAfter(end)) 495 + let root = (this.#roots[i] ??= Root.insertAfter(this.#span._end)) 515 496 516 497 if (key !== undefined && root._key !== key) { 517 498 const j = this.#roots.findIndex(r => r._key === key) ··· 531 512 root2._span = tmpSpan 532 513 533 514 // swap the roots 534 - root = this.#roots[i] = root2 535 515 this.#roots[j] = root1 516 + root = this.#roots[i] = root2 536 517 } 537 518 } 538 519 539 520 root.render(item) 540 - end = root._span._end 521 + this.#span._end = root._span._end 541 522 542 523 i++ 543 524 } ··· 550 531 root._span._deleteContents() 551 532 } 552 533 553 - this.#span._end = end 554 534 if (endsWereEqual) this.#parentSpan._end = this.#span._end 555 535 556 536 return ··· 573 553 574 554 if (this.#value != null && value !== null && !(this.#value instanceof Node) && !(value instanceof Node)) { 575 555 // we previously rendered a string, and we're rendering a string again. 576 - assert(this.#span.length === 1, `expected a single node, got ${this.#span.length}`) 577 - assert(this.#span._start instanceof Text) 578 - this.#span._start.data = value.toString() 556 + DEV: assert(this.#span._start === this.#span._end && this.#span._start instanceof Text) 557 + this.#span._start.data = '' + value 579 558 } else { 580 559 this.#span._deleteContents() 581 - if (value !== null) this.#span._insertNode(value instanceof Node ? value : new Text(value.toString())) 560 + if (value !== null) this.#span._insertNode(value instanceof Node ? value : new Text('' + value)) 582 561 } 583 562 } 584 563
-1
src/types.ts
··· 18 18 export type Key = string | number | bigint | boolean | symbol | object | null 19 19 20 20 export declare class Span { 21 - parentNode: ParentNode 22 21 _start: Node | null 23 22 _end: Node | null 24 23 }