@teleporthq/teleport-plugin-common 0.41.0 → 0.42.0

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.
Files changed (117) hide show
  1. package/__tests__/builders/html-builders.ts +9 -2
  2. package/__tests__/node-handlers/node-to-html/index.ts +2 -0
  3. package/__tests__/node-handlers/node-to-jsx/index.ts +1 -1
  4. package/__tests__/node-handlers/node-to-jsx/utils.ts +16 -3
  5. package/dist/cjs/builders/ast-builders.d.ts +1 -0
  6. package/dist/cjs/builders/ast-builders.d.ts.map +1 -1
  7. package/dist/cjs/builders/ast-builders.js +19 -3
  8. package/dist/cjs/builders/ast-builders.js.map +1 -1
  9. package/dist/cjs/builders/hast-builders.d.ts +1 -0
  10. package/dist/cjs/builders/hast-builders.d.ts.map +1 -1
  11. package/dist/cjs/builders/hast-builders.js +11 -1
  12. package/dist/cjs/builders/hast-builders.js.map +1 -1
  13. package/dist/cjs/builders/style-builders.d.ts +2 -2
  14. package/dist/cjs/builders/style-builders.d.ts.map +1 -1
  15. package/dist/cjs/builders/style-builders.js +39 -19
  16. package/dist/cjs/builders/style-builders.js.map +1 -1
  17. package/dist/cjs/index.d.ts +2 -1
  18. package/dist/cjs/index.d.ts.map +1 -1
  19. package/dist/cjs/index.js +3 -1
  20. package/dist/cjs/index.js.map +1 -1
  21. package/dist/cjs/node-handlers/node-to-html/constants.d.ts.map +1 -1
  22. package/dist/cjs/node-handlers/node-to-html/constants.js +2 -0
  23. package/dist/cjs/node-handlers/node-to-html/constants.js.map +1 -1
  24. package/dist/cjs/node-handlers/node-to-html/index.d.ts.map +1 -1
  25. package/dist/cjs/node-handlers/node-to-html/index.js +53 -1
  26. package/dist/cjs/node-handlers/node-to-html/index.js.map +1 -1
  27. package/dist/cjs/node-handlers/node-to-html/types.d.ts +5 -1
  28. package/dist/cjs/node-handlers/node-to-html/types.d.ts.map +1 -1
  29. package/dist/cjs/node-handlers/node-to-html/utils.d.ts.map +1 -1
  30. package/dist/cjs/node-handlers/node-to-html/utils.js +48 -4
  31. package/dist/cjs/node-handlers/node-to-html/utils.js.map +1 -1
  32. package/dist/cjs/node-handlers/node-to-jsx/index.d.ts.map +1 -1
  33. package/dist/cjs/node-handlers/node-to-jsx/index.js +325 -44
  34. package/dist/cjs/node-handlers/node-to-jsx/index.js.map +1 -1
  35. package/dist/cjs/node-handlers/node-to-jsx/types.d.ts +5 -5
  36. package/dist/cjs/node-handlers/node-to-jsx/types.d.ts.map +1 -1
  37. package/dist/cjs/node-handlers/node-to-jsx/utils.d.ts +5 -5
  38. package/dist/cjs/node-handlers/node-to-jsx/utils.d.ts.map +1 -1
  39. package/dist/cjs/node-handlers/node-to-jsx/utils.js +79 -36
  40. package/dist/cjs/node-handlers/node-to-jsx/utils.js.map +1 -1
  41. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  42. package/dist/cjs/utils/ast-utils.d.ts +16 -6
  43. package/dist/cjs/utils/ast-utils.d.ts.map +1 -1
  44. package/dist/cjs/utils/ast-utils.js +182 -40
  45. package/dist/cjs/utils/ast-utils.js.map +1 -1
  46. package/dist/cjs/utils/hast-utils.d.ts +8 -6
  47. package/dist/cjs/utils/hast-utils.d.ts.map +1 -1
  48. package/dist/cjs/utils/hast-utils.js +28 -4
  49. package/dist/cjs/utils/hast-utils.js.map +1 -1
  50. package/dist/cjs/utils/types.d.ts +3 -0
  51. package/dist/cjs/utils/types.d.ts.map +1 -0
  52. package/dist/cjs/utils/types.js +3 -0
  53. package/dist/cjs/utils/types.js.map +1 -0
  54. package/dist/esm/builders/ast-builders.d.ts +1 -0
  55. package/dist/esm/builders/ast-builders.d.ts.map +1 -1
  56. package/dist/esm/builders/ast-builders.js +18 -3
  57. package/dist/esm/builders/ast-builders.js.map +1 -1
  58. package/dist/esm/builders/hast-builders.d.ts +1 -0
  59. package/dist/esm/builders/hast-builders.d.ts.map +1 -1
  60. package/dist/esm/builders/hast-builders.js +9 -0
  61. package/dist/esm/builders/hast-builders.js.map +1 -1
  62. package/dist/esm/builders/style-builders.d.ts +2 -2
  63. package/dist/esm/builders/style-builders.d.ts.map +1 -1
  64. package/dist/esm/builders/style-builders.js +37 -18
  65. package/dist/esm/builders/style-builders.js.map +1 -1
  66. package/dist/esm/index.d.ts +2 -1
  67. package/dist/esm/index.d.ts.map +1 -1
  68. package/dist/esm/index.js +2 -1
  69. package/dist/esm/index.js.map +1 -1
  70. package/dist/esm/node-handlers/node-to-html/constants.d.ts.map +1 -1
  71. package/dist/esm/node-handlers/node-to-html/constants.js +2 -0
  72. package/dist/esm/node-handlers/node-to-html/constants.js.map +1 -1
  73. package/dist/esm/node-handlers/node-to-html/index.d.ts.map +1 -1
  74. package/dist/esm/node-handlers/node-to-html/index.js +54 -2
  75. package/dist/esm/node-handlers/node-to-html/index.js.map +1 -1
  76. package/dist/esm/node-handlers/node-to-html/types.d.ts +5 -1
  77. package/dist/esm/node-handlers/node-to-html/types.d.ts.map +1 -1
  78. package/dist/esm/node-handlers/node-to-html/utils.d.ts.map +1 -1
  79. package/dist/esm/node-handlers/node-to-html/utils.js +45 -4
  80. package/dist/esm/node-handlers/node-to-html/utils.js.map +1 -1
  81. package/dist/esm/node-handlers/node-to-jsx/index.d.ts.map +1 -1
  82. package/dist/esm/node-handlers/node-to-jsx/index.js +326 -45
  83. package/dist/esm/node-handlers/node-to-jsx/index.js.map +1 -1
  84. package/dist/esm/node-handlers/node-to-jsx/types.d.ts +5 -5
  85. package/dist/esm/node-handlers/node-to-jsx/types.d.ts.map +1 -1
  86. package/dist/esm/node-handlers/node-to-jsx/utils.d.ts +5 -5
  87. package/dist/esm/node-handlers/node-to-jsx/utils.d.ts.map +1 -1
  88. package/dist/esm/node-handlers/node-to-jsx/utils.js +79 -36
  89. package/dist/esm/node-handlers/node-to-jsx/utils.js.map +1 -1
  90. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  91. package/dist/esm/utils/ast-utils.d.ts +16 -6
  92. package/dist/esm/utils/ast-utils.d.ts.map +1 -1
  93. package/dist/esm/utils/ast-utils.js +176 -39
  94. package/dist/esm/utils/ast-utils.js.map +1 -1
  95. package/dist/esm/utils/hast-utils.d.ts +8 -6
  96. package/dist/esm/utils/hast-utils.d.ts.map +1 -1
  97. package/dist/esm/utils/hast-utils.js +26 -3
  98. package/dist/esm/utils/hast-utils.js.map +1 -1
  99. package/dist/esm/utils/types.d.ts +3 -0
  100. package/dist/esm/utils/types.d.ts.map +1 -0
  101. package/dist/esm/utils/types.js +2 -0
  102. package/dist/esm/utils/types.js.map +1 -0
  103. package/package.json +4 -4
  104. package/src/builders/ast-builders.ts +36 -2
  105. package/src/builders/hast-builders.ts +11 -0
  106. package/src/builders/style-builders.ts +51 -27
  107. package/src/index.ts +12 -1
  108. package/src/node-handlers/node-to-html/constants.ts +2 -0
  109. package/src/node-handlers/node-to-html/index.ts +95 -5
  110. package/src/node-handlers/node-to-html/types.ts +6 -0
  111. package/src/node-handlers/node-to-html/utils.ts +60 -5
  112. package/src/node-handlers/node-to-jsx/index.ts +566 -62
  113. package/src/node-handlers/node-to-jsx/types.ts +10 -29
  114. package/src/node-handlers/node-to-jsx/utils.ts +106 -42
  115. package/src/utils/ast-utils.ts +283 -82
  116. package/src/utils/hast-utils.ts +37 -9
  117. package/src/utils/types.ts +25 -0
