UNPKG

marko

Version:

UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.

260 lines (218 loc) • 6.92 kB
"use strict";var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");exports.__esModule = true;exports.buildEventHandlerArray = buildEventHandlerArray;exports.evaluateAttr = evaluateAttr;exports.getAttrs = getAttrs;var _compiler = require("@marko/compiler"); var _babelUtils = require("@marko/compiler/babel-utils"); var _classValue = _interopRequireDefault(require("marko/src/runtime/helpers/class-value")); var _styleValue = _interopRequireDefault(require("marko/src/runtime/helpers/style-value")); function getAttrs(path, preserveNames, isAttrTag) { const { node } = path; const { extra, attributes, attributeTags, body: { body, params } } = node; const attrsLen = attributes.length; const childLen = body.length; const properties = []; const targetObjects = {}; const tagDef = (0, _babelUtils.getTagDef)(path); const foundProperties = {}; const hasAttributeTags = !!attributeTags.length; const isTagsAPI = findRootTag(path)?.node.extra?.featureType === "tags"; const renderBodyKey = isTagsAPI ? "content" : "renderBody"; for (let i = 0; i < attrsLen; i++) { const { name, value } = attributes[i]; if (name) { const attrDef = tagDef && tagDef.getAttribute(name); let targetProperties = properties; let targetProperty = name; let preserveName = preserveNames; if (attrDef) { if (attrDef.targetProperty) { const key = attrDef.targetProperty; preserveName = attrDef.preserveName !== false && attrDef.removeDashes !== true; if (attrDef.dynamicAttribute) { let targetObject = targetObjects[key]; if (!targetObject) { properties.push( _compiler.types.objectProperty( _compiler.types.stringLiteral(key), targetObject = targetObjects[key] = _compiler.types.objectExpression([]) ) ); } targetProperties = targetObject.properties; } else { targetProperty = key; } } else if ( !preserveName && ( attrDef.preserveName === true || attrDef.removeDashes === false)) { preserveName = true; } } if (!preserveName) { targetProperty = camelCase(targetProperty); } foundProperties[targetProperty] = true; targetProperties.push( _compiler.types.objectProperty(_compiler.types.stringLiteral(targetProperty), value) ); } else { mergeSpread(properties, value); } } if (childLen && !hasAttributeTags) { properties.push( _compiler.types.objectProperty( _compiler.types.stringLiteral(renderBodyKey), _compiler.types.arrowFunctionExpression( [_compiler.types.identifier("out"), ...params], _compiler.types.blockStatement(body) ) ) ); } // Default parameters tagDef && tagDef.forEachAttribute && tagDef.forEachAttribute((attr) => { if (foundProperties[attr.name] || attr.dynamicAttribute) { return; } if (attr.defaultValue !== undefined) { properties.push( _compiler.types.objectProperty( _compiler.types.stringLiteral(attr.name), _compiler.types.stringLiteral(attr.defaultValue + "") ) ); } else if (attr.required) { throw path. get("name"). buildCodeFrameError(`The "${attr.name}" attribute is required.`); } }); let attrsObject = properties.length === 0 ? _compiler.types.nullLiteral() : !hasAttributeTags && !isAttrTag && properties.length === 1 && _compiler.types.isSpreadElement(properties[0]) ? properties[0].argument : _compiler.types.objectExpression(properties); if (hasAttributeTags) { let attrTagBody = attributeTags; if (body.length) { attrTagBody = attrTagBody.concat( _compiler.types.returnStatement( _compiler.types.arrowFunctionExpression( [_compiler.types.identifier("out"), ...params], _compiler.types.blockStatement(body) ) ) ); } const attrTagFn = _compiler.types.arrowFunctionExpression( [], _compiler.types.blockStatement(attrTagBody) ); attrsObject = _compiler.types.callExpression( (0, _babelUtils.importNamed)( path.hub.file, "marko/src/runtime/helpers/attr-tag.js", "i", "marko_render_input" ), properties.length === 0 ? [attrTagFn] : [attrTagFn, attrsObject] ); } return attrsObject; } function buildEventHandlerArray(path) { const { handlers } = path.node; if (!handlers) { return []; } return [ _compiler.types.arrayExpression( Object.entries(handlers).map(([eventName, { arguments: args, once }]) => { const parts = [ _compiler.types.stringLiteral(eventName), args[0], _compiler.types.booleanLiteral(once)]; if (args.length > 1) { parts.push(_compiler.types.arrayExpression(args.slice(1))); } return _compiler.types.arrayExpression(parts); }) )]; } function evaluateAttr(attr) { const computed = (0, _babelUtils.computeNode)(attr.node.value); if (computed) { const { value } = computed; switch (attr.node.name) { case "class": return { value: (0, _classValue.default)(value)?.replace(/\s+/, " ").trim() }; case "style": return { value: (0, _styleValue.default)(value)?. replace(/\s+/, " "). trim(). replace(/;$/, "") }; } if (value == null || value === false) { return { value: undefined }; } if (value === true) { return { value: "" }; } if (typeof value === "object") { switch (value.toString) { case Object.prototype.toString: case Array.prototype.toString: return { value: JSON.stringify(value) }; case RegExp.prototype.toString: return { value: value.source }; } } return { value: value + "" }; } } function camelCase(string) { return string.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()); } function findLastIndex(arr, check) { for (let i = arr.length; i--;) { if (check(arr[i])) { return i; } } return -1; } function mergeSpread(properties, value) { if (_compiler.types.isObjectExpression(value)) { for (const prop of value.properties) { if (_compiler.types.isSpreadElement(prop)) { mergeSpread(properties, prop.argument); } else { properties.push(prop); } } } else { properties.push(_compiler.types.spreadElement(value)); } } function findRootTag(tag) { let cur = tag; while ((0, _babelUtils.isAttributeTag)(cur)) { cur = (0, _babelUtils.findParentTag)(cur); } return cur; }