From 8d915d7b5cd004cdaef38a8b531711ff2141b93a Mon Sep 17 00:00:00 2001 From: TudorCe Date: Wed, 11 Dec 2024 11:41:59 +0200 Subject: [PATCH] Fix static array mapper in HTML and missing styles/attrs on its inner nodes --- .../src/utils/hast-utils.ts | 34 ++++++- .../src/node-handlers.ts | 94 ++++++++++--------- 2 files changed, 81 insertions(+), 47 deletions(-) diff --git a/packages/teleport-plugin-common/src/utils/hast-utils.ts b/packages/teleport-plugin-common/src/utils/hast-utils.ts index 45d4d2178..30dd53b5b 100644 --- a/packages/teleport-plugin-common/src/utils/hast-utils.ts +++ b/packages/teleport-plugin-common/src/utils/hast-utils.ts @@ -3,24 +3,48 @@ import { createTextNode } from '../builders/hast-builders' import type { JSXElement } from '@babel/types' import { isJSXElement } from './ast-utils' -export const addBooleanAttributeToNode = (node: HastNode, key: string, value: boolean = true) => { +export const addBooleanAttributeToNode = ( + node: HastNode | HastNode[], + key: string, + value: boolean = true +) => { + if (Array.isArray(node)) { + node.forEach((subnode) => (subnode.properties[key] = value === true ? '' : false)) + return + } node.properties[key] = value === true ? '' : false // Adding boolean attributes is currently onyl supported for template generators } -export const addAttributeToNode = (node: HastNode, key: string, value: string) => { +export const addAttributeToNode = (node: HastNode | HastNode[], key: string, value: string) => { + if (Array.isArray(node)) { + node.forEach((subnode) => (subnode.properties[key] = value)) + return + } node.properties[key] = value } -export const addClassToNode = (node: HastNode, className: string) => { +export const addClassToNode = (node: HastNode | HastNode[], className: string) => { + if (Array.isArray(node)) { + node.forEach((subnode) => (subnode.properties.class = className)) + return + } node.properties.class = className } -export const addChildNode = (node: HastNode, child: HastNode | HastText) => { +export const addChildNode = (node: HastNode | HastNode[], child: HastNode | HastText) => { + if (Array.isArray(node)) { + node.forEach((subnode) => subnode.children.push(child)) + return + } node.children.push(child) } -export const addTextNode = (node: HastNode, text: string) => { +export const addTextNode = (node: HastNode | HastNode[], text: string) => { + if (Array.isArray(node)) { + node.forEach((subnode) => subnode.children.push(createTextNode(text))) + return + } node.children.push(createTextNode(text)) } diff --git a/packages/teleport-plugin-html-base-component/src/node-handlers.ts b/packages/teleport-plugin-html-base-component/src/node-handlers.ts index 977ac78a6..d93a07473 100644 --- a/packages/teleport-plugin-html-base-component/src/node-handlers.ts +++ b/packages/teleport-plugin-html-base-component/src/node-handlers.ts @@ -46,23 +46,21 @@ const isValidURL = (url: string) => { const addNodeToLookup = ( key: string, - node: UIDLElementNode, - tag: HastNode | HastText, - nodesLoookup: Record, - hierarchy: string[] = [] + tag: HastNode | HastText | Array, + nodesLoookup: Record> ) => { // In html code-generation we combine the nodes of the component that is being consumed with the current component. // As html can't load the component at runtime like react or any other frameworks. So, we merge the component as a standalone // component in the current component. - if (nodesLoookup[key]) { - throw new HTMLComponentGeneratorError( - `\n${hierarchy.join(' -> ')} \n -Duplicate key found in nodesLookup: ${node.content.key} \n + const currentLookup = nodesLoookup[key] + if (currentLookup) { + if (Array.isArray(currentLookup)) { + Array.isArray(tag) ? currentLookup.push(...tag) : currentLookup.push(tag) + } else { + nodesLoookup[key] = Array.isArray(tag) ? [currentLookup, ...tag] : [currentLookup, tag] + } -A node with the same key already exists\n -Received \n\n ${JSON.stringify(tag)}\n ${JSON.stringify(node)} -Existing \n\n ${JSON.stringify(nodesLoookup[key])} \n\n` - ) + return } nodesLoookup[key] = tag @@ -71,7 +69,7 @@ Existing \n\n ${JSON.stringify(nodesLoookup[key])} \n\n` type NodeToHTML = ( node: NodeType, componentName: string, - nodesLookup: Record, + nodesLookup: Record>, propDefinitions: Record, stateDefinitions: Record, subComponentOptions: { @@ -93,7 +91,10 @@ type NodeToHTML = ( } ) => ReturnType -export const generateHtmlSyntax: NodeToHTML> = async ( +export const generateHtmlSyntax: NodeToHTML< + UIDLNode, + Promise> +> = async ( node, compName, nodesLookup, @@ -307,22 +308,39 @@ const generateRepeaterNode: NodeToHTML< const { nodes } = node.content const contextId = node.content.renderPropIdentifier - const sourceProp = node.content.source - const propDef = + const sourceValue = node.content.source + let propDef = propDefinitions[ - Object.keys(propDefinitions).find((propKey) => sourceProp.includes(propKey)) || '' + Object.keys(propDefinitions).find((propKey) => sourceValue.includes(propKey)) || '' ] - propDefinitions[contextId] = propDef + if (!propDef || !Array.isArray(propDef.defaultValue)) { + // If no prop is found we might have a static source value + try { + const parsedSource = JSON.parse(sourceValue) + propDef = { + defaultValue: parsedSource, + id: contextId, + type: 'array', + } + } catch { + // Silent fail + } + } + // We do the check again to keep typescript happy, otherwise this could be in catch if (!propDef || !Array.isArray(propDef.defaultValue)) { return HASTBuilders.createComment( 'CMS Array Mapper/Repeater not supported in HTML without a prop source' ) } + propDefinitions[contextId] = propDef const elementNode = HASTBuilders.createHTMLNode('div') node.content.nodes.list.content.style = { display: { type: 'static', content: 'contents' } } + if (node.content.nodes.empty) { + node.content.nodes.empty.content.style = { display: { type: 'static', content: 'contents' } } + } // Empty case if (propDef.defaultValue.length === 0) { const emptyChildren = nodes.empty.content.children @@ -345,6 +363,8 @@ const generateRepeaterNode: NodeToHTML< } } } + + addNodeToLookup(`${node.content.nodes.empty.content.key}`, elementNode, nodesLookup) return elementNode } @@ -372,17 +392,14 @@ const generateRepeaterNode: NodeToHTML< } } - addNodeToLookup( - `${node.content.nodes.list.content.key}`, - node.content.nodes.list, - elementNode, - nodesLookup, - [compName] - ) + addNodeToLookup(`${node.content.nodes.list.content.key}`, elementNode, nodesLookup) return elementNode } -const generateElementNode: NodeToHTML> = async ( +const generateElementNode: NodeToHTML< + UIDLElementNode, + Promise> +> = async ( node, compName, nodesLookup, @@ -404,7 +421,6 @@ const generateElementNode: NodeToHTML + nodesLookup: Record> ): ElementsLookup => { const lookup: ElementsLookup = {} for (const node of Object.keys(nodesLookup)) { @@ -499,8 +507,7 @@ const createLookupTable = ( const generateComponentContent = async ( node: UIDLElementNode, - compName: string, - nodesLookup: Record, + nodesLookup: Record>, propDefinitions: Record, stateDefinitions: Record, subComponentOptions: { @@ -768,11 +775,14 @@ const generateComponentContent = async ( } }) - addNodeToLookup(node.content.key, node, compTag, nodesLookup, [compName, component.name]) + addNodeToLookup(node.content.key, compTag, nodesLookup) return compTag } -const generateDynamicNode: NodeToHTML> = async ( +const generateDynamicNode: NodeToHTML< + UIDLDynamicReference, + Promise> +> = async ( node, compName, nodesLookup, @@ -780,7 +790,7 @@ const generateDynamicNode: NodeToHTML => { +): Promise> => { if (node.content.referenceType === 'locale') { const localeTag = HASTBuilders.createHTMLNode('span') const commentNode = HASTBuilders.createComment(`Content for locale ${node.content.id}`)