@teleporthq/teleport-plugin-html-base-component 0.38.5 → 0.38.9

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teleporthq/teleport-plugin-html-base-component",
3
- "version": "0.38.5",
3
+ "version": "0.38.9",
4
4
  "description": "A plugin for handling the skeleton/baseline of a base html component",
5
5
  "author": "teleportHQ",
6
6
  "license": "MIT",
@@ -24,11 +24,11 @@
24
24
  "build": "tsc -p tsconfig.json && tsc -p tsconfig.json --module commonjs --outDir dist/cjs"
25
25
  },
26
26
  "dependencies": {
27
- "@teleporthq/teleport-plugin-common": "^0.38.5",
28
- "@teleporthq/teleport-plugin-css": "^0.38.5",
27
+ "@teleporthq/teleport-plugin-common": "^0.38.9",
28
+ "@teleporthq/teleport-plugin-css": "^0.38.9",
29
29
  "@teleporthq/teleport-shared": "^0.38.3",
30
30
  "@teleporthq/teleport-types": "^0.38.3",
31
31
  "@teleporthq/teleport-uidl-builders": "^0.38.3"
32
32
  },
33
- "gitHead": "3ac5a427c7ce0620dc1e61dfc71544ef515537ad"
33
+ "gitHead": "a0c0698b2c4f14e9e3c4cbe62f9e57d231a6c2e4"
34
34
  }
@@ -24,6 +24,7 @@ import {
24
24
  ElementsLookup,
25
25
  UIDLConditionalNode,
26
26
  PropDefaultValueTypes,
27
+ UIDLCMSListRepeaterNode,
27
28
  } from '@teleporthq/teleport-types'
28
29
  import { join, relative } from 'path'
29
30
  import { HASTBuilders, HASTUtils, ASTUtils } from '@teleporthq/teleport-plugin-common'
