mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
fix: native translate improvements and taglib cleanup
This commit is contained in:
parent
17c4ba7edf
commit
fdc46fb3e7
5
.changeset/chilly-plants-attack.md
Normal file
5
.changeset/chilly-plants-attack.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"marko": patch
|
||||
---
|
||||
|
||||
Move body tag transform logic into translate.
|
||||
5
.changeset/fancy-mammals-punch.md
Normal file
5
.changeset/fancy-mammals-punch.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@marko/compiler": patch
|
||||
---
|
||||
|
||||
Default title tag to be text only for Marko 6.
|
||||
5
.changeset/funky-clowns-shop.md
Normal file
5
.changeset/funky-clowns-shop.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@marko/runtime-tags": patch
|
||||
---
|
||||
|
||||
Move title tag logic into native tag translator.
|
||||
7
.changeset/lemon-carpets-hunt.md
Normal file
7
.changeset/lemon-carpets-hunt.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
"marko": patch
|
||||
"@marko/runtime-tags": patch
|
||||
"@marko/compiler": patch
|
||||
---
|
||||
|
||||
Normalize taglib ids to be consistent with register ids and across Marko 5/6.
|
||||
5
.changeset/stale-buttons-lead.md
Normal file
5
.changeset/stale-buttons-lead.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"marko": patch
|
||||
---
|
||||
|
||||
Merge migrate taglib into core taglib.
|
||||
@ -19,9 +19,9 @@ const registeredTaglibs = [];
|
||||
const loadedTranslatorsTaglibs = new Map();
|
||||
let lookupCache = Object.create(null);
|
||||
|
||||
register("marko/html", markoHTMLTaglib);
|
||||
register("marko/svg", markoSVGTaglib);
|
||||
register("marko/math", markoMathTaglib);
|
||||
register(markoHTMLTaglib["taglib-id"], markoHTMLTaglib);
|
||||
register(markoSVGTaglib["taglib-id"], markoSVGTaglib);
|
||||
register(markoMathTaglib["taglib-id"], markoMathTaglib);
|
||||
|
||||
export function buildLookup(dirname, requestedTranslator, onError) {
|
||||
const translator = tryLoadTranslator(requestedTranslator);
|
||||
|
||||
@ -873,7 +873,10 @@
|
||||
},
|
||||
"<title>": {
|
||||
"html": true,
|
||||
"attribute-groups": ["html-attributes"]
|
||||
"attribute-groups": ["html-attributes"],
|
||||
"parse-options": {
|
||||
"text": true
|
||||
}
|
||||
},
|
||||
"<tr>": {
|
||||
"html": true,
|
||||
|
||||
@ -57,7 +57,29 @@ export default {
|
||||
moveIgnoredAttrTags(path);
|
||||
}
|
||||
|
||||
if (isDynamicTag(path) || !(isMacroTag(path) || isNativeTag(path))) {
|
||||
if (isNativeTag(path)) {
|
||||
if (tagDef && tagDef.name === "body") {
|
||||
path
|
||||
.get("body")
|
||||
.pushContainer("body", [
|
||||
t.markoTag(
|
||||
t.stringLiteral("init-components"),
|
||||
[],
|
||||
t.markoTagBody(),
|
||||
),
|
||||
t.markoTag(
|
||||
t.stringLiteral("await-reorderer"),
|
||||
[],
|
||||
t.markoTagBody(),
|
||||
),
|
||||
t.markoTag(
|
||||
t.stringLiteral("_preferred-script-location"),
|
||||
[],
|
||||
t.markoTagBody(),
|
||||
),
|
||||
]);
|
||||
}
|
||||
} else if (!isMacroTag(path)) {
|
||||
analyzeAttributeTags(path);
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import * as translateElseIf from "./conditional/translate-else-if";
|
||||
import * as translateIf from "./conditional/translate-if";
|
||||
import * as parseMacro from "./macro/parse";
|
||||
import * as translateMacro from "./macro/translate";
|
||||
import migrate from "./migrate";
|
||||
import * as parseClass from "./parse-class";
|
||||
import * as parseExport from "./parse-export";
|
||||
import * as parseImport from "./parse-import";
|
||||
@ -18,7 +19,8 @@ import * as translateServerOnly from "./translate-server-only";
|
||||
import * as translateWhile from "./translate-while";
|
||||
|
||||
export default {
|
||||
"taglib-id": "marko-default-core",
|
||||
taglibId: "marko-core",
|
||||
migrate,
|
||||
"<import>": {
|
||||
"node-factory": parseImport,
|
||||
"parse-options": {
|
||||
@ -285,9 +287,6 @@ export default {
|
||||
"code-generator": translateServerOnly,
|
||||
renderer: "marko/src/core-tags/components/preferred-script-location-tag.js",
|
||||
},
|
||||
"<body>": {
|
||||
transformer: transformBody,
|
||||
},
|
||||
"<await>": {
|
||||
renderer: "marko/src/core-tags/core/await/renderer.js",
|
||||
types: "marko/src/core-tags/core/await/index.d.marko",
|
||||
|
||||
@ -14,5 +14,5 @@ export default function (path) {
|
||||
body = body[0].body;
|
||||
}
|
||||
|
||||
path.replaceWith(t.MarkoScriptlet(body, true));
|
||||
path.replaceWith(t.markoScriptlet(body, true));
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import coreTaglib from "./core";
|
||||
import migrateTaglib from "./migrate";
|
||||
export const optionalTaglibs = ["marko-widgets", "@marko/compat-v4"];
|
||||
export default [
|
||||
["marko/core", coreTaglib],
|
||||
["marko/migrate", migrateTaglib],
|
||||
["marko-html-title", { "<title>": { parseOptions: { text: false } } }], // In Marko 5 the title tag parses as html even though only text is really allowed.
|
||||
[coreTaglib.taglibId, coreTaglib],
|
||||
];
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import * as migrateAllTemplates from "./all-templates";
|
||||
export default {
|
||||
"taglib-id": "marko-default-migrate",
|
||||
migrator: migrateAllTemplates,
|
||||
};
|
||||
@ -3,18 +3,13 @@ const _marko_componentType = "M__dLOJ",
|
||||
_marko_template = _t(_marko_componentType);
|
||||
export default _marko_template;
|
||||
import _marko_constElement from "marko/dist/runtime/vdom/helpers/const-element.js";
|
||||
const _marko_node = _marko_constElement("head", null, 1).e("title", null, 1).t("Title of the document");
|
||||
const _marko_node = _marko_constElement("html", null, 2).e("head", null, 1).e("title", null, 1).t("Title of the document").e("body", null, 1).t("The content of the document......");
|
||||
import _marko_renderer from "marko/dist/runtime/components/renderer.js";
|
||||
import { r as _marko_registerComponent } from "marko/dist/runtime/components/registry.js";
|
||||
_marko_registerComponent(_marko_componentType, () => _marko_template);
|
||||
const _marko_component = {};
|
||||
_marko_template._ = _marko_renderer(function (input, out, _componentDef, _component, state, $global) {
|
||||
out.be("html", null, "0", _component, null, 0);
|
||||
out.n(_marko_node, _component);
|
||||
out.be("body", null, "3", _component, null, 0);
|
||||
out.t("The content of the document......", _component);
|
||||
out.ee();
|
||||
out.ee();
|
||||
}, {
|
||||
t: _marko_componentType,
|
||||
i: true
|
||||
|
||||
@ -22,7 +22,6 @@ import ScriptTag from "./script";
|
||||
import ServerTag from "./server";
|
||||
import StaticTag from "./static";
|
||||
import StyleTag from "./style";
|
||||
import TitleTag from "./title";
|
||||
import TryTag from "./try";
|
||||
|
||||
export default {
|
||||
@ -52,6 +51,5 @@ export default {
|
||||
"<server>": ServerTag,
|
||||
"<static>": StaticTag,
|
||||
"<style>": StyleTag,
|
||||
"<title>": TitleTag,
|
||||
"<try>": TryTag,
|
||||
};
|
||||
|
||||
@ -1,421 +0,0 @@
|
||||
// TODO: this shares a bunch of logic with the native tag translator.
|
||||
// we should probably attempt to share that logic where possible.
|
||||
// Also need to ensure it stays in sync.
|
||||
|
||||
import { types as t } from "@marko/compiler";
|
||||
import {
|
||||
assertNoArgs,
|
||||
assertNoParams,
|
||||
getProgram,
|
||||
type Tag,
|
||||
} from "@marko/compiler/babel-utils";
|
||||
|
||||
import { getEventHandlerName, isEventHandler } from "../../common/helpers";
|
||||
import { WalkCode } from "../../common/types";
|
||||
import { bodyToTextLiteral } from "../util/body-to-text-literal";
|
||||
import evaluate from "../util/evaluate";
|
||||
import { isOutputHTML } from "../util/marko-config";
|
||||
import { type Opt, push } from "../util/optional";
|
||||
import {
|
||||
type Binding,
|
||||
BindingType,
|
||||
createBinding,
|
||||
dropReferences,
|
||||
getScopeAccessorLiteral,
|
||||
mergeReferences,
|
||||
trackDomVarReferences,
|
||||
} from "../util/references";
|
||||
import { callRuntime, getHTMLRuntime } from "../util/runtime";
|
||||
import { createScopeReadExpression } from "../util/scope-read";
|
||||
import {
|
||||
getOrCreateSection,
|
||||
getScopeIdIdentifier,
|
||||
getSection,
|
||||
} from "../util/sections";
|
||||
import {
|
||||
addSerializeExpr,
|
||||
getSerializeReason,
|
||||
} from "../util/serialize-reasons";
|
||||
import { addHTMLEffectCall, addStatement } from "../util/signals";
|
||||
import { toObjectProperty } from "../util/to-property-name";
|
||||
import { propsToExpression } from "../util/translate-attrs";
|
||||
import { translateDomVar } from "../util/translate-var";
|
||||
import * as walks from "../util/walks";
|
||||
import * as writer from "../util/writer";
|
||||
import { scopeIdentifier } from "../visitors/program";
|
||||
|
||||
const kNodeBinding = Symbol("title tag node binding");
|
||||
|
||||
declare module "@marko/compiler/dist/types" {
|
||||
export interface NodeExtra {
|
||||
[kNodeBinding]?: Binding;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
analyze(tag) {
|
||||
assertNoArgs(tag);
|
||||
assertNoParams(tag);
|
||||
|
||||
const { node } = tag;
|
||||
if (node.var && !t.isIdentifier(node.var)) {
|
||||
throw tag
|
||||
.get("var")
|
||||
.buildCodeFrameError(
|
||||
"Tag variables on native elements cannot be destructured.",
|
||||
);
|
||||
}
|
||||
|
||||
const seen: Record<string, t.MarkoAttribute> = {};
|
||||
const { attributes } = tag.node;
|
||||
let spreadReferenceNodes: t.Node[] | undefined;
|
||||
let exprExtras: Opt<t.NodeExtra>;
|
||||
let hasEventHandlers = false;
|
||||
let hasDynamicAttributes = false;
|
||||
|
||||
for (let i = attributes.length; i--; ) {
|
||||
const attr = attributes[i];
|
||||
const valueExtra = (attr.value.extra ??= {});
|
||||
|
||||
if (t.isMarkoAttribute(attr)) {
|
||||
if (seen[attr.name]) {
|
||||
// drop references for duplicated attributes.
|
||||
dropReferences(attr.value);
|
||||
continue;
|
||||
}
|
||||
|
||||
seen[attr.name] = attr;
|
||||
|
||||
if (isEventHandler(attr.name)) {
|
||||
valueExtra.isEffect = true;
|
||||
hasEventHandlers = true;
|
||||
} else if (!evaluate(attr.value).confident) {
|
||||
hasDynamicAttributes = true;
|
||||
}
|
||||
} else if (t.isMarkoSpreadAttribute(attr)) {
|
||||
valueExtra.isEffect = true;
|
||||
hasEventHandlers = true;
|
||||
hasDynamicAttributes = true;
|
||||
}
|
||||
|
||||
if (spreadReferenceNodes) {
|
||||
spreadReferenceNodes.push(attr.value);
|
||||
} else if (t.isMarkoSpreadAttribute(attr)) {
|
||||
spreadReferenceNodes = [attr.value];
|
||||
} else {
|
||||
exprExtras = push(exprExtras, valueExtra);
|
||||
}
|
||||
}
|
||||
|
||||
const bodyPlaceholderNodes: t.Node[] = [];
|
||||
let hasBodyPlaceholders = false;
|
||||
for (const child of tag.node.body.body) {
|
||||
if (t.isMarkoPlaceholder(child)) {
|
||||
bodyPlaceholderNodes.push(child.value);
|
||||
hasBodyPlaceholders = true;
|
||||
} else if (!t.isMarkoText(child)) {
|
||||
throw tag.hub.buildError(
|
||||
child,
|
||||
"Invalid child. Only text is allowed inside a `<title>`.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
node.var ||
|
||||
hasEventHandlers ||
|
||||
hasDynamicAttributes ||
|
||||
hasBodyPlaceholders
|
||||
) {
|
||||
const tagExtra = (node.extra ??= {});
|
||||
const tagSection = getOrCreateSection(tag);
|
||||
const nodeBinding = (tagExtra[kNodeBinding] = createBinding(
|
||||
"#title",
|
||||
BindingType.dom,
|
||||
tagSection,
|
||||
));
|
||||
if (hasEventHandlers) {
|
||||
getProgram().node.extra.isInteractive = true;
|
||||
}
|
||||
|
||||
if (spreadReferenceNodes) {
|
||||
mergeReferences(tagSection, tag.node, spreadReferenceNodes);
|
||||
}
|
||||
|
||||
if (hasBodyPlaceholders) {
|
||||
exprExtras = push(
|
||||
exprExtras,
|
||||
bodyPlaceholderNodes.length === 1
|
||||
? (bodyPlaceholderNodes[0].extra ??= {})
|
||||
: mergeReferences(
|
||||
tagSection,
|
||||
bodyPlaceholderNodes[0],
|
||||
bodyPlaceholderNodes.slice(1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
trackDomVarReferences(tag, nodeBinding);
|
||||
|
||||
addSerializeExpr(
|
||||
tagSection,
|
||||
!!(node.var || hasEventHandlers),
|
||||
nodeBinding,
|
||||
);
|
||||
|
||||
addSerializeExpr(tagSection, push(exprExtras, tagExtra), nodeBinding);
|
||||
}
|
||||
},
|
||||
translate: {
|
||||
enter(tag) {
|
||||
const tagExtra = tag.node.extra!;
|
||||
const nodeBinding = tagExtra[kNodeBinding];
|
||||
const isHTML = isOutputHTML();
|
||||
const write = writer.writeTo(tag);
|
||||
const tagSection = getSection(tag);
|
||||
|
||||
if (isHTML) {
|
||||
translateDomVar(tag, nodeBinding);
|
||||
}
|
||||
|
||||
if (nodeBinding) {
|
||||
walks.visit(tag, WalkCode.Get);
|
||||
}
|
||||
|
||||
write`<title`;
|
||||
|
||||
const usedAttrs = getUsedAttrs(tag.node);
|
||||
const { staticAttrs, skipExpression, spreadExpression } = usedAttrs;
|
||||
|
||||
for (const attr of staticAttrs) {
|
||||
const { name, value } = attr;
|
||||
const { confident, computed } = value.extra || {};
|
||||
const valueReferences = value.extra?.referencedBindings;
|
||||
|
||||
switch (name) {
|
||||
case "class":
|
||||
case "style": {
|
||||
const helper = `_attr_${name}` as const;
|
||||
if (confident) {
|
||||
write`${getHTMLRuntime()[helper](computed)}`;
|
||||
} else if (isHTML) {
|
||||
write`${callRuntime(helper, value)}`;
|
||||
} else {
|
||||
addStatement(
|
||||
"render",
|
||||
tagSection,
|
||||
valueReferences,
|
||||
t.expressionStatement(
|
||||
callRuntime(
|
||||
helper,
|
||||
createScopeReadExpression(nodeBinding!),
|
||||
value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (confident) {
|
||||
write`${getHTMLRuntime()._attr(name, computed)}`;
|
||||
} else if (isHTML) {
|
||||
if (isEventHandler(name)) {
|
||||
addHTMLEffectCall(tagSection, valueReferences);
|
||||
} else {
|
||||
write`${callRuntime("_attr", t.stringLiteral(name), value)}`;
|
||||
}
|
||||
} else if (isEventHandler(name)) {
|
||||
addStatement(
|
||||
"effect",
|
||||
tagSection,
|
||||
valueReferences,
|
||||
t.expressionStatement(
|
||||
callRuntime(
|
||||
"_on",
|
||||
createScopeReadExpression(nodeBinding!),
|
||||
t.stringLiteral(getEventHandlerName(name)),
|
||||
value,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
addStatement(
|
||||
"render",
|
||||
tagSection,
|
||||
valueReferences,
|
||||
t.expressionStatement(
|
||||
callRuntime(
|
||||
"_attr",
|
||||
createScopeReadExpression(nodeBinding!),
|
||||
t.stringLiteral(name),
|
||||
value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (spreadExpression) {
|
||||
const visitAccessor = getScopeAccessorLiteral(nodeBinding!);
|
||||
if (isHTML) {
|
||||
addHTMLEffectCall(tagSection, tagExtra.referencedBindings);
|
||||
|
||||
if (skipExpression) {
|
||||
write`${callRuntime("_attrs_partial", spreadExpression, skipExpression, visitAccessor, getScopeIdIdentifier(tagSection), t.stringLiteral("title"))}`;
|
||||
} else {
|
||||
write`${callRuntime("_attrs", spreadExpression, visitAccessor, getScopeIdIdentifier(tagSection), t.stringLiteral("title"))}`;
|
||||
}
|
||||
} else {
|
||||
if (skipExpression) {
|
||||
addStatement(
|
||||
"render",
|
||||
tagSection,
|
||||
tagExtra.referencedBindings,
|
||||
t.expressionStatement(
|
||||
callRuntime(
|
||||
"_attrs_partial",
|
||||
scopeIdentifier,
|
||||
visitAccessor,
|
||||
spreadExpression,
|
||||
skipExpression,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
addStatement(
|
||||
"render",
|
||||
tagSection,
|
||||
tagExtra.referencedBindings,
|
||||
t.expressionStatement(
|
||||
callRuntime(
|
||||
"_attrs",
|
||||
scopeIdentifier,
|
||||
visitAccessor,
|
||||
spreadExpression,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
addStatement(
|
||||
"effect",
|
||||
tagSection,
|
||||
tagExtra.referencedBindings,
|
||||
t.expressionStatement(
|
||||
callRuntime("_attrs_script", scopeIdentifier, visitAccessor),
|
||||
),
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
write`>`;
|
||||
walks.enter(tag);
|
||||
},
|
||||
exit(tag) {
|
||||
const tagSection = getSection(tag);
|
||||
const tagExtra = tag.node.extra!;
|
||||
const nodeBinding = tagExtra[kNodeBinding];
|
||||
const write = writer.writeTo(tag);
|
||||
|
||||
if (isOutputHTML()) {
|
||||
for (const child of tag.node.body.body) {
|
||||
if (t.isMarkoText(child)) {
|
||||
write`${child.value}`;
|
||||
} else if (t.isMarkoPlaceholder(child)) {
|
||||
write`${callRuntime("_to_text", child.value)}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const textLiteral = bodyToTextLiteral(tag.node.body);
|
||||
|
||||
if (t.isStringLiteral(textLiteral)) {
|
||||
write`${textLiteral}`;
|
||||
} else {
|
||||
addStatement(
|
||||
"render",
|
||||
getSection(tag),
|
||||
textLiteral.extra?.referencedBindings,
|
||||
t.expressionStatement(
|
||||
callRuntime(
|
||||
"_text_content",
|
||||
createScopeReadExpression(nodeBinding!),
|
||||
textLiteral,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
write`</title>`;
|
||||
|
||||
if (nodeBinding) {
|
||||
writer.markNode(
|
||||
tag,
|
||||
nodeBinding,
|
||||
getSerializeReason(tagSection, nodeBinding),
|
||||
);
|
||||
}
|
||||
|
||||
walks.exit(tag);
|
||||
tag.remove();
|
||||
},
|
||||
},
|
||||
parseOptions: {
|
||||
text: true,
|
||||
},
|
||||
} as Tag;
|
||||
|
||||
function getUsedAttrs(tag: t.MarkoTag) {
|
||||
const seen: Record<string, t.MarkoAttribute> = {};
|
||||
const { attributes } = tag;
|
||||
const maybeStaticAttrs = new Set<t.MarkoAttribute>();
|
||||
let spreadExpression: undefined | t.Expression;
|
||||
let skipExpression: undefined | t.Expression;
|
||||
let spreadProps: undefined | t.ObjectExpression["properties"];
|
||||
let skipProps: undefined | t.ObjectExpression["properties"];
|
||||
for (let i = attributes.length; i--; ) {
|
||||
const attr = attributes[i];
|
||||
const { value } = attr;
|
||||
if (t.isMarkoSpreadAttribute(attr)) {
|
||||
if (!spreadProps) {
|
||||
spreadProps = [];
|
||||
}
|
||||
spreadProps.push(t.spreadElement(value));
|
||||
} else if (!seen[attr.name]) {
|
||||
seen[attr.name] = attr;
|
||||
|
||||
if (spreadProps) {
|
||||
spreadProps.push(toObjectProperty(attr.name, attr.value));
|
||||
} else {
|
||||
maybeStaticAttrs.add(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const staticAttrs = [...maybeStaticAttrs].reverse();
|
||||
|
||||
if (spreadProps) {
|
||||
spreadProps.reverse();
|
||||
|
||||
for (const { name } of staticAttrs) {
|
||||
(skipProps ||= []).push(toObjectProperty(name, t.numericLiteral(1)));
|
||||
}
|
||||
|
||||
if (skipProps) {
|
||||
skipExpression = t.objectExpression(skipProps);
|
||||
}
|
||||
|
||||
spreadExpression = propsToExpression(spreadProps);
|
||||
}
|
||||
|
||||
return {
|
||||
staticAttrs,
|
||||
spreadExpression,
|
||||
skipExpression,
|
||||
};
|
||||
}
|
||||
@ -37,7 +37,7 @@ export const preferAPI = "tags";
|
||||
export const { transform, analyze, translate } = visitors;
|
||||
export const taglibs = [
|
||||
[
|
||||
__dirname,
|
||||
coreTagLib.taglibId,
|
||||
{
|
||||
...coreTagLib,
|
||||
migrate: visitors.migrate,
|
||||
|
||||
@ -15,13 +15,7 @@ export function isCoreTag(
|
||||
if (tagDef) {
|
||||
switch (tagDef.taglibId) {
|
||||
case taglibId:
|
||||
return true;
|
||||
case interopTaglibId:
|
||||
switch (tagDef.name) {
|
||||
// The body tag is registered in the v5 translator, without this it'd be seen as a core tag.
|
||||
case "body":
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case htmlTaglibId:
|
||||
switch (tagDef.name) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { types as t } from "@marko/compiler";
|
||||
import { getTagDef } from "@marko/compiler/babel-utils";
|
||||
|
||||
import { isCoreTag } from "./is-core-tag";
|
||||
|
||||
@ -7,16 +8,29 @@ export function isNonHTMLText(
|
||||
) {
|
||||
const parentTag =
|
||||
placeholder.parentPath.isMarkoTagBody() &&
|
||||
placeholder.parentPath.parentPath;
|
||||
if (parentTag && isCoreTag(parentTag)) {
|
||||
switch (parentTag.node.name.value) {
|
||||
case "html-comment":
|
||||
case "html-script":
|
||||
case "html-style":
|
||||
case "title":
|
||||
return true;
|
||||
(placeholder.parentPath.parentPath as t.NodePath<t.MarkoTag>);
|
||||
if (parentTag) {
|
||||
if (isCoreTag(parentTag)) {
|
||||
switch (parentTag.node.name.value) {
|
||||
case "html-comment":
|
||||
case "html-script":
|
||||
case "html-style":
|
||||
return true;
|
||||
}
|
||||
} else if (isTextOnlyNativeTag(parentTag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isTextOnlyNativeTag(tag: t.NodePath<t.MarkoTag>) {
|
||||
const def = getTagDef(tag);
|
||||
// Have to special case `title` here for the compat with v5 which does not treat title as a text only tag.
|
||||
return !!(
|
||||
def &&
|
||||
def.html &&
|
||||
(def.name === "title" || def.parseOptions?.text)
|
||||
);
|
||||
}
|
||||
|
||||
@ -289,7 +289,6 @@ export function getNodeContentType(
|
||||
return ContentType.Comment;
|
||||
case "html-script":
|
||||
case "html-style":
|
||||
case "title":
|
||||
return ContentType.Tag;
|
||||
case "for":
|
||||
case "if":
|
||||
@ -486,7 +485,6 @@ function isNativeNode(tag: t.NodePath<t.MarkoTag>) {
|
||||
case "html-comment":
|
||||
case "html-script":
|
||||
case "html-style":
|
||||
case "title":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@ -10,9 +10,11 @@ import {
|
||||
import { assertExclusiveAttrs } from "../../../common/errors";
|
||||
import { getEventHandlerName, isEventHandler } from "../../../common/helpers";
|
||||
import { WalkCode } from "../../../common/types";
|
||||
import { bodyToTextLiteral } from "../../util/body-to-text-literal";
|
||||
import evaluate from "../../util/evaluate";
|
||||
import { generateUidIdentifier } from "../../util/generate-uid";
|
||||
import { getTagName } from "../../util/get-tag-name";
|
||||
import { isTextOnlyNativeTag } from "../../util/is-non-html-text";
|
||||
import { type Opt, push } from "../../util/optional";
|
||||
import {
|
||||
type Binding,
|
||||
@ -81,13 +83,14 @@ export default {
|
||||
}
|
||||
|
||||
const tagName = getTagName(tag)!;
|
||||
const textOnly = isTextOnlyNativeTag(tag);
|
||||
const seen: Record<string, t.MarkoAttribute> = {};
|
||||
const { attributes } = tag.node;
|
||||
let hasDynamicAttributes = false;
|
||||
let hasEventHandlers = false;
|
||||
let relatedControllable: RelatedControllable;
|
||||
let spreadReferenceNodes: t.Node[] | undefined;
|
||||
let attrExprExtras: Opt<t.NodeExtra>;
|
||||
let exprExtras: Opt<t.NodeExtra>;
|
||||
|
||||
for (let i = attributes.length; i--; ) {
|
||||
const attr = attributes[i];
|
||||
@ -123,7 +126,7 @@ export default {
|
||||
spreadReferenceNodes = [attr.value];
|
||||
relatedControllable = getRelatedControllable(tagName, seen);
|
||||
} else {
|
||||
attrExprExtras = push(attrExprExtras, valueExtra);
|
||||
exprExtras = push(exprExtras, valueExtra);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,10 +134,25 @@ export default {
|
||||
throw tag.get("name").buildCodeFrameError(msg);
|
||||
});
|
||||
|
||||
let textPlaceholders: undefined | t.Node[];
|
||||
if (textOnly) {
|
||||
for (const child of tag.node.body.body) {
|
||||
if (t.isMarkoPlaceholder(child)) {
|
||||
(textPlaceholders ||= []).push(child.value);
|
||||
} else if (!t.isMarkoText(child)) {
|
||||
throw tag.hub.buildError(
|
||||
child,
|
||||
`Only text is allowed inside a \`<${tagName}>\`.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
node.var ||
|
||||
hasEventHandlers ||
|
||||
hasDynamicAttributes ||
|
||||
textPlaceholders ||
|
||||
getRelatedControllable(tagName, seen)?.special
|
||||
) {
|
||||
const tagExtra = (node.extra ??= {});
|
||||
@ -177,6 +195,19 @@ export default {
|
||||
);
|
||||
}
|
||||
|
||||
if (textPlaceholders) {
|
||||
exprExtras = push(
|
||||
exprExtras,
|
||||
textPlaceholders.length === 1
|
||||
? (textPlaceholders[0].extra ??= {})
|
||||
: mergeReferences(
|
||||
tagSection,
|
||||
textPlaceholders[0],
|
||||
textPlaceholders.slice(1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
addSerializeExpr(
|
||||
tagSection,
|
||||
!!(node.var || hasEventHandlers),
|
||||
@ -185,11 +216,7 @@ export default {
|
||||
|
||||
trackDomVarReferences(tag, nodeBinding);
|
||||
|
||||
addSerializeExpr(
|
||||
tagSection,
|
||||
push(attrExprExtras, tagExtra),
|
||||
nodeBinding,
|
||||
);
|
||||
addSerializeExpr(tagSection, push(exprExtras, tagExtra), nodeBinding);
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -426,9 +453,18 @@ export default {
|
||||
const tagExtra = tag.node.extra!;
|
||||
const nodeBinding = tagExtra[kNativeTagBinding];
|
||||
const openTagOnly = getTagDef(tag)?.parseOptions?.openTagOnly;
|
||||
const textOnly = isTextOnlyNativeTag(tag);
|
||||
const selectArgs = htmlSelectArgs.get(tag.node);
|
||||
const tagName = getTagName(tag);
|
||||
const tagSection = getSection(tag);
|
||||
const markerSerializeReason =
|
||||
!tagExtra[kSkipEndTag] &&
|
||||
nodeBinding &&
|
||||
getSerializeReason(tagSection, nodeBinding);
|
||||
const write = writer.writeTo(
|
||||
tag,
|
||||
!markerSerializeReason && (tagName === "html" || tagName === "body"),
|
||||
);
|
||||
|
||||
if (tagExtra[kTagContentAttr]) {
|
||||
writer.flushBefore(tag);
|
||||
@ -440,7 +476,7 @@ export default {
|
||||
|
||||
if (selectArgs) {
|
||||
if (!tagExtra[kSkipEndTag]) {
|
||||
writer.writeTo(tag)`</${tag.node.name}>`;
|
||||
write`</${tag.node.name}>`;
|
||||
}
|
||||
|
||||
writer.flushInto(tag);
|
||||
@ -459,21 +495,20 @@ export default {
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (textOnly) {
|
||||
for (const child of tag.node.body.body) {
|
||||
if (t.isMarkoText(child)) {
|
||||
write`${child.value}`;
|
||||
} else if (t.isMarkoPlaceholder(child)) {
|
||||
write`${callRuntime("_to_text", child.value)}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tag.insertBefore(tag.node.body.body).forEach((child) => child.skip());
|
||||
}
|
||||
|
||||
const markerSerializeReason =
|
||||
!tagExtra[kSkipEndTag] &&
|
||||
nodeBinding &&
|
||||
getSerializeReason(tagSection, nodeBinding);
|
||||
|
||||
if (!tagExtra[kSkipEndTag] && !openTagOnly && !selectArgs) {
|
||||
writer.writeTo(
|
||||
tag,
|
||||
!markerSerializeReason &&
|
||||
(tagName === "html" || tagName === "body"),
|
||||
)`</${tag.node.name}>`;
|
||||
write`</${tag.node.name}>`;
|
||||
}
|
||||
|
||||
// dynamic tag stuff
|
||||
@ -734,10 +769,36 @@ export default {
|
||||
walks.enter(tag);
|
||||
},
|
||||
exit(tag) {
|
||||
const tagExtra = tag.node.extra!;
|
||||
const nodeBinding = tagExtra[kNativeTagBinding];
|
||||
const openTagOnly = getTagDef(tag)?.parseOptions?.openTagOnly;
|
||||
tag.insertBefore(tag.node.body.body).forEach((child) => child.skip());
|
||||
const textOnly = isTextOnlyNativeTag(tag);
|
||||
|
||||
if (!openTagOnly) {
|
||||
if (textOnly) {
|
||||
const textLiteral = bodyToTextLiteral(tag.node.body);
|
||||
if (t.isStringLiteral(textLiteral)) {
|
||||
writer.writeTo(tag)`${textLiteral}`;
|
||||
} else {
|
||||
addStatement(
|
||||
"render",
|
||||
getSection(tag),
|
||||
textLiteral.extra?.referencedBindings,
|
||||
t.expressionStatement(
|
||||
callRuntime(
|
||||
"_text_content",
|
||||
createScopeReadExpression(nodeBinding!),
|
||||
textLiteral,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
tag
|
||||
.insertBefore(tag.node.body.body)
|
||||
.forEach((child) => child.skip());
|
||||
}
|
||||
|
||||
writer.writeTo(tag)`</${tag.node.name}>`;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user