@@ -12,9 +12,10 @@ import {
12
12
  UIDLPropValue,
13
13
  UIDLExpressionValue,
14
14
  UIDLStateValue,
15
+ HastNode,
15
16
  } from '@teleporthq/teleport-types'
16
17
  import babelPresetReact from '@babel/preset-react'
17
- import { ASTUtils } from '..'
18
+ import { UnaryOperation, BinaryOperator } from './types'
18
19
 
19
20
  /**
20
21
  * Adds a class definition string to an existing string of classes
@@ -23,14 +24,20 @@ export const addClassStringOnJSXTag = (
23
24
  jsxNode: types.JSXElement,
24
25
  classString: string,
25
26
  classAttributeName?: string,
26
- dynamicValues: Array<types.MemberExpression | types.Identifier> = []
27
+ dynamicValues: Array<types.MemberExpression | types.Identifier | types.ConditionalExpression> = []
27
28
  ) => {
28
- const classAttribute = getClassAttribute(jsxNode, { createIfNotFound: true, classAttributeName })
29
+ if (classString === '' && dynamicValues.length === 0) {
30
+ return
31
+ }
29
32
 
33
+ const classAttribute = getClassAttribute(jsxNode, { createIfNotFound: true, classAttributeName })
30
34
  if (dynamicValues.length === 0) {
31
35
  if (classAttribute.value && classAttribute.value.type === 'StringLiteral') {
32
36
  const classArray = classAttribute.value.value.split(' ')
33
- classArray.push(classString)
37
+ if (classString) {
38
+ classArray.push(classString)
39
+ }
40
+ classArray.filter((item) => item)
34
41
  classAttribute.value.value = classArray.join(' ').trim()
35
42
  } else {
36
43
  throw new Error(
@@ -43,7 +50,9 @@ export const addClassStringOnJSXTag = (
43
50
  if (classAttribute.value && classAttribute.value.type === 'StringLiteral') {
44
51
  const classArray = classAttribute.value.value.split(' ')
45
52
  const quasis: types.TemplateElement[] = []
46
- const expression: Array<types.MemberExpression | types.Identifier> = []
53
+ const expression: Array<
54
+ types.MemberExpression | types.Identifier | types.ConditionalExpression
55
+ > = []
47
56
 
48
57
  quasis.push(
49
58
  types.templateElement({
@@ -164,17 +173,21 @@ export const addDynamicExpressionAttributeToJSXTag = (
164
173
  export const addMultipleDynamicAttributesToJSXTag = (
165
174
  jsxASTNode: types.JSXElement,
166
175
  name: string,
167
- attrValues: Array<types.MemberExpression | types.Identifier> = [],
176
+ attrValues: Array<types.MemberExpression | types.Identifier | types.StringLiteral> = [],
168
177
  t = types
169
178
  ) => {
170
- const memberExpressions: Array<types.Identifier | types.MemberExpression> = []
179
+ const memberExpressions: Array<types.Identifier | types.MemberExpression | types.StringLiteral> =
180
+ []
171
181
  const templateElements: types.TemplateElement[] = []
172
-
173
182
  if (attrValues.length === 0) {
174
183
  return
175
184
  }
176
185
 
177
- let content: types.TemplateLiteral | types.MemberExpression | types.Identifier
186
+ let content:
187
+ | types.TemplateLiteral
188
+ | types.MemberExpression
189
+ | types.Identifier
190
+ | types.StringLiteral
178
191
  if (attrValues.length === 1) {
179
192
  content = attrValues[0]
180
193
  } else {
@@ -210,19 +223,36 @@ export const stringAsTemplateLiteral = (str: string): types.TemplateLiteral => {
210
223
  return container.expression as types.TemplateLiteral
211
224
  }
212
225
 
226
+ /**
227
+ * Converts HTML attribute names to React/JSX camelCase format
228
+ * Preserves data-* and aria-* attributes as-is
229
+ */
230
+ const convertToReactAttributeName = (attrName: string): string => {
231
+ // Keep data-* and aria-* attributes as-is (React supports these)
232
+ if (attrName.startsWith('data-') || attrName.startsWith('aria-')) {
233
+ return attrName
234
+ }
235
+
236
+ // Convert hyphenated attributes to camelCase for React
237
+ return attrName.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
238
+ }
239
+
213
240
  export const addAttributeToJSXTag = (
214
241
  jsxNode: types.JSXElement,
215
242
  attrName: string,
216
243
  attrValue?: boolean | unknown,
217
244
  t = types
218
245
  ) => {
219
- const nameOfAttribute = t.jsxIdentifier(attrName)
246
+ const reactAttrName = convertToReactAttributeName(attrName)
247
+ const nameOfAttribute = t.jsxIdentifier(reactAttrName)
220
248
  let attributeDefinition
221
249
  if (typeof attrValue === 'boolean') {
222
250
  attributeDefinition = t.jsxAttribute(
223
251
  nameOfAttribute,
224
252
  attrValue === true ? undefined : t.jsxExpressionContainer(t.booleanLiteral(attrValue))
225
253
  )
254
+ } else if (t.isNode(attrValue) && t.isJSXElement(attrValue)) {
255
+ attributeDefinition = t.jsxAttribute(nameOfAttribute, t.jsxExpressionContainer(attrValue))
226
256
  } else {
227
257
  attributeDefinition = t.jsxAttribute(
228
258
  nameOfAttribute,
@@ -230,6 +260,17 @@ export const addAttributeToJSXTag = (
230
260
  )
231
261
  }
232
262
 
263
+ const attribute: types.JSXAttribute = jsxNode.openingElement.attributes.find((attr) => {
264
+ if (attr.type === 'JSXAttribute') {
265
+ return attr.name.name === reactAttrName
266
+ }
267
+ }) as types.JSXAttribute
268
+
269
+ if (attribute && attribute.value && attribute.value.type === 'StringLiteral') {
270
+ attribute.value.value = `${attribute.value.value} ${attrValue}`
271
+ return
272
+ }
273
+
233
274
  jsxNode.openingElement.attributes.push(attributeDefinition)
234
275
  }
235
276
 
@@ -300,7 +341,18 @@ export const objectToObjectExpression = (
300
341
  const value = objectMap[key]
301
342
  let computedLiteralValue = null
302
343
 
303
- if (value instanceof ParsedASTNode || value.constructor.name === 'ParsedASTNode') {
344
+ if (value === undefined) {
345
+ return acc
346
+ }
347
+
348
+ // Handle null values explicitly - they are valid in array mappers with different schemas
349
+ if (value === null) {
350
+ computedLiteralValue = t.nullLiteral()
351
+ }
352
+ // This is for function props that have successfully been parsed.
353
+ else if (typeof value === 'object' && 'functionExpressionParseResult' in value) {
354
+ computedLiteralValue = value.functionExpressionParseResult
355
+ } else if (value instanceof ParsedASTNode || value.constructor.name === 'ParsedASTNode') {
304
356
  computedLiteralValue = (value as ParsedASTNode).ast
305
357
  } else if (typeof value === 'boolean') {
306
358
  computedLiteralValue = t.booleanLiteral(value)
@@ -396,7 +448,7 @@ export const getTSAnnotationForType = (type: any, t = types) => {
396
448
  case 'boolean':
397
449
  return t.tsBooleanKeyword()
398
450
  default:
399
- return t.tsUnknownKeyword()
451
+ return t.tsAnyKeyword()
400
452
  }
401
453
  }
402
454
 
@@ -496,7 +548,7 @@ export const createReturnExpressionSyntax = (
496
548
  createStateHookAST(stateKey, stateDefinitions[stateKey])
497
549
  )
498
550
 
499
- return t.blockStatement([...stateHooks, ...Object.values(windowImports), returnStatement] || [])
551
+ return t.blockStatement([...stateHooks, ...Object.values(windowImports), returnStatement])
500
552
  }
501
553
 
502
554
  /**
@@ -543,9 +595,7 @@ export const wrapObjectPropertiesWithExpression = (properties: types.ObjectPrope
543
595
 
544
596
  export const generateRemoteResourceASTs = (resource: UIDLResourceItem) => {
545
597
  const fetchUrl = computeFetchUrl(resource)
546
- const authHeaderAST = computeAuthorizationHeaderAST(resource?.headers)
547
598
  const headersASTs = resource?.headers ? generateRESTHeadersAST(resource.headers) : []
548
-
549
599
  const queryParams = generateURLParamsAST(resource?.params)
550
600
  const fetchUrlQuasis = fetchUrl.quasis
551
601
  const queryParamsQuasis = queryParams?.quasis || [types.templateElement({ raw: '', cooked: '' })]
@@ -560,60 +610,8 @@ export const generateRemoteResourceASTs = (resource: UIDLResourceItem) => {
560
610
  queryParamsQuasis.pop()
561
611
  }
562
612
 
563
- const paramsDeclerations: Array<types.ObjectProperty | types.SpreadElement> = Object.keys(
564
- resource?.params || {}
565
- ).reduce((acc: Array<types.ObjectProperty | types.SpreadElement>, item) => {
566
- const prop = resource.params[item]
567
- if (prop.type === 'static') {
568
- acc.push(types.objectProperty(types.stringLiteral(item), ASTUtils.resolveObjectValue(prop)))
569
- }
570
-
571
- if (prop.type === 'expr') {
572
- acc.push(
573
- types.objectProperty(
574
- types.stringLiteral(item),
575
- ASTUtils.getExpressionFromUIDLExpressionNode(prop)
576
- )
577
- )
578
- }
579
-
580
- if (prop.type === 'dynamic') {
581
- acc.push(
582
- types.spreadElement(
583
- types.logicalExpression(
584
- '&&',
585
- types.memberExpression(
586
- types.identifier('params'),
587
- types.stringLiteral(prop.content.id),
588
- true,
589
- false
590
- ),
591
- types.objectExpression([
592
- types.objectProperty(
593
- types.stringLiteral(item),
594
- types.memberExpression(
595
- types.identifier('params'),
596
- types.stringLiteral(prop.content.id),
597
- true,
598
- false
599
- )
600
- ),
601
- ])
602
- )
603
- )
604
- )
605
- }
606
-
607
- return acc
608
- }, [])
609
-
610
- const paramsAST = types.variableDeclaration('const', [
611
- types.variableDeclarator(
612
- types.identifier('urlParams'),
613
- types.objectExpression(paramsDeclerations)
614
- ),
615
- ])
616
-
613
+ const urlParamsDecleration = generateParamsAST(resource?.params)
614
+ const bodyParamsDecleration = generateParamsAST(resource?.body)
617
615
  const url = queryParams?.quasis
618
616
  ? types.templateLiteral(
619
617
  [...fetchUrlQuasis, ...queryParamsQuasis],
@@ -628,31 +626,113 @@ export const generateRemoteResourceASTs = (resource: UIDLResourceItem) => {
628
626
 
629
627
  let allHeaders: types.ObjectProperty[] = []
630
628
 
631
- if (authHeaderAST) {
632
- allHeaders.push(authHeaderAST)
629
+ if (resource?.headers?.authToken) {
630
+ allHeaders.push(computeAuthorizationHeaderAST(resource?.headers))
633
631
  }
634
632
 
635
633
  if (headersASTs.length) {
636
634
  allHeaders = allHeaders.concat(headersASTs)
637
635
  }
638
636
 
639
- const headers = types.objectProperty(
640
- types.identifier('headers'),
641
- types.objectExpression(allHeaders)
642
- )
643
-
644
- const fetchAST = types.variableDeclaration('const', [
637
+ const fetchAST = types.variableDeclaration('let', [
645
638
  types.variableDeclarator(
646
639
  types.identifier('data'),
647
640
  types.awaitExpression(
648
641
  types.callExpression(types.identifier('fetch'), [
649
642
  url,
650
- types.objectExpression([method, headers]),
643
+ types.objectExpression([
644
+ method,
645
+ ...(allHeaders.length > 0
646
+ ? [
647
+ types.objectProperty(
648
+ types.identifier('headers'),
649
+ types.objectExpression(allHeaders)
650
+ ),
651
+ ]
652
+ : []),
653
+ ...(bodyParamsDecleration.length > 0 && resource?.method === 'POST'
654
+ ? [
655
+ types.objectProperty(
656
+ types.identifier('body'),
657
+ types.callExpression(
658
+ types.memberExpression(
659
+ types.identifier('JSON'),
660
+ types.identifier('stringify')
661
+ ),
662
+ [types.identifier('bodyParams')]
663
+ )
664
+ ),
665
+ ]
666
+ : []),
667
+ ]),
651
668
  ])
652
669
  )
653
670
  ),
654
671
  ])
655
672
 
673
+ // Fallback in case i18n interferes with normal CMS flows
674
+ // Only generate fallback if locale parameter exists
675
+ const hasLocaleParam = resource?.params?.locale !== undefined
676
+ let fallbackAST = null
677
+
678
+ if (hasLocaleParam) {
679
+ const fallbackParams = JSON.parse(JSON.stringify(resource))
680
+ delete fallbackParams?.params?.locale
681
+ const fallbackUrlParamsDeclaration = generateParamsAST(fallbackParams?.params)
682
+ const assignmentOfNewUrlParams = types.expressionStatement(
683
+ types.assignmentExpression(
684
+ '=',
685
+ types.identifier('urlParams'),
686
+ types.objectExpression([...fallbackUrlParamsDeclaration])
687
+ )
688
+ )
689
+
690
+ const assignmentExpressionAST = types.expressionStatement(
691
+ types.assignmentExpression(
692
+ '=',
693
+ types.identifier('data'),
694
+ types.awaitExpression(
695
+ types.callExpression(types.identifier('fetch'), [
696
+ url,
697
+ types.objectExpression([
698
+ method,
699
+ ...(allHeaders.length > 0
700
+ ? [
701
+ types.objectProperty(
702
+ types.identifier('headers'),
703
+ types.objectExpression(allHeaders)
704
+ ),
705
+ ]
706
+ : []),
707
+ ...(bodyParamsDecleration.length > 0 && resource?.method === 'POST'
708
+ ? [
709
+ types.objectProperty(
710
+ types.identifier('body'),
711
+ types.callExpression(
712
+ types.memberExpression(
713
+ types.identifier('JSON'),
714
+ types.identifier('stringify')
715
+ ),
716
+ [types.identifier('bodyParams')]
717
+ )
718
+ ),
719
+ ]
720
+ : []),
721
+ ]),
722
+ ])
723
+ )
724
+ )
725
+ )
726
+
727
+ fallbackAST = types.ifStatement(
728
+ types.binaryExpression(
729
+ '!==',
730
+ types.memberExpression(types.identifier('data'), types.identifier('status')),
731
+ types.numericLiteral(200)
732
+ ),
733
+ types.blockStatement([assignmentOfNewUrlParams, assignmentExpressionAST])
734
+ )
735
+ }
656
736
  const responseType = resource?.response?.type ?? 'json'
657
737
  let responseJSONAST
658
738
 
@@ -719,7 +799,80 @@ export const generateRemoteResourceASTs = (resource: UIDLResourceItem) => {
719
799
  }
720
800
  }
721
801
 
722
- return [paramsAST, fetchAST, responseJSONAST]
802
+ return [
803
+ ...(urlParamsDecleration.length > 0
804
+ ? [
805
+ types.variableDeclaration('let', [
806
+ types.variableDeclarator(
807
+ types.identifier('urlParams'),
808
+ types.objectExpression(urlParamsDecleration)
809
+ ),
810
+ ]),
811
+ ]
812
+ : []),
813
+ ...(bodyParamsDecleration.length > 0
814
+ ? [
815
+ types.variableDeclaration('const', [
816
+ types.variableDeclarator(
817
+ types.identifier('bodyParams'),
818
+ types.objectExpression(bodyParamsDecleration)
819
+ ),
820
+ ]),
821
+ ]
822
+ : []),
823
+ fetchAST,
824
+ ...(fallbackAST ? [fallbackAST] : []),
825
+ responseJSONAST,
826
+ ].filter(Boolean)
827
+ }
828
+
829
+ const generateParamsAST = (
830
+ props: Record<string, UIDLStaticValue | UIDLPropValue | UIDLStateValue | UIDLExpressionValue>
831
+ ): Array<types.ObjectProperty | types.SpreadElement> => {
832
+ return Object.keys(props || {}).reduce(
833
+ (acc: Array<types.ObjectProperty | types.SpreadElement>, item) => {
834
+ const prop = props[item]
835
+ if (prop.type === 'static') {
836
+ acc.push(types.objectProperty(types.stringLiteral(item), resolveObjectValue(prop)))
837
+ }
838
+
839
+ if (prop.type === 'expr') {
840
+ acc.push(
841
+ types.objectProperty(types.stringLiteral(item), getExpressionFromUIDLExpressionNode(prop))
842
+ )
843
+ }
844
+
845
+ if (prop.type === 'dynamic') {
846
+ acc.push(
847
+ types.spreadElement(
848
+ types.logicalExpression(
849
+ '&&',
850
+ types.memberExpression(
851
+ types.identifier('params'),
852
+ types.stringLiteral(prop.content.id),
853
+ true,
854
+ false
855
+ ),
856
+ types.objectExpression([
857
+ types.objectProperty(
858
+ types.stringLiteral(item),
859
+ types.memberExpression(
860
+ types.identifier('params'),
861
+ types.stringLiteral(prop.content.id),
862
+ true,
863
+ false
864
+ )
865
+ ),
866
+ ])
867
+ )
868
+ )
869
+ )
870
+ }
871
+
872
+ return acc
873
+ },
874
+ []
875
+ )
723
876
  }
724
877
 
725
878
  const generateRESTHeadersAST = (headers: UIDLResourceItem['headers']): types.ObjectProperty[] => {
@@ -754,6 +907,22 @@ export const generateMemberExpressionASTFromBase = (
754
907
  )
755
908
  }
756
909
 
910
+ export const parseValuePath = (valuePath: Array<string | number>): Array<string | number> => {
911
+ return valuePath.map((segment) => {
912
+ if (typeof segment === 'string') {
913
+ const bracketMatch = segment.match(/^\[(\d+)\]$/)
914
+ if (bracketMatch) {
915
+ return parseInt(bracketMatch[1], 10)
916
+ }
917
+ const numericMatch = segment.match(/^\d+$/)
918
+ if (numericMatch) {
919
+ return parseInt(segment, 10)
920
+ }
921
+ }
922
+ return segment
923
+ })
924
+ }
925
+
757
926
  export const generateMemberExpressionASTFromPath = (
758
927
  path: Array<string | number>
759
928
  ): types.OptionalMemberExpression | types.Identifier => {
@@ -769,7 +938,7 @@ export const generateMemberExpressionASTFromPath = (
769
938
  return types.optionalMemberExpression(
770
939
  generateMemberExpressionASTFromPath(pathClone),
771
940
  types.numericLiteral(currentPath),
772
- false,
941
+ true,
773
942
  true
774
943
  )
775
944
  }
@@ -885,7 +1054,7 @@ const computeAuthorizationHeaderAST = (headers: UIDLResourceItem['headers']) =>
885
1054
  )
886
1055
  }
887
1056
 
888
- const computeFetchUrl = (resource: UIDLResourceItem) => {
1057
+ export const computeFetchUrl = (resource: UIDLResourceItem) => {
889
1058
  const { path } = resource
890
1059
  const fetchBaseUrl = resolveResourceValue(path.baseUrl)
891
1060
  const resourceRoute = resolveResourceValue(path.route)
@@ -894,7 +1063,11 @@ const computeFetchUrl = (resource: UIDLResourceItem) => {
894
1063
  const routeType = path.route?.type
895
1064
 
896
1065
  if (baseUrlType === 'static' && routeType === 'static') {
897
- const stringsToJoin = [fetchBaseUrl, resourceRoute].filter((item) => item).join('/')
1066
+ const baseUrlStr = typeof fetchBaseUrl === 'string' ? fetchBaseUrl : String(fetchBaseUrl || '')
1067
+ const routeStr = typeof resourceRoute === 'string' ? resourceRoute : String(resourceRoute || '')
1068
+ const cleanBaseUrl = baseUrlStr.endsWith('/') ? baseUrlStr.slice(0, -1) : baseUrlStr
1069
+ const cleanRoute = routeStr.startsWith('/') ? routeStr.slice(1) : routeStr
1070
+ const stringsToJoin = [cleanBaseUrl, cleanRoute].filter((item) => item).join('/')
898
1071
  return types.templateLiteral(
899
1072
  [types.templateElement({ cooked: `${stringsToJoin}`, raw: `${stringsToJoin}` }, true)],
900
1073
  []
@@ -983,6 +1156,7 @@ export const resolveObjectValue = (
983
1156
  | types.NumericLiteral
984
1157
  | types.BooleanLiteral
985
1158
  | types.ObjectExpression
1159
+ | types.ArrayExpression
986
1160
  | types.Expression => {
987
1161
  if (prop.type === 'static') {
988
1162
  const value =
@@ -992,6 +1166,8 @@ export const resolveObjectValue = (
992
1166
  ? types.booleanLiteral(prop.content)
993
1167
  : typeof prop.content === 'number'
994
1168
  ? types.numericLiteral(prop.content)
1169
+ : Array.isArray(prop.content)
1170
+ ? types.arrayExpression(prop.content.map((element) => convertValueToLiteral(element)))
995
1171
  : typeof prop.content === 'object'
996
1172
  ? objectToObjectExpression(prop.content as unknown as Record<string, unknown>)
997
1173
  : types.identifier(String(prop.content))
@@ -1025,3 +1201,28 @@ export const getExpressionFromUIDLExpressionNode = (
1025
1201
 
1026
1202
  return theStatementOnlyWihtoutTheProgram.expression
1027
1203
  }
1204
+
1205
+ export const isJSXElement = (value: types.JSXElement | HastNode): value is types.JSXElement =>
1206
+ value.type === 'JSXElement'
1207
+
1208
+ /**
1209
+ * Because of the restrictions of the AST Types we need to have a clear subset of binary operators we can use
1210
+ * @param operation - the operation defined in the UIDL for the current state branch
1211
+ */
1212
+ export const convertToBinaryOperator = (operation: string): BinaryOperator => {
1213
+ const allowedOperations = ['===', '!==', '>=', '<=', '>', '<']
1214
+ if (allowedOperations.includes(operation)) {
1215
+ return operation as BinaryOperator
1216
+ } else {
1217
+ return '==='
1218
+ }
1219
+ }
1220
+
1221
+ export const convertToUnaryOperator = (operation: string): UnaryOperation => {
1222
+ const allowedOperations = ['!']
1223
+ if (allowedOperations.includes(operation)) {
1224
+ return operation as UnaryOperation
1225
+ } else {
1226
+ return '!'
1227
+ }
1228
+ }
@@ -1,25 +1,53 @@
1
- import { HastNode } from '@teleporthq/teleport-types'
1
+ import { HastNode, HastText } from '@teleporthq/teleport-types'
2
2
  import { createTextNode } from '../builders/hast-builders'
3
+ import type { JSXElement } from '@babel/types'
4
+ import { isJSXElement } from './ast-utils'
3
5
 
4
- export const addBooleanAttributeToNode = (node: HastNode, key: string, value: boolean = true) => {
6
+ export const addBooleanAttributeToNode = (
7
+ node: HastNode | HastNode[],
8
+ key: string,
9
+ value: boolean = true
10
+ ) => {
11
+ if (Array.isArray(node)) {
12
+ node.forEach((subnode) => (subnode.properties[key] = value === true ? '' : false))
13
+ return
14
+ }
5
15
  node.properties[key] = value === true ? '' : false
6
- /* adding empty string as @starptech/prettyhtml-hast-to-html which we are currently
7
- using for generating HTML supports boolean way of adding attributes only for HTML
8
- attributes but not for Vue*/
16
+ // Adding boolean attributes is currently onyl supported for template generators
9
17
  }
10
18
 
11
- export const addAttributeToNode = (node: HastNode, key: string, value: string) => {
19
+ export const addAttributeToNode = (node: HastNode | HastNode[], key: string, value: string) => {
20
+ if (Array.isArray(node)) {
21
+ node.forEach((subnode) => (subnode.properties[key] = value))
22
+ return
23
+ }
12
24
  node.properties[key] = value
13
25
  }
14
26
 
15
- export const addClassToNode = (node: HastNode, className: string) => {
27
+ export const addClassToNode = (node: HastNode | HastNode[], className: string) => {
28
+ if (Array.isArray(node)) {
29
+ node.forEach((subnode) => (subnode.properties.class = className))
30
+ return
31
+ }
16
32
  node.properties.class = className
17
33
  }
18
34
 
19
- export const addChildNode = (node: HastNode, child: HastNode) => {
35
+ export const addChildNode = (node: HastNode | HastNode[], child: HastNode | HastText) => {
36
+ if (Array.isArray(node)) {
37
+ node.forEach((subnode) => subnode.children.push(child))
38
+ return
39
+ }
20
40
  node.children.push(child)
21
41
  }
22
42
 
23
- export const addTextNode = (node: HastNode, text: string) => {
43
+ export const addTextNode = (node: HastNode | HastNode[], text: string) => {
44
+ if (Array.isArray(node)) {
45
+ node.forEach((subnode) => subnode.children.push(createTextNode(text)))
46
+ return
47
+ }
24
48
  node.children.push(createTextNode(text))
25
49
  }
50
+
51
+ export const isHastElement = (value: JSXElement | HastNode): value is HastNode =>
52
+ isJSXElement(value) === false &&
53
+ ('properties' in value || 'children' in value || 'tagName' in value)
@@ -0,0 +1,25 @@
1
+ export type BinaryOperator =
2
+ | '==='
3
+ | '+'
4
+ | '-'
5
+ | '/'
6
+ | '%'
7
+ | '*'
8
+ | '**'
9
+ | '&'
10
+ | '|'
11
+ | '>>'
12
+ | '>>>'
13
+ | '<<'
14
+ | '^'
15
+ | '=='
16
+ | '!='
17
+ | '!=='
18
+ | 'in'
19
+ | 'instanceof'
20
+ | '>'
21
+ | '<'
22
+ | '>='
23
+ | '<='
24
+
25
+ export type UnaryOperation = '+' | '-' | 'void' | 'throw' | 'delete' | '!' | '~' | 'typeof'