@teleporthq/teleport-plugin-html-base-component 0.36.5 → 0.37.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,10 +22,11 @@ import {
22
22
  UIDLComponentOutputOptions,
23
23
  UIDLElement,
24
24
  ElementsLookup,
25
+ UIDLConditionalNode,
25
26
  } from '@teleporthq/teleport-types'
26
27
  import { join, relative } from 'path'
27
- import { HASTBuilders, HASTUtils } from '@teleporthq/teleport-plugin-common'
28
- import { StringUtils, UIDLUtils } from '@teleporthq/teleport-shared'
28
+ import { HASTBuilders, HASTUtils, ASTUtils } from '@teleporthq/teleport-plugin-common'
29
+ import { GenericUtils, StringUtils, UIDLUtils } from '@teleporthq/teleport-shared'
29
30
  import { staticNode } from '@teleporthq/teleport-uidl-builders'
30
31
  import { createCSSPlugin } from '@teleporthq/teleport-plugin-css'
31
32
  import { generateUniqueKeys, createNodesLookup } from '@teleporthq/teleport-uidl-resolver'
@@ -128,7 +129,69 @@ export const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastTex
128
129
  return dynamicNode
129
130
 
130
131
  case 'conditional':
131
- return HASTBuilders.createComment('Conditional nodes are not supported in HTML')
132
+ const conditionalNodeComment = HASTBuilders.createComment(
133
+ 'Conditional nodes are not supported in HTML'
134
+ )
135
+ const {
136
+ value: staticValue,
137
+ reference,
138
+ condition: { conditions, matchingCriteria },
139
+ } = node.content
140
+
141
+ if (reference.type !== 'dynamic') {
142
+ return conditionalNodeComment
143
+ }
144
+
145
+ const {
146
+ content: { referenceType, id },
147
+ } = reference
148
+
149
+ switch (referenceType) {
150
+ case 'prop': {
151
+ const usedProp = propDefinitions[id]
152
+ if (usedProp === undefined || usedProp.defaultValue === undefined) {
153
+ return conditionalNodeComment
154
+ }
155
+
156
+ // Since we know the operand and the default value from the prop.
157
+ // We can try building the condition and check if the condition is true or false.
158
+ // @todo: You can only use a 'value' in UIDL or 'conditions' but not both.
159
+ // UIDL validations need to be improved on this aspect.
160
+ const dynamicConditions = createConditionalStatement(
161
+ staticValue !== undefined ? [{ operand: staticValue, operation: '===' }] : conditions,
162
+ usedProp.defaultValue
163
+ )
164
+ const matchCondition = matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
165
+ const conditionString = dynamicConditions.join(` ${matchCondition} `)
166
+
167
+ try {
168
+ // tslint:disable-next-line function-constructor
169
+ const isConditionPassing = new Function(`return ${conditionString}`)()
170
+ if (isConditionPassing) {
171
+ return generateHtmlSyntax(
172
+ node.content.node,
173
+ compName,
174
+ nodesLookup,
175
+ propDefinitions,
176
+ stateDefinitions,
177
+ subComponentOptions,
178
+ structure
179
+ )
180
+ }
181
+ } catch (error) {
182
+ return conditionalNodeComment
183
+ }
184
+
185
+ return conditionalNodeComment
186
+ }
187
+
188
+ case 'state':
189
+ default:
190
+ return conditionalNodeComment
191
+ }
192
+
193
+ case 'expr':
194
+ return HASTBuilders.createComment('Expressions are not supported in HTML')
132
195
 
133
196
  default:
134
197
  throw new HTMLComponentGeneratorError(
@@ -141,6 +204,40 @@ export const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastTex
141
204
  }
142
205
  }
143
206
 
