marko
Version:
UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.
149 lines (120 loc) • 4.04 kB
JavaScript
;exports.__esModule = true;exports.analyzeAttributeTags = analyzeAttributeTags;exports.default = translateAttributeTag;var _compiler = require("@marko/compiler");
var _babelUtils = require("@marko/compiler/babel-utils");
var _util = require("./util");
const attributeTagsForTag = new WeakMap();
const contentTypeCache = new WeakMap();
const ContentType = {
attribute: 0,
render: 1,
mixed: 2
};
function analyzeAttributeTags(rootTag) {
const visit = [rootTag];
const parentTags = [rootTag];
let i = 0;
let attributeTags;
while (i < visit.length) {
const tag = visit[i++];
const attrTags = tag.node.body.attributeTags ?
tag.get("body").get("body") :
tag.get("attributeTags");
for (const child of attrTags) {
if ((0, _babelUtils.isAttributeTag)(child)) {
(0, _babelUtils.assertNoArgs)(child);
const tagDef = (0, _babelUtils.getTagDef)(child) || {};
const name = (0, _babelUtils.getFullyResolvedTagName)(child);
let {
targetProperty = child.node.name.value.slice(1),
isRepeated = false
} = tagDef;
const preserveName =
tagDef.preserveName === true || tagDef.removeDashes === false;
if (!preserveName) {
targetProperty = removeDashes(targetProperty);
}
const attrTagMeta = (attributeTags ||= {})[name] ||= {
targetProperty,
isRepeated
};
(child.node.extra ||= {}).attributeTag = attrTagMeta;
const parentTag = (0, _babelUtils.findParentTag)(child);
const parentTagExtra = parentTag.node.extra ||= {};
const parentSeenAttributeTagProperties =
attributeTagsForTag.get(parentTag);
let hasAttributeTags = false;
if (!parentSeenAttributeTagProperties) {
parentTagExtra.hasAttributeTags = true;
attributeTagsForTag.set(parentTag, new Set([targetProperty]));
} else if (parentSeenAttributeTagProperties.has(targetProperty)) {
hasAttributeTags = true;
} else {
parentSeenAttributeTagProperties.add(targetProperty);
}
if (!hasAttributeTags) {
if (
parentTag.
get("attributes").
some(
(attr) =>
attr.isMarkoSpreadAttribute() ||
attr.node.name === targetProperty
))
{
parentTag.pushContainer(
"attributes",
_compiler.types.markoAttribute(
targetProperty,
_compiler.types.unaryExpression("void", _compiler.types.numericLiteral(0))
)
);
}
}
parentTags.push(child);
visit.push(child);
} else if ((0, _babelUtils.isTransparentTag)(child)) {
visit.push(child);
}
}
}
if (attributeTags) {
(rootTag.node.extra ??= {}).attributeTags = attributeTags;
}
}
function translateAttributeTag(tag) {
const { node } = tag;
const meta = node.extra?.attributeTag;
if (!meta) {
throw tag.
get("name").
buildCodeFrameError("@tags must be nested within another element.");
}
(0, _babelUtils.assertNoArgs)(tag);
tag.replaceWith(
_compiler.types.expressionStatement(
_compiler.types.callExpression(
(0, _babelUtils.importNamed)(
tag.hub.file,
"marko/src/runtime/helpers/attr-tag.js",
meta.isRepeated ? "r" : "a",
meta.isRepeated ?
"marko_repeated_attr_tag" :
"marko_repeatable_attr_tag"
),
[_compiler.types.stringLiteral(meta.targetProperty), getAttrTagObject(tag)]
)
)
);
}
function getAttrTagObject(tag) {
const attrs = (0, _util.getAttrs)(tag, false, true);
if (_compiler.types.isNullLiteral(attrs)) {
return _compiler.types.objectExpression([]);
}
return attrs;
}
function removeDashes(str) {
return str.replace(/-([a-z])/g, matchToUpperCase);
}
function matchToUpperCase(_match, lower) {
return lower.toUpperCase();
}