'use strict'; var SyntaxJSX = require('@babel/plugin-syntax-jsx'); var t = require('@babel/types'); var helperModuleImports = require('@babel/helper-module-imports'); var htmlEntities = require('html-entities'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var t__namespace = /*#__PURE__*/_interopNamespaceDefault(t); // list of lowercase booleans const booleans = [ "allowfullscreen", "async", "alpha", // HTMLInputElement "autofocus", // HTMLElement prop "autoplay", "checked", "controls", "default", "disabled", "formnovalidate", "hidden", // HTMLElement prop - not a boolean "indeterminate", "inert", // HTMLElement prop "ismap", "loop", "multiple", "muted", "nomodule", "novalidate", "open", "playsinline", "readonly", "required", "reversed", "seamless", // HTMLIframeElement - non-standard "selected", "adauctionheaders", // experimental "browsingtopics", // experimental "credentialless", // experimental "defaultchecked", "defaultmuted", "defaultselected", "defer", "disablepictureinpicture", "disableremoteplayback", "preservespitch", // appears as camelCase property only (not as attribute) "shadowrootclonable", "shadowrootcustomelementregistry", // experimental - doesnt seem to have a prop yet "shadowrootdelegatesfocus", "shadowrootserializable", // experimental "sharedstoragewritable" // experimental ]; const BooleanAttributes = /*#__PURE__*/new Set(booleans); const Properties = /*#__PURE__*/new Set([ // locked to properties "className", "value", // booleans with camelCase "readOnly", "noValidate", "formNoValidate", "isMap", "noModule", "playsInline", "adAuctionHeaders", // experimental "allowFullscreen", "browsingTopics", // experimental "defaultChecked", "defaultMuted", "defaultSelected", "disablePictureInPicture", "disableRemotePlayback", "preservesPitch", "shadowRootClonable", "shadowRootCustomElementRegistry", // experimental "shadowRootDelegatesFocus", "shadowRootSerializable", // experimental "sharedStorageWritable", // experimental ...booleans]); const ChildProperties = /*#__PURE__*/new Set([ "innerHTML", "textContent", "innerText", "children"]); // React Compat const Aliases = /*#__PURE__*/Object.assign(Object.create(null), { className: "class", htmlFor: "for" }); const PropAliases = /*#__PURE__*/Object.assign(Object.create(null), { // locked to properties class: "className", // booleans map novalidate: { $: "noValidate", FORM: 1 }, formnovalidate: { $: "formNoValidate", BUTTON: 1, INPUT: 1 }, ismap: { $: "isMap", IMG: 1 }, nomodule: { $: "noModule", SCRIPT: 1 }, playsinline: { $: "playsInline", VIDEO: 1 }, readonly: { $: "readOnly", INPUT: 1, TEXTAREA: 1 }, adauctionheaders: { $: "adAuctionHeaders", IFRAME: 1 }, allowfullscreen: { $: "allowFullscreen", IFRAME: 1 }, browsingtopics: { $: "browsingTopics", IMG: 1 }, defaultchecked: { $: "defaultChecked", INPUT: 1 }, defaultmuted: { $: "defaultMuted", AUDIO: 1, VIDEO: 1 }, defaultselected: { $: "defaultSelected", OPTION: 1 }, disablepictureinpicture: { $: "disablePictureInPicture", VIDEO: 1 }, disableremoteplayback: { $: "disableRemotePlayback", AUDIO: 1, VIDEO: 1 }, preservespitch: { $: "preservesPitch", AUDIO: 1, VIDEO: 1 }, shadowrootclonable: { $: "shadowRootClonable", TEMPLATE: 1 }, shadowrootdelegatesfocus: { $: "shadowRootDelegatesFocus", TEMPLATE: 1 }, shadowrootserializable: { $: "shadowRootSerializable", TEMPLATE: 1 }, sharedstoragewritable: { $: "sharedStorageWritable", IFRAME: 1, IMG: 1 } }); function getPropAlias(prop, tagName) { const a = PropAliases[prop]; return typeof a === "object" ? a[tagName] ? a["$"] : undefined : a; } // list of Element events that will be delegated const DelegatedEvents = /*#__PURE__*/new Set([ "beforeinput", "click", "dblclick", "contextmenu", "focusin", "focusout", "input", "keydown", "keyup", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup", "pointerdown", "pointermove", "pointerout", "pointerover", "pointerup", "touchend", "touchmove", "touchstart"]); const SVGElements = /*#__PURE__*/new Set([ // "a", "altGlyph", "altGlyphDef", "altGlyphItem", "animate", "animateColor", "animateMotion", "animateTransform", "circle", "clipPath", "color-profile", "cursor", "defs", "desc", "ellipse", "feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence", "filter", "font", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignObject", "g", "glyph", "glyphRef", "hkern", "image", "line", "linearGradient", "marker", "mask", "metadata", "missing-glyph", "mpath", "path", "pattern", "polygon", "polyline", "radialGradient", "rect", // "script", "set", "stop", // "style", "svg", "switch", "symbol", "text", "textPath", // "title", "tref", "tspan", "use", "view", "vkern"]); const SVGNamespace = { xlink: "http://www.w3.org/1999/xlink", xml: "http://www.w3.org/XML/1998/namespace" }; var VoidElements = [ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr']; const reservedNameSpaces = new Set([ "class", "on", "oncapture", "style", "use", "prop", "attr", "bool"]); const nonSpreadNameSpaces = new Set(["class", "style", "use", "prop", "attr", "bool"]); function getConfig(path) { return path.hub.file.metadata.config; } const getRendererConfig = (path, renderer) => { const config = getConfig(path); return config?.renderers?.find((r) => r.name === renderer) ?? config; }; function registerImportMethod(path, name, moduleName) { const imports = path.scope.getProgramParent().data.imports || ( path.scope.getProgramParent().data.imports = new Map()); moduleName = moduleName || getConfig(path).moduleName; if (!imports.has(`${moduleName}:${name}`)) { let id = helperModuleImports.addNamed(path, name, moduleName, { nameHint: `_$${name}` }); imports.set(`${moduleName}:${name}`, id); return id; } else { let iden = imports.get(`${moduleName}:${name}`); // the cloning is required to play well with babel-preset-env which is // transpiling import as we add them and using the same identifier causes // problems with the multiple identifiers of the same thing return t__namespace.cloneNode(iden); } } function jsxElementNameToString(node) { if (t__namespace.isJSXMemberExpression(node)) { return `${jsxElementNameToString(node.object)}.${node.property.name}`; } if (t__namespace.isJSXIdentifier(node) || t__namespace.isIdentifier(node)) { return node.name; } return `${node.namespace.name}:${node.name.name}`; } function getTagName(tag) { const jsxName = tag.openingElement.name; return jsxElementNameToString(jsxName); } function isComponent(tagName) { return ( tagName[0] && tagName[0].toLowerCase() !== tagName[0] || tagName.includes(".") || /[^a-zA-Z]/.test(tagName[0])); } function hasStaticMarker(object, path) { if (!object) return false; if ( object.leadingComments && object.leadingComments[0] && object.leadingComments[0].value.trim() === getConfig(path).staticMarker) return true; if (object.expression) return hasStaticMarker(object.expression, path); } function isDynamic(path, { checkMember, checkTags, checkCallExpressions = true, native }) { const config = getConfig(path); if (config.generate === "ssr" && native) { checkMember = false; checkCallExpressions = false; } const expr = path.node; if (t__namespace.isFunction(expr)) return false; if ( expr.leadingComments && expr.leadingComments[0] && expr.leadingComments[0].value.trim() === config.staticMarker) { return false; } if ( checkCallExpressions && ( t__namespace.isCallExpression(expr) || t__namespace.isOptionalCallExpression(expr) || t__namespace.isTaggedTemplateExpression(expr))) { return true; } if (checkMember && t__namespace.isMemberExpression(expr)) { // Do not assume property access on namespaced imports as dynamic. const object = path.get("object").node; if ( t__namespace.isIdentifier(object) && ( !expr.computed || !isDynamic(path.get("property"), { checkMember, checkTags, checkCallExpressions, native }))) { const binding = path.scope.getBinding(object.name); if (binding && binding.path.isImportNamespaceSpecifier()) { return false; } } return true; } if ( checkMember && ( t__namespace.isOptionalMemberExpression(expr) || t__namespace.isSpreadElement(expr) || t__namespace.isBinaryExpression(expr) && expr.operator === "in")) { return true; } if (checkTags && (t__namespace.isJSXElement(expr) || t__namespace.isJSXFragment(expr) && expr.children.length)) { return true; } let dynamic; path.traverse({ Function(p) { if (t__namespace.isObjectMethod(p.node) && p.node.computed) { dynamic = isDynamic(p.get("key"), { checkMember, checkTags, checkCallExpressions, native }); } p.skip(); }, CallExpression(p) { checkCallExpressions && (dynamic = true) && p.stop(); }, OptionalCallExpression(p) { checkCallExpressions && (dynamic = true) && p.stop(); }, MemberExpression(p) { checkMember && (dynamic = true) && p.stop(); }, OptionalMemberExpression(p) { checkMember && (dynamic = true) && p.stop(); }, SpreadElement(p) { checkMember && (dynamic = true) && p.stop(); }, BinaryExpression(p) { checkMember && p.node.operator === "in" && (dynamic = true) && p.stop(); }, JSXElement(p) { checkTags ? (dynamic = true) && p.stop() : p.skip(); }, JSXFragment(p) { checkTags && p.node.children.length ? (dynamic = true) && p.stop() : p.skip(); } }); return dynamic; } function getStaticExpression(path) { const node = path.node; let value, type; return ( t__namespace.isJSXExpressionContainer(node) && t__namespace.isJSXElement(path.parent) && !isComponent(getTagName(path.parent)) && !t__namespace.isSequenceExpression(node.expression) && (value = path.get("expression").evaluate().value) !== undefined && ( (type = typeof value) === "string" || type === "number") && value); } // remove unnecessary JSX Text nodes function filterChildren(children) { return children.filter( ({ node: child }) => !(t__namespace.isJSXExpressionContainer(child) && t__namespace.isJSXEmptyExpression(child.expression)) && ( !t__namespace.isJSXText(child) || !/^[\r\n]\s*$/.test(child.extra.raw))); } function checkLength(children) { let i = 0; children.forEach((path) => { const child = path.node; !(t__namespace.isJSXExpressionContainer(child) && t__namespace.isJSXEmptyExpression(child.expression)) && ( !t__namespace.isJSXText(child) || !/^\s*$/.test(child.extra.raw) || /^ *$/.test(child.extra.raw)) && i++; }); return i > 1; } function trimWhitespace(text) { text = text.replace(/\r/g, ""); if (/\n/g.test(text)) { text = text. split("\n"). map((t, i) => i ? t.replace(/^\s*/g, "") : t). filter((s) => !/^\s*$/.test(s)). join(" "); } return text.replace(/\s+/g, " "); } function toEventName(name) { return name.slice(2).toLowerCase(); } function toPropertyName(name) { return name.toLowerCase().replace(/-([a-z])/g, (_, w) => w.toUpperCase()); } function wrappedByText(list, startIndex) { let index = startIndex, wrapped; while (--index >= 0) { const node = list[index]; if (!node) continue; if (node.text) { wrapped = true; break; } if (node.id) return false; } if (!wrapped) return false; index = startIndex; while (++index < list.length) { const node = list[index]; if (!node) continue; if (node.text) return true; if (node.id) return false; } return false; } function transformCondition(path, inline, deep) { const config = getConfig(path); const expr = path.node; const memo = registerImportMethod(path, config.memoWrapper); let dTest, cond, id; if ( t__namespace.isConditionalExpression(expr) && ( isDynamic(path.get("consequent"), { checkTags: true, checkMember: true }) || isDynamic(path.get("alternate"), { checkTags: true, checkMember: true }))) { dTest = isDynamic(path.get("test"), { checkMember: true }); if (dTest) { cond = expr.test; if (!t__namespace.isBinaryExpression(cond)) cond = t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", cond, true), true); id = inline ? t__namespace.callExpression(memo, [t__namespace.arrowFunctionExpression([], cond)]) : path.scope.generateUidIdentifier("_c$"); expr.test = t__namespace.callExpression(id, []); if (t__namespace.isConditionalExpression(expr.consequent) || t__namespace.isLogicalExpression(expr.consequent)) { expr.consequent = transformCondition(path.get("consequent"), true, true); } if (t__namespace.isConditionalExpression(expr.alternate) || t__namespace.isLogicalExpression(expr.alternate)) { expr.alternate = transformCondition(path.get("alternate"), true, true); } } } else if (t__namespace.isLogicalExpression(expr)) { let nextPath = path; // handle top-level or, ie cond && || while (nextPath.node.operator !== "&&" && t__namespace.isLogicalExpression(nextPath.node.left)) { nextPath = nextPath.get("left"); } nextPath.node.operator === "&&" && isDynamic(nextPath.get("right"), { checkTags: true, checkMember: true }) && ( dTest = isDynamic(nextPath.get("left"), { checkMember: true })); if (dTest) { cond = nextPath.node.left; if (!t__namespace.isBinaryExpression(cond)) cond = t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", cond, true), true); id = inline ? t__namespace.callExpression(memo, [t__namespace.arrowFunctionExpression([], cond)]) : path.scope.generateUidIdentifier("_c$"); nextPath.node.left = t__namespace.callExpression(id, []); } } if (dTest && !inline) { const statements = [ t__namespace.variableDeclaration("var", [ t__namespace.variableDeclarator( id, config.memoWrapper ? t__namespace.callExpression(memo, [t__namespace.arrowFunctionExpression([], cond)]) : t__namespace.arrowFunctionExpression([], cond))]), t__namespace.arrowFunctionExpression([], expr)]; return deep ? t__namespace.callExpression( t__namespace.arrowFunctionExpression( [], t__namespace.blockStatement([statements[0], t__namespace.returnStatement(statements[1])])), []) : statements; } return deep ? expr : t__namespace.arrowFunctionExpression([], expr); } function escapeHTML(s, attr) { if (typeof s !== "string") return s; const delim = attr ? '"' : "<"; const escDelim = attr ? """ : "<"; let iDelim = s.indexOf(delim); let iAmp = s.indexOf("&"); if (iDelim < 0 && iAmp < 0) return s; let left = 0, out = ""; while (iDelim >= 0 && iAmp >= 0) { if (iDelim < iAmp) { if (left < iDelim) out += s.substring(left, iDelim); out += escDelim; left = iDelim + 1; iDelim = s.indexOf(delim, left); } else { if (left < iAmp) out += s.substring(left, iAmp); out += "&"; left = iAmp + 1; iAmp = s.indexOf("&", left); } } if (iDelim >= 0) { do { if (left < iDelim) out += s.substring(left, iDelim); out += escDelim; left = iDelim + 1; iDelim = s.indexOf(delim, left); } while (iDelim >= 0); } else { while (iAmp >= 0) { if (left < iAmp) out += s.substring(left, iAmp); out += "&"; left = iAmp + 1; iAmp = s.indexOf("&", left); } } return left < s.length ? out + s.substring(left) : out; } function convertJSXIdentifier(node) { if (t__namespace.isJSXIdentifier(node)) { if (t__namespace.isValidIdentifier(node.name)) { node.type = "Identifier"; } else { return t__namespace.stringLiteral(node.name); } } else if (t__namespace.isJSXMemberExpression(node)) { return t__namespace.memberExpression( convertJSXIdentifier(node.object), convertJSXIdentifier(node.property)); } else if (t__namespace.isJSXNamespacedName(node)) { return t__namespace.stringLiteral(`${node.namespace.name}:${node.name.name}`); } return node; } function canNativeSpread(key, { checkNameSpaces } = {}) { if (checkNameSpaces && key.includes(":") && nonSpreadNameSpaces.has(key.split(":")[0])) return false; // TODO: figure out how to detect definitely function ref if (key === "ref") return false; return true; } const chars = "etaoinshrdlucwmfygpbTAOISWCBvkxjqzPHFMDRELNGUKVYJQZX_$"; const base = chars.length; function getNumberedId(num) { let out = ""; do { const digit = num % base; num = Math.floor(num / base); out = chars[digit] + out; } while (num !== 0); return out; } function escapeStringForTemplate(str) { return str.replace(/[{\\`\n\t\b\f\v\r\u2028\u2029]/g, (ch) => templateEscapes.get(ch)); } const templateEscapes = new Map([ ["{", "\\{"], ["`", "\\`"], ["\\", "\\\\"], ["\n", "\\n"], ["\t", "\\t"], ["\b", "\\b"], ["\f", "\\f"], ["\v", "\\v"], ["\r", "\\r"], ["\u2028", "\\u2028"], ["\u2029", "\\u2029"]]); function evaluateAndInline(value, valueNode) { if (t__namespace.isJSXExpressionContainer(value)) { evaluateAndInline(value.expression, valueNode.get("expression")); } else if (t__namespace.isObjectProperty(value)) { evaluateAndInline(value.value, valueNode.get("value")); } else if ( t__namespace.isStringLiteral(value) || t__namespace.isNumericLiteral(value) || t__namespace.isBooleanLiteral(value) || t__namespace.isNullLiteral(value)) ; else if (t__namespace.isObjectExpression(value)) {const properties = value.properties; const propertiesNode = valueNode.get("properties"); for (let i = 0; i < properties.length; i++) { evaluateAndInline(properties[i], propertiesNode[i]); } } else { const r = valueNode.evaluate(); if (r.confident) { if (typeof r.value === "string") { valueNode.replaceWith(t__namespace.stringLiteral(r.value)); } else if (typeof r.value === "number") { valueNode.replaceWith(t__namespace.numericLiteral(r.value)); } else if (typeof r.value === "boolean") { valueNode.replaceWith(t__namespace.booleanLiteral(r.value)); } } } } const InlineElements = [ "a", "abbr", "acronym", "b", "bdi", "bdo", "big", "br", "button", "canvas", "cite", "code", "data", "datalist", "del", "dfn", "em", "embed", "i", "iframe", "img", "input", "ins", "kbd", "label", "map", "mark", "meter", "noscript", "object", "output", "picture", "progress", "q", "ruby", "s", "samp", "script", "select", "slot", "small", "span", "strong", "sub", "sup", "svg", "template", "textarea", "time", "u", "tt", "var", "video"]; const BlockElements = [ "address", "article", "aside", "blockquote", "dd", "details", "dialog", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "li", "main", "menu", "nav", "ol", "p", "pre", "section", "table", "ul"]; const alwaysClose = [ "title", "style", "a", "strong", "small", "b", "u", "i", "em", "s", "code", "object", "table", "button", "textarea", "select", "iframe", "script", "noscript", "template", "fieldset"]; function transformElement$3(path, info) { path. get("openingElement"). get("attributes"). forEach((attr) => { evaluateAndInline(attr.node.value, attr.get("value")); }); let tagName = getTagName(path.node), config = getConfig(path), wrapSVG = info.topLevel && tagName != "svg" && SVGElements.has(tagName), voidTag = VoidElements.indexOf(tagName) > -1, isCustomElement = tagName.indexOf("-") > -1 || path. get("openingElement"). get("attributes"). some((a) => a.node?.name?.name === "is" || a.name?.name === "is"), isImportNode = (tagName === "img" || tagName === "iframe") && path. get("openingElement"). get("attributes"). some((a) => a.node.name?.name === "loading"), results = { template: `<${tagName}`, templateWithClosingTags: `<${tagName}`, declarations: [], exprs: [], dynamics: [], postExprs: [], isSVG: wrapSVG, hasCustomElement: isCustomElement, isImportNode, tagName, renderer: "dom", skipTemplate: false }; if (!config.inlineStyles) { path. get("openingElement"). get("attributes"). forEach((a) => { if (a.node.name?.name === "style") { let value = a.node.value.expression ? a.node.value.expression : a.node.value; if (t__namespace.isStringLiteral(value)) { // jsx attribute value is a sting that may takes more than one line value = t__namespace.templateLiteral( [t__namespace.templateElement({ raw: value.value, cooked: value.value })], []); } a.get("value").replaceWith( t__namespace.jSXExpressionContainer(t__namespace.callExpression(t__namespace.arrowFunctionExpression([], value), []))); } }); } path. get("openingElement"). get("attributes"). some((a) => { if (a.node.name?.name === "data-hk") { a.remove(); let filename = ""; try { filename = path.scope.getProgramParent().path.hub.file.opts.filename; } catch (e) {} console.log( "\n" + path. buildCodeFrameError( `"data-hk" attribute found in template, which could potentially cause hydration miss-matches. Usually happens when copying and pasting Solid SSRed code into JSX. Please remove the attribute from the JSX. \n\n${filename}\n`). toString()); } }); if (config.hydratable && (tagName === "html" || tagName === "head" || tagName === "body")) { results.skipTemplate = true; if (tagName === "head" && info.topLevel) { const createComponent = registerImportMethod( path, "createComponent", getRendererConfig(path, "dom").moduleName); const NoHydration = registerImportMethod( path, "NoHydration", getRendererConfig(path, "dom").moduleName); results.exprs.push( t__namespace.expressionStatement( t__namespace.callExpression(createComponent, [NoHydration, t__namespace.objectExpression([])]))); return results; } } if (wrapSVG) { results.template = "" + results.template; results.templateWithClosingTags = "" + results.templateWithClosingTags; } if (!info.skipId) { results.id = path.scope.generateUidIdentifier("el$"); } transformAttributes$2(path, results); if (config.contextToCustomElements && (tagName === "slot" || isCustomElement)) { contextToCustomElement(path, results); } results.template += ">"; results.templateWithClosingTags += ">"; if (!voidTag) { // always close tags can still be skipped if they have no closing parents and are the last element const toBeClosed = !info.lastElement || !config.omitLastClosingTag || info.toBeClosed && (!config.omitNestedClosingTags || info.toBeClosed.has(tagName)); if (toBeClosed) { results.toBeClosed = new Set(info.toBeClosed || alwaysClose); results.toBeClosed.add(tagName); if (InlineElements.includes(tagName)) BlockElements.forEach((i) => results.toBeClosed.add(i)); } else results.toBeClosed = info.toBeClosed; if (tagName !== "noscript") transformChildren$2(path, results, config); if (toBeClosed) results.template += ``; results.templateWithClosingTags += ``; } if (info.topLevel && config.hydratable && results.hasHydratableEvent) { let runHydrationEvents = registerImportMethod( path, "runHydrationEvents", getRendererConfig(path, "dom").moduleName); results.postExprs.push(t__namespace.expressionStatement(t__namespace.callExpression(runHydrationEvents, []))); } if (wrapSVG) { results.template += ""; results.templateWithClosingTags += ""; } return results; } function setAttr$2(path, elem, name, value, { isSVG, dynamic, prevId, isCE, tagName }) { // pull out namespace const config = getConfig(path); let parts, namespace; if ((parts = name.split(":")) && parts[1] && reservedNameSpaces.has(parts[0])) { name = parts[1]; namespace = parts[0]; } if (namespace === "style") { const setStyleProperty = registerImportMethod( path, "setStyleProperty", getRendererConfig(path, "dom").moduleName); return t__namespace.callExpression(setStyleProperty, [ elem, t__namespace.stringLiteral(name), t__namespace.isAssignmentExpression(value) && t__namespace.isIdentifier(value.left) ? value.right : value]); } if (namespace === "class") { return t__namespace.callExpression( t__namespace.memberExpression( t__namespace.memberExpression(elem, t__namespace.identifier("classList")), t__namespace.identifier("toggle")), [ t__namespace.stringLiteral(name), dynamic ? value : t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", value))]); } if (name === "style") { return t__namespace.callExpression( registerImportMethod(path, "style", getRendererConfig(path, "dom").moduleName), prevId ? [elem, value, prevId] : [elem, value]); } if (!isSVG && name === "class") { return t__namespace.callExpression( registerImportMethod(path, "className", getRendererConfig(path, "dom").moduleName), [elem, value]); } if (name === "classList") { return t__namespace.callExpression( registerImportMethod(path, "classList", getRendererConfig(path, "dom").moduleName), prevId ? [elem, value, prevId] : [elem, value]); } if (dynamic && name === "textContent") { if (config.hydratable) { return t__namespace.callExpression(registerImportMethod(path, "setProperty"), [ elem, t__namespace.stringLiteral("data"), value]); } return t__namespace.assignmentExpression("=", t__namespace.memberExpression(elem, t__namespace.identifier("data")), value); } if (namespace === "bool") { return t__namespace.callExpression( registerImportMethod(path, "setBoolAttribute", getRendererConfig(path, "dom").moduleName), [elem, t__namespace.stringLiteral(name), value]); } const isChildProp = ChildProperties.has(name); const isProp = Properties.has(name); const alias = getPropAlias(name, tagName.toUpperCase()); if (namespace !== "attr" && (isChildProp || !isSVG && isProp || isCE || namespace === "prop")) { if (isCE && !isChildProp && !isProp && namespace !== "prop") name = toPropertyName(name); if (config.hydratable && namespace !== "prop") { return t__namespace.callExpression(registerImportMethod(path, "setProperty"), [ elem, t__namespace.stringLiteral(alias || name), value]); } return t__namespace.assignmentExpression( "=", t__namespace.memberExpression(elem, t__namespace.identifier(alias || name)), value); } let isNameSpaced = name.indexOf(":") > -1; name = Aliases[name] || name; !isSVG && (name = name.toLowerCase()); const ns = isNameSpaced && SVGNamespace[name.split(":")[0]]; if (ns) { return t__namespace.callExpression( registerImportMethod(path, "setAttributeNS", getRendererConfig(path, "dom").moduleName), [elem, t__namespace.stringLiteral(ns), t__namespace.stringLiteral(name), value]); } else { return t__namespace.callExpression( registerImportMethod(path, "setAttribute", getRendererConfig(path, "dom").moduleName), [elem, t__namespace.stringLiteral(name), value]); } } function detectResolvableEventHandler(attribute, handler) { while (t__namespace.isIdentifier(handler)) { const lookup = attribute.scope.getBinding(handler.name); if (lookup) { if (t__namespace.isVariableDeclarator(lookup.path.node)) { handler = lookup.path.node.init; } else if (t__namespace.isFunctionDeclaration(lookup.path.node)) { return true; } else return false; } else return false; } return t__namespace.isFunction(handler); } function transformAttributes$2(path, results) { let elem = results.id, hasHydratableEvent = false, children, spreadExpr, attributes = path.get("openingElement").get("attributes"); const tagName = getTagName(path.node), isSVG = SVGElements.has(tagName), isCE = tagName.includes("-") || attributes.some((a) => a.node.name?.name === "is"), hasChildren = path.node.children.length > 0, config = getConfig(path); // preprocess spreads if (attributes.some((attribute) => t__namespace.isJSXSpreadAttribute(attribute.node))) { [attributes, spreadExpr] = processSpreads$1(path, attributes, { elem, isSVG, hasChildren, wrapConditionals: config.wrapConditionals }); path.get("openingElement").set( "attributes", attributes.map((a) => a.node)); //NOTE: can't be checked at compile time so add to compiled output hasHydratableEvent = true; } /** * Inline styles * * 1. When string * 2. When is an object, the key is a string, and value is string/numeric * 3. Remove properties from object when value is undefined/null * 4. When `value.evaluate().confident` * * Also, when `key` is computed value is also `value.evaluate().confident` */ attributes = path.get("openingElement").get("attributes"); const styleAttributes = attributes.filter((a) => a.node.name && a.node.name.name === "style"); if (styleAttributes.length > 0) { let inlinedStyle = ""; for (let i = 0; i < styleAttributes.length; i++) { const attr = styleAttributes[i]; let value = attr.node.value; const node = attr.get("value"); if (t__namespace.isJSXExpressionContainer(value)) { value = value.expression; } if (t__namespace.isStringLiteral(value)) { inlinedStyle += `${value.value.replace(/;$/, "")};`; attr.remove(); } else if (t__namespace.isObjectExpression(value)) { const properties = value.properties; const propertiesNode = node.get("expression").get("properties"); const toRemoveProperty = []; for (let i = 0; i < properties.length; i++) { const property = properties[i]; if (property.computed) { /* { [computed]: `${1+1}px` } => { [computed]: `2px` } */ const r = propertiesNode[i].get("value").evaluate(); if (r.confident && (typeof r.value === "string" || typeof r.value === "number")) { property.value = t__namespace.inherits(t__namespace.stringLiteral(`${r.value}`), property.value); } // computed cannot be inlined - maybe can be evaluated but this is pretty rare continue; } if (t__namespace.isObjectProperty(property)) { const key = t__namespace.isIdentifier(property.key) ? property.key.name : property.key.value; if (t__namespace.isStringLiteral(property.value) || t__namespace.isNumericLiteral(property.value)) { inlinedStyle += `${key}:${property.value.value};`; toRemoveProperty.push(property); } else if ( t__namespace.isIdentifier(property.value) && property.value.name === "undefined" || t__namespace.isNullLiteral(property.value)) { toRemoveProperty.push(property); } else { const r = propertiesNode[i].get("value").evaluate(); if (r.confident && (typeof r.value === "string" || typeof r.value === "number")) { inlinedStyle += `${key}:${r.value};`; toRemoveProperty.push(property); } } } } for (const remove of toRemoveProperty) { value.properties.splice(value.properties.indexOf(remove), 1); } if (value.properties.length === 0) { attr.remove(); } } } if (inlinedStyle !== "") { const styleAttribute = t__namespace.jsxAttribute( t__namespace.jsxIdentifier("style"), t__namespace.stringLiteral(inlinedStyle.replace(/;$/, ""))); path.get("openingElement").node.attributes.push(styleAttribute); } } // preprocess styles const styleAttribute = path. get("openingElement"). get("attributes"). find( (a) => a.node.name && a.node.name.name === "style" && t__namespace.isJSXExpressionContainer(a.node.value) && t__namespace.isObjectExpression(a.node.value.expression) && !a.node.value.expression.properties.some((p) => t__namespace.isSpreadElement(p))); if (styleAttribute) { let i = 0, leading = styleAttribute.node.value.expression.leadingComments; styleAttribute.node.value.expression.properties.slice().forEach((p, index) => { if (!p.computed) { if (leading) p.value.leadingComments = leading; path. get("openingElement"). node.attributes.splice( styleAttribute.key + ++i, 0, t__namespace.jsxAttribute( t__namespace.jsxNamespacedName( t__namespace.jsxIdentifier("style"), t__namespace.jsxIdentifier(t__namespace.isIdentifier(p.key) ? p.key.name : p.key.value)), t__namespace.jsxExpressionContainer(p.value))); styleAttribute.node.value.expression.properties.splice(index - i - 1, 1); } }); if (!styleAttribute.node.value.expression.properties.length) path.get("openingElement").node.attributes.splice(styleAttribute.key, 1); } // preprocess classList attributes = path.get("openingElement").get("attributes"); const classListAttribute = attributes.find( (a) => a.node.name && a.node.name.name === "classList" && t__namespace.isJSXExpressionContainer(a.node.value) && t__namespace.isObjectExpression(a.node.value.expression) && !a.node.value.expression.properties.some( (p) => t__namespace.isSpreadElement(p) || p.computed || t__namespace.isStringLiteral(p.key) && (p.key.value.includes(" ") || p.key.value.includes(":")))); if (classListAttribute) { let i = 0, leading = classListAttribute.node.value.expression.leadingComments, classListProperties = classListAttribute.get("value").get("expression").get("properties"); classListProperties.slice().forEach((propPath, index) => { const p = propPath.node; const { confident, value: computed } = propPath.get("value").evaluate(); if (leading) p.value.leadingComments = leading; if (!confident) { path. get("openingElement"). node.attributes.splice( classListAttribute.key + ++i, 0, t__namespace.jsxAttribute( t__namespace.jsxNamespacedName( t__namespace.jsxIdentifier("class"), t__namespace.jsxIdentifier(t__namespace.isIdentifier(p.key) ? p.key.name : p.key.value)), t__namespace.jsxExpressionContainer(p.value))); } else if (computed) { path. get("openingElement"). node.attributes.splice( classListAttribute.key + ++i, 0, t__namespace.jsxAttribute( t__namespace.jsxIdentifier("class"), t__namespace.stringLiteral(t__namespace.isIdentifier(p.key) ? p.key.name : p.key.value))); } classListProperties.splice(index - i - 1, 1); }); if (!classListProperties.length) path.get("openingElement").node.attributes.splice(classListAttribute.key, 1); } // combine class properties attributes = path.get("openingElement").get("attributes"); const classAttributes = attributes.filter( (a) => a.node.name && (a.node.name.name === "class" || a.node.name.name === "className")); if (classAttributes.length > 1) { const first = classAttributes[0].node, values = [], quasis = [t__namespace.templateElement({ raw: "" })]; for (let i = 0; i < classAttributes.length; i++) { const attr = classAttributes[i].node, isLast = i === classAttributes.length - 1; if (!t__namespace.isJSXExpressionContainer(attr.value)) { const prev = quasis.pop(); quasis.push( t__namespace.templateElement({ raw: (prev ? prev.value.raw : "") + `${attr.value.value}` + (isLast ? "" : " ") })); } else { values.push(t__namespace.logicalExpression("||", attr.value.expression, t__namespace.stringLiteral(""))); quasis.push(t__namespace.templateElement({ raw: isLast ? "" : " " })); } i && attributes.splice(attributes.indexOf(classAttributes[i]), 1); } if (values.length) first.value = t__namespace.jsxExpressionContainer(t__namespace.templateLiteral(quasis, values));else first.value = t__namespace.stringLiteral(quasis[0].value.raw); } path.get("openingElement").set( "attributes", attributes.map((a) => a.node)); let needsSpacing = true; // scoped because of `needsSpacing` function inlineAttributeOnTemplate(isSVG, key, results, value) { !isSVG && (key = key.toLowerCase()); results.template += `${needsSpacing ? " " : ""}${key}`; if (!value) { needsSpacing = true; return; } let text = value.value; if (typeof text === "number") text = String(text); let needsQuoting = !config.omitQuotes; if (key === "style" || key === "class") { text = trimWhitespace(text); if (key === "style") { text = text.replace(/; /g, ";").replace(/: /g, ":"); } } if (!text.length) { needsSpacing = true; results.template += ``; return; } for (let i = 0, len = text.length; i < len; i++) { let char = text[i]; if ( char === "'" || char === '"' || char === " " || char === "\t" || char === "\n" || char === "\r" || char === "`" || char === "=" || char === "<" || char === ">") { needsQuoting = true; } } if (needsQuoting) { needsSpacing = false; results.template += `="${escapeHTML(text, true)}"`; } else { needsSpacing = true; results.template += `=${escapeHTML(text, true)}`; } } path. get("openingElement"). get("attributes"). forEach((attribute) => { const node = attribute.node; let value = node.value, key = t__namespace.isJSXNamespacedName(node.name) ? `${node.name.namespace.name}:${node.name.name.name}` : node.name.name, reservedNameSpace = t__namespace.isJSXNamespacedName(node.name) && reservedNameSpaces.has(node.name.namespace.name); if (t__namespace.isJSXExpressionContainer(value) && !key.startsWith("use:")) { const evaluated = attribute.get("value").get("expression").evaluate().value; let type; if ( evaluated !== undefined && ( (type = typeof evaluated) === "string" || type === "number")) { if (type === "number" && (Properties.has(key) || key.startsWith("prop:"))) { value = t__namespace.jsxExpressionContainer(t__namespace.numericLiteral(evaluated)); } else value = t__namespace.stringLiteral(String(evaluated)); } } if ( t__namespace.isJSXNamespacedName(node.name) && reservedNameSpace && !t__namespace.isJSXExpressionContainer(value)) { node.value = value = t__namespace.jsxExpressionContainer(value || t__namespace.jsxEmptyExpression()); } if ( t__namespace.isJSXExpressionContainer(value) && ( reservedNameSpace || !(t__namespace.isStringLiteral(value.expression) || t__namespace.isNumericLiteral(value.expression)))) { if (key === "ref") { // Normalize expressions for non-null and type-as while ( t__namespace.isTSNonNullExpression(value.expression) || t__namespace.isTSAsExpression(value.expression)) { value.expression = value.expression.expression; } let binding, isConstant = t__namespace.isIdentifier(value.expression) && ( binding = path.scope.getBinding(value.expression.name)) && ( binding.kind === "const" || binding.kind === "module"); if (!isConstant && t__namespace.isLVal(value.expression)) { const refIdentifier = path.scope.generateUidIdentifier("_ref$"); results.exprs.unshift( t__namespace.variableDeclaration("var", [t__namespace.variableDeclarator(refIdentifier, value.expression)]), t__namespace.expressionStatement( t__namespace.conditionalExpression( t__namespace.binaryExpression( "===", t__namespace.unaryExpression("typeof", refIdentifier), t__namespace.stringLiteral("function")), t__namespace.callExpression( registerImportMethod(path, "use", getRendererConfig(path, "dom").moduleName), [refIdentifier, elem]), t__namespace.assignmentExpression("=", value.expression, elem)))); } else if (isConstant || t__namespace.isFunction(value.expression)) { results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod(path, "use", getRendererConfig(path, "dom").moduleName), [value.expression, elem]))); } else { const refIdentifier = path.scope.generateUidIdentifier("_ref$"); results.exprs.unshift( t__namespace.variableDeclaration("var", [t__namespace.variableDeclarator(refIdentifier, value.expression)]), t__namespace.expressionStatement( t__namespace.logicalExpression( "&&", t__namespace.binaryExpression( "===", t__namespace.unaryExpression("typeof", refIdentifier), t__namespace.stringLiteral("function")), t__namespace.callExpression( registerImportMethod(path, "use", getRendererConfig(path, "dom").moduleName), [refIdentifier, elem])))); } } else if (key.startsWith("use:")) { // Some trick to treat JSXIdentifier as Identifier node.name.name.type = "Identifier"; results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod(path, "use", getRendererConfig(path, "dom").moduleName), [ node.name.name, elem, t__namespace.arrowFunctionExpression( [], t__namespace.isJSXEmptyExpression(value.expression) ? t__namespace.booleanLiteral(true) : value.expression)]))); } else if (key === "children") { children = value; } else if (key.startsWith("on")) { const ev = toEventName(key); if (key.startsWith("on:")) { const args = [elem, t__namespace.stringLiteral(key.split(":")[1]), value.expression]; results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod( path, "addEventListener", getRendererConfig(path, "dom").moduleName), args))); } else if (key.startsWith("oncapture:")) { // deprecated see above condition const args = [ t__namespace.stringLiteral(key.split(":")[1]), value.expression, t__namespace.booleanLiteral(true)]; results.exprs.push( t__namespace.expressionStatement( t__namespace.callExpression(t__namespace.memberExpression(elem, t__namespace.identifier("addEventListener")), args))); } else if ( config.delegateEvents && ( DelegatedEvents.has(ev) || config.delegatedEvents.indexOf(ev) !== -1)) { // can only hydrate delegated events hasHydratableEvent = true; const events = attribute.scope.getProgramParent().data.events || ( attribute.scope.getProgramParent().data.events = new Set()); events.add(ev); let handler = value.expression; const resolveable = detectResolvableEventHandler(attribute, handler); if (t__namespace.isArrayExpression(handler)) { if (handler.elements.length > 1) { results.exprs.unshift( t__namespace.expressionStatement( t__namespace.assignmentExpression( "=", t__namespace.memberExpression(elem, t__namespace.identifier(`$$${ev}Data`)), handler.elements[1]))); } handler = handler.elements[0]; results.exprs.unshift( t__namespace.expressionStatement( t__namespace.assignmentExpression( "=", t__namespace.memberExpression(elem, t__namespace.identifier(`$$${ev}`)), handler))); } else if (t__namespace.isFunction(handler) || resolveable) { results.exprs.unshift( t__namespace.expressionStatement( t__namespace.assignmentExpression( "=", t__namespace.memberExpression(elem, t__namespace.identifier(`$$${ev}`)), handler))); } else { results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod( path, "addEventListener", getRendererConfig(path, "dom").moduleName), [elem, t__namespace.stringLiteral(ev), handler, t__namespace.booleanLiteral(true)]))); } } else { let handler = value.expression; const resolveable = detectResolvableEventHandler(attribute, handler); if (t__namespace.isArrayExpression(handler)) { if (handler.elements.length > 1) { handler = t__namespace.arrowFunctionExpression( [t__namespace.identifier("e")], t__namespace.callExpression(handler.elements[0], [handler.elements[1], t__namespace.identifier("e")])); } else handler = handler.elements[0]; results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression(t__namespace.memberExpression(elem, t__namespace.identifier("addEventListener")), [ t__namespace.stringLiteral(ev), handler]))); } else if (t__namespace.isFunction(handler) || resolveable) { results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression(t__namespace.memberExpression(elem, t__namespace.identifier("addEventListener")), [ t__namespace.stringLiteral(ev), handler]))); } else { results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod( path, "addEventListener", getRendererConfig(path, "dom").moduleName), [elem, t__namespace.stringLiteral(ev), handler]))); } } } else if ( config.effectWrapper && ( isDynamic(attribute.get("value").get("expression"), { checkMember: true }) || (key === "classList" || key === "style") && !attribute.get("value").get("expression").evaluate().confident && !hasStaticMarker(value, path))) { /* Following code doesn't repect static marker `@once`. https://github.com/ryansolid/dom-expressions/pull/438 || ( ( key === "classList" || key === "style") && !attribute.get("value").get("expression").evaluate().confident ) ) */ let nextElem = elem; if (key === "value" || key === "checked") { const effectWrapperId = registerImportMethod(path, config.effectWrapper); results.postExprs.push( t__namespace.expressionStatement( t__namespace.callExpression(effectWrapperId, [ t__namespace.arrowFunctionExpression( [], setAttr$2(path, elem, key, value.expression, { tagName, isSVG, isCE }))]))); return; } if (key === "textContent") { nextElem = attribute.scope.generateUidIdentifier("el$"); children = t__namespace.jsxText(" "); children.extra = { raw: " ", rawValue: " " }; results.declarations.push( t__namespace.variableDeclarator(nextElem, t__namespace.memberExpression(elem, t__namespace.identifier("firstChild")))); } results.dynamics.push({ elem: nextElem, key, value: value.expression, isSVG, isCE, tagName }); } else if (key.slice(0, 5) === "attr:") { if (t__namespace.isJSXExpressionContainer(value)) value = value.expression; if (t__namespace.isStringLiteral(value) || t__namespace.isNumericLiteral(value)) { // inlined "attr:" inlineAttributeOnTemplate(isSVG, key.slice(5), results, value); } else { // dynamic "attr:" results.exprs.push( t__namespace.expressionStatement(setAttr$2(attribute, elem, key, value, { isSVG, isCE, tagName }))); } } else if (key.slice(0, 5) === "bool:") { // inline it on the template when possible let content = value; if (t__namespace.isJSXExpressionContainer(content)) content = content.expression; function addBoolAttribute() { results.template += `${needsSpacing ? " " : ""}${key.slice(5)}`; needsSpacing = true; } switch (content.type) { case "StringLiteral":{ if (content.value.length && content.value !== "0") { addBoolAttribute(); } return; } case "NullLiteral":{ return; } case "BooleanLiteral":{ if (content.value) { addBoolAttribute(); } return; } case "Identifier":{ if (content.name === "undefined") { return; } break; }} // when not possible to inline it in the template results.exprs.push( t__namespace.expressionStatement( setAttr$2( attribute, elem, key, t__namespace.isJSXExpressionContainer(value) ? value.expression : value, { isSVG, isCE, tagName }))); } else { results.exprs.push( t__namespace.expressionStatement( setAttr$2(attribute, elem, key, value.expression, { isSVG, isCE, tagName }))); } } else { if (config.hydratable && key === "$ServerOnly") { results.skipTemplate = true; return; } if (t__namespace.isJSXExpressionContainer(value)) value = value.expression; // properties key = Aliases[key] || key; if (value && ChildProperties.has(key)) { results.exprs.push( t__namespace.expressionStatement(setAttr$2(attribute, elem, key, value, { isSVG, isCE, tagName }))); } else { inlineAttributeOnTemplate(isSVG, key, results, value); } } }); if (!hasChildren && children) { path.node.children.push(children); } if (spreadExpr) results.exprs.push(spreadExpr); results.hasHydratableEvent = results.hasHydratableEvent || hasHydratableEvent; } function findLastElement(children, hydratable) { let lastElement = -1, tagName; for (let i = children.length - 1; i >= 0; i--) { const node = children[i].node; if ( hydratable || t__namespace.isJSXText(node) || getStaticExpression(children[i]) !== false || t__namespace.isJSXElement(node) && (tagName = getTagName(node)) && !isComponent(tagName)) { lastElement = i; break; } } return lastElement; } function transformChildren$2(path, results, config) { let tempPath = results.id && results.id.name, tagName = getTagName(path.node), nextPlaceholder, childPostExprs = [], i = 0; const filteredChildren = filterChildren(path.get("children")), lastElement = findLastElement(filteredChildren, config.hydratable), childNodes = filteredChildren.reduce((memo, child, index) => { if (child.isJSXFragment()) { throw new Error( `Fragments can only be used top level in JSX. Not used under a <${tagName}>.`); } const transformed = transformNode(child, { toBeClosed: results.toBeClosed, lastElement: index === lastElement, skipId: !results.id || !detectExpressions(filteredChildren, index, config) }); if (!transformed) return memo; const i = memo.length; if (transformed.text && i && memo[i - 1].text) { memo[i - 1].template += transformed.template; memo[i - 1].templateWithClosingTags += transformed.templateWithClosingTags || transformed.template; } else memo.push(transformed); return memo; }, []); childNodes.forEach((child, index) => { if (!child) return; if (child.tagName && child.renderer !== "dom") { throw new Error(`<${child.tagName}> is not supported in <${tagName}>. Wrap the usage with a component that would render this element, eg. Canvas`); } results.template += child.template; results.templateWithClosingTags += child.templateWithClosingTags || child.template; results.isImportNode = results.isImportNode || child.isImportNode; if (child.id) { if (child.tagName === "head") { if (config.hydratable) { const createComponent = registerImportMethod( path, "createComponent", getRendererConfig(path, "dom").moduleName); const NoHydration = registerImportMethod( path, "NoHydration", getRendererConfig(path, "dom").moduleName); results.exprs.push( t__namespace.expressionStatement( t__namespace.callExpression(createComponent, [NoHydration, t__namespace.objectExpression([])]))); } return; } let getNextMatch; if (config.hydratable && tagName === "html") { getNextMatch = registerImportMethod( path, "getNextMatch", getRendererConfig(path, "dom").moduleName); } const walk = t__namespace.memberExpression( t__namespace.identifier(tempPath), t__namespace.identifier(i === 0 ? "firstChild" : "nextSibling")); results.declarations.push( t__namespace.variableDeclarator( child.id, config.hydratable && tagName === "html" ? t__namespace.callExpression(getNextMatch, [walk, t__namespace.stringLiteral(child.tagName)]) : walk)); results.declarations.push(...child.declarations); results.exprs.push(...child.exprs); results.dynamics.push(...child.dynamics); childPostExprs.push(...child.postExprs); results.hasHydratableEvent = results.hasHydratableEvent || child.hasHydratableEvent; results.hasCustomElement = results.hasCustomElement || child.hasCustomElement; results.isImportNode = results.isImportNode || child.isImportNode; tempPath = child.id.name; nextPlaceholder = null; i++; } else if (child.exprs.length) { let insert = registerImportMethod(path, "insert", getRendererConfig(path, "dom").moduleName); const multi = checkLength(filteredChildren), markers = config.hydratable && multi; // boxed by textNodes if (markers || wrappedByText(childNodes, index)) { let exprId, contentId; if (markers) tempPath = createPlaceholder(path, results, tempPath, i++, "$")[0].name; if (nextPlaceholder) { exprId = nextPlaceholder; } else { [exprId, contentId] = createPlaceholder(path, results, tempPath, i++, markers ? "/" : ""); } if (!markers) nextPlaceholder = exprId; results.exprs.push( t__namespace.expressionStatement( t__namespace.callExpression( insert, contentId ? [results.id, child.exprs[0], exprId, contentId] : [results.id, child.exprs[0], exprId]))); tempPath = exprId.name; } else if (multi) { results.exprs.push( t__namespace.expressionStatement( t__namespace.callExpression(insert, [ results.id, child.exprs[0], nextChild$1(childNodes, index) || t__namespace.nullLiteral()]))); } else { results.exprs.push( t__namespace.expressionStatement(t__namespace.callExpression(insert, [results.id, child.exprs[0]]))); } } else nextPlaceholder = null; }); results.postExprs.unshift(...childPostExprs); } function createPlaceholder(path, results, tempPath, i, char) { const exprId = path.scope.generateUidIdentifier("el$"), config = getConfig(path); let contentId; results.template += ``; results.templateWithClosingTags += ``; if (config.hydratable && char === "/") { contentId = path.scope.generateUidIdentifier("co$"); results.declarations.push( t__namespace.variableDeclarator( t__namespace.arrayPattern([exprId, contentId]), t__namespace.callExpression( registerImportMethod(path, "getNextMarker", getRendererConfig(path, "dom").moduleName), [t__namespace.memberExpression(t__namespace.identifier(tempPath), t__namespace.identifier("nextSibling"))]))); } else results.declarations.push( t__namespace.variableDeclarator( exprId, t__namespace.memberExpression( t__namespace.identifier(tempPath), t__namespace.identifier(i === 0 ? "firstChild" : "nextSibling")))); return [exprId, contentId]; } function nextChild$1(children, index) { return children[index + 1] && (children[index + 1].id || nextChild$1(children, index + 1)); } // reduce unnecessary refs function detectExpressions(children, index, config) { if (children[index - 1]) { const node = children[index - 1].node; if ( t__namespace.isJSXExpressionContainer(node) && !t__namespace.isJSXEmptyExpression(node.expression) && getStaticExpression(children[index - 1]) === false) return true; let tagName; if (t__namespace.isJSXElement(node) && (tagName = getTagName(node)) && isComponent(tagName)) return true; } for (let i = index; i < children.length; i++) { const child = children[i].node; if (t__namespace.isJSXExpressionContainer(child)) { if (!t__namespace.isJSXEmptyExpression(child.expression) && getStaticExpression(children[i]) === false) return true; } else if (t__namespace.isJSXElement(child)) { const tagName = getTagName(child); if (isComponent(tagName)) return true; if ( config.contextToCustomElements && ( tagName === "slot" || tagName.indexOf("-") > -1 || child.openingElement.attributes.some((a) => a.name?.name === "is"))) return true; if ( child.openingElement.attributes.some( (attr) => t__namespace.isJSXSpreadAttribute(attr) || ["textContent", "innerHTML", "innerText"].includes(attr.name.name) || attr.name.namespace && ( attr.name.namespace.name === "use" || attr.name.namespace.name === "prop") || t__namespace.isJSXExpressionContainer(attr.value) && !( t__namespace.isStringLiteral(attr.value.expression) || t__namespace.isNumericLiteral(attr.value.expression)))) return true; const nextChildren = filterChildren(children[i].get("children")); if (nextChildren.length) if (detectExpressions(nextChildren, 0, config)) return true; } } } function contextToCustomElement(path, results) { results.exprs.push( t__namespace.expressionStatement( t__namespace.assignmentExpression( "=", t__namespace.memberExpression(results.id, t__namespace.identifier("_$owner")), t__namespace.callExpression( registerImportMethod(path, "getOwner", getRendererConfig(path, "dom").moduleName), [])))); } function processSpreads$1(path, attributes, { elem, isSVG, hasChildren, wrapConditionals }) { const config = getConfig(path); // TODO: skip but collect the names of any properties after the last spread to not overwrite them const filteredAttributes = []; const spreadArgs = []; let runningObject = []; let dynamicSpread = false; let firstSpread = false; attributes.forEach((attribute) => { const node = attribute.node; const key = !t__namespace.isJSXSpreadAttribute(node) && ( t__namespace.isJSXNamespacedName(node.name) ? `${node.name.namespace.name}:${node.name.name.name}` : node.name.name); if (t__namespace.isJSXSpreadAttribute(node)) { const isStatic = node.innerComments && node.innerComments[0] && node.innerComments[0].value.trim() === config.staticMarker; firstSpread = true; if (runningObject.length) { spreadArgs.push(t__namespace.objectExpression(runningObject)); runningObject = []; } const s = isDynamic(attribute.get("argument"), { checkMember: true }) && (dynamicSpread = true) ? t__namespace.isCallExpression(node.argument) && !node.argument.arguments.length && !t__namespace.isCallExpression(node.argument.callee) && !t__namespace.isMemberExpression(node.argument.callee) ? node.argument.callee : t__namespace.arrowFunctionExpression([], node.argument) : node.argument; spreadArgs.push(isStatic ? t__namespace.objectExpression([t__namespace.spreadElement(s)]) : s); } else if ( (firstSpread || t__namespace.isJSXExpressionContainer(node.value) && isDynamic(attribute.get("value").get("expression"), { checkMember: true })) && canNativeSpread(key, { checkNameSpaces: true })) { const isContainer = t__namespace.isJSXExpressionContainer(node.value); const dynamic = isContainer && isDynamic(attribute.get("value").get("expression"), { checkMember: true }); if (dynamic) { const id = convertJSXIdentifier(node.name); let expr = wrapConditionals && ( t__namespace.isLogicalExpression(node.value.expression) || t__namespace.isConditionalExpression(node.value.expression)) ? transformCondition(attribute.get("value").get("expression"), true) : t__namespace.arrowFunctionExpression([], node.value.expression); runningObject.push( t__namespace.objectMethod( "get", id, [], t__namespace.blockStatement([t__namespace.returnStatement(expr.body)]), !t__namespace.isValidIdentifier(key))); } else { runningObject.push( t__namespace.objectProperty( t__namespace.stringLiteral(key), isContainer ? node.value.expression : node.value || (Properties.has(key) ? t__namespace.booleanLiteral(true) : t__namespace.stringLiteral("")))); } } else filteredAttributes.push(attribute); }); if (runningObject.length) { spreadArgs.push(t__namespace.objectExpression(runningObject)); } const props = spreadArgs.length === 1 && !dynamicSpread ? spreadArgs[0] : t__namespace.callExpression(registerImportMethod(path, "mergeProps"), spreadArgs); return [ filteredAttributes, t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod(path, "spread", getRendererConfig(path, "dom").moduleName), [elem, props, t__namespace.booleanLiteral(isSVG), t__namespace.booleanLiteral(hasChildren)]))]; } function createTemplate$2(path, result, wrap) { const config = getConfig(path); if (result.id) { registerTemplate(path, result); if ( !(result.exprs.length || result.dynamics.length || result.postExprs.length) && result.decl.declarations.length === 1) { return result.decl.declarations[0].init; } else { return t__namespace.callExpression( t__namespace.arrowFunctionExpression( [], t__namespace.blockStatement([ result.decl, ...result.exprs.concat( wrapDynamics$1(path, result.dynamics) || [], result.postExprs || []), t__namespace.returnStatement(result.id)])), []); } } if (wrap && result.dynamic && config.memoWrapper) { return t__namespace.callExpression(registerImportMethod(path, config.memoWrapper), [result.exprs[0]]); } return result.exprs[0]; } function appendTemplates$1(path, templates) { const declarators = templates.map((template) => { const tmpl = { cooked: template.template, raw: escapeStringForTemplate(template.template) }; const shouldUseImportNode = template.isCE || template.isImportNode; const isMathML = /^<(math|annotation|annotation-xml|maction|math|merror|mfrac|mi|mmultiscripts|mn|mo|mover|mpadded|mphantom|mprescripts|mroot|mrow|ms|mspace|msqrt|mstyle|msub|msubsup|msup|mtable|mtd|mtext|mtr|munder|munderover|semantics|menclose|mfenced)(\s|>)/.test( template.template); return t__namespace.variableDeclarator( template.id, t__namespace.addComment( t__namespace.callExpression( registerImportMethod(path, "template", getRendererConfig(path, "dom").moduleName), [t__namespace.templateLiteral([t__namespace.templateElement(tmpl, true)], [])].concat( template.isSVG || shouldUseImportNode || isMathML ? [ t__namespace.booleanLiteral(!!shouldUseImportNode), t__namespace.booleanLiteral(template.isSVG), t__namespace.booleanLiteral(isMathML)] : [])), "leading", "#__PURE__")); }); path.node.body.unshift(t__namespace.variableDeclaration("var", declarators)); } function registerTemplate(path, results) { const { hydratable } = getConfig(path); let decl; if (results.template.length) { let templateDef, templateId; if (!results.skipTemplate) { const templates = path.scope.getProgramParent().data.templates || ( path.scope.getProgramParent().data.templates = []); if (templateDef = templates.find((t) => t.template === results.template)) { templateId = templateDef.id; } else { templateId = path.scope.generateUidIdentifier("tmpl$"); templates.push({ id: templateId, template: results.template, templateWithClosingTags: results.templateWithClosingTags, isSVG: results.isSVG, isCE: results.hasCustomElement, isImportNode: results.isImportNode, renderer: "dom" }); } } decl = t__namespace.variableDeclarator( results.id, hydratable ? t__namespace.callExpression( registerImportMethod(path, "getNextElement", getRendererConfig(path, "dom").moduleName), templateId ? [templateId] : []) : t__namespace.callExpression(templateId, [])); } results.declarations.unshift(decl); results.decl = t__namespace.variableDeclaration("var", results.declarations); } function wrapDynamics$1(path, dynamics) { if (!dynamics.length) return; const config = getConfig(path); const effectWrapperId = registerImportMethod(path, config.effectWrapper); if (dynamics.length === 1) { let dynamicStyle; const prevValue = dynamics[0].key === "classList" || dynamics[0].key === "style" || ( dynamicStyle = dynamics[0].key.startsWith("style:")) ? t__namespace.identifier("_$p") : undefined; if (dynamicStyle) { dynamics[0].value = t__namespace.assignmentExpression("=", prevValue, dynamics[0].value); } else if ( dynamics[0].key.startsWith("class:") && !t__namespace.isBooleanLiteral(dynamics[0].value) && !t__namespace.isUnaryExpression(dynamics[0].value)) { dynamics[0].value = t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", dynamics[0].value)); } return t__namespace.expressionStatement( t__namespace.callExpression(effectWrapperId, [ t__namespace.arrowFunctionExpression( prevValue ? [prevValue] : [], setAttr$2(path, dynamics[0].elem, dynamics[0].key, dynamics[0].value, { isSVG: dynamics[0].isSVG, isCE: dynamics[0].isCE, tagName: dynamics[0].tagName, dynamic: true, prevId: prevValue }))])); } const prevId = t__namespace.identifier("_p$"); /** @type {t.VariableDeclarator[]} */ const declarations = []; /** @type {t.ExpressionStatement[]} */ const statements = []; /** @type {t.Identifier[]} */ const properties = []; dynamics.forEach(({ elem, key, value, isSVG, isCE, tagName }, index) => { const varIdent = path.scope.generateUidIdentifier("v$"); const propIdent = t__namespace.identifier(getNumberedId(index)); const propMember = t__namespace.memberExpression(prevId, propIdent); if (key.startsWith("class:") && !t__namespace.isBooleanLiteral(value) && !t__namespace.isUnaryExpression(value)) { value = t__namespace.unaryExpression("!", t__namespace.unaryExpression("!", value)); } properties.push(propIdent); declarations.push(t__namespace.variableDeclarator(varIdent, value)); if (key === "classList" || key === "style") { statements.push( t__namespace.expressionStatement( t__namespace.assignmentExpression( "=", propMember, setAttr$2(path, elem, key, varIdent, { isSVG, isCE, tagName, dynamic: true, prevId: propMember })))); } else { const prev = key.startsWith("style:") ? varIdent : undefined; statements.push( t__namespace.expressionStatement( t__namespace.logicalExpression( "&&", t__namespace.binaryExpression("!==", varIdent, propMember), setAttr$2(path, elem, key, t__namespace.assignmentExpression("=", propMember, varIdent), { isSVG, isCE, tagName, dynamic: true, prevId: prev })))); } }); return t__namespace.expressionStatement( t__namespace.callExpression(effectWrapperId, [ t__namespace.arrowFunctionExpression( [prevId], t__namespace.blockStatement([ t__namespace.variableDeclaration("var", declarations), ...statements, t__namespace.returnStatement(prevId)])), t__namespace.objectExpression(properties.map((id) => t__namespace.objectProperty(id, t__namespace.identifier("undefined"))))])); } function createTemplate$1(path, result) { if (!result.template) { return result.exprs[0]; } let template, id; if (!Array.isArray(result.template)) { template = t__namespace.stringLiteral(result.template); } else if (result.template.length === 1) { template = t__namespace.stringLiteral(result.template[0]); } else { const strings = result.template.map((tmpl) => t__namespace.stringLiteral(tmpl)); template = t__namespace.arrayExpression(strings); } const templates = path.scope.getProgramParent().data.templates || ( path.scope.getProgramParent().data.templates = []); const found = templates.find((tmp) => { if (t__namespace.isArrayExpression(tmp.template) && t__namespace.isArrayExpression(template)) { return tmp.template.elements.every( (el, i) => template.elements[i] && el.value === template.elements[i].value); } return tmp.template.value === template.value; }); if (!found) { id = path.scope.generateUidIdentifier("tmpl$"); templates.push({ id, template, templateWithClosingTags: template, renderer: "ssr" }); } else id = found.id; if (result.wontEscape) { if (!Array.isArray(result.template) || result.template.length === 1) return id;else if ( Array.isArray(result.template) && result.template.length === 2 && result.templateValues[0].type === "CallExpression" && result.templateValues[0].callee.name === "_$ssrHydrationKey") { // remove unnecessary ssr call when only hydration key is used return t__namespace.binaryExpression( "+", t__namespace.binaryExpression( "+", t__namespace.memberExpression(id, t__namespace.numericLiteral(0), true), result.templateValues[0]), t__namespace.memberExpression(id, t__namespace.numericLiteral(1), true)); } } return t__namespace.callExpression( registerImportMethod(path, "ssr"), Array.isArray(result.template) && result.template.length > 1 ? [id, ...result.templateValues] : [id]); } function appendTemplates(path, templates) { const declarators = templates.map((template) => { return t__namespace.variableDeclarator(template.id, template.template); }); path.node.body.unshift(t__namespace.variableDeclaration("var", declarators)); } function appendToTemplate(template, value) { let array; if (Array.isArray(value)) { [value, ...array] = value; } template[template.length - 1] += value; if (array && array.length) template.push.apply(template, array); } function transformElement$2(path, info) { path. get("openingElement"). get("attributes"). forEach((attr) => { evaluateAndInline(attr.node.value, attr.get("value")); }); const config = getConfig(path); const tagName = getTagName(path.node); if (tagName === "script" || tagName === "style") path.doNotEscape = true; // contains spread attributes if (path.node.openingElement.attributes.some((a) => t__namespace.isJSXSpreadAttribute(a))) return createElement(path, { ...info, ...config }); const voidTag = VoidElements.indexOf(tagName) > -1, results = { template: [`<${tagName}`], templateValues: [], declarations: [], exprs: [], dynamics: [], tagName, wontEscape: path.node.wontEscape, renderer: "ssr" }; if (info.topLevel && config.hydratable) { if (tagName === "head") { registerImportMethod(path, "NoHydration"); registerImportMethod(path, "createComponent"); const child = transformElement$2(path, { ...info, topLevel: false }); results.template = ""; results.templateWithClosingTags = ""; results.exprs.push( t__namespace.callExpression(t__namespace.identifier("_$createComponent"), [ t__namespace.identifier("_$NoHydration"), t__namespace.objectExpression([ t__namespace.objectMethod( "get", t__namespace.identifier("children"), [], t__namespace.blockStatement([t__namespace.returnStatement(createTemplate$1(path, child))]))])])); return results; } results.template.push(""); results.templateValues.push( t__namespace.callExpression(registerImportMethod(path, "ssrHydrationKey"), [])); } transformAttributes$1(path, results, { ...config, ...info }); appendToTemplate(results.template, ">"); if (!voidTag) { transformChildren$1(path, results, { ...config, ...info }); appendToTemplate(results.template, ``); } return results; } function toAttribute(key, isSVG) { key = Aliases[key] || key; !isSVG && (key = key.toLowerCase()); return key; } function setAttr$1(attribute, results, name, value, isSVG) { // strip out namespaces for now, everything at this point is an attribute let parts; if ((parts = name.split(":")) && parts[1] && reservedNameSpaces.has(parts[0])) { name = parts[1]; parts[0]; } name = toAttribute(name, isSVG); const attr = t__namespace.callExpression(registerImportMethod(attribute, "ssrAttribute"), [ t__namespace.stringLiteral(name), value, t__namespace.booleanLiteral(false)]); if (results.template[results.template.length - 1].length) { results.template.push(""); results.templateValues.push(attr); } else { const last = results.templateValues.length - 1; results.templateValues[last] = t__namespace.binaryExpression("+", results.templateValues[last], attr); } } function escapeExpression(path, expression, attr, escapeLiterals) { if ( t__namespace.isStringLiteral(expression) || t__namespace.isNumericLiteral(expression) || t__namespace.isTemplateLiteral(expression) && expression.expressions.length === 0) { if (escapeLiterals) { if (t__namespace.isStringLiteral(expression)) return t__namespace.stringLiteral(escapeHTML(expression.value, attr));else if (t__namespace.isTemplateLiteral(expression)) return t__namespace.stringLiteral(escapeHTML(expression.quasis[0].value.raw, attr)); } return expression; } else if (t__namespace.isFunction(expression)) { if (t__namespace.isBlockStatement(expression.body)) { expression.body.body = expression.body.body.map((e) => { if (t__namespace.isReturnStatement(e)) e.argument = escapeExpression(path, e.argument, attr, escapeLiterals); return e; }); } else expression.body = escapeExpression(path, expression.body, attr, escapeLiterals); return expression; } else if (t__namespace.isTemplateLiteral(expression)) { expression.expressions = expression.expressions.map((e) => escapeExpression(path, e, attr, escapeLiterals)); return expression; } else if (t__namespace.isUnaryExpression(expression)) { return expression; } else if (t__namespace.isBinaryExpression(expression)) { expression.left = escapeExpression(path, expression.left, attr, escapeLiterals); expression.right = escapeExpression(path, expression.right, attr, escapeLiterals); return expression; } else if (t__namespace.isConditionalExpression(expression)) { expression.consequent = escapeExpression(path, expression.consequent, attr, escapeLiterals); expression.alternate = escapeExpression(path, expression.alternate, attr, escapeLiterals); return expression; } else if (t__namespace.isLogicalExpression(expression)) { // Preserve the old short-circuit shape for && while still escaping the // selected result of || and ?? as a whole. if (expression.operator === "&&") { expression.right = escapeExpression(path, expression.right, attr, escapeLiterals); return expression; } } else if (t__namespace.isCallExpression(expression) && t__namespace.isFunction(expression.callee)) { if (t__namespace.isBlockStatement(expression.callee.body)) { expression.callee.body.body = expression.callee.body.body.map((e) => { if (t__namespace.isReturnStatement(e)) e.argument = escapeExpression(path, e.argument, attr, escapeLiterals); return e; }); } else expression.callee.body = escapeExpression(path, expression.callee.body, attr, escapeLiterals); return expression; } else if (t__namespace.isJSXElement(expression) && !isComponent(getTagName(expression))) { expression.wontEscape = true; return expression; } return t__namespace.callExpression( registerImportMethod(path, "escape"), [expression].concat(attr ? [t__namespace.booleanLiteral(true)] : [])); } function transformToObject(attrName, attributes, selectedAttributes) { const properties = []; const existingAttribute = attributes.find((a) => a.node.name.name === attrName); for (let i = 0; i < selectedAttributes.length; i++) { const attr = selectedAttributes[i].node; const computed = !t__namespace.isValidIdentifier(attr.name.name.name); if (!computed) { attr.name.name.type = "Identifier"; } properties.push( t__namespace.objectProperty( computed ? t__namespace.stringLiteral(attr.name.name.name) : attr.name.name, t__namespace.isJSXExpressionContainer(attr.value) ? attr.value.expression : attr.value)); (existingAttribute || i) && attributes.splice(selectedAttributes[i].key, 1); } if ( existingAttribute && t__namespace.isJSXExpressionContainer(existingAttribute.node.value) && t__namespace.isObjectExpression(existingAttribute.node.value.expression)) { existingAttribute.node.value.expression.properties.push(...properties); } else { selectedAttributes[0].node = t__namespace.jsxAttribute( t__namespace.jsxIdentifier(attrName), t__namespace.jsxExpressionContainer(t__namespace.objectExpression(properties))); } } function normalizeAttributes(path) { const attributes = path.get("openingElement").get("attributes"), styleAttributes = attributes.filter( (a) => t__namespace.isJSXNamespacedName(a.node.name) && a.node.name.namespace.name === "style"), classNamespaceAttributes = attributes.filter( (a) => t__namespace.isJSXNamespacedName(a.node.name) && a.node.name.namespace.name === "class"); if (classNamespaceAttributes.length) transformToObject("classList", attributes, classNamespaceAttributes); const classAttributes = attributes.filter( (a) => a.node.name && ( a.node.name.name === "class" || a.node.name.name === "className" || a.node.name.name === "classList")); // combine class propertoes if (classAttributes.length > 1) { const first = classAttributes[0].node, values = [], quasis = [t__namespace.templateElement({ raw: "" })]; for (let i = 0; i < classAttributes.length; i++) { const attr = classAttributes[i].node, isLast = i === classAttributes.length - 1; if (!t__namespace.isJSXExpressionContainer(attr.value)) { const prev = quasis.pop(); quasis.push( t__namespace.templateElement({ raw: (prev ? prev.value.raw : "") + `${attr.value.value}` + (isLast ? "" : " ") })); } else { let expr = attr.value.expression; if (attr.name.name === "classList") { if (t__namespace.isObjectExpression(expr) && !expr.properties.some((p) => t__namespace.isSpreadElement(p))) { transformClasslistObject(path, expr, values, quasis); if (!isLast) quasis[quasis.length - 1].value.raw += " "; i && attributes.splice(attributes.indexOf(classAttributes[i]), 1); continue; } expr = t__namespace.callExpression(registerImportMethod(path, "ssrClassList"), [expr]); } values.push(t__namespace.logicalExpression("||", expr, t__namespace.stringLiteral(""))); quasis.push(t__namespace.templateElement({ raw: isLast ? "" : " " })); } i && attributes.splice(attributes.indexOf(classAttributes[i]), 1); } first.name = t__namespace.jsxIdentifier("class"); first.value = t__namespace.jsxExpressionContainer(t__namespace.templateLiteral(quasis, values)); } if (styleAttributes.length) transformToObject("style", attributes, styleAttributes); return attributes; } function transformAttributes$1(path, results, info) { const tagName = getTagName(path.node), isSVG = SVGElements.has(tagName), hasChildren = path.node.children.length > 0, attributes = normalizeAttributes(path); let children; attributes.forEach((attribute) => { const node = attribute.node; let value = node.value, key = t__namespace.isJSXNamespacedName(node.name) ? `${node.name.namespace.name}:${node.name.name.name}` : node.name.name, reservedNameSpace = t__namespace.isJSXNamespacedName(node.name) && reservedNameSpaces.has(node.name.namespace.name); if ( (t__namespace.isJSXNamespacedName(node.name) && reservedNameSpace || ChildProperties.has(key)) && !t__namespace.isJSXExpressionContainer(value)) { node.value = value = t__namespace.jsxExpressionContainer(value || t__namespace.jsxEmptyExpression()); } if ( t__namespace.isJSXExpressionContainer(value) && ( reservedNameSpace || ChildProperties.has(key) || !( t__namespace.isStringLiteral(value.expression) || t__namespace.isNumericLiteral(value.expression) || t__namespace.isBooleanLiteral(value.expression)))) { if ( key === "ref" || key.startsWith("use:") || key.startsWith("prop:") || key.startsWith("on")) return;else if (ChildProperties.has(key)) { if (info.hydratable && key === "textContent" && value && value.expression) { value.expression = t__namespace.logicalExpression("||", value.expression, t__namespace.stringLiteral(" ")); } if (key === "innerHTML") path.doNotEscape = true; children = value; } else { let doEscape = true; if (key.startsWith("attr:")) key = key.replace("attr:", ""); if (BooleanAttributes.has(key) || key.startsWith("bool:")) { key = key.replace("bool:", ""); results.template.push(""); const fn = t__namespace.callExpression(registerImportMethod(attribute, "ssrAttribute"), [ t__namespace.stringLiteral(key), value.expression, t__namespace.booleanLiteral(true)]); results.templateValues.push(fn); return; } if (key === "style") { if ( t__namespace.isJSXExpressionContainer(value) && t__namespace.isObjectExpression(value.expression) && !value.expression.properties.some((p) => t__namespace.isSpreadElement(p))) { if (value.expression.properties.length === 0) { return; } const props = value.expression.properties.map((p, i) => { if (p.computed) { return t__namespace.callExpression(registerImportMethod(path, "ssrStyleProperty"), [ t__namespace.binaryExpression("+", p.key, t__namespace.stringLiteral(":")), escapeExpression(path, p.value, true, true)]); } return t__namespace.callExpression(registerImportMethod(path, "ssrStyleProperty"), [ t__namespace.stringLiteral( (i ? ";" : "") + (t__namespace.isIdentifier(p.key) ? p.key.name : p.key.value) + ":"), escapeExpression(path, p.value, true, true)]); }); let res = props[0]; for (let i = 1; i < props.length; i++) { res = t__namespace.binaryExpression("+", res, props[i]); } value.expression = res; } else { value.expression = t__namespace.callExpression(registerImportMethod(path, "ssrStyle"), [ value.expression]); } doEscape = false; } if (key === "classList") { if ( t__namespace.isObjectExpression(value.expression) && !value.expression.properties.some((p) => t__namespace.isSpreadElement(p))) { const values = [], quasis = [t__namespace.templateElement({ raw: "" })]; transformClasslistObject(path, value.expression, values, quasis); if (!values.length) value.expression = t__namespace.stringLiteral(quasis[0].value.raw);else if (values.length === 1 && !quasis[0].value.raw && !quasis[1].value.raw) { value.expression = values[0]; } else value.expression = t__namespace.templateLiteral(quasis, values); } else { value.expression = t__namespace.callExpression(registerImportMethod(path, "ssrClassList"), [ value.expression]); } key = "class"; doEscape = false; } if (doEscape) value.expression = escapeExpression(path, value.expression, true); if (!doEscape || t__namespace.isLiteral(value.expression)) { key = toAttribute(key, isSVG); appendToTemplate(results.template, ` ${key}="`); results.template.push(`"`); results.templateValues.push(value.expression); } else setAttr$1(attribute, results, key, value.expression, isSVG); } } else { if (key === "$ServerOnly") return; if (t__namespace.isJSXExpressionContainer(value)) value = value.expression; key = toAttribute(key, isSVG); const isBoolean = BooleanAttributes.has(key); if (isBoolean && value && value.value !== "" && !value.value) return; appendToTemplate(results.template, ` ${key}`); if (!value) return; let text = isBoolean ? "" : value.value; if (key === "style" || key === "class") { text = trimWhitespace(String(text)); if (key === "style") { text = text.replace(/; /g, ";").replace(/: /g, ":"); } } appendToTemplate( results.template, // `String(text)` is needed, as text.length will mess up `attr=10>` becomes `attr>` without it String(text) === "" ? `` : `="${escapeHTML(text, true)}"`); } }); if (!hasChildren && children) { path.node.children.push(children); } } function transformClasslistObject(path, expr, values, quasis) { expr.properties.forEach((prop, i) => { const isLast = expr.properties.length - 1 === i; let key = prop.key; if (t__namespace.isIdentifier(prop.key) && !prop.computed) key = t__namespace.stringLiteral(key.name);else if (prop.computed) { key = t__namespace.callExpression(registerImportMethod(path, "escape"), [ prop.key, t__namespace.booleanLiteral(true)]); } else key = t__namespace.stringLiteral(escapeHTML(prop.key.value)); if (t__namespace.isBooleanLiteral(prop.value)) { if (prop.value.value === true) { if (!prop.computed) { const prev = quasis.pop(); quasis.push( t__namespace.templateElement({ raw: (prev ? prev.value.raw : "") + (i ? " " : "") + `${key.value}` + (isLast ? "" : " ") })); } else { values.push(key); quasis.push(t__namespace.templateElement({ raw: isLast ? "" : " " })); } } } else { values.push(t__namespace.conditionalExpression(prop.value, key, t__namespace.stringLiteral(""))); quasis.push(t__namespace.templateElement({ raw: isLast ? "" : " " })); } }); } function transformChildren$1(path, results, { hydratable }) { const doNotEscape = path.doNotEscape; const filteredChildren = filterChildren(path.get("children")); const multi = checkLength(filteredChildren), markers = hydratable && multi; filteredChildren.forEach((node) => { if (t__namespace.isJSXElement(node.node) && getTagName(node.node) === "head") { const child = transformNode(node, { doNotEscape, hydratable: false }); registerImportMethod(path, "NoHydration"); registerImportMethod(path, "createComponent"); results.template.push(""); results.templateValues.push( t__namespace.callExpression(t__namespace.identifier("_$createComponent"), [ t__namespace.identifier("_$NoHydration"), t__namespace.objectExpression([ t__namespace.objectMethod( "get", t__namespace.identifier("children"), [], t__namespace.blockStatement([t__namespace.returnStatement(createTemplate$1(path, child))]))])])); return; } const child = transformNode(node, { doNotEscape }); if (!child) return; appendToTemplate(results.template, child.template); results.templateValues.push.apply(results.templateValues, child.templateValues || []); if (child.exprs.length) { if (!doNotEscape && !child.spreadElement) child.exprs[0] = escapeExpression(path, child.exprs[0]); // boxed by textNodes if (markers && !child.spreadElement) { appendToTemplate(results.template, ``); results.template.push(""); results.templateValues.push(child.exprs[0]); appendToTemplate(results.template, ``); } else { results.template.push(""); results.templateValues.push(child.exprs[0]); } } }); } function createElement(path, { topLevel, hydratable }) { const tagName = getTagName(path.node), config = getConfig(path), attributes = normalizeAttributes(path), doNotEscape = path.doNotEscape; const filteredChildren = filterChildren(path.get("children")), multi = checkLength(filteredChildren), markers = hydratable && multi, childNodes = filteredChildren.reduce((memo, path) => { if (t__namespace.isJSXText(path.node)) { const v = htmlEntities.decode(trimWhitespace(path.node.extra.raw)); if (v.length) memo.push(t__namespace.stringLiteral(v)); } else { const child = transformNode(path); if (markers && child.exprs.length && !child.spreadElement) memo.push(t__namespace.stringLiteral("")); if (child.exprs.length && !doNotEscape && !child.spreadElement) child.exprs[0] = escapeExpression(path, child.exprs[0]); memo.push(getCreateTemplate(config, path, child)(path, child, true)); if (markers && child.exprs.length && !child.spreadElement) memo.push(t__namespace.stringLiteral("")); } return memo; }, []); let props; if (attributes.length === 1) { props = [attributes[0].node.argument]; } else { props = []; let runningObject = [], dynamicSpread = false, hasChildren = path.node.children.length > 0; attributes.forEach((attribute) => { const node = attribute.node; if (t__namespace.isJSXSpreadAttribute(node)) { if (runningObject.length) { props.push(t__namespace.objectExpression(runningObject)); runningObject = []; } props.push( isDynamic(attribute.get("argument"), { checkMember: true }) && (dynamicSpread = true) ? t__namespace.isCallExpression(node.argument) && !node.argument.arguments.length && !t__namespace.isCallExpression(node.argument.callee) && !t__namespace.isMemberExpression(node.argument.callee) ? node.argument.callee : t__namespace.arrowFunctionExpression([], node.argument) : node.argument); } else { const value = node.value || t__namespace.booleanLiteral(true), id = convertJSXIdentifier(node.name), key = t__namespace.isJSXNamespacedName(node.name) ? `${node.name.namespace.name}:${node.name.name.name}` : node.name.name; if (hasChildren && key === "children") return; if ( key === "ref" || key.startsWith("use:") || key.startsWith("prop:") || key.startsWith("on")) return; if (t__namespace.isJSXExpressionContainer(value)) { if ( isDynamic(attribute.get("value").get("expression"), { checkMember: true, checkTags: true })) { let expr = t__namespace.arrowFunctionExpression([], value.expression); runningObject.push( t__namespace.objectMethod( "get", id, [], t__namespace.blockStatement([t__namespace.returnStatement(expr.body)]), !t__namespace.isValidIdentifier(key))); } else runningObject.push(t__namespace.objectProperty(id, value.expression));} else runningObject.push(t__namespace.objectProperty(id, value)); } }); if (runningObject.length || !props.length) props.push(t__namespace.objectExpression(runningObject)); if (props.length > 1 || dynamicSpread) { props = [t__namespace.callExpression(registerImportMethod(path, "mergeProps"), props)]; } } const exprs = [ t__namespace.callExpression(registerImportMethod(path, "ssrElement"), [ t__namespace.stringLiteral(tagName), props[0], childNodes.length ? hydratable ? t__namespace.arrowFunctionExpression( [], childNodes.length === 1 ? childNodes[0] : t__namespace.arrayExpression(childNodes)) : childNodes.length === 1 ? childNodes[0] : t__namespace.arrayExpression(childNodes) : t__namespace.identifier("undefined"), t__namespace.booleanLiteral(Boolean(topLevel && config.hydratable))])]; return { exprs, template: "", spreadElement: true }; } function transformElement$1(path, info) { path. get("openingElement"). get("attributes"). forEach((attr) => { evaluateAndInline(attr.node.value, attr.get("value")); }); let tagName = getTagName(path.node), results = { id: path.scope.generateUidIdentifier("el$"), declarations: [], exprs: [], dynamics: [], postExprs: [], tagName, renderer: "universal" }; results.declarations.push( t__namespace.variableDeclarator( results.id, t__namespace.callExpression( registerImportMethod( path, "createElement", getRendererConfig(path, "universal").moduleName), [t__namespace.stringLiteral(tagName)]))); transformAttributes(path, results); transformChildren(path, results); return results; } function transformAttributes(path, results) { let children, spreadExpr; let attributes = path.get("openingElement").get("attributes"); const elem = results.id, hasChildren = path.node.children.length > 0, config = getConfig(path); // preprocess spreads if (attributes.some((attribute) => t__namespace.isJSXSpreadAttribute(attribute.node))) { [attributes, spreadExpr] = processSpreads(path, attributes, { elem, hasChildren, wrapConditionals: config.wrapConditionals }); path.get("openingElement").set( "attributes", attributes.map((a) => a.node)); } path. get("openingElement"). get("attributes"). forEach((attribute) => { const node = attribute.node; let value = node.value, key = t__namespace.isJSXNamespacedName(node.name) ? `${node.name.namespace.name}:${node.name.name.name}` : node.name.name, reservedNameSpace = t__namespace.isJSXNamespacedName(node.name) && node.name.namespace.name === "use"; if ( t__namespace.isJSXNamespacedName(node.name) && reservedNameSpace && !t__namespace.isJSXExpressionContainer(value)) { node.value = value = t__namespace.jsxExpressionContainer(value || t__namespace.jsxEmptyExpression()); } if (t__namespace.isJSXExpressionContainer(value)) { if (key === "ref") { // Normalize expressions for non-null and type-as while ( t__namespace.isTSNonNullExpression(value.expression) || t__namespace.isTSAsExpression(value.expression)) { value.expression = value.expression.expression; } let binding, isConstant = t__namespace.isIdentifier(value.expression) && ( binding = path.scope.getBinding(value.expression.name)) && ( binding.kind === "const" || binding.kind === "module"); if (!isConstant && t__namespace.isLVal(value.expression)) { const refIdentifier = path.scope.generateUidIdentifier("_ref$"); results.exprs.unshift( t__namespace.variableDeclaration("var", [t__namespace.variableDeclarator(refIdentifier, value.expression)]), t__namespace.expressionStatement( t__namespace.conditionalExpression( t__namespace.binaryExpression( "===", t__namespace.unaryExpression("typeof", refIdentifier), t__namespace.stringLiteral("function")), t__namespace.callExpression( registerImportMethod( path, "use", getRendererConfig(path, "universal").moduleName), [refIdentifier, elem]), t__namespace.assignmentExpression("=", value.expression, elem)))); } else if (isConstant || t__namespace.isFunction(value.expression)) { results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod( path, "use", getRendererConfig(path, "universal").moduleName), [value.expression, elem]))); } else { const refIdentifier = path.scope.generateUidIdentifier("_ref$"); results.exprs.unshift( t__namespace.variableDeclaration("var", [t__namespace.variableDeclarator(refIdentifier, value.expression)]), t__namespace.expressionStatement( t__namespace.logicalExpression( "&&", t__namespace.binaryExpression( "===", t__namespace.unaryExpression("typeof", refIdentifier), t__namespace.stringLiteral("function")), t__namespace.callExpression( registerImportMethod( path, "use", getRendererConfig(path, "universal").moduleName), [refIdentifier, elem])))); } } else if (key.startsWith("use:")) { // Some trick to treat JSXIdentifier as Identifier node.name.name.type = "Identifier"; results.exprs.unshift( t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod(path, "use", getRendererConfig(path, "universal").moduleName), [ node.name.name, elem, t__namespace.arrowFunctionExpression( [], t__namespace.isJSXEmptyExpression(value.expression) ? t__namespace.booleanLiteral(true) : value.expression)]))); } else if (key === "children") { children = value; } else if ( config.effectWrapper && isDynamic(attribute.get("value").get("expression"), { checkMember: true })) { results.dynamics.push({ elem, key, value: value.expression }); } else { results.exprs.push( t__namespace.expressionStatement(setAttr(attribute, elem, key, value.expression))); } } else { results.exprs.push(t__namespace.expressionStatement(setAttr(attribute, elem, key, value))); } }); if (spreadExpr) results.exprs.push(spreadExpr); if (!hasChildren && children) { path.node.children.push(children); } } function setAttr(path, elem, name, value, { prevId } = {}) { if (!value) value = t__namespace.booleanLiteral(true); return t__namespace.callExpression( registerImportMethod(path, "setProp", getRendererConfig(path, "universal").moduleName), prevId ? [elem, t__namespace.stringLiteral(name), value, prevId] : [elem, t__namespace.stringLiteral(name), value]); } function transformChildren(path, results) { const filteredChildren = filterChildren(path.get("children")), multi = checkLength(filteredChildren), childNodes = filteredChildren.map(transformNode).reduce((memo, child) => { if (!child) return memo; const i = memo.length; if (child.text && i && memo[i - 1].text) { memo[i - 1].template += child.template; memo[i - 1].templateWithClosingTags += child.templateWithClosingTags || child.template; } else memo.push(child); return memo; }, []); const appends = []; childNodes.forEach((child, index) => { if (!child) return; if (child.tagName && child.renderer !== "universal") { throw new Error(`<${child.tagName}> is not supported in <${getTagName(path.node)}>. Wrap the usage with a component that would render this element, eg. Canvas`); } if (child.id) { let insertNode = registerImportMethod( path, "insertNode", getRendererConfig(path, "universal").moduleName); let insert = child.id; if (child.text) { let createTextNode = registerImportMethod( path, "createTextNode", getRendererConfig(path, "universal").moduleName); if (multi) { results.declarations.push( t__namespace.variableDeclarator( child.id, t__namespace.callExpression(createTextNode, [ t__namespace.templateLiteral( [t__namespace.templateElement({ raw: escapeStringForTemplate(child.template) })], [])]))); } else insert = t__namespace.callExpression(createTextNode, [ t__namespace.templateLiteral( [t__namespace.templateElement({ raw: escapeStringForTemplate(child.template) })], [])]); } appends.push(t__namespace.expressionStatement(t__namespace.callExpression(insertNode, [results.id, insert]))); results.declarations.push(...child.declarations); results.exprs.push(...child.exprs); results.dynamics.push(...child.dynamics); } else if (child.exprs.length) { let insert = registerImportMethod( path, "insert", getRendererConfig(path, "universal").moduleName); if (multi) { results.exprs.push( t__namespace.expressionStatement( t__namespace.callExpression(insert, [ results.id, child.exprs[0], nextChild(childNodes, index) || t__namespace.nullLiteral()]))); } else { results.exprs.push( t__namespace.expressionStatement(t__namespace.callExpression(insert, [results.id, child.exprs[0]]))); } } }); results.exprs.unshift(...appends); } function nextChild(children, index) { return children[index + 1] && (children[index + 1].id || nextChild(children, index + 1)); } function processSpreads(path, attributes, { elem, hasChildren, wrapConditionals }) { // TODO: skip but collect the names of any properties after the last spread to not overwrite them const filteredAttributes = []; const spreadArgs = []; let runningObject = []; let dynamicSpread = false; let firstSpread = false; attributes.forEach((attribute) => { const node = attribute.node; const key = !t__namespace.isJSXSpreadAttribute(node) && ( t__namespace.isJSXNamespacedName(node.name) ? `${node.name.namespace.name}:${node.name.name.name}` : node.name.name); if (t__namespace.isJSXSpreadAttribute(node)) { firstSpread = true; if (runningObject.length) { spreadArgs.push(t__namespace.objectExpression(runningObject)); runningObject = []; } spreadArgs.push( isDynamic(attribute.get("argument"), { checkMember: true }) && (dynamicSpread = true) ? t__namespace.isCallExpression(node.argument) && !node.argument.arguments.length && !t__namespace.isCallExpression(node.argument.callee) && !t__namespace.isMemberExpression(node.argument.callee) ? node.argument.callee : t__namespace.arrowFunctionExpression([], node.argument) : node.argument); } else if ( (firstSpread || t__namespace.isJSXExpressionContainer(node.value) && isDynamic(attribute.get("value").get("expression"), { checkMember: true })) && canNativeSpread(key, { checkNameSpaces: true })) { const isContainer = t__namespace.isJSXExpressionContainer(node.value); const dynamic = isContainer && isDynamic(attribute.get("value").get("expression"), { checkMember: true }); if (dynamic) { const id = convertJSXIdentifier(node.name); let expr = wrapConditionals && ( t__namespace.isLogicalExpression(node.value.expression) || t__namespace.isConditionalExpression(node.value.expression)) ? transformCondition(attribute.get("value").get("expression"), true) : t__namespace.arrowFunctionExpression([], node.value.expression); runningObject.push( t__namespace.objectMethod( "get", id, [], t__namespace.blockStatement([t__namespace.returnStatement(expr.body)]), !t__namespace.isValidIdentifier(key))); } else { runningObject.push( t__namespace.objectProperty( t__namespace.stringLiteral(key), isContainer ? node.value.expression : node.value || t__namespace.booleanLiteral(true))); } } else filteredAttributes.push(attribute); }); if (runningObject.length) { spreadArgs.push(t__namespace.objectExpression(runningObject)); } const props = spreadArgs.length === 1 && !dynamicSpread ? spreadArgs[0] : t__namespace.callExpression(registerImportMethod(path, "mergeProps"), spreadArgs); return [ filteredAttributes, t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod(path, "spread", getRendererConfig(path, "universal").moduleName), [elem, props, t__namespace.booleanLiteral(hasChildren)]))]; } function createTemplate(path, result, wrap) { const config = getConfig(path); if (result.id) { result.decl = t__namespace.variableDeclaration("var", result.declarations); if ( !(result.exprs.length || result.dynamics.length || result.postExprs.length) && result.decl.declarations.length === 1) { return result.decl.declarations[0].init; } else { return t__namespace.callExpression( t__namespace.arrowFunctionExpression( [], t__namespace.blockStatement([ result.decl, ...result.exprs.concat( wrapDynamics(path, result.dynamics) || [], result.postExprs || []), t__namespace.returnStatement(result.id)])), []); } } if (wrap && result.dynamic && config.memoWrapper) { return t__namespace.callExpression(registerImportMethod(path, config.memoWrapper), [result.exprs[0]]); } return result.exprs[0]; } function wrapDynamics(path, dynamics) { if (!dynamics.length) return; const config = getConfig(path); const effectWrapperId = registerImportMethod(path, config.effectWrapper); if (dynamics.length === 1) { const prevValue = t__namespace.identifier("_$p"); return t__namespace.expressionStatement( t__namespace.callExpression(effectWrapperId, [ t__namespace.arrowFunctionExpression( [prevValue], setAttr(path, dynamics[0].elem, dynamics[0].key, dynamics[0].value, { dynamic: true, prevId: prevValue }))])); } const prevId = t__namespace.identifier("_p$"); /** @type {t.VariableDeclarator[]} */ const declarations = []; /** @type {t.ExpressionStatement[]} */ const statements = []; /** @type {t.Identifier[]} */ const properties = []; dynamics.forEach(({ elem, key, value }, index) => { const varIdent = path.scope.generateUidIdentifier("v$"); const propIdent = t__namespace.identifier(getNumberedId(index)); const propMember = t__namespace.memberExpression(prevId, propIdent); properties.push(propIdent); declarations.push(t__namespace.variableDeclarator(varIdent, value)); statements.push( t__namespace.expressionStatement( t__namespace.logicalExpression( "&&", t__namespace.binaryExpression("!==", varIdent, propMember), t__namespace.assignmentExpression( "=", propMember, setAttr(path, elem, key, varIdent, { dynamic: true, prevId: propMember }))))); }); return t__namespace.expressionStatement( t__namespace.callExpression(effectWrapperId, [ t__namespace.arrowFunctionExpression( [prevId], t__namespace.blockStatement([ t__namespace.variableDeclaration("var", declarations), ...statements, t__namespace.returnStatement(prevId)])), t__namespace.objectExpression( properties.map((id) => t__namespace.objectProperty(id, t__namespace.identifier("undefined"))))])); } function convertComponentIdentifier(node) { if (t__namespace.isJSXIdentifier(node)) { if (node.name === "this") return t__namespace.thisExpression(); if (t__namespace.isValidIdentifier(node.name)) node.type = "Identifier";else return t__namespace.stringLiteral(node.name); } else if (t__namespace.isJSXMemberExpression(node)) { const prop = convertComponentIdentifier(node.property); const computed = t__namespace.isStringLiteral(prop); return t__namespace.memberExpression(convertComponentIdentifier(node.object), prop, computed); } return node; } function transformComponent(path) { let exprs = [], config = getConfig(path), tagId = convertComponentIdentifier(path.node.openingElement.name), props = [], runningObject = [], dynamicSpread = false, hasChildren = path.node.children.length > 0; if (config.builtIns.indexOf(tagId.name) > -1 && !path.scope.hasBinding(tagId.name)) { const newTagId = registerImportMethod(path, tagId.name); tagId.name = newTagId.name; } path. get("openingElement"). get("attributes"). forEach((attribute) => { const node = attribute.node; if (t__namespace.isJSXSpreadAttribute(node)) { if (runningObject.length) { props.push(t__namespace.objectExpression(runningObject)); runningObject = []; } props.push( isDynamic(attribute.get("argument"), { checkMember: true }) && (dynamicSpread = true) ? t__namespace.isCallExpression(node.argument) && !node.argument.arguments.length && !t__namespace.isCallExpression(node.argument.callee) && !t__namespace.isMemberExpression(node.argument.callee) ? node.argument.callee : t__namespace.arrowFunctionExpression([], node.argument) : node.argument); } else { // handle weird babel bug around HTML entities const value = (t__namespace.isStringLiteral(node.value) ? t__namespace.stringLiteral(node.value.value) : node.value) || t__namespace.booleanLiteral(true), id = convertJSXIdentifier(node.name), key = id.name; if (hasChildren && key === "children") return; if (t__namespace.isJSXExpressionContainer(value)) { if (key === "ref") { if (config.generate === "ssr") return; // Normalize expressions for non-null and type-as while ( t__namespace.isTSNonNullExpression(value.expression) || t__namespace.isTSAsExpression(value.expression) || t__namespace.isTSSatisfiesExpression(value.expression)) { value.expression = value.expression.expression; } let binding, isConstant = t__namespace.isIdentifier(value.expression) && ( binding = path.scope.getBinding(value.expression.name)) && ( binding.kind === "const" || binding.kind === "module"); if (!isConstant && t__namespace.isLVal(value.expression)) { const refIdentifier = path.scope.generateUidIdentifier("_ref$"); runningObject.push( t__namespace.objectMethod( "method", t__namespace.identifier("ref"), [t__namespace.identifier("r$")], t__namespace.blockStatement([ t__namespace.variableDeclaration("var", [ t__namespace.variableDeclarator(refIdentifier, value.expression)]), t__namespace.expressionStatement( t__namespace.conditionalExpression( t__namespace.binaryExpression( "===", t__namespace.unaryExpression("typeof", refIdentifier), t__namespace.stringLiteral("function")), t__namespace.callExpression(refIdentifier, [t__namespace.identifier("r$")]), t__namespace.assignmentExpression("=", value.expression, t__namespace.identifier("r$"))))]))); } else if (!isConstant && t__namespace.isOptionalMemberExpression(value.expression)) { const refIdentifier = path.scope.generateUidIdentifier("_ref$"); runningObject.push( t__namespace.objectMethod( "method", t__namespace.identifier("ref"), [t__namespace.identifier("r$")], t__namespace.blockStatement([ t__namespace.variableDeclaration("var", [ t__namespace.variableDeclarator(refIdentifier, value.expression)]), t__namespace.expressionStatement( t__namespace.conditionalExpression( t__namespace.binaryExpression( "===", t__namespace.unaryExpression("typeof", refIdentifier), t__namespace.stringLiteral("function")), t__namespace.callExpression(refIdentifier, [t__namespace.identifier("r$")]), t__namespace.logicalExpression( "&&", t__namespace.unaryExpression( "!", t__namespace.unaryExpression("!", t__namespace.identifier(value.expression.object.name))), t__namespace.assignmentExpression( "=", t__namespace.memberExpression( t__namespace.identifier(value.expression.object.name), t__namespace.identifier(value.expression.property.name)), t__namespace.identifier("r$")))))]))); } else if (isConstant || t__namespace.isFunction(value.expression)) { runningObject.push(t__namespace.objectProperty(t__namespace.identifier("ref"), value.expression)); } else if (t__namespace.isCallExpression(value.expression)) { const refIdentifier = path.scope.generateUidIdentifier("_ref$"); runningObject.push( t__namespace.objectMethod( "method", t__namespace.identifier("ref"), [t__namespace.identifier("r$")], t__namespace.blockStatement([ t__namespace.variableDeclaration("var", [ t__namespace.variableDeclarator(refIdentifier, value.expression)]), t__namespace.expressionStatement( t__namespace.logicalExpression( "&&", t__namespace.binaryExpression( "===", t__namespace.unaryExpression("typeof", refIdentifier), t__namespace.stringLiteral("function")), t__namespace.callExpression(refIdentifier, [t__namespace.identifier("r$")])))]))); } } else if ( isDynamic(attribute.get("value").get("expression"), { checkMember: true, checkTags: true })) { if ( config.wrapConditionals && config.generate !== "ssr" && ( t__namespace.isLogicalExpression(value.expression) || t__namespace.isConditionalExpression(value.expression))) { const expr = transformCondition(attribute.get("value").get("expression"), true); runningObject.push( t__namespace.objectMethod( "get", id, [], t__namespace.blockStatement([t__namespace.returnStatement(expr.body)]), !t__namespace.isValidIdentifier(key))); } else if ( t__namespace.isCallExpression(value.expression) && t__namespace.isArrowFunctionExpression(value.expression.callee) && value.expression.callee.params.length === 0) { const callee = value.expression.callee; const body = t__namespace.isBlockStatement(callee.body) ? callee.body : t__namespace.blockStatement([t__namespace.returnStatement(callee.body)]); runningObject.push(t__namespace.objectMethod("get", id, [], body, !t__namespace.isValidIdentifier(key))); } else { runningObject.push( t__namespace.objectMethod( "get", id, [], t__namespace.blockStatement([t__namespace.returnStatement(value.expression)]), !t__namespace.isValidIdentifier(key))); } } else runningObject.push(t__namespace.objectProperty(id, value.expression));} else runningObject.push(t__namespace.objectProperty(id, value)); } }); const childResult = transformComponentChildren(path.get("children"), config); if (childResult && childResult[0]) { if (childResult[1]) { const body = t__namespace.isCallExpression(childResult[0]) && t__namespace.isFunction(childResult[0].arguments[0]) ? childResult[0].arguments[0].body : childResult[0].body ? childResult[0].body : childResult[0]; runningObject.push( t__namespace.objectMethod( "get", t__namespace.identifier("children"), [], t__namespace.isExpression(body) ? t__namespace.blockStatement([t__namespace.returnStatement(body)]) : body)); } else runningObject.push(t__namespace.objectProperty(t__namespace.identifier("children"), childResult[0])); } if (runningObject.length || !props.length) props.push(t__namespace.objectExpression(runningObject)); if (props.length > 1 || dynamicSpread) { props = [t__namespace.callExpression(registerImportMethod(path, "mergeProps"), props)]; } const componentArgs = [tagId, props[0]]; exprs.push(t__namespace.callExpression(registerImportMethod(path, "createComponent"), componentArgs)); // handle hoisting conditionals if (exprs.length > 1) { const ret = exprs.pop(); exprs = [ t__namespace.callExpression( t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([...exprs, t__namespace.returnStatement(ret)])), [])]; } return { exprs, template: "", component: true }; } function transformComponentChildren(children, config) { const filteredChildren = filterChildren(children); if (!filteredChildren.length) return; let dynamic = false; let pathNodes = []; let transformedChildren = filteredChildren.reduce((memo, path) => { if (t__namespace.isJSXText(path.node)) { const v = htmlEntities.decode(trimWhitespace(path.node.extra.raw)); if (v.length) { pathNodes.push(path.node); memo.push(t__namespace.stringLiteral(v)); } } else { const child = transformNode(path, { topLevel: true, componentChild: true, lastElement: true }); dynamic = dynamic || child.dynamic; if ( config.generate === "ssr" && filteredChildren.length > 1 && child.dynamic && t__namespace.isFunction(child.exprs[0])) { child.exprs[0] = child.exprs[0].body; } pathNodes.push(path.node); memo.push(getCreateTemplate(config, path, child)(path, child, filteredChildren.length > 1)); } return memo; }, []); if (transformedChildren.length === 1) { transformedChildren = transformedChildren[0]; if ( !t__namespace.isJSXExpressionContainer(pathNodes[0]) && !t__namespace.isJSXSpreadChild(pathNodes[0]) && !t__namespace.isJSXText(pathNodes[0])) { transformedChildren = t__namespace.isCallExpression(transformedChildren) && !transformedChildren.arguments.length && !t__namespace.isIdentifier(transformedChildren.callee) ? transformedChildren.callee : t__namespace.arrowFunctionExpression([], transformedChildren); dynamic = true; } } else { transformedChildren = t__namespace.arrowFunctionExpression([], t__namespace.arrayExpression(transformedChildren)); dynamic = true; } return [transformedChildren, dynamic]; } function transformFragmentChildren(children, results, config) { const filteredChildren = filterChildren(children), childNodes = filteredChildren.reduce((memo, path) => { if (t__namespace.isJSXText(path.node)) { const v = htmlEntities.decode(trimWhitespace(path.node.extra.raw)); if (v.length) memo.push(t__namespace.stringLiteral(v)); } else { const child = transformNode(path, { topLevel: true, fragmentChild: true, lastElement: true }); memo.push(getCreateTemplate(config, path, child)(path, child, true)); } return memo; }, []); results.exprs.push(childNodes.length === 1 ? childNodes[0] : t__namespace.arrayExpression(childNodes)); } function transformJSX(path, state) { if (state.skip) return; const config = getConfig(path); const replace = transformThis(path); const result = transformNode( path, t__namespace.isJSXFragment(path.node) ? {} : { topLevel: true, lastElement: true }); const template = getCreateTemplate(config, path, result); path.replaceWith(replace(template(path, result, false))); path.traverse({ enter(path) { if ( path.node.leadingComments && path.node.leadingComments[0] && path.node.leadingComments[0].value.trim() === config.staticMarker) { path.node.leadingComments.shift(); } } }); } function getTargetFunctionParent(path, parent) { let current = path.scope.getFunctionParent(); while (current !== parent && current.path.isArrowFunctionExpression()) { current = current.path.parentPath.scope.getFunctionParent(); } return current; } function transformThis(path) { const parent = path.scope.getFunctionParent(); let thisId; path.traverse({ ThisExpression(path) { const current = getTargetFunctionParent(path, parent); if (current === parent) { thisId || (thisId = path.scope.generateUidIdentifier("self$")); path.replaceWith(thisId); } }, JSXElement(path) { let source = path.get("openingElement").get("name"); while (source.isJSXMemberExpression()) { source = source.get("object"); } if (source.isJSXIdentifier() && source.node.name === "this") { const current = getTargetFunctionParent(path, parent); if (current === parent) { thisId || (thisId = path.scope.generateUidIdentifier("self$")); source.replaceWith(t__namespace.jsxIdentifier(thisId.name)); if (path.node.closingElement) { path.node.closingElement.name = path.node.openingElement.name; } } } } }); return (node) => { if (thisId) { if (!parent || parent.block.type === "ClassMethod") { const decl = t__namespace.variableDeclaration("const", [ t__namespace.variableDeclarator(thisId, t__namespace.thisExpression())]); if (parent) { const stmt = path.getStatementParent(); stmt.insertBefore(decl); } else { return t__namespace.callExpression( t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([decl, t__namespace.returnStatement(node)])), []); } } else { parent.push({ id: thisId, init: t__namespace.thisExpression(), kind: "const" }); } } return node; }; } function transformNode(path, info = {}) { const config = getConfig(path); const node = path.node; let staticValue; if (t__namespace.isJSXElement(node)) { return transformElement(config, path, info); } else if (t__namespace.isJSXFragment(node)) { let results = { template: "", declarations: [], exprs: [], dynamics: [] }; // <>
transformFragmentChildren(path.get("children"), results, config); return results; } else if (t__namespace.isJSXText(node) || (staticValue = getStaticExpression(path)) !== false) { const text = staticValue !== undefined ? info.doNotEscape ? staticValue.toString() : escapeHTML(staticValue.toString()) : trimWhitespace(node.extra.raw); if (!text.length) return null; const results = { template: text, declarations: [], exprs: [], dynamics: [], postExprs: [], text: true }; if (!info.skipId && config.generate !== "ssr") results.id = path.scope.generateUidIdentifier("el$"); return results; } else if (t__namespace.isJSXExpressionContainer(node)) { if (t__namespace.isJSXEmptyExpression(node.expression)) return null; if ( !isDynamic(path.get("expression"), { checkMember: true, checkTags: !!info.componentChild, native: !info.componentChild })) { return { exprs: [node.expression], template: "" }; } const expr = config.wrapConditionals && config.generate !== "ssr" && ( t__namespace.isLogicalExpression(node.expression) || t__namespace.isConditionalExpression(node.expression)) ? transformCondition(path.get("expression"), info.componentChild || info.fragmentChild) : !info.componentChild && ( config.generate !== "ssr" || info.fragmentChild) && t__namespace.isCallExpression(node.expression) && !t__namespace.isCallExpression(node.expression.callee) && !t__namespace.isMemberExpression(node.expression.callee) && node.expression.arguments.length === 0 ? node.expression.callee : t__namespace.arrowFunctionExpression([], node.expression); return { exprs: expr.length > 1 ? [ t__namespace.callExpression( t__namespace.arrowFunctionExpression( [], t__namespace.blockStatement([expr[0], t__namespace.returnStatement(expr[1])])), [])] : [expr], template: "", dynamic: true }; } else if (t__namespace.isJSXSpreadChild(node)) { if ( !isDynamic(path.get("expression"), { checkMember: true, native: !info.componentChild })) return { exprs: [node.expression], template: "" }; const expr = t__namespace.arrowFunctionExpression([], node.expression); return { exprs: [expr], template: "", dynamic: true }; } } function getCreateTemplate(config, path, result) { if (result.tagName && result.renderer === "dom" || config.generate === "dom") { return createTemplate$2; } if (result.renderer === "ssr" || config.generate === "ssr") { return createTemplate$1; } return createTemplate; } function transformElement(config, path, info = {}) { const node = path.node; let tagName = getTagName(node); // if (isComponent(tagName)) return transformComponent(path); //
// const element = getTransformElemet(config, path, tagName); const tagRenderer = (config.renderers ?? []).find((renderer) => renderer.elements.includes(tagName)); if (tagRenderer?.name === "dom" || getConfig(path).generate === "dom") { return transformElement$3(path, info); } if (getConfig(path).generate === "ssr") { return transformElement$2(path, info); } return transformElement$1(path); } // import parse5 from "parse5"; const parse5 = require("parse5"); /** `bodyElement` will be used as a `context` (The place where we run `innerHTML`) */ const bodyElement = parse5.parse( `` // @ts-ignore ).childNodes[1].childNodes[1]; function innerHTML(htmlFragment) { /** `htmlFragment` will be parsed as if it was set to the `bodyElement`'s `innerHTML` property. */ const parsedFragment = parse5.parseFragment(bodyElement, htmlFragment); /** `serialize` returns back a string from the parsed nodes */ return parse5.serialize(parsedFragment); } /** * Returns an object with information when the markup is invalid * * @param {string} html - The html string to validate * @returns {{ * html: string; // html stripped of attributives and content * browser: string; // what the browser returned from evaluating `html` * } | null} */ function isInvalidMarkup(html) { html = html // normalize dom-expressions comments, so comments location are also validated .replaceAll("", ""). replaceAll("", ""). replaceAll("", "") // replace text nodes // text nodes are problematic, think "doesn't" vs "doesn't" // we can detect if text nodes were moved by the browser when the `#text` moves // replace text nodes that isnt in between tags by `#text` .replace(/^[^<]+/, "#text"). replace(/[^>]+$/, "#text") // replace text nodes in between tags by `#text` .replace(/>[^<]+#text<") // remove attributes (the lack of quotes will make it mismatch) // attributes are not longer added to `templateWithClosingTags` // https://github.com/solidjs/solid/issues/2338 // .replace(/<([a-z0-9-:]+)\s+[^>]+>/gi, "<$1>") // fix escaping, so doesnt mess up the validation // `<script>a();</script>` -> `<script>a();</script>` .replace(/<([^>]+)>/gi, "<$1>"); // edge cases (safe to assume they will use the partial in the right place) // table cells if (/^<(td|th)>/.test(html)) { html = `${html}
`; } // table rows if (/^/.test(html)) { html = `${html}
`; } // table misc if (/^/.test(html)) { html = `${html}
`; } // table components if (/^<(thead|tbody|tfoot|colgroup|caption)>/.test(html)) { html = `${html}
`; } // skip when equal to: switch (html) { // empty table components case "
": case "
": case "
": case "
":{ return; }} /** Parse HTML. `browser` is a string with the supposed resulting html of a real `innerHTML` call */ const browser = innerHTML(html); if (html.toLowerCase() !== browser.toLowerCase()) { return { html, browser }; } } // add to the top/bottom of the module. var postprocess = ((path, state) => { if (state.skip) return; if (path.scope.data.events) { path.node.body.push( t__namespace.expressionStatement( t__namespace.callExpression( registerImportMethod(path, "delegateEvents", getRendererConfig(path, "dom").moduleName), [t__namespace.arrayExpression(Array.from(path.scope.data.events).map((e) => t__namespace.stringLiteral(e)))]))); } if (path.scope.data.templates?.length) { if (path.hub.file.metadata.config.validate) { for (const template of path.scope.data.templates) { const html = template.templateWithClosingTags; // not sure when/why this is not a string if (typeof html === "string") { const result = isInvalidMarkup(html); if (result) { const message = "\nThe HTML provided is malformed and will yield unexpected output when evaluated by a browser.\n"; console.warn(message); console.warn("User HTML:\n", result.html); console.warn("Browser HTML:\n", result.browser); console.warn("Original HTML:\n", html); // throw path.buildCodeFrameError(); } } } } let domTemplates = path.scope.data.templates.filter((temp) => temp.renderer === "dom"); let ssrTemplates = path.scope.data.templates.filter((temp) => temp.renderer === "ssr"); domTemplates.length > 0 && appendTemplates$1(path, domTemplates); ssrTemplates.length > 0 && appendTemplates(path, ssrTemplates); } }); var config = { moduleName: "dom", generate: "dom", hydratable: false, delegateEvents: true, delegatedEvents: [], builtIns: [], requireImportSource: false, wrapConditionals: true, omitNestedClosingTags: false, omitLastClosingTag: true, omitQuotes: true, contextToCustomElements: false, staticMarker: "@once", effectWrapper: "effect", memoWrapper: "memo", validate: true, inlineStyles: true }; var preprocess = ((path, state) => { const merged = path.hub.file.metadata.config = Object.assign({}, config, state.opts); const lib = merged.requireImportSource; if (lib) { const comments = path.hub.file.ast.comments; let process = false; for (let i = 0; i < comments.length; i++) { const comment = comments[i]; const pieces = comment.value.split("@jsxImportSource"); if (pieces.length === 2 && pieces[1].trim() === lib) { process = true; break; } } if (!process) { state.skip = true; return; } } }); var index = (() => { return { name: "JSX DOM Expressions", inherits: SyntaxJSX.default, visitor: { JSXElement: transformJSX, JSXFragment: transformJSX, Program: { enter: preprocess, exit: postprocess } } }; }); module.exports = index;