From 2cfef7b89ea6959aed518b5da71d507d286071a6 Mon Sep 17 00:00:00 2001 From: Luke LaValva Date: Thu, 2 Feb 2023 19:30:52 -0500 Subject: [PATCH] fix: replace empty text nodes with zero width joiners (#140) --- .sizes.json | 68 +++++++++---------- packages/runtime/src/dom/dom.ts | 8 +-- packages/runtime/src/dom/index.ts | 1 - packages/runtime/src/dom/signals.ts | 22 +++++- packages/runtime/src/html/content.ts | 7 +- .../__snapshots__/csr-sanitized.expected.md | 14 ++++ .../__snapshots__/csr.expected.md | 24 +++++++ .../__snapshots__/dom.expected/template.js | 11 +++ .../__snapshots__/html.expected/template.js | 9 +++ .../hydrate-sanitized.expected.md | 14 ++++ .../__snapshots__/hydrate.expected.md | 42 ++++++++++++ .../__snapshots__/ssr-sanitized.expected.md | 6 ++ .../__snapshots__/ssr.expected.md | 31 +++++++++ .../let-undefined-until-dom/template.marko | 6 ++ .../fixtures/let-undefined-until-dom/test.ts | 3 + .../__snapshots__/csr-sanitized.expected.md | 2 +- .../__snapshots__/csr.expected.md | 4 +- .../hydrate-sanitized.expected.md | 2 +- .../__snapshots__/hydrate.expected.md | 4 +- .../__snapshots__/ssr-sanitized.expected.md | 2 +- .../__snapshots__/ssr.expected.md | 4 +- packages/translator/src/core/let.ts | 24 ++----- 22 files changed, 233 insertions(+), 75 deletions(-) create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/csr-sanitized.expected.md create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/csr.expected.md create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/dom.expected/template.js create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/html.expected/template.js create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/hydrate-sanitized.expected.md create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/hydrate.expected.md create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/ssr-sanitized.expected.md create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/ssr.expected.md create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/template.marko create mode 100644 packages/translator/src/__tests__/fixtures/let-undefined-until-dom/test.ts diff --git a/.sizes.json b/.sizes.json index fe7ee0c0b..4e81460db 100644 --- a/.sizes.json +++ b/.sizes.json @@ -7,63 +7,63 @@ { "name": "*", "total": { - "min": 12770, - "gzip": 5389, - "brotli": 4906 + "min": 12741, + "gzip": 5401, + "brotli": 4909 } }, { "name": "counter", "user": { "min": 369, - "gzip": 282, - "brotli": 243 + "gzip": 285, + "brotli": 272 }, "runtime": { - "min": 3183, - "gzip": 1505, - "brotli": 1346 + "min": 3214, + "gzip": 1528, + "brotli": 1372 }, "total": { - "min": 3552, - "gzip": 1787, - "brotli": 1589 + "min": 3583, + "gzip": 1813, + "brotli": 1644 } }, { "name": "counter 💧", "user": { "min": 207, - "gzip": 182, + "gzip": 181, "brotli": 153 }, "runtime": { - "min": 2691, - "gzip": 1354, - "brotli": 1210 + "min": 2722, + "gzip": 1382, + "brotli": 1229 }, "total": { - "min": 2898, - "gzip": 1536, - "brotli": 1363 + "min": 2929, + "gzip": 1563, + "brotli": 1382 } }, { "name": "comments", "user": { - "min": 1158, - "gzip": 708, - "brotli": 638 + "min": 1163, + "gzip": 710, + "brotli": 636 }, "runtime": { - "min": 7257, - "gzip": 3277, - "brotli": 2973 + "min": 7284, + "gzip": 3291, + "brotli": 2990 }, "total": { - "min": 8415, - "gzip": 3985, - "brotli": 3611 + "min": 8447, + "gzip": 4001, + "brotli": 3626 } }, { @@ -71,17 +71,17 @@ "user": { "min": 949, "gzip": 592, - "brotli": 543 + "brotli": 545 }, "runtime": { - "min": 8306, - "gzip": 3738, - "brotli": 3389 + "min": 8333, + "gzip": 3753, + "brotli": 3397 }, "total": { - "min": 9255, - "gzip": 4330, - "brotli": 3932 + "min": 9282, + "gzip": 4345, + "brotli": 3942 } } ] diff --git a/packages/runtime/src/dom/dom.ts b/packages/runtime/src/dom/dom.ts index 3c1ffae4d..be11eb598 100644 --- a/packages/runtime/src/dom/dom.ts +++ b/packages/runtime/src/dom/dom.ts @@ -114,16 +114,12 @@ export function props(scope: Scope, nodeIndex: number, index: number) { scope[index + "-"] = nextProps; } -export function innerHTML(element: Element, value: string) { - element.innerHTML = normalizeString(value); -} - function normalizeAttrValue(value: unknown) { - return value == null || value === false ? undefined : value + ""; + return value || value === 0 ? value + "" : undefined; } function normalizeString(value: unknown) { - return value == null ? "" : value + ""; + return value || value === 0 ? value + "" : "\u200d"; } type EffectFn = (scope: S) => void | (() => void); diff --git a/packages/runtime/src/dom/index.ts b/packages/runtime/src/dom/index.ts index fff0490d5..3d06cdf43 100644 --- a/packages/runtime/src/dom/index.ts +++ b/packages/runtime/src/dom/index.ts @@ -11,7 +11,6 @@ export { export { data, html, - innerHTML, attr, attrs, classAttr, diff --git a/packages/runtime/src/dom/signals.ts b/packages/runtime/src/dom/signals.ts index 48f0de9b7..965d2fded 100644 --- a/packages/runtime/src/dom/signals.ts +++ b/packages/runtime/src/dom/signals.ts @@ -32,10 +32,11 @@ function applyValue( value: V, valueAccessor: Accessor, subscribers: Signal[], + isCreate: boolean, action?: (scope: S, value: V) => void ) { const stale = write(scope, valueAccessor as number, value); - if (stale) { + if (stale || isCreate) { action?.(scope, value); } notifySubscribers(scope, stale as any as boolean, subscribers); @@ -66,8 +67,16 @@ export function source( } }, ___apply(scope, data) { + const isCreate = scope[markAccessor] === undefined; scope[markAccessor] = 1; - applyValue(scope as S, data as V, valueAccessor, subscribers, action); + applyValue( + scope as S, + data as V, + valueAccessor, + subscribers, + isCreate, + action + ); scope[markAccessor] = 0; }, }; @@ -201,7 +210,14 @@ export function derivation( subscribers, defaultMark, (scope: S) => { - applyValue(scope, compute(scope), valueAccessor, subscribers, action); + applyValue( + scope, + compute(scope), + valueAccessor, + subscribers, + false, // TODO: This should be true on creation + action + ); } ); if (MARKO_DEBUG) { diff --git a/packages/runtime/src/html/content.ts b/packages/runtime/src/html/content.ts index ac9568538..48ea0cd46 100644 --- a/packages/runtime/src/html/content.ts +++ b/packages/runtime/src/html/content.ts @@ -1,5 +1,5 @@ export function toString(val: unknown) { - return val == null ? "" : val + ""; + return val || val === 0 ? val + "" : ""; } export const escapeXML = escapeIfNeeded((val: string) => { @@ -82,14 +82,15 @@ export function escapeAttrValue(val: string) { function escapeIfNeeded(escape: (val: string) => string) { return (val: unknown) => { - if (val == null) { - return ""; + if (!val && val !== 0) { + return "‍"; } switch (typeof val) { case "string": return escape(val); case "boolean": + return "true"; case "number": return val + ""; default: diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/csr-sanitized.expected.md b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/csr-sanitized.expected.md new file mode 100644 index 000000000..a1604f9ef --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/csr-sanitized.expected.md @@ -0,0 +1,14 @@ +# Render {} +```html +
+ ‍ +
+``` + + +# Render "ASYNC" +```html +
+ Client Only +
+``` \ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/csr.expected.md b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/csr.expected.md new file mode 100644 index 000000000..3ee1e2c02 --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/csr.expected.md @@ -0,0 +1,24 @@ +# Render {} +```html +
+ ‍ +
+``` + +# Mutations +``` +inserted div0 +``` + + +# Render "ASYNC" +```html +
+ Client Only +
+``` + +# Mutations +``` +div0/#text0: "‍" => "Client Only" +``` \ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/dom.expected/template.js b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/dom.expected/template.js new file mode 100644 index 000000000..b121106c6 --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/dom.expected/template.js @@ -0,0 +1,11 @@ +import { setSource as _setSource, queueSource as _queueSource, data as _data, source as _source, register as _register, queueHydrate as _queueHydrate, createRenderFn as _createRenderFn } from "@marko/runtime-fluurt/src/dom"; +const _x = /* @__PURE__ */_source("x", [], (_scope, x) => _data(_scope["#text/0"], x)); +const _hydrate_setup = _register("packages/translator/src/__tests__/fixtures/let-undefined-until-dom/template.marko_0", _scope => _queueSource(_scope, _x, "Client Only")); +const _setup = _scope => { + _setSource(_scope, _x, undefined); + _queueHydrate(_scope, _hydrate_setup); +}; +export const template = "
"; +export const walks = /* next(1), get, out(1) */"D l"; +export const setup = _setup; +export default /* @__PURE__ */_createRenderFn(template, walks, setup, null, null, "packages/translator/src/__tests__/fixtures/let-undefined-until-dom/template.marko"); \ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/html.expected/template.js b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/html.expected/template.js new file mode 100644 index 000000000..60264fd11 --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/html.expected/template.js @@ -0,0 +1,9 @@ +import { escapeXML as _escapeXML, markHydrateNode as _markHydrateNode, write as _write, nextScopeId as _nextScopeId, writeHydrateCall as _writeHydrateCall, register as _register, createRenderer as _createRenderer } from "@marko/runtime-fluurt/src/html"; +const _renderer = _register((input, _tagVar, _scope0_) => { + const _scope0_id = _nextScopeId(); + const x = undefined; + _write(`
${_escapeXML(x)}${_markHydrateNode(_scope0_id, "#text/0")}
`); + _writeHydrateCall(_scope0_id, "packages/translator/src/__tests__/fixtures/let-undefined-until-dom/template.marko_0"); +}, "packages/translator/src/__tests__/fixtures/let-undefined-until-dom/template.marko"); +export default _renderer; +export const render = /* @__PURE__ */_createRenderer(_renderer); \ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/hydrate-sanitized.expected.md b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/hydrate-sanitized.expected.md new file mode 100644 index 000000000..a1604f9ef --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/hydrate-sanitized.expected.md @@ -0,0 +1,14 @@ +# Render {} +```html +
+ ‍ +
+``` + + +# Render "ASYNC" +```html +
+ Client Only +
+``` \ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/hydrate.expected.md b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/hydrate.expected.md new file mode 100644 index 000000000..8ba96e9e5 --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/hydrate.expected.md @@ -0,0 +1,42 @@ +# Render {} +```html + + + +
+ ‍ + +
+ + + +``` + +# Mutations +``` + +``` + + +# Render "ASYNC" +```html + + + +
+ Client Only + +
+ + + +``` + +# Mutations +``` +#document/html0/body1/div0/#text0: "‍" => "Client Only" +``` \ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/ssr-sanitized.expected.md b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/ssr-sanitized.expected.md new file mode 100644 index 000000000..5f7abac2c --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/ssr-sanitized.expected.md @@ -0,0 +1,6 @@ +# Render "End" +```html +
+ ‍ +
+``` \ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/ssr.expected.md b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/ssr.expected.md new file mode 100644 index 000000000..99cf8d2f5 --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/__snapshots__/ssr.expected.md @@ -0,0 +1,31 @@ +# Write +
+ + +# Render "End" +```html + + + +
+ ‍ + +
+ + + +``` + +# Mutations +``` +inserted #document/html0 +inserted #document/html0/head0 +inserted #document/html0/body1 +inserted #document/html0/body1/div0 +inserted #document/html0/body1/div0/#text0 +inserted #document/html0/body1/div0/#comment1 +inserted #document/html0/body1/script1 +inserted #document/html0/body1/script1/#text0 +``` \ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/template.marko b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/template.marko new file mode 100644 index 000000000..f55ff8b5c --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/template.marko @@ -0,0 +1,6 @@ + + + +
${x}
\ No newline at end of file diff --git a/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/test.ts b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/test.ts new file mode 100644 index 000000000..2e1746dab --- /dev/null +++ b/packages/translator/src/__tests__/fixtures/let-undefined-until-dom/test.ts @@ -0,0 +1,3 @@ +import { wait } from "../../utils/resolve"; + +export const steps = [{}, wait(1)]; diff --git a/packages/translator/src/__tests__/fixtures/lifecycle-tag-assignment/__snapshots__/csr-sanitized.expected.md b/packages/translator/src/__tests__/fixtures/lifecycle-tag-assignment/__snapshots__/csr-sanitized.expected.md index a5663e178..06d016f4b 100644 --- a/packages/translator/src/__tests__/fixtures/lifecycle-tag-assignment/__snapshots__/csr-sanitized.expected.md +++ b/packages/translator/src/__tests__/fixtures/lifecycle-tag-assignment/__snapshots__/csr-sanitized.expected.md @@ -5,7 +5,7 @@ 0 - , was=false + , was=‍ +
x=0, was=
# Render "End" @@ -15,7 +15,7 @@ , was= - false + ‍