Skip to content

Commit

Permalink
fix: add debug info for unserializable values
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed Feb 3, 2025
1 parent b883c8c commit 09c0558
Show file tree
Hide file tree
Showing 22 changed files with 272 additions and 60 deletions.
5 changes: 5 additions & 0 deletions .changeset/happy-cycles-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@marko/runtime-tags": patch
---

Fix issue where streams that had aborted but then finished were swallowing the abort error.
5 changes: 5 additions & 0 deletions .changeset/tame-balloons-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@marko/runtime-tags": patch
---

Add errors with debug info when attempting to serialize a non serializable value.
8 changes: 8 additions & 0 deletions .changeset/thin-vans-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"marko": patch
"@marko/runtime-tags": patch
"@marko/compiler": patch
"@marko/translator-interop-class-tags": patch
---

Ensure Marko 5 renderBodies are serialized properly across the compat layer.
4 changes: 2 additions & 2 deletions .sizes.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
{
"name": "*",
"total": {
"min": 18072,
"brotli": 6619
"min": 18085,
"brotli": 6624
}
},
{
Expand Down
7 changes: 4 additions & 3 deletions .sizes/dom.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// size: 18072 (min) 6619 (brotli)
// size: 18085 (min) 6624 (brotli)
var empty = [],
rest = Symbol();
function attrTag(attrs2) {
Expand Down Expand Up @@ -1535,10 +1535,11 @@ var classIdToBranch = new Map(),
(conditionalOnlyChild = fn(conditionalOnlyChild));
},
queueEffect: queueEffect,
init() {
init(warp10Noop) {
register("$C_s", (branch) => {
classIdToBranch.set(branch.m5c, branch);
});
}),
register("$C_b", warp10Noop);
},
registerRenderer(fn) {
register("$C_r", fn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ exports.p = function (domCompat) {
}

domCompat.registerRenderer(create5to6Renderer);
domCompat.init();
domCompat.init(require("../serialize-noop").___noop);

function renderAndMorph(scope, renderer, renderBody, input) {
const out = defaultCreateOut();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ exports.p = function (htmlCompat) {
tag.renderer;
const renderBody5 = tag.renderBody || tag;

if (!renderer5 && renderBody5) {
htmlCompat.registerRenderBody(renderBody5);
}
return (input, ...args) => {
const out = defaultCreateOut();
let customEvents;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as _$ from "@marko/runtime-tags/debug/html";
const _renderer = /* @__PURE__ */_$.createRenderer((input, _tagVar) => {
const _scope0_id = _$.nextScopeId();
const Foo = {
content: _$.register(/* @__PURE__ */_$.createRenderer(() => {
const _scope1_id = _$.nextScopeId();
const unserializable = {
nested: {
thing: Buffer.from("")
}
};
const test = _$.register(function () {
return unserializable;
}, "__tests__/template.marko_1/test", _scope1_id);
_$.writeEffect(_scope1_id, "__tests__/template.marko_1_test");
_$.debug(_$.writeScope(_scope1_id, {
"unserializable": unserializable,
"test": test
}), "__tests__/template.marko", "1:2", {
"unserializable": "2:10",
"test": "7:10"
});
}), "__tests__/template.marko_1_renderer", _scope0_id)
};
const _dynamicScope = _$.peekNextScope();
_$.dynamicTagInput(_scope0_id, "#text/0", Foo, {});
_$.debug(_$.writeScope(_scope0_id, {
"#text/0!": _$.writeExistingScope(_dynamicScope),
"#text/0(": _$.normalizeDynamicRenderer(Foo)
}), "__tests__/template.marko", 0, {
"Foo": "1:9"
});
});
export default /* @__PURE__ */_$.createTemplate("__tests__/template.marko", _renderer);
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Emit error
```
TypeError: Unable to serialize "unserializable" in __tests__/template.marko:2:10 (reading nested.thing)
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<define/Foo>
<const/unserializable = {
nested: {
thing: Buffer.from("")
}
}/>
<const/test() {
return unserializable;
}/>
<script>
test();
</script>
</>

<Foo/>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const skip_dom = true;
1 change: 1 addition & 0 deletions packages/runtime-tags/src/common/compat-meta.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const prefix = MARKO_DEBUG ? "$compat_" : "$C_";
export const RENDERER_REGISTER_ID = prefix + (MARKO_DEBUG ? "renderer" : "r");
export const SET_SCOPE_REGISTER_ID = prefix + (MARKO_DEBUG ? "setScope" : "s");
export const RENDER_BODY_ID = prefix + (MARKO_DEBUG ? "renderBody" : "b");
5 changes: 4 additions & 1 deletion packages/runtime-tags/src/dom/compat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
RENDER_BODY_ID,
RENDERER_REGISTER_ID,
SET_SCOPE_REGISTER_ID,
} from "../common/compat-meta";
Expand All @@ -15,10 +16,12 @@ const classIdToBranch = new Map<string, BranchScope>();
export const compat = {
patchConditionals,
queueEffect,
init() {
init(warp10Noop: any) {
register(SET_SCOPE_REGISTER_ID, (branch: BranchScope & { m5c: string }) => {
classIdToBranch.set(branch.m5c, branch);
});

register(RENDER_BODY_ID, warp10Noop);
},
registerRenderer(fn: any) {
register(RENDERER_REGISTER_ID, fn);
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-tags/src/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export {
dynamicTagInput,
} from "./html/dynamic-tag";
export { forIn, forInBy, forOf, forOfBy, forTo, forToBy } from "./html/for";
export { debug } from "./html/serializer";
export { createTemplate } from "./html/template";
export {
$global,
Expand Down
19 changes: 14 additions & 5 deletions packages/runtime-tags/src/html/compat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
RENDER_BODY_ID,
RENDERER_REGISTER_ID,
SET_SCOPE_REGISTER_ID,
} from "../common/compat-meta";
Expand Down Expand Up @@ -104,11 +105,16 @@ export const compat = {
asyncOut.error(boundary.signal.reason);
} else {
queueMicrotask(() => {
const { scripts, html } = (head = prepareChunk(head));
asyncOut.script(scripts);
asyncOut.write(html);
asyncOut.end();
head.html = head.scripts = "";
head = prepareChunk(head);
// `prepareChunk` will call the serializer which could
// have new promises, the boundary may no longer be `done`.
if (boundary.done) {
const { scripts, html } = head;
asyncOut.script(scripts);
asyncOut.write(html);
asyncOut.end();
head.html = head.scripts = "";
}
});
}
}
Expand All @@ -121,4 +127,7 @@ export const compat = {
register(id, () => {}),
);
},
registerRenderBody(fn: any) {
register(RENDER_BODY_ID, fn);
},
};
80 changes: 79 additions & 1 deletion packages/runtime-tags/src/html/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ class State {
}

class Reference {
declare debug?: Debug;
public init = "";
public assigns = "";
constructor(
Expand All @@ -289,6 +290,29 @@ class Reference {
}
}

interface Debug {
file: string;
loc: string | 0;
vars: Record<string, string> | undefined;
}
const DEBUG = new WeakMap<WeakKey, Debug>();
export function debug(
obj: WeakKey,
file: string,
loc: string | 0,
vars?: Record<string, string>,
) {
if (MARKO_DEBUG) {
DEBUG.set(obj, {
file,
loc,
vars,
});
}

return obj;
}

export class Serializer {
#state = new State();
get flushed() {
Expand Down Expand Up @@ -420,6 +444,7 @@ function writeProp(
return writeObject(state, val, parent, accessor);

default:
MARKO_DEBUG && throwUnserializable(state, val, parent, accessor);
return false;
}
}
Expand Down Expand Up @@ -459,6 +484,11 @@ function writeReferenceOr(
val,
(ref = new Reference(parent, accessor, state.flush, state.buf.length)),
);

if (MARKO_DEBUG) {
ref.debug = DEBUG.get(val);
}

if (write(state, val, ref)) return true;

state.refs.delete(ref);
Expand Down Expand Up @@ -567,7 +597,8 @@ function writeUnknownSymbol(state: State) {
return true;
}

function writeNever() {
function writeNever(state: State, val: unknown, ref: Reference) {
MARKO_DEBUG && throwUnserializable(state, val, ref);
return false;
}

Expand Down Expand Up @@ -658,6 +689,8 @@ function writeUnknownObject(state: State, val: object, ref: Reference) {
case globalThis.Response:
return writeResponse(state, val as Response, ref);
}

MARKO_DEBUG && throwUnserializable(state, val, ref);
return false;
}

Expand Down Expand Up @@ -1198,6 +1231,51 @@ function writeAsyncCall(
boundary.endAsync();
}

function throwUnserializable(
state: State,
cause: unknown,
ref: Reference | null = null,
accessor: string = "",
) {
if (cause !== undefined && state.boundary?.abort) {
let message = "Unable to serialize";
let access = "";
while (ref?.accessor) {
const debug = ref.parent?.debug;
if (debug) {
const varLoc = debug.vars?.[ref.accessor];
if (varLoc) {
message += ` "${ref.accessor}" in ${debug.file}:${varLoc}`;
} else {
message += ` ${JSON.stringify(ref.accessor)} in ${debug.file}`;
if (debug.loc) {
message += `:${debug.loc}`;
}
}
break;
}
access = toAccess(ref.accessor) + access;
ref = ref.parent;
}

if (accessor) {
access = toAccess(accessor) + access;
}

if (access[0] === ".") {
access = access.slice(1);
}

if (access) {
message += ` (reading ${access})`;
}

const err = new TypeError(message, { cause });
err.stack = undefined;
state.boundary.abort(err);
}
}

function isCircular(
parent: Reference | null,
ref: Reference,
Expand Down
Loading

0 comments on commit 09c0558

Please sign in to comment.