@teleporthq/teleport-uidl-validator 0.18.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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -0
  3. package/__tests__/parser/componennt-with-referenced-styles.json +111 -0
  4. package/__tests__/parser/component-parsing.ts +116 -0
  5. package/__tests__/parser/component-referenced.ts +50 -0
  6. package/__tests__/parser/component-with-primitive-values.json +61 -0
  7. package/__tests__/parser/component-with-proper-values.json +97 -0
  8. package/__tests__/parser/component-with-reusalble-styles.json +117 -0
  9. package/__tests__/validator/component-element-with-empty-name.json +36 -0
  10. package/__tests__/validator/component-invalid-sample.json +184 -0
  11. package/__tests__/validator/component-uidl-with-event-modifier-undefined.json +45 -0
  12. package/__tests__/validator/component-uidl-with-null-undefined.json +24 -0
  13. package/__tests__/validator/index.ts +216 -0
  14. package/__tests__/validator/old-project-invalid-sample.json +317 -0
  15. package/__tests__/validator/project-invalid-sample-no-route.json +667 -0
  16. package/__tests__/validator/project-invalid-sample.json +671 -0
  17. package/__tests__/validator/project-sample.json +667 -0
  18. package/dist/cjs/decoders/component-decoder.d.ts +4 -0
  19. package/dist/cjs/decoders/component-decoder.js +30 -0
  20. package/dist/cjs/decoders/component-decoder.js.map +1 -0
  21. package/dist/cjs/decoders/custom-combinators.d.ts +8 -0
  22. package/dist/cjs/decoders/custom-combinators.js +59 -0
  23. package/dist/cjs/decoders/custom-combinators.js.map +1 -0
  24. package/dist/cjs/decoders/index.d.ts +2 -0
  25. package/dist/cjs/decoders/index.js +9 -0
  26. package/dist/cjs/decoders/index.js.map +1 -0
  27. package/dist/cjs/decoders/project-decoder.d.ts +5 -0
  28. package/dist/cjs/decoders/project-decoder.js +38 -0
  29. package/dist/cjs/decoders/project-decoder.js.map +1 -0
  30. package/dist/cjs/decoders/utils.d.ts +59 -0
  31. package/dist/cjs/decoders/utils.js +305 -0
  32. package/dist/cjs/decoders/utils.js.map +1 -0
  33. package/dist/cjs/index.d.ts +7 -0
  34. package/dist/cjs/index.js +38 -0
  35. package/dist/cjs/index.js.map +1 -0
  36. package/dist/cjs/parser/index.d.ts +10 -0
  37. package/dist/cjs/parser/index.js +156 -0
  38. package/dist/cjs/parser/index.js.map +1 -0
  39. package/dist/cjs/tsconfig.tsbuildinfo +1 -0
  40. package/dist/cjs/validator/index.d.ts +15 -0
  41. package/dist/cjs/validator/index.js +94 -0
  42. package/dist/cjs/validator/index.js.map +1 -0
  43. package/dist/cjs/validator/utils.d.ts +16 -0
  44. package/dist/cjs/validator/utils.js +373 -0
  45. package/dist/cjs/validator/utils.js.map +1 -0
  46. package/dist/esm/decoders/component-decoder.d.ts +4 -0
  47. package/dist/esm/decoders/component-decoder.js +27 -0
  48. package/dist/esm/decoders/component-decoder.js.map +1 -0
  49. package/dist/esm/decoders/custom-combinators.d.ts +8 -0
  50. package/dist/esm/decoders/custom-combinators.js +56 -0
  51. package/dist/esm/decoders/custom-combinators.js.map +1 -0
  52. package/dist/esm/decoders/index.d.ts +2 -0
  53. package/dist/esm/decoders/index.js +3 -0
  54. package/dist/esm/decoders/index.js.map +1 -0
  55. package/dist/esm/decoders/project-decoder.d.ts +5 -0
  56. package/dist/esm/decoders/project-decoder.js +35 -0
  57. package/dist/esm/decoders/project-decoder.js.map +1 -0
  58. package/dist/esm/decoders/utils.d.ts +59 -0
  59. package/dist/esm/decoders/utils.js +301 -0
  60. package/dist/esm/decoders/utils.js.map +1 -0
  61. package/dist/esm/index.d.ts +7 -0
  62. package/dist/esm/index.js +8 -0
  63. package/dist/esm/index.js.map +1 -0
  64. package/dist/esm/parser/index.d.ts +10 -0
  65. package/dist/esm/parser/index.js +151 -0
  66. package/dist/esm/parser/index.js.map +1 -0
  67. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  68. package/dist/esm/validator/index.d.ts +15 -0
  69. package/dist/esm/validator/index.js +73 -0
  70. package/dist/esm/validator/index.js.map +1 -0
  71. package/dist/esm/validator/utils.d.ts +16 -0
  72. package/dist/esm/validator/utils.js +359 -0
  73. package/dist/esm/validator/utils.js.map +1 -0
  74. package/package.json +32 -0
  75. package/src/decoders/component-decoder.ts +48 -0
  76. package/src/decoders/custom-combinators.ts +59 -0
  77. package/src/decoders/index.ts +2 -0
  78. package/src/decoders/project-decoder.ts +40 -0
  79. package/src/decoders/utils.ts +571 -0
  80. package/src/index.ts +9 -0
  81. package/src/parser/index.ts +227 -0
  82. package/src/validator/index.ts +105 -0
  83. package/src/validator/utils.ts +463 -0
  84. package/tsconfig.json +9 -0
