@teleporthq/teleport-plugin-common 0.27.2 → 0.27.3-alpha.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.
Files changed (57) hide show
  1. package/__tests__/builders/style-builders.ts +12 -1
  2. package/__tests__/node-handlers/node-to-jsx/index.ts +3 -2
  3. package/dist/cjs/builders/style-builders.d.ts +3 -3
  4. package/dist/cjs/builders/style-builders.d.ts.map +1 -1
  5. package/dist/cjs/builders/style-builders.js +16 -9
  6. package/dist/cjs/builders/style-builders.js.map +1 -1
  7. package/dist/cjs/node-handlers/node-to-html/utils.d.ts.map +1 -1
  8. package/dist/cjs/node-handlers/node-to-html/utils.js.map +1 -1
  9. package/dist/cjs/node-handlers/node-to-jsx/constants.d.ts.map +1 -1
  10. package/dist/cjs/node-handlers/node-to-jsx/constants.js +1 -0
  11. package/dist/cjs/node-handlers/node-to-jsx/constants.js.map +1 -1
  12. package/dist/cjs/node-handlers/node-to-jsx/index.d.ts.map +1 -1
  13. package/dist/cjs/node-handlers/node-to-jsx/index.js +154 -44
  14. package/dist/cjs/node-handlers/node-to-jsx/index.js.map +1 -1
  15. package/dist/cjs/node-handlers/node-to-jsx/types.d.ts +5 -1
  16. package/dist/cjs/node-handlers/node-to-jsx/types.d.ts.map +1 -1
  17. package/dist/cjs/node-handlers/node-to-jsx/utils.d.ts +1 -1
  18. package/dist/cjs/node-handlers/node-to-jsx/utils.d.ts.map +1 -1
  19. package/dist/cjs/node-handlers/node-to-jsx/utils.js +25 -6
  20. package/dist/cjs/node-handlers/node-to-jsx/utils.js.map +1 -1
  21. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  22. package/dist/cjs/utils/ast-utils.d.ts +27 -3
  23. package/dist/cjs/utils/ast-utils.d.ts.map +1 -1
  24. package/dist/cjs/utils/ast-utils.js +271 -7
  25. package/dist/cjs/utils/ast-utils.js.map +1 -1
  26. package/dist/esm/builders/style-builders.d.ts +3 -3
  27. package/dist/esm/builders/style-builders.d.ts.map +1 -1
  28. package/dist/esm/builders/style-builders.js +16 -9
  29. package/dist/esm/builders/style-builders.js.map +1 -1
  30. package/dist/esm/node-handlers/node-to-html/utils.d.ts.map +1 -1
  31. package/dist/esm/node-handlers/node-to-html/utils.js.map +1 -1
  32. package/dist/esm/node-handlers/node-to-jsx/constants.d.ts.map +1 -1
  33. package/dist/esm/node-handlers/node-to-jsx/constants.js +1 -0
  34. package/dist/esm/node-handlers/node-to-jsx/constants.js.map +1 -1
  35. package/dist/esm/node-handlers/node-to-jsx/index.d.ts.map +1 -1
  36. package/dist/esm/node-handlers/node-to-jsx/index.js +155 -45
  37. package/dist/esm/node-handlers/node-to-jsx/index.js.map +1 -1
  38. package/dist/esm/node-handlers/node-to-jsx/types.d.ts +5 -1
  39. package/dist/esm/node-handlers/node-to-jsx/types.d.ts.map +1 -1
  40. package/dist/esm/node-handlers/node-to-jsx/utils.d.ts +1 -1
  41. package/dist/esm/node-handlers/node-to-jsx/utils.d.ts.map +1 -1
  42. package/dist/esm/node-handlers/node-to-jsx/utils.js +25 -6
  43. package/dist/esm/node-handlers/node-to-jsx/utils.js.map +1 -1
  44. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  45. package/dist/esm/utils/ast-utils.d.ts +27 -3
  46. package/dist/esm/utils/ast-utils.d.ts.map +1 -1
  47. package/dist/esm/utils/ast-utils.js +264 -6
  48. package/dist/esm/utils/ast-utils.js.map +1 -1
  49. package/package.json +5 -5
  50. package/src/builders/style-builders.ts +33 -10
  51. package/src/node-handlers/node-to-html/utils.ts +0 -1
  52. package/src/node-handlers/node-to-jsx/constants.ts +1 -0
  53. package/src/node-handlers/node-to-jsx/index.ts +223 -51
  54. package/src/node-handlers/node-to-jsx/types.ts +11 -1
  55. package/src/node-handlers/node-to-jsx/utils.ts +41 -6
  56. package/src/types.d.ts +1 -0
  57. package/src/utils/ast-utils.ts +471 -5