@@ -45,23 +46,21 @@ const isValidURL = (url: string) => {
45
46
 
46
47
  const addNodeToLookup = (
47
48
  key: string,
48
- node: UIDLElementNode,
49
- tag: HastNode | HastText,
50
- nodesLoookup: Record<string, HastNode | HastText>,
51
- hierarchy: string[] = []
49
+ tag: HastNode | HastText | Array<HastNode | HastText>,
50
+ nodesLoookup: Record<string, HastNode | HastText | Array<HastNode | HastText>>
52
51
  ) => {
53
52
  // In html code-generation we combine the nodes of the component that is being consumed with the current component.
54
53
  // As html can't load the component at runtime like react or any other frameworks. So, we merge the component as a standalone
55
54
  // component in the current component.
56
- if (nodesLoookup[key]) {
57
- throw new HTMLComponentGeneratorError(
58
- `\n${hierarchy.join(' -> ')} \n
59
- Duplicate key found in nodesLookup: ${node.content.key} \n
55
+ const currentLookup = nodesLoookup[key]
56
+ if (currentLookup) {
57
+ if (Array.isArray(currentLookup)) {
58
+ Array.isArray(tag) ? currentLookup.push(...tag) : currentLookup.push(tag)
59
+ } else {
60
+ nodesLoookup[key] = Array.isArray(tag) ? [currentLookup, ...tag] : [currentLookup, tag]
61
+ }
60
62
 
61
- A node with the same key already exists\n
62
- Received \n\n ${JSON.stringify(tag)}\n ${JSON.stringify(node)}
63
- Existing \n\n ${JSON.stringify(nodesLoookup[key])} \n\n`
64
- )
63
+ return
65
64
  }
66
65
 
67
66
  nodesLoookup[key] = tag
@@ -70,7 +69,7 @@ Existing \n\n ${JSON.stringify(nodesLoookup[key])} \n\n`
70
69
  type NodeToHTML<NodeType, ReturnType> = (
71
70
  node: NodeType,
72
71
  componentName: string,
73
- nodesLookup: Record<string, HastNode | HastText>,
72
+ nodesLookup: Record<string, HastNode | HastText | Array<HastNode | HastText>>,
74
73
  propDefinitions: Record<string, UIDLPropDefinition>,
75
74
  stateDefinitions: Record<string, UIDLStateDefinition>,
76
75
  subComponentOptions: {
@@ -82,17 +81,28 @@ type NodeToHTML<NodeType, ReturnType> = (
82
81
  dependencies: Record<string, UIDLDependency>
83
82
  options: GeneratorOptions
84
83
  outputOptions: UIDLComponentOutputOptions
84
+ },
85
+ /**
86
+ * This param is just to be able to handle CMS array mappers/Repeater nodes. A bit hacky, better support should be implemented
87
+ */
88
+ resolvedExpressions?: {
89
+ expressions: Record<string, UIDLPropDefinition>
90
+ currentIndex: number
85
91
  }
86
92
  ) => ReturnType
87
93
 
88
- export const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastText>> = async (
94
+ export const generateHtmlSyntax: NodeToHTML<
95
+ UIDLNode,
96
+ Promise<HastNode | HastText | Array<HastNode | HastText>>
97
+ > = async (
89
98
  node,
90
99
  compName,
91
100
  nodesLookup,
92
101
  propDefinitions,
93
102
  stateDefinitions,
94
103
  subComponentOptions,
95
- structure
104
+ structure,
105
+ resolvedExpressions
96
106
  ) => {
97
107
  switch (node.type) {
98
108
  case 'inject':
@@ -113,7 +123,8 @@ export const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastTex
113
123
  propDefinitions,
114
124
  stateDefinitions,
115
125
  subComponentOptions,
116
- structure
126
+ structure,
127
+ resolvedExpressions
117
128
  )
118
129
  return elementNode
119
130
 
@@ -186,7 +197,8 @@ export const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastTex
186
197
  propDefinitions,
187
198
  stateDefinitions,
188
199
  subComponentOptions,
189
- structure
200
+ structure,
201
+ resolvedExpressions
190
202
  )
191
203
  }
192
204
  } catch (error) {
@@ -202,9 +214,40 @@ export const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastTex
202
214
  }
203
215
 
204
216
  case 'expr':
217
+ const content = node.content.split('?.')
218
+
219
+ if (resolvedExpressions && resolvedExpressions.expressions?.[content[0] || '']) {
220
+ const uidlDynamicRef: UIDLDynamicReference = {
221
+ type: 'dynamic',
222
+ content: {
223
+ referenceType: 'prop',
224
+ refPath: [resolvedExpressions.currentIndex.toString(), ...content.slice(1)],
225
+ id: content[0],
226
+ },
227
+ }
228
+ const generatedNode = await generateDynamicNode(
229
+ uidlDynamicRef,
230
+ compName,
231
+ nodesLookup,
232
+ resolvedExpressions.expressions,
233
+ stateDefinitions,
234
+ subComponentOptions,
235
+ structure
236
+ )
237
+ return generatedNode
238
+ }
239
+
205
240
  return HASTBuilders.createComment('Expressions are not supported in HTML')
206
241
  case 'cms-list-repeater':
207
- return HASTBuilders.createComment('CMS Repeater/Array Mapper nodes are not supported in HTML')
242
+ return generateRepeaterNode(
243
+ node,
244
+ compName,
245
+ nodesLookup,
246
+ propDefinitions,
247
+ stateDefinitions,
248
+ subComponentOptions,
249
+ structure
250
+ )
208
251
  default:
209
252
  throw new HTMLComponentGeneratorError(
210
253
  `generateHtmlSyntax encountered a node of unsupported type: ${JSON.stringify(
@@ -250,7 +293,10 @@ const getValueType = (value: UIDLPropDefinition['defaultValue']) => {
250
293
  }
251
294
  }
252
295
 
253
- const generateElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastText>> = async (
296
+ const generateRepeaterNode: NodeToHTML<
297
+ UIDLCMSListRepeaterNode,
298
+ Promise<HastNode | HastText>
299
+ > = async (
254
300
  node,
255
301
  compName,
256
302
  nodesLookup,
@@ -258,6 +304,110 @@ const generateElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastTe
258
304
  stateDefinitions,
259
305
  subComponentOptions,
260
306
  structure
307
+ ) => {
308
+ const { nodes } = node.content
309
+
310
+ const contextId = node.content.renderPropIdentifier
311
+ const sourceValue = node.content.source
312
+ let propDef =
313
+ propDefinitions[
314
+ Object.keys(propDefinitions).find((propKey) => sourceValue.includes(propKey)) || ''
315
+ ]
316
+
317
+ if (!propDef || !Array.isArray(propDef.defaultValue)) {
318
+ // If no prop is found we might have a static source value
319
+ try {
320
+ const parsedSource = JSON.parse(sourceValue)
321
+ propDef = {
322
+ defaultValue: parsedSource,
323
+ id: contextId,
324
+ type: 'array',
325
+ }
326
+ } catch {
327
+ // Silent fail
328
+ }
329
+ }
330
+
331
+ // We do the check again to keep typescript happy, otherwise this could be in catch
332
+ if (!propDef || !Array.isArray(propDef.defaultValue)) {
333
+ return HASTBuilders.createComment(
334
+ 'CMS Array Mapper/Repeater not supported in HTML without a prop source'
335
+ )
336
+ }
337
+ propDefinitions[contextId] = propDef
338
+
339
+ const elementNode = HASTBuilders.createHTMLNode('div')
340
+ node.content.nodes.list.content.style = { display: { type: 'static', content: 'contents' } }
341
+ if (node.content.nodes.empty) {
342
+ node.content.nodes.empty.content.style = { display: { type: 'static', content: 'contents' } }
343
+ }
344
+ // Empty case
345
+ if (propDef.defaultValue.length === 0) {
346
+ const emptyChildren = nodes.empty.content.children
347
+ if (emptyChildren) {
348
+ for (const child of emptyChildren) {
349
+ const childTag = await generateHtmlSyntax(
350
+ child,
351
+ compName,
352
+ nodesLookup,
353
+ propDefinitions,
354
+ stateDefinitions,
355
+ subComponentOptions,
356
+ structure
357
+ )
358
+
359
+ if (typeof childTag === 'string') {
360
+ HASTUtils.addTextNode(elementNode, childTag)
361
+ } else {
362
+ HASTUtils.addChildNode(elementNode, childTag as HastNode)
363
+ }
364
+ }
365
+ }
366
+
367
+ addNodeToLookup(`${node.content.nodes.empty.content.key}`, elementNode, nodesLookup)
368
+ return elementNode
369
+ }
370
+
371
+ const listChildren = nodes.list.content.children
372
+ if (listChildren) {
373
+ for (let index = 0; index < propDef.defaultValue.length; index++) {
374
+ for (const child of listChildren) {
375
+ const childTag = await generateHtmlSyntax(
376
+ child,
377
+ compName,
378
+ nodesLookup,
379
+ propDefinitions,
380
+ stateDefinitions,
381
+ subComponentOptions,
382
+ structure,
383
+ { currentIndex: index, expressions: { [contextId]: propDef } }
384
+ )
385
+
386
+ if (typeof childTag === 'string') {
387
+ HASTUtils.addTextNode(elementNode, childTag)
388
+ } else {
389
+ HASTUtils.addChildNode(elementNode, childTag as HastNode)
390
+ }
391
+ }
392
+ }
393
+ }
394
+
395
+ addNodeToLookup(`${node.content.nodes.list.content.key}`, elementNode, nodesLookup)
396
+ return elementNode
397
+ }
398
+
399
+ const generateElementNode: NodeToHTML<
400
+ UIDLElementNode,
401
+ Promise<HastNode | HastText | Array<HastNode | HastText>>
402
+ > = async (
403
+ node,
404
+ compName,
405
+ nodesLookup,
406
+ propDefinitions,
407
+ stateDefinitions,
408
+ subComponentOptions,
409
+ structure,
410
+ resolvedExpressions
261
411
  ) => {
262
412
  const {
263
413
  elementType,
@@ -271,7 +421,6 @@ const generateElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastTe
271
421
  if (dependency && (dependency as UIDLDependency)?.type === 'local') {
272
422
  const compTag = await generateComponentContent(
273
423
  node,
274
- compName,
275
424
  nodesLookup,
276
425
  propDefinitions,
277
426
  stateDefinitions,
@@ -301,7 +450,8 @@ const generateElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastTe
301
450
  propDefinitions,
302
451
  stateDefinitions,
303
452
  subComponentOptions,
304
- structure
453
+ structure,
454
+ resolvedExpressions
305
455
  )
306
456
 
307
457
  if (typeof childTag === 'string') {
@@ -332,16 +482,17 @@ const generateElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastTe
332
482
  propDefinitions,
333
483
  stateDefinitions,
334
484
  structure.options.projectRouteDefinition,
335
- structure.outputOptions
485
+ structure.outputOptions,
486
+ resolvedExpressions?.currentIndex
336
487
  )
337
488
 
338
- addNodeToLookup(node.content.key, node, elementNode, nodesLookup, [compName])
489
+ addNodeToLookup(node.content.key, elementNode, nodesLookup)
339
490
  return elementNode
340
491
  }
341
492
 
342
493
  const createLookupTable = (
343
494
  component: ComponentUIDL,
344
- nodesLookup: Record<string, HastNode | HastText>
495
+ nodesLookup: Record<string, HastNode | HastText | Array<HastNode | HastText>>
345
496
  ): ElementsLookup => {
346
497
  const lookup: ElementsLookup = {}
347
498
  for (const node of Object.keys(nodesLookup)) {
@@ -356,8 +507,7 @@ const createLookupTable = (
356
507
 
357
508
  const generateComponentContent = async (
358
509
  node: UIDLElementNode,
359
- compName: string,
360
- nodesLookup: Record<string, HastNode | HastText>,
510
+ nodesLookup: Record<string, HastNode | HastText | Array<HastNode | HastText>>,
361
511
  propDefinitions: Record<string, UIDLPropDefinition>,
362
512
  stateDefinitions: Record<string, UIDLStateDefinition>,
363
513
  subComponentOptions: {
@@ -505,7 +655,7 @@ const generateComponentContent = async (
505
655
  propsForInstance[propKey] = combinedStates[propKey]
506
656
  break
507
657
  case 'expr':
508
- // Ignore expr type attributes in html for the time being.
658
+ // Ignore expr type attributes in html comp instances for the time being.
509
659
  break
510
660
  default:
511
661
  throw new Error(
@@ -625,11 +775,14 @@ const generateComponentContent = async (
625
775
  }
626
776
  })
627
777
 
628
- addNodeToLookup(node.content.key, node, compTag, nodesLookup, [compName, component.name])
778
+ addNodeToLookup(node.content.key, compTag, nodesLookup)
629
779
  return compTag
630
780
  }
631
781
 
632
- const generateDynamicNode: NodeToHTML<UIDLDynamicReference, Promise<HastNode | HastText>> = async (
782
+ const generateDynamicNode: NodeToHTML<
783
+ UIDLDynamicReference,
784
+ Promise<HastNode | HastText | Array<HastNode | HastText>>
785
+ > = async (
633
786
  node,
634
787
  compName,
635
788
  nodesLookup,
@@ -637,7 +790,7 @@ const generateDynamicNode: NodeToHTML<UIDLDynamicReference, Promise<HastNode | H
637
790
  stateDefinitions,
638
791
  subComponentOptions,
639
792
  structure
640
- ): Promise<HastNode | HastText> => {
793
+ ): Promise<HastNode | HastText | Array<HastNode | HastText>> => {
641
794
  if (node.content.referenceType === 'locale') {
642
795
  const localeTag = HASTBuilders.createHTMLNode('span')
643
796
  const commentNode = HASTBuilders.createComment(`Content for locale ${node.content.id}`)
@@ -727,7 +880,8 @@ const handleAttributes = (
727
880
  propDefinitions: Record<string, UIDLPropDefinition>,
728
881
  stateDefinitions: Record<string, UIDLStateDefinition>,
729
882
  routeDefinitions: UIDLRouteDefinitions,
730
- outputOptions: UIDLComponentOutputOptions
883
+ outputOptions: UIDLComponentOutputOptions,
884
+ currentIndex?: number
731
885
  ) => {
732
886
  for (const attrKey of Object.keys(attrs)) {
733
887
  const attrValue = attrs[attrKey]
@@ -820,9 +974,28 @@ const handleAttributes = (
820
974
  break
821
975
  }
822
976
 
977
+ case 'expr': {
978
+ const fullPath = content.split('?.')
979
+ const prop = propDefinitions[fullPath?.[0] || '']
980
+
981
+ if (!prop) {
982
+ break
983
+ }
984
+
985
+ const path =
986
+ typeof currentIndex === 'number'
987
+ ? [currentIndex.toString(), ...fullPath.slice(1)]
988
+ : fullPath.slice(1)
989
+ const value = extractDefaultValueFromRefPath(prop.defaultValue, path)
990
+ if (!value) {
991
+ break
992
+ }
993
+ HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value))
994
+ break
995
+ }
996
+
823
997
  case 'element':
824
998
  case 'import':
825
- case 'expr':
826
999
  case 'object':
827
1000
  break
828
1001