@@ -0,0 +1,227 @@
1
+ import { UIDLUtils } from '@teleporthq/teleport-shared'
2
+
3
+ import {
4
+ UIDLDynamicReference,
5
+ ComponentUIDL,
6
+ ProjectUIDL,
7
+ UIDLNode,
8
+ UIDLConditionalNode,
9
+ UIDLRepeatNode,
10
+ UIDLSlotNode,
11
+ UIDLElementNode,
12
+ UIDLStaticValue,
13
+ UIDLStyleSetConditions,
14
+ UIDLDesignTokens,
15
+ ParserError,
16
+ } from '@teleporthq/teleport-types'
17
+
18
+ interface ParseComponentJSONParams {
19
+ noClone?: boolean
20
+ }
21
+
22
+ export const parseComponentJSON = (
23
+ input: Record<string, unknown>,
24
+ params: ParseComponentJSONParams = {}
25
+ ): ComponentUIDL => {
26
+ const safeInput = params.noClone ? input : UIDLUtils.cloneObject(input)
27
+
28
+ if (input?.styleSetDefinitions) {
29
+ const { styleSetDefinitions } = safeInput
30
+
31
+ Object.values(styleSetDefinitions).forEach((styleRef) => {
32
+ const { conditions = [] } = styleRef
33
+ styleRef.content = UIDLUtils.transformStylesAssignmentsToJson(styleRef.content)
34
+ if (conditions.length > 0) {
35
+ conditions.forEach((style: UIDLStyleSetConditions) => {
36
+ style.content = UIDLUtils.transformStylesAssignmentsToJson(style.content) as Record<
37
+ string,
38
+ UIDLStaticValue
39
+ >
40
+ })
41
+ }
42
+ })
43
+ }
44
+
45
+ const node = safeInput.node as Record<string, unknown>
46
+ const result: ComponentUIDL = {
47
+ ...(safeInput as unknown as ComponentUIDL),
48
+ }
49
+
50
+ // other parsers for other sections of the component here
51
+ result.node = parseComponentNode(node) as UIDLElementNode
52
+
53
+ return result
54
+ }
55
+
56
+ interface ParseProjectJSONParams {
57
+ noClone?: boolean
58
+ }
59
+
60
+ export const parseProjectJSON = (
61
+ input: Record<string, unknown>,
62
+ params: ParseProjectJSONParams = {}
63
+ ): ProjectUIDL => {
64
+ const safeInput = params.noClone ? input : UIDLUtils.cloneObject(input)
65
+ const root = safeInput.root as Record<string, unknown>
66
+ const result = {
67
+ ...(safeInput as unknown as ProjectUIDL),
68
+ }
69
+ result.root = parseComponentJSON(root, { noClone: true })
70
+
71
+ if (result.root?.designLanguage) {
72
+ const { tokens = {} } = result.root.designLanguage
73
+
74
+ result.root.designLanguage.tokens = Object.keys(tokens).reduce(
75
+ (acc: UIDLDesignTokens, tokenId: string) => {
76
+ const token = tokens[tokenId]
77
+ if (typeof token === 'string' || typeof token === 'number') {
78
+ acc[tokenId] = {
79
+ type: 'static',
80
+ content: token,
81
+ }
82
+ } else {
83
+ acc[tokenId] = token
84
+ }
85
+ return acc
86
+ },
87
+ {}
88
+ )
89
+ }
90
+
91
+ if (result.components) {
92
+ result.components = Object.keys(result.components).reduce(
93
+ (parsedComponnets: Record<string, ComponentUIDL>, key) => {
94
+ parsedComponnets[key] = parseComponentJSON(
95
+ result.components[key] as unknown as Record<string, unknown>
96
+ )
97
+ return parsedComponnets
98
+ },
99
+ {}
100
+ )
101
+ }
102
+
103
+ return result
104
+ }
105
+
106
+ const parseComponentNode = (node: Record<string, unknown>): UIDLNode => {
107
+ switch ((node as unknown as UIDLNode).type) {
108
+ case 'element':
109
+ const elementContent = node.content as Record<string, unknown>
110
+
111
+ if (elementContent?.referencedStyles) {
112
+ Object.values(elementContent.referencedStyles).forEach((styleRef) => {
113
+ const { content } = styleRef
114
+
115
+ switch (content.mapType) {
116
+ case 'inlined': {
117
+ content.styles = UIDLUtils.transformStylesAssignmentsToJson(
118
+ content.styles as Record<string, string>
119
+ )
120
+ break
121
+ }
122
+
123
+ case 'project-referenced':
124
+ break
125
+
126
+ case 'component-referenced': {
127
+ if (['string', 'number'].includes(typeof styleRef.content.content)) {
128
+ styleRef.content.content = {
129
+ type: 'static',
130
+ content: styleRef.content.content,
131
+ }
132
+ }
133
+ break
134
+ }
135
+
136
+ default: {
137
+ throw new ParserError(
138
+ `Un-expected mapType passed in referencedStyles - ${content.mapType}`
139
+ )
140
+ }
141
+ }
142
+ })
143
+ }
144
+
145
+ if (elementContent.style) {
146
+ elementContent.style = UIDLUtils.transformStylesAssignmentsToJson(
147
+ elementContent.style as Record<string, unknown>
148
+ )
149
+ }
150
+
151
+ if (elementContent.attrs) {
152
+ elementContent.attrs = UIDLUtils.transformAttributesAssignmentsToJson(
153
+ elementContent.attrs as Record<string, unknown>
154
+ )
155
+ }
156
+
157
+ // @ts-ignore
158
+ if (elementContent.abilities?.link) {
159
+ // @ts-ignore
160
+ const { content, type } = elementContent.abilities?.link
161
+ if (type === 'url' && typeof content.url === 'string') {
162
+ content.url = UIDLUtils.transformStringAssignmentToJson(content.url)
163
+ }
164
+ }
165
+
166
+ if (Array.isArray(elementContent.children)) {
167
+ elementContent.children = elementContent.children.map((child) => {
168
+ if (typeof child === 'string') {
169
+ return UIDLUtils.transformStringAssignmentToJson(child)
170
+ } else {
171
+ return parseComponentNode(child)
172
+ }
173
+ }, [])
174
+ }
175
+
176
+ return node as unknown as UIDLNode
177
+
178
+ case 'conditional':
179
+ const conditionalNode = node as unknown as UIDLConditionalNode
180
+ const { reference } = conditionalNode.content
181
+
182
+ conditionalNode.content.node = parseComponentNode(
183
+ conditionalNode.content.node as unknown as Record<string, unknown>
184
+ )
185
+
186
+ if (typeof reference === 'string') {
187
+ conditionalNode.content.reference = UIDLUtils.transformStringAssignmentToJson(
188
+ reference
189
+ ) as UIDLDynamicReference
190
+ }
191
+
192
+ return conditionalNode
193
+
194
+ case 'repeat':
195
+ const repeatNode = node as unknown as UIDLRepeatNode
196
+ const { dataSource } = repeatNode.content
197
+
198
+ repeatNode.content.node = parseComponentNode(
199
+ repeatNode.content.node as unknown as Record<string, unknown>
200
+ ) as UIDLElementNode
201
+
202
+ if (typeof dataSource === 'string') {
203
+ repeatNode.content.dataSource = UIDLUtils.transformStringAssignmentToJson(dataSource)
204
+ }
205
+
206
+ return repeatNode
207
+
208
+ case 'slot':
209
+ const slotNode = node as unknown as UIDLSlotNode
210
+
211
+ if (slotNode.content.fallback) {
212
+ slotNode.content.fallback = parseComponentNode(
213
+ slotNode.content.fallback as unknown as Record<string, unknown>
214
+ ) as UIDLElementNode | UIDLStaticValue | UIDLDynamicReference
215
+ }
216
+
217
+ return slotNode
218
+
219
+ case 'dynamic':
220
+ case 'static':
221
+ case 'raw':
222
+ return node as unknown as UIDLNode
223
+
224
+ default:
225
+ throw new ParserError(`parseComponentNode attempted to parsed invalid node type ${node.type}`)
226
+ }
227
+ }
@@ -0,0 +1,105 @@
1
+ import {
2
+ ProjectUIDL,
3
+ ComponentUIDL,
4
+ ProjectValidationError,
5
+ ComponentValidationError,
6
+ VComponentUIDL,
7
+ VProjectUIDL,
8
+ } from '@teleporthq/teleport-types'
9
+ import { componentUIDLDecoder, rootComponentUIDLDecoder, projectUIDLDecoder } from '../decoders'
10
+ import * as utils from './utils'
11
+
12
+ interface ValidationResult {
13
+ valid: boolean
14
+ errorMsg: string
15
+ componentUIDL?: VComponentUIDL
16
+ projectUIDL?: VProjectUIDL
17
+ }
18
+
19
+ export default class Validator {
20
+ public validateComponentSchema(input: Record<string, unknown>): ValidationResult {
21
+ try {
22
+ const cleanedUIDL = utils.validateNulls(input)
23
+ const uidl = componentUIDLDecoder.runWithException(cleanedUIDL)
24
+
25
+ return { valid: true, errorMsg: '', componentUIDL: uidl }
26
+ } catch (e) {
27
+ const errorMsg = utils.formatErrors([{ kind: e.kind, message: e.message, at: e.at }])
28
+ throw new ComponentValidationError(`Component - ${input?.name} \n
29
+ ${errorMsg}`)
30
+ }
31
+ }
32
+
33
+ public validateRootComponentSchema(input: Record<string, unknown>): ValidationResult {
34
+ try {
35
+ const cleanedUIDL = utils.validateNulls(input)
36
+ const uidl = rootComponentUIDLDecoder.runWithException(cleanedUIDL)
37
+
38
+ return { valid: true, errorMsg: '', componentUIDL: uidl }
39
+ } catch (e) {
40
+ const errorMsg = utils.formatErrors([{ kind: e.kind, message: e.message, at: e.at }])
41
+ throw new ComponentValidationError(`Component - ${input?.name}\n
42
+ ${errorMsg}`)
43
+ }
44
+ }
45
+
46
+ public validateProjectSchema(input: Record<string, unknown>): ValidationResult {
47
+ try {
48
+ const cleanedUIDL = utils.validateNulls(input)
49
+ const uidl = projectUIDLDecoder.runWithException(cleanedUIDL)
50
+
51
+ return { valid: true, errorMsg: '', projectUIDL: uidl }
52
+ } catch (e) {
53
+ const errorMsg = utils.formatErrors([{ kind: e.kind, message: e.message, at: e.at }])
54
+ throw new ProjectValidationError(`${input.name}\n
55
+ ${errorMsg}`)
56
+ }
57
+ }
58
+
59
+ public validateComponentContent(input: ComponentUIDL): ValidationResult {
60
+ const errorsInDefinitions = utils.checkDynamicDefinitions(
61
+ input as unknown as Record<string, unknown>
62
+ )
63
+ const errorsWithLocalVariables = utils.checkForLocalVariables(input)
64
+
65
+ utils.checkForDuplicateDefinitions(input)
66
+ const errors = [...errorsInDefinitions, ...errorsWithLocalVariables]
67
+
68
+ if (errors.length > 0) {
69
+ throw new ComponentValidationError(
70
+ `UIDL Component Content Validation Error. Please check the following: \n${errors}`
71
+ )
72
+ }
73
+
74
+ return { valid: true, errorMsg: '' }
75
+ }
76
+
77
+ public validateProjectContent(input: ProjectUIDL): ValidationResult {
78
+ const errorsOnRouteNode = utils.checkRouteDefinition(input) || []
79
+ let allErrors = errorsOnRouteNode
80
+
81
+ if (errorsOnRouteNode.length === 0) {
82
+ const errorsInStyleSet = utils.checkProjectStyleSet(input)
83
+ const errorsInRootComponent = utils.checkRootComponent(input)
84
+
85
+ const errorsWithComponentNaming = utils.checkComponentNaming(input)
86
+ const errorsWtihComponentExistence = utils.checkComponentExistenceAndReferences(input)
87
+
88
+ allErrors = [
89
+ ...errorsOnRouteNode,
90
+ ...errorsWtihComponentExistence,
91
+ ...errorsWithComponentNaming,
92
+ ...errorsInRootComponent,
93
+ ...errorsInStyleSet,
94
+ ]
95
+ }
96
+
97
+ if (allErrors.length > 0) {
98
+ throw new ProjectValidationError(
99
+ `UIDL Project Content Validation Error. Please check the following: ${allErrors}`
100
+ )
101
+ }
102
+
103
+ return { valid: true, errorMsg: '' }
104
+ }
105
+ }