-
Notifications
You must be signed in to change notification settings - Fork 654
Description
Description
It is easy to make the mistake of formatting an object in a template placeholder, instead of a property on it.
<div>
<p>good morning ${input.user}</p>
// ^^^^
// oh no it's supposed to be "user.name", build succeeds
</div>
I haven't configured TypeScript with Marko (especially within neovim) but i would guess that this doesn't error there since that syntax in a template string passes type checking (which is true since all objects can coerce to strings, it just isn't useful in this context)
I propose that the Marko language tools make this a type-checking error, and additionally a runtime error:
Unexpected object in template placeholder: '{ name: "clover" }'. To emit a literal '[object Object]', use ${String(value)}
Possible Implementation & Open Questions
One stretch of this proposal is to also ban null
and undefined
. While I have not actively run into this issue, I feel like it might be possible to hit it and accidentally emit a blank slot instead of properly handling the null case. It is a familiar mechanic of React/JSX, but the case of { condition ? <div /> : null}
is already solved more beautifully with <if=condition><div/></>
. I'm curious about real world use cases of null
in template content, I think I'm missing something extremely obvious that prevents this from being a reasonable addition.
As for just plain objects being stringified, I have this implemented in the clover sitegen tool as an overlay to the Marko HTML runtime, since my adoption of the language is still light. So this is only part of the way there.
export function escapeXML(input: unknown) {
// The rationale of this check is that the default toString method
// creating `[object Object]` is universally useless to any end user.
if (
(typeof input === "object" && input &&
// only block this if it's the default `toString`
input.toString === Object.prototype.toString)
) {
throw new Error(
`Unexpected object in template placeholder: '` +
util.inspect({ name: "clover" }) + "'. " +
`To emit a literal '[object Object]', use \${String(value)}`,
);
}
return marko.escapeXML(input);
}
Is this something you're interested in working on?
Yes