207
+ const createConditionalStatement = (
208
+ conditions: UIDLConditionalNode['content']['condition']['conditions'],
209
+ leftOperand: UIDLPropDefinition['defaultValue']
210
+ ) => {
211
+ return conditions.map((condition) => {
212
+ const { operation, operand } = condition
213
+
214
+ if (operand === undefined) {
215
+ return `${ASTUtils.convertToUnaryOperator(operation)}${getValueType(operand)}`
216
+ }
217
+
218
+ return `${getValueType(leftOperand)} ${ASTUtils.convertToBinaryOperator(
219
+ operation
220
+ )} ${getValueType(operand)}`
221
+ })
222
+ }
223
+
224
+ const getValueType = (value: UIDLPropDefinition['defaultValue']) => {
225
+ const valueType = typeof value
226
+ switch (valueType) {
227
+ case 'string':
228
+ return `"${value}"`
229
+ case 'number':
230
+ return value
231
+ case 'boolean':
232
+ return value
233
+ default:
234
+ throw new HTMLComponentGeneratorError(
235
+ `Conditional node received an operand of type ${valueType} \n
236
+ Received ${JSON.stringify(value)}`
237
+ )
238
+ }
239
+ }
240
+
144
241
  const generateElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastText>> = async (
145
242
  node,
146
243
  compName,
@@ -339,10 +436,15 @@ const generateComponentContent = async (
339
436
  const combinedStates = { ...stateDefinitions, ...(componentClone?.stateDefinitions || {}) }
340
437
  const statesForInstance = Object.keys(combinedStates).reduce(
341
438
  (acc: Record<string, UIDLStateDefinition>, propKey) => {
342
- if (attrs[propKey]) {
439
+ const attr = attrs[propKey]
440
+ if (attr.type === 'object') {
441
+ throw new Error(`Object attributes are not supported in html exports`)
442
+ }
443
+
444
+ if (attr) {
343
445
  acc[propKey] = {
344
446
  ...combinedStates[propKey],
345
- defaultValue: attrs[propKey]?.content || combinedStates[propKey]?.defaultValue,
447
+ defaultValue: attr?.content || combinedStates[propKey]?.defaultValue,
346
448
  }
347
449
  } else {
348
450
  acc[propKey] = combinedStates[propKey]
@@ -358,8 +460,9 @@ const generateComponentContent = async (
358
460
  // We check if we are passing any props and pick the value from the atrrs, if not we pick the value from the propDefinitions of
359
461
  // the component instance that we are using here.
360
462
  for (const propKey of Object.keys(combinedProps)) {
361
- const prop = attrs[propKey]
362
- if (prop?.type === 'element') {
463
+ const attribute = attrs[propKey]
464
+
465
+ if (attribute?.type === 'element') {
363
466
  propsForInstance[propKey] = {
364
467
  ...combinedProps[propKey],
365
468
  defaultValue: attrs[propKey],
@@ -373,13 +476,15 @@ const generateComponentContent = async (
373
476
  subComponentOptions,
374
477
  structure
375
478
  )
376
- } else if (prop?.type === 'dynamic') {
479
+ }
480
+
481
+ if (attribute?.type === 'dynamic') {
377
482
  // When we are using a component instance in a component and the attribute
378
483
  // that is passed to the component is of dynamic reference.
379
484
  // If means, the component is redirecting the prop that is received to the prop of the component that it is consuming.
380
485
  // In this case, we need to pass the value of the prop that is received to the prop of the component that it is consuming.
381
486
  // And similary we do the same for the states.
382
- switch (prop.content.referenceType) {
487
+ switch (attribute.content.referenceType) {
383
488
  case 'prop':
384
489
  propsForInstance[propKey] = combinedProps[propKey]
385
490
  break
@@ -388,15 +493,30 @@ const generateComponentContent = async (
388
493
  break
389
494
  default:
390
495
  throw new Error(
391
- `ReferenceType ${prop.content.referenceType} is not supported in HTML Export.`
496
+ `ReferenceType ${attribute.content.referenceType} is not supported in HTML Export.`
392
497
  )
393
498
  }
394
- } else if (prop) {
499
+ }
500
+
501
+ if (attribute?.type === 'object') {
395
502
  propsForInstance[propKey] = {
396
503
  ...combinedProps[propKey],
397
- defaultValue: attrs[propKey]?.content || combinedProps[propKey]?.defaultValue,
504
+ defaultValue: (attribute?.content as object) || combinedProps[propKey]?.defaultValue,
398
505
  }
399
- } else {
506
+ }
507
+
508
+ if (
509
+ attribute?.type !== 'dynamic' &&
510
+ attribute?.type !== 'element' &&
511
+ attribute?.type !== 'object'
512
+ ) {
513
+ propsForInstance[propKey] = {
514
+ ...combinedProps[propKey],
515
+ defaultValue: attribute?.content || combinedProps[propKey]?.defaultValue,
516
+ }
517
+ }
518
+
519
+ if (attribute === undefined) {
400
520
  const propFromCurrentComponent = combinedProps[propKey]
401
521
  if (propFromCurrentComponent.type === 'element' && propFromCurrentComponent.defaultValue) {
402
522
  await generateHtmlSyntax(
@@ -501,11 +621,35 @@ const generateDynamicNode: NodeToHTML<UIDLDynamicReference, Promise<HastNode | H
501
621
  propDefinitions,
502
622
  stateDefinitions
503
623
  ): Promise<HastNode | HastText> => {
624
+ if (node.content.referenceType === 'locale') {
625
+ const localeTag = HASTBuilders.createHTMLNode('span')
626
+ const commentNode = HASTBuilders.createComment(`Content for locale ${node.content.id}`)
627
+ HASTUtils.addChildNode(localeTag, commentNode)
628
+ return localeTag
629
+ }
630
+
504
631
  const usedReferenceValue = getValueFromReference(
505
632
  node.content.id,
506
633
  node.content.referenceType === 'prop' ? propDefinitions : stateDefinitions
507
634
  )
508
635
 
636
+ if (usedReferenceValue.type === 'object' && usedReferenceValue.defaultValue) {
637
+ // Let's say users are biding the prop to a node using something like this "fields.Title"
638
+ // But the fields in the object is the value where the object is defined either in propDefinitions
639
+ // or on the attrs. So, we just need to parsed the rest of the object path and get the value from the object.
640
+ const pathKeys: string[] = node.content.id.split(/\.|\[(['"]?)(.+?)\1\]/).filter(Boolean)
641
+ pathKeys.shift()
642
+
643
+ const value = GenericUtils.getValueFromPath(
644
+ pathKeys.join('.'),
645
+ usedReferenceValue.defaultValue as Record<string, UIDLPropDefinition>
646
+ )
647
+
648
+ if (value) {
649
+ return HASTBuilders.createTextNode(String(value))
650
+ }
651
+ }
652
+
509
653
  if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue) {
510
654
  const elementNode = usedReferenceValue.defaultValue as UIDLElementNode
511
655
 
@@ -651,6 +795,7 @@ const handleAttributes = (
651
795
  case 'element':
652
796
  case 'import':
653
797
  case 'expr':
798
+ case 'object':
654
799
  break
655
800
 
656
801
  default: {