@@ -1,7 +1,21 @@
1
1
  import * as types from '@babel/types'
2
+ import { parse, traverse } from '@babel/core'
2
3
  import ParsedASTNode from './parsed-ast'
3
4
  import { StringUtils } from '@teleporthq/teleport-shared'
4
- import { UIDLStateDefinition, UIDLPropDefinition, UIDLRawValue } from '@teleporthq/teleport-types'
5
+ import {
6
+ UIDLStateDefinition,
7
+ UIDLPropDefinition,
8
+ UIDLRawValue,
9
+ Resource,
10
+ ResourceValue,
11
+ ResourceUrlParams,
12
+ UIDLStaticValue,
13
+ UIDLDynamicReference,
14
+ ResourceUrlValues,
15
+ UIDLExpressionValue,
16
+ } from '@teleporthq/teleport-types'
17
+ import { JSXGenerationOptions, JSXGenerationParams } from '../node-handlers/node-to-jsx/types'
18
+ import { createDynamicValueExpression } from '../node-handlers/node-to-jsx/utils'
5
19
  /**
6
20
  * Adds a class definition string to an existing string of classes
7
21
  */
@@ -80,6 +94,7 @@ const getClassAttribute = (
80
94
 
81
95
  /**
82
96
  * Makes `${name}={${prefix}.${value}}` happen in AST
97
+ * doesn't handle ctx or expr referenceType
83
98
  */
84
99
  export const addDynamicAttributeToJSXTag = (
85
100
  jsxASTNode: types.JSXElement,
@@ -98,6 +113,103 @@ export const addDynamicAttributeToJSXTag = (
98
113
  )
99
114
  }
100
115
 
116
+ /**
117
+ * Makes `${name}={${ContextName}.${path}}` happen in AST
118
+ */
119
+ export const addDynamicCtxAttributeToJSXTag = (params: {
120
+ jsxASTNode: types.JSXElement
121
+ name: string
122
+ attrValue: UIDLDynamicReference
123
+ options: JSXGenerationOptions
124
+ generationParams: JSXGenerationParams
125
+ t?: typeof types
126
+ }) => {
127
+ const { jsxASTNode, name, t = types, attrValue, options, generationParams } = params
128
+
129
+ const content = createDynamicValueExpression(attrValue, options, t, generationParams)
130
+
131
+ jsxASTNode.openingElement.attributes.push(
132
+ t.jsxAttribute(t.jsxIdentifier(name), t.jsxExpressionContainer(content))
133
+ )
134
+ }
135
+
136
+ /**
137
+ * Make code expressions happen in AST
138
+ * Replace variables that are found in AST with
139
+ * the corresponding value from the contexts for now
140
+ * and in the future with other sources.
141
+ */
142
+ export const addDynamicExpressionAttributeToJSXTag = (
143
+ jsxASTNode: types.JSXElement,
144
+ dynamicRef: UIDLDynamicReference,
145
+ params: JSXGenerationParams,
146
+ t = types
147
+ ) => {
148
+ const dynamicContent = dynamicRef.content
149
+ if (dynamicContent.referenceType !== 'expr') {
150
+ throw new Error(`This method only works with dynamic nodes that have code expressions`)
151
+ }
152
+
153
+ const code = dynamicContent.expression
154
+ const options = {
155
+ sourceType: 'module' as const,
156
+ }
157
+
158
+ const ast = parse(code, options)
159
+
160
+ replaceIdentifiersWithScopes(ast, dynamicContent.scope, (_, name) => {
161
+ const entityToResolve = dynamicContent.scope[name]
162
+ if (entityToResolve.type === 'dynamic') {
163
+ const { projectContexts } = params
164
+ const contextMeta = projectContexts[entityToResolve.content.ctxId]
165
+ const nameOfContext = StringUtils.camelize(contextMeta.providerName)
166
+
167
+ return nameOfContext
168
+ }
169
+ return name
170
+ })
171
+
172
+ if (!('program' in ast)) {
173
+ throw new Error(
174
+ `The AST does not have a program node in the expression inside addDynamicExpressionAttributeToJSXTag`
175
+ )
176
+ }
177
+
178
+ const theStatementOnlyWihtoutTheProgram = ast.program.body[0]
179
+
180
+ if (theStatementOnlyWihtoutTheProgram.type !== 'ExpressionStatement') {
181
+ throw new Error(`Expr dynamic attribute only support expressions statements at the moment.`)
182
+ }
183
+
184
+ jsxASTNode.openingElement.attributes.push(
185
+ t.jsxAttribute(
186
+ t.jsxIdentifier('href'),
187
+ t.jsxExpressionContainer(theStatementOnlyWihtoutTheProgram.expression)
188
+ )
189
+ )
190
+ }
191
+
192
+ type ReplacementCallback = (path: babel.NodePath<types.Identifier>, name: string) => string
193
+ type ExpressionScopes = Record<string, UIDLStaticValue | UIDLDynamicReference>
194
+
195
+ function replaceIdentifiersWithScopes(
196
+ node: babel.Node,
197
+ scopes: ExpressionScopes,
198
+ callback: ReplacementCallback
199
+ ) {
200
+ const visitor = {
201
+ Identifier(path: babel.NodePath<types.Identifier>) {
202
+ const { name } = path.node
203
+ if (scopes[name]) {
204
+ const newName = callback(path, name)
205
+ if (newName !== name) {
206
+ path.replaceWith(types.identifier(newName))
207
+ }
208
+ }
209
+ },
210
+ }
211
+ traverse(node, visitor)
212
+ }
101
213
  /*
102
214
  Use, when we need to add a mix of dynamic and static values to
103
215
  the same attribute at the same time.
@@ -409,9 +521,14 @@ export const createPureComponent = (
409
521
  name: string,
410
522
  stateDefinitions: Record<string, UIDLStateDefinition>,
411
523
  jsxTagTree: types.JSXElement,
524
+ windowImports: Record<string, types.ExpressionStatement> = {},
412
525
  t = types
413
526
  ): types.VariableDeclaration => {
414
- const arrowFunctionBody = createReturnExpressionSyntax(stateDefinitions, jsxTagTree)
527
+ const arrowFunctionBody = createReturnExpressionSyntax(
528
+ stateDefinitions,
529
+ jsxTagTree,
530
+ windowImports
531
+ )
415
532
  const arrowFunction = t.arrowFunctionExpression([t.identifier('props')], arrowFunctionBody)
416
533
 
417
534
  const declarator = t.variableDeclarator(t.identifier(name), arrowFunction)
@@ -423,6 +540,7 @@ export const createPureComponent = (
423
540
  export const createReturnExpressionSyntax = (
424
541
  stateDefinitions: Record<string, UIDLStateDefinition>,
425
542
  jsxTagTree: types.JSXElement,
543
+ windowImports: Record<string, types.ExpressionStatement> = {},
426
544
  t = types
427
545
  ) => {
428
546
  const returnStatement = t.returnStatement(jsxTagTree)
@@ -431,7 +549,7 @@ export const createReturnExpressionSyntax = (
431
549
  createStateHookAST(stateKey, stateDefinitions[stateKey])
432
550
  )
433
551
 
434
- return t.blockStatement([...stateHooks, returnStatement] || [])
552
+ return t.blockStatement([...stateHooks, ...Object.values(windowImports), returnStatement] || [])
435
553
  }
436
554
 
437
555
  /**
@@ -450,13 +568,361 @@ export const createStateHookAST = (
450
568
  return t.variableDeclaration('const', [
451
569
  t.variableDeclarator(
452
570
  t.arrayPattern([
453
- t.identifier(stateKey),
454
- t.identifier(`set${StringUtils.capitalize(stateKey)}`),
571
+ t.identifier(StringUtils.createStateOrPropStoringValue(stateKey)),
572
+ t.identifier(StringUtils.createStateStoringFunction(stateKey)),
455
573
  ]),
456
574
  t.callExpression(t.identifier('useState'), [defaultValueArgument])
457
575
  ),
458
576
  ])
459
577
  }
460
578
 
579
+ export const generateDynamicWindowImport = (
580
+ hookName = 'useEffect',
581
+ dependency: string
582
+ ): types.ExpressionStatement => {
583
+ return types.expressionStatement(
584
+ types.callExpression(types.identifier(hookName), [
585
+ types.arrowFunctionExpression(
586
+ [],
587
+ types.callExpression(types.identifier('import'), [types.stringLiteral(dependency)])
588
+ ),
589
+ types.arrayExpression([]),
590
+ ])
591
+ )
592
+ }
593
+
461
594
  export const wrapObjectPropertiesWithExpression = (properties: types.ObjectProperty[]) =>
462
595
  types.objectExpression(properties)
596
+
597
+ export const generateRemoteResourceASTs = (
598
+ resource: Resource,
599
+ propsPrefix: string = '',
600
+ extraUrlParamsGenerator?: () => types.ObjectProperty[]
601
+ ) => {
602
+ const fetchUrl = computeFetchUrl(resource)
603
+ const authHeaderAST = computeAuthorizationHeaderAST(resource)
604
+
605
+ const queryParams = generateURLParamsAST(
606
+ resource.urlParams as ResourceUrlParams,
607
+ propsPrefix,
608
+ extraUrlParamsGenerator
609
+ )
610
+
611
+ const fetchUrlQuasis = fetchUrl.quasis
612
+ const queryParamsQuasis = queryParams.quasis
613
+
614
+ if (queryParams.expressions.length > 0) {
615
+ fetchUrlQuasis[fetchUrlQuasis.length - 1].value.raw =
616
+ fetchUrlQuasis[fetchUrlQuasis.length - 1].value.raw + '?'
617
+
618
+ fetchUrlQuasis[fetchUrlQuasis.length - 1].value.cooked =
619
+ fetchUrlQuasis[fetchUrlQuasis.length - 1].value.cooked + '?'
620
+
621
+ queryParamsQuasis.pop()
622
+ }
623
+
624
+ const url = queryParams
625
+ ? types.templateLiteral(
626
+ [...fetchUrlQuasis, ...queryParamsQuasis],
627
+ [...fetchUrl.expressions.concat(queryParams.expressions)]
628
+ )
629
+ : fetchUrl
630
+
631
+ const fetchAST = types.variableDeclaration('const', [
632
+ types.variableDeclarator(
633
+ types.identifier('data'),
634
+ types.awaitExpression(
635
+ types.callExpression(types.identifier('fetch'), [
636
+ url,
637
+ types.objectExpression([
638
+ types.objectProperty(
639
+ types.identifier('headers'),
640
+ types.objectExpression([
641
+ types.objectProperty(
642
+ types.identifier('"Content-Type"'),
643
+ types.stringLiteral('application/json'),
644
+ false,
645
+ false
646
+ ),
647
+ authHeaderAST,
648
+ ])
649
+ ),
650
+ ]),
651
+ ])
652
+ )
653
+ ),
654
+ ])
655
+
656
+ const responseJSONAST = types.variableDeclaration('const', [
657
+ types.variableDeclarator(
658
+ types.identifier('response'),
659
+ types.awaitExpression(
660
+ types.callExpression(
661
+ types.memberExpression(types.identifier('data'), types.identifier('json'), false),
662
+ []
663
+ )
664
+ )
665
+ ),
666
+ ])
667
+
668
+ return [fetchAST, responseJSONAST]
669
+ }
670
+
671
+ export const generateMemberExpressionASTFromBase = (
672
+ base: types.MemberExpression | types.Identifier,
673
+ path: string[]
674
+ ): types.MemberExpression => {
675
+ if (path.length === 1) {
676
+ return types.memberExpression(base, types.identifier(path[0]), false)
677
+ }
678
+
679
+ const pathClone = [...path]
680
+ pathClone.pop()
681
+
682
+ return types.memberExpression(
683
+ generateMemberExpressionASTFromBase(base, pathClone),
684
+ types.identifier(path[path.length - 1]),
685
+ false
686
+ )
687
+ }
688
+
689
+ export const generateMemberExpressionASTFromPath = (
690
+ path: string[]
691
+ ): types.MemberExpression | types.Identifier => {
692
+ const pathClone = [...path]
693
+ if (path.length === 1) {
694
+ return types.identifier(path[0])
695
+ }
696
+
697
+ pathClone.pop()
698
+
699
+ return types.memberExpression(
700
+ generateMemberExpressionASTFromPath(pathClone),
701
+ types.identifier(path[path.length - 1]),
702
+ false
703
+ )
704
+ }
705
+
706
+ const generateURLParamsAST = (
707
+ urlParams: ResourceUrlParams,
708
+ propsPrefix?: string,
709
+ extraUrlParamsGenerator?: () => types.ObjectProperty[]
710
+ ) => {
711
+ const queryString: Record<string, types.Expression> = {}
712
+ Object.keys(urlParams).forEach((key) => {
713
+ resolveDynamicValuesFromUrlParams(urlParams[key], queryString, key, propsPrefix)
714
+ })
715
+
716
+ const urlObject = types.objectExpression([
717
+ ...Object.keys(queryString).map((key) => {
718
+ return types.objectProperty(types.stringLiteral(`${key}`), queryString[key])
719
+ }),
720
+ ...(extraUrlParamsGenerator ? extraUrlParamsGenerator() : []),
721
+ ])
722
+
723
+ return types.templateLiteral(
724
+ [
725
+ types.templateElement({ raw: '', cooked: '' }, false),
726
+ types.templateElement({ raw: '', cooked: '' }, true),
727
+ ],
728
+ [types.newExpression(types.identifier('URLSearchParams'), [urlObject])]
729
+ )
730
+ }
731
+
732
+ const resolveDynamicValuesFromUrlParams = (
733
+ field: ResourceUrlValues,
734
+ query: Record<string, types.Expression>,
735
+ prefix: string = null,
736
+ propsPrefix: string = ''
737
+ ) => {
738
+ if (Array.isArray(field)) {
739
+ const arrayValues = field.map((value) => {
740
+ return resolveUrlParamsValue(value, propsPrefix)
741
+ })
742
+ query[prefix] = types.arrayExpression(arrayValues)
743
+ return
744
+ }
745
+
746
+ if (field.type === 'dynamic' || field.type === 'static' || field.type === 'expr') {
747
+ query[prefix] = resolveUrlParamsValue(field, propsPrefix)
748
+ return
749
+ }
750
+
751
+ Object.keys(field).forEach((key) => {
752
+ const value = field[key]
753
+ const newPrefix = prefix ? `${prefix}[${key}]` : key
754
+
755
+ if (typeof value === 'object') {
756
+ resolveDynamicValuesFromUrlParams(value, query, newPrefix, propsPrefix)
757
+ return
758
+ }
759
+
760
+ query[newPrefix] = resolveUrlParamsValue(value, propsPrefix)
761
+ })
762
+ }
763
+
764
+ const resolveUrlParamsValue = (
765
+ urlParams: UIDLStaticValue | UIDLDynamicReference | UIDLExpressionValue,
766
+ propsPrefix: string = ''
767
+ ) => {
768
+ if (urlParams.type === 'static') {
769
+ return types.stringLiteral(`${urlParams.content}`)
770
+ }
771
+
772
+ if (urlParams.type === 'expr') {
773
+ return types.identifier(urlParams.content)
774
+ }
775
+
776
+ if (urlParams.content.referenceType !== 'prop') {
777
+ throw new Error('Only prop references are supported for url params')
778
+ }
779
+
780
+ const paramPath = [...(propsPrefix ? [propsPrefix] : []), ...(urlParams.content.path || [])]
781
+ const templateLiteralElements = paramPath
782
+ .map((_, index) => {
783
+ if (index === paramPath.length - 1) {
784
+ return null
785
+ }
786
+
787
+ const isTail = index === paramPath.length - 2
788
+ return types.templateElement(
789
+ {
790
+ cooked: '',
791
+ raw: '',
792
+ },
793
+ isTail
794
+ )
795
+ })
796
+ .filter((el) => el)
797
+
798
+ return types.templateLiteral(templateLiteralElements, [
799
+ generateMemberExpressionASTFromPath(paramPath),
800
+ ])
801
+ }
802
+
803
+ const computeAuthorizationHeaderAST = (resource: Resource) => {
804
+ const authToken = resolveResourceValue(resource.authToken)
805
+ if (!authToken) {
806
+ return null
807
+ }
808
+
809
+ const authTokenType = resource.authToken?.type
810
+
811
+ return types.objectProperty(
812
+ types.identifier('Authorization'),
813
+ types.templateLiteral(
814
+ [
815
+ types.templateElement(
816
+ {
817
+ cooked: authTokenType === 'static' ? `Bearer ${authToken}` : 'Bearer ',
818
+ raw: authTokenType === 'static' ? `Bearer ${authToken}` : 'Bearer ',
819
+ },
820
+ false
821
+ ),
822
+ ...(authTokenType === 'static'
823
+ ? []
824
+ : [
825
+ types.templateElement(
826
+ {
827
+ cooked: '',
828
+ raw: '',
829
+ },
830
+ true
831
+ ),
832
+ ]),
833
+ ],
834
+ [...(authTokenType === 'static' ? [] : [types.identifier(authToken)])]
835
+ ),
836
+ false,
837
+ false
838
+ )
839
+ }
840
+
841
+ const computeFetchUrl = (resource: Resource) => {
842
+ const fetchBaseUrl = resolveResourceValue(resource.baseUrl)
843
+ const resourceRoute = resolveResourceValue(resource.route)
844
+
845
+ const baseUrlType = resource.baseUrl?.type
846
+ const routeType = resource.route?.type
847
+
848
+ if (baseUrlType === 'static' && routeType === 'static') {
849
+ const stringsToJoin = [fetchBaseUrl, resourceRoute].filter((item) => item).join('/')
850
+ return types.templateLiteral(
851
+ [types.templateElement({ cooked: `${stringsToJoin}`, raw: `${stringsToJoin}` }, true)],
852
+ []
853
+ )
854
+ }
855
+
856
+ if (!routeType) {
857
+ return baseUrlType === 'static'
858
+ ? types.templateLiteral(
859
+ [types.templateElement({ cooked: `${fetchBaseUrl}`, raw: `${fetchBaseUrl}` }, true)],
860
+ []
861
+ )
862
+ : types.templateLiteral(
863
+ [
864
+ types.templateElement(
865
+ {
866
+ cooked: '',
867
+ raw: '',
868
+ },
869
+ false
870
+ ),
871
+ types.templateElement(
872
+ {
873
+ cooked: '',
874
+ raw: '',
875
+ },
876
+ true
877
+ ),
878
+ ],
879
+ [types.identifier(fetchBaseUrl)]
880
+ )
881
+ }
882
+
883
+ return types.templateLiteral(
884
+ [
885
+ types.templateElement(
886
+ {
887
+ cooked: '',
888
+ raw: '',
889
+ },
890
+ false
891
+ ),
892
+ types.templateElement(
893
+ {
894
+ cooked: routeType === 'static' ? `/${resourceRoute}` : '/',
895
+ raw: routeType === 'static' ? `/${resourceRoute}` : '/',
896
+ },
897
+ false
898
+ ),
899
+ ...(routeType === 'static'
900
+ ? []
901
+ : [
902
+ types.templateElement(
903
+ {
904
+ cooked: '',
905
+ raw: '',
906
+ },
907
+ false
908
+ ),
909
+ ]),
910
+ ],
911
+ [
912
+ types.identifier(fetchBaseUrl),
913
+ ...(routeType === 'static' ? [] : [types.identifier(resourceRoute)]),
914
+ ]
915
+ )
916
+ }
917
+
918
+ const resolveResourceValue = (value: ResourceValue) => {
919
+ if (!value) {
920
+ return ''
921
+ }
922
+
923
+ if (value.type === 'static') {
924
+ return value.value
925
+ }
926
+
927
+ return `process.env.${value.value}` || value.fallback
928
+ }