Skip to content

1.20.0 release - #101

Open
ECorreia45 wants to merge 137 commits into
mainfrom
next
Open

1.20.0 release#101
ECorreia45 wants to merge 137 commits into
mainfrom
next

Conversation

@ECorreia45

@ECorreia45 ECorreia45 commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR prepares @beforesemicolon/markup@1.20.0 from the next branch. It replaces the renderer internals with a compiled, persistent-part architecture while preserving the documented html, state, effect, lifecycle, ref, helper, and rendering behavior established in 1.19.x.

The redesign reduces repeated parsing and DOM discovery, makes reactive ownership explicit, improves list reconciliation, and removes renderer code that is no longer required. It also expands the package-owned documentation used by developers and AI tools.

There are no intentional breaking changes. One additive API, unsafeHTML, makes deliberate raw-HTML parsing explicit. The legacy html(string[]) form remains operational for compatibility but is deprecated in favor of the named unsafe boundary.

Renderer architecture

Cached compilation and persistent parts

  • Compiles each tagged-template call site once through a cached native <template> definition.
  • Represents dynamic children, raw text, attributes, events, refs, and attribute spreads as persistent parts.
  • Resolves nodes in cloned templates through precomputed tree-walker indexes instead of repeated selectors or path traversal.
  • Reuses compiled anchors for simple primitive child bindings.
  • Updates same-shape nested templates in place when their bindings can be safely rebound.
  • Initializes dynamic custom-element properties before connection.

DOM and table parsing

  • Preserves interpolation positions in tables, table sections, rows, cells, captions, columns, and column groups.
  • Retains the established behavior for standalone table rows containing direct text.
  • Preserves raw <script> and <style> interpolation behavior.
  • Keeps dynamic tag names escaped rather than interpreting them as elements.
  • Avoids unnecessary live childNodes traversal during insertion.

Lists and reconciliation

  • Retains DOM identity for keyed and automatically identified repeat rows where rebinding is valid.
  • Safely replaces event handlers while retaining row DOM.
  • Returns immediately for unchanged child sequences.
  • Mounts initial list content directly when lifecycle staging is unnecessary.
  • Lazily allocates renderer bookkeeping used only by templates that need it.
  • Disposes nested template trees in memory and removes the owning DOM range once.

Reactivity, lifecycle, and refs

  • Renderer-owned effects are isolated from surrounding user effects and disposed by their owning template.
  • Conditional nested effects are reconciled by execution slot, with stale effects removed when their branch disappears.
  • State reads inside lifecycle callbacks and custom-element connection callbacks do not leak into an active render dependency set.
  • Effects created by connected custom elements remain active, including elements connected inside a live shadow root.
  • Retained nested-template effects survive parent reevaluation.
  • Mount, move, update, cleanup, nested unmount, ref reset, and remount behavior remain covered.
  • Parent template refs now reflect currently rendered dynamic descendants before dependent user effects run.

HTML safety boundary

Normal html tagged-template interpolations remain outside the browser HTML parser. Runtime strings are applied as values, including in standalone table-row content and attributes.

This PR adds unsafeHTML(source) for the deliberate parsing of trusted or independently sanitized markup. User-controlled content must remain in ordinary html interpolations and must not be passed to unsafeHTML.

The older manually constructed array form, such as html([source]), remains supported at runtime for compatibility and is now deprecated. Documentation and regression coverage make the distinction explicit and reviewable.

Package and build cleanup

  • Removes the previous ReactiveNode, normalization, dynamic-raw parsing, render-content, and syncNodes implementation paths.
  • Removes the obsolete @beforesemicolon/html-parser runtime dependency.
  • Uses @beforesemicolon/builder 2.0.1 with minification-oriented build options.
  • Produces ESM, CommonJS, declarations, and a direct browser bundle.
  • Marks the CommonJS output boundary explicitly so Node can load the package through \u0060require()\u0060.
  • Verifies both package-root \u0060require()\u0060 and ESM \u0060import()\u0060 during the build and against the packed tarball.
  • Keeps the established package root and dist publishing layout.
  • Orders the types export condition correctly.
  • Keeps internal renderer/effect ownership helpers out of the root public barrel.

