marko
Version:
UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.
150 lines (125 loc) • 4.41 kB
JavaScript
;var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");exports.__esModule = true;exports.default = void 0;var _babelUtils = require("@marko/compiler/babel-utils");
var _directives = _interopRequireDefault(require("./directives"));
var _modifiers = _interopRequireDefault(require("./modifiers"));
const EMPTY_ARRAY = [];
const EVENT_REG = /^(on(?:ce)?)(-)?(.*)$/;
const attachedDetachedLoaded = new WeakSet();var _default = exports.default =
{
enter(attr) {
const {
hub: { file }
} = attr;
const tag = attr.parentPath;
const value = attr.get("value");
const { name, arguments: args } = attr.node;
const isVDOM = file.markoOpts.output !== "html";
if (execModifiersAndDirectives("enter", tag, attr, value)) {
return;
}
// Event handlers.
let [, eventType, isDash, eventName] = EVENT_REG.exec(name) || EMPTY_ARRAY;
if (eventType && args) {
if (!args.length) {
throw attr.buildCodeFrameError("Event handler is missing arguments.");
}
if (!value.isBooleanLiteral(true)) {
throw value.buildCodeFrameError(
`"${name}(handler, ...args)" does not accept a value.`
);
}
if (!isDash) {
// When the event is not in dash case we normalized differently for html tags and custom tags.
if ((0, _babelUtils.isNativeTag)(tag)) {
// Lowercase the string
// Example: onMouseOver → mouseover
eventName = eventName.toLowerCase();
} else {
// Convert first character to lower case:
// Example: onBeforeShow → beforeShow
eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
}
}
const handlers = tag.node.handlers = tag.node.handlers || {};
if (handlers[eventName]) {
throw attr.buildCodeFrameError(
"Duplicate event handlers are not supported."
);
}
handlers[eventName] = {
arguments: args,
once: eventType === "once"
};
if (isVDOM) {
if (eventName === "attach" || eventName === "detach") {
if (!attachedDetachedLoaded.has(file)) {
// Pull in helper for element attach/detach;
attachedDetachedLoaded.add(file);
(0, _babelUtils.importDefault)(
file,
"marko/src/runtime/components/attach-detach.js"
);
}
}
}
attr.remove();
return;
}
},
exit(attr) {
const tag = attr.parentPath;
const { name, arguments: args } = attr.node;
const value = attr.get("value");
if (execModifiersAndDirectives("exit", tag, attr, value)) {
return;
}
const tagDef = (0, _babelUtils.getTagDef)(tag);
if (tagDef) {
if (!tagDef.html && !tagDef.getAttribute(name)) {
throw attr.buildCodeFrameError(
`<${
tag.get("name.value").node}> does not support the "${
name}" attribute.`
);
}
}
if (args && args.length) {
throw attr.buildCodeFrameError(
`Unsupported arguments on the "${name}" attribute.`
);
}
if (attr.node.bound) {
throw attr.buildCodeFrameError(
`The binding syntax (:=) is only supported when using the "Tags API".`
);
}
}
};
function execModifiersAndDirectives(type, tag, attr, value) {
const { node } = attr;
const { name, modifier } = node;
if (modifier) {
const modifierTranslate = _modifiers.default[modifier];
if (modifierTranslate) {
if (modifierTranslate[type]) {
const tagNode = tag.node;
const attrNode = attr.node;
modifierTranslate[type](tag, attr, value);
if (tag.node !== tagNode || attr.node !== attrNode) return true;
}
} else if (name === "xlink" && modifier === "href" && (0, _babelUtils.isNativeTag)(tag)) {
node.name += `:${modifier}`;
node.modifier = undefined;
} else {
throw attr.buildCodeFrameError(`Unsupported modifier "${modifier}".`);
}
}
const directiveTranslate = _directives.default[name];
if (directiveTranslate) {
if (directiveTranslate[type]) {
const tagNode = tag.node;
const attrNode = attr.node;
directiveTranslate[type](tag, attr, value);
if (tag.node !== tagNode || attr.node !== attrNode) return true;
}
}
}