Current package output:

Artifact Size
Browser client 24.7 kB raw
Browser client, gzip 8.66 kB
npm tarball 35.1 kB packed
npm tarball contents 121.7 kB unpacked / 87 files

Documentation and AI context

  • Expands the package-owned llms.txt with the API contract, behavioral rules, examples, and ecosystem boundaries.
  • Enables generation of the complete resolved llms-full.txt documentation artifact.
  • Connects Markup documentation to the Web Component, Router, and Intl package documentation.
  • Documents reactive ref timing in the effects and refs guides.
  • Updates browser bundle-size claims to match the built artifact.
  • Documents safe interpolation and the explicit unsafeHTML boundary in the README, templating guide, localized content, and AI context.

Compatibility coverage

Regression coverage includes:

  • same-shape nested template rebinding;
  • keyed and automatic repeat identity;
  • event handler replacement on retained rows;
  • adjacent dynamic cells and standalone table contexts;
  • custom-element properties and connection timing;
  • lifecycle state reads remaining untracked;
  • custom-element effects in light DOM and connected shadow roots;
  • detached element and shadow-root move behavior;
  • conditional nested effect cleanup;
  • bulk nested teardown and remount;
  • dynamic descendant refs and effect ordering;
  • marker-looking static text and attributes remaining intact;
  • reactive interpolation inside native \u0060.content\u0060;
  • union-typed \u0060TemplateStringsArray | string[]\u0060 callers and emitted deprecation documentation;
  • untrusted table-row content and attributes remaining outside HTML parsing;
  • deliberate trusted markup through unsafeHTML.
  • Performance

    The renderer was compared with 1.19.1 using 1,000 representative rows on an Apple M3 with Node 24.14.0. Results are medians from seven warmed, alternating paired runs with explicit garbage collection; absolute timings vary by machine.

    Scenario 1.19.1 1.20.0 Change
    Minimal mount + unmount 86.44 ms 45.43 ms 47.4% faster
    Moderate mount + unmount 177.34 ms 98.40 ms 44.5% faster
    Filesystem-like mount + unmount 391.25 ms 324.36 ms 17.1% faster
    Immutable list update, automatic identity 557.13 ms 5.41 ms about 103x faster
    Immutable list update, keyed 548.55 ms 5.28 ms about 104x faster

    The deepest filesystem-like standalone teardown remains slower than 1.19.1, but its mount improvement is larger than that cost, leaving the complete cycle faster. The repository benchmark suite completes successfully across repeat bookkeeping, DOM mount/update, and reactive lifecycle scenarios.

    Verification

    Verification performed on the final implementation:

    • npm run build
      • ESLint and Prettier passed.
      • 19 suites and 199 tests passed with no failures.
      • TypeScript declarations, ESM, CommonJS, browser bundle, and documentation built successfully.
      • Public JSDoc and the deprecated array-form annotation are retained in emitted declarations.
    • npm pack --dry-run --json
      • Produced the expected 87-file package manifest.
    • npm run benchmark
      • Repeat-cache, DOM mount/update, and reactive-list benchmark suites completed successfully.
    • Focused security regressions
      • Untrusted standalone-row content is rendered as text.
      • Untrusted standalone-row attribute values cannot create event attributes.
      • Explicit trusted raw HTML remains supported through unsafeHTML.

    Review notes

    The main review areas are the compiled-part renderer in src/html.ts, effect ownership in src/effect-context.ts and src/state.ts, repeat rebinding, table-context parsing, and the compatibility regressions consolidated in the existing test suites.

ECorreia45 and others added 30 commits August 20, 2026 16:21
@codesandbox

codesandbox Bot commented Aug 23, 2026

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

Comment thread src/html.ts
Comment thread src/html.ts
@ECorreia45 ECorreia45 changed the title Next 1.20.1 release Aug 23, 2026
@ECorreia45 ECorreia45 changed the title 1.20.1 release 1.20.0 release Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants