@teleporthq/teleport-plugin-html-base-component 0.34.0-alpha.0 → 0.34.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.
- package/__tests__/index.ts +26 -20
- package/dist/cjs/constants.d.ts +1 -1
- package/dist/cjs/constants.d.ts.map +1 -1
- package/dist/cjs/constants.js +1 -1
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +35 -11
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/node-handlers.d.ts +7 -3
- package/dist/cjs/node-handlers.d.ts.map +1 -1
- package/dist/cjs/node-handlers.js +272 -149
- package/dist/cjs/node-handlers.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/constants.d.ts +1 -1
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.js +1 -1
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +36 -12
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node-handlers.d.ts +7 -3
- package/dist/esm/node-handlers.d.ts.map +1 -1
- package/dist/esm/node-handlers.js +270 -147
- package/dist/esm/node-handlers.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/constants.ts +1 -1
- package/src/index.ts +56 -18
- package/src/node-handlers.ts +293 -149
package/src/node-handlers.ts
CHANGED
|
@@ -17,34 +17,51 @@ import {
|
|
|
17
17
|
UIDLStyleValue,
|
|
18
18
|
GeneratorOptions,
|
|
19
19
|
UIDLRouteDefinitions,
|
|
20
|
+
ComponentPlugin,
|
|
21
|
+
ComponentStructure,
|
|
22
|
+
UIDLComponentOutputOptions,
|
|
23
|
+
UIDLElement,
|
|
20
24
|
} from '@teleporthq/teleport-types'
|
|
25
|
+
import { join, relative } from 'path'
|
|
21
26
|
import { HASTBuilders, HASTUtils } from '@teleporthq/teleport-plugin-common'
|
|
22
27
|
import { StringUtils, UIDLUtils } from '@teleporthq/teleport-shared'
|
|
23
28
|
import { staticNode } from '@teleporthq/teleport-uidl-builders'
|
|
24
29
|
import { createCSSPlugin } from '@teleporthq/teleport-plugin-css'
|
|
25
30
|
import { DEFAULT_COMPONENT_CHUNK_NAME } from './constants'
|
|
26
31
|
|
|
32
|
+
const isValidURL = (url: string) => {
|
|
33
|
+
try {
|
|
34
|
+
/* tslint:disable:no-unused-expression */
|
|
35
|
+
new URL(url)
|
|
36
|
+
return true
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
type NodeToHTML<NodeType, ReturnType> = (
|
|
28
43
|
node: NodeType,
|
|
29
|
-
|
|
44
|
+
nodesLookup: Record<string, HastNode | HastText>,
|
|
30
45
|
propDefinitions: Record<string, UIDLPropDefinition>,
|
|
31
46
|
stateDefinitions: Record<string, UIDLStateDefinition>,
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
subComponentOptions: {
|
|
48
|
+
externals: Record<string, ComponentUIDL>
|
|
49
|
+
plugins: ComponentPlugin[]
|
|
50
|
+
},
|
|
34
51
|
structure: {
|
|
35
52
|
chunks: ChunkDefinition[]
|
|
36
53
|
dependencies: Record<string, UIDLDependency>
|
|
37
54
|
options: GeneratorOptions
|
|
55
|
+
outputOptions: UIDLComponentOutputOptions
|
|
38
56
|
}
|
|
39
57
|
) => ReturnType
|
|
40
58
|
|
|
41
|
-
export const
|
|
59
|
+
export const generateHtmlSyntax: NodeToHTML<UIDLNode, Promise<HastNode | HastText>> = async (
|
|
42
60
|
node,
|
|
43
|
-
|
|
61
|
+
nodesLookup,
|
|
44
62
|
propDefinitions,
|
|
45
63
|
stateDefinitions,
|
|
46
|
-
|
|
47
|
-
routeDefinitions,
|
|
64
|
+
subComponentOptions,
|
|
48
65
|
structure
|
|
49
66
|
) => {
|
|
50
67
|
switch (node.type) {
|
|
@@ -59,27 +76,28 @@ export const generateHtmlSynatx: NodeToHTML<UIDLNode, Promise<HastNode | HastTex
|
|
|
59
76
|
return HASTBuilders.createHTMLNode(node.type)
|
|
60
77
|
|
|
61
78
|
case 'element':
|
|
62
|
-
|
|
79
|
+
const elementNode = await generateElementNode(
|
|
63
80
|
node,
|
|
64
|
-
|
|
81
|
+
nodesLookup,
|
|
65
82
|
propDefinitions,
|
|
66
83
|
stateDefinitions,
|
|
67
|
-
|
|
68
|
-
routeDefinitions,
|
|
84
|
+
subComponentOptions,
|
|
69
85
|
structure
|
|
70
86
|
)
|
|
87
|
+
return elementNode
|
|
71
88
|
|
|
72
89
|
case 'dynamic':
|
|
73
|
-
|
|
90
|
+
const dynamicNode = await generateDynamicNode(
|
|
74
91
|
node,
|
|
75
|
-
|
|
92
|
+
nodesLookup,
|
|
76
93
|
propDefinitions,
|
|
77
94
|
stateDefinitions,
|
|
78
|
-
|
|
79
|
-
routeDefinitions,
|
|
95
|
+
subComponentOptions,
|
|
80
96
|
structure
|
|
81
97
|
)
|
|
82
98
|
|
|
99
|
+
return dynamicNode
|
|
100
|
+
|
|
83
101
|
default:
|
|
84
102
|
throw new HTMLComponentGeneratorError(
|
|
85
103
|
`generateHtmlSyntax encountered a node of unsupported type: ${JSON.stringify(
|
|
@@ -91,13 +109,12 @@ export const generateHtmlSynatx: NodeToHTML<UIDLNode, Promise<HastNode | HastTex
|
|
|
91
109
|
}
|
|
92
110
|
}
|
|
93
111
|
|
|
94
|
-
const
|
|
112
|
+
const generateElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastText>> = async (
|
|
95
113
|
node,
|
|
96
|
-
|
|
114
|
+
nodesLookup,
|
|
97
115
|
propDefinitions,
|
|
98
116
|
stateDefinitions,
|
|
99
|
-
|
|
100
|
-
routeDefinitions,
|
|
117
|
+
subComponentOptions,
|
|
101
118
|
structure
|
|
102
119
|
) => {
|
|
103
120
|
const {
|
|
@@ -109,43 +126,51 @@ const generatElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastTex
|
|
|
109
126
|
dependency,
|
|
110
127
|
key,
|
|
111
128
|
} = node.content
|
|
112
|
-
|
|
113
|
-
const elementNode = HASTBuilders.createHTMLNode(elementType)
|
|
114
|
-
templatesLookUp[key] = elementNode
|
|
115
|
-
|
|
116
129
|
const { dependencies } = structure
|
|
117
130
|
if (dependency && (dependency as UIDLDependency)?.type !== 'local') {
|
|
118
131
|
dependencies[dependency.path] = dependency
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
if (dependency && (dependency as UIDLDependency)?.type === 'local') {
|
|
135
|
+
for (const attrKey of Object.keys(attrs)) {
|
|
136
|
+
const attr = attrs[attrKey]
|
|
137
|
+
if (attr.type === 'element') {
|
|
138
|
+
await generateElementNode(
|
|
139
|
+
attr,
|
|
140
|
+
nodesLookup,
|
|
141
|
+
propDefinitions,
|
|
142
|
+
stateDefinitions,
|
|
143
|
+
subComponentOptions,
|
|
144
|
+
structure
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
122
149
|
const compTag = await generateComponentContent(
|
|
123
150
|
node,
|
|
151
|
+
nodesLookup,
|
|
124
152
|
propDefinitions,
|
|
125
153
|
stateDefinitions,
|
|
126
|
-
|
|
127
|
-
routeDefinitions,
|
|
154
|
+
subComponentOptions,
|
|
128
155
|
structure
|
|
129
156
|
)
|
|
157
|
+
|
|
130
158
|
return compTag
|
|
131
159
|
}
|
|
132
160
|
|
|
161
|
+
const elementNode = HASTBuilders.createHTMLNode(elementType)
|
|
162
|
+
|
|
133
163
|
if (children) {
|
|
134
164
|
for (const child of children) {
|
|
135
|
-
const childTag = await
|
|
165
|
+
const childTag = await generateHtmlSyntax(
|
|
136
166
|
child,
|
|
137
|
-
|
|
167
|
+
nodesLookup,
|
|
138
168
|
propDefinitions,
|
|
139
169
|
stateDefinitions,
|
|
140
|
-
|
|
141
|
-
routeDefinitions,
|
|
170
|
+
subComponentOptions,
|
|
142
171
|
structure
|
|
143
172
|
)
|
|
144
173
|
|
|
145
|
-
if (!childTag) {
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
|
|
149
174
|
if (typeof childTag === 'string') {
|
|
150
175
|
HASTUtils.addTextNode(elementNode, childTag)
|
|
151
176
|
} else {
|
|
@@ -168,39 +193,52 @@ const generatElementNode: NodeToHTML<UIDLElementNode, Promise<HastNode | HastTex
|
|
|
168
193
|
handleStyles(node, style, propDefinitions, stateDefinitions)
|
|
169
194
|
}
|
|
170
195
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
196
|
+
handleAttributes(
|
|
197
|
+
elementType,
|
|
198
|
+
elementNode,
|
|
199
|
+
attrs,
|
|
200
|
+
propDefinitions,
|
|
201
|
+
stateDefinitions,
|
|
202
|
+
structure.options.projectRouteDefinition,
|
|
203
|
+
structure.outputOptions
|
|
204
|
+
)
|
|
174
205
|
|
|
206
|
+
nodesLookup[key] = elementNode
|
|
175
207
|
return elementNode
|
|
176
208
|
}
|
|
177
209
|
|
|
178
210
|
const generateComponentContent = async (
|
|
179
211
|
node: UIDLElementNode,
|
|
212
|
+
nodesLookup: Record<string, HastNode | HastText>,
|
|
180
213
|
propDefinitions: Record<string, UIDLPropDefinition>,
|
|
181
214
|
stateDefinitions: Record<string, UIDLStateDefinition>,
|
|
182
|
-
|
|
183
|
-
|
|
215
|
+
subComponentOptions: {
|
|
216
|
+
externals: Record<string, ComponentUIDL>
|
|
217
|
+
plugins: ComponentPlugin[]
|
|
218
|
+
},
|
|
184
219
|
structure: {
|
|
185
220
|
chunks: ChunkDefinition[]
|
|
186
221
|
dependencies: Record<string, UIDLDependency>
|
|
187
222
|
options: GeneratorOptions
|
|
223
|
+
outputOptions: UIDLComponentOutputOptions
|
|
188
224
|
}
|
|
189
225
|
) => {
|
|
226
|
+
const { externals, plugins } = subComponentOptions
|
|
190
227
|
const { elementType, attrs = {}, key, children = [] } = node.content
|
|
191
|
-
const { dependencies, chunks, options } = structure
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
throw new HTMLComponentGeneratorError(`${elementType} is not found from the externals. \n
|
|
198
|
-
Received ${JSON.stringify(Object.keys(externals), null, 2)}`)
|
|
228
|
+
const { dependencies, chunks = [], options } = structure
|
|
229
|
+
// "Component" will not exist when generating a component because the resolver checks for illegal class names
|
|
230
|
+
const compName = elementType === 'Component' ? 'AppComponent' : elementType
|
|
231
|
+
const component = externals[compName]
|
|
232
|
+
if (component === undefined) {
|
|
233
|
+
throw new HTMLComponentGeneratorError(`${compName} is missing from externals object`)
|
|
199
234
|
}
|
|
200
235
|
|
|
236
|
+
const componentClone = UIDLUtils.cloneObject(component) as ComponentUIDL
|
|
237
|
+
let compHasSlots: boolean = false
|
|
238
|
+
|
|
201
239
|
if (children.length) {
|
|
202
240
|
compHasSlots = true
|
|
203
|
-
UIDLUtils.traverseNodes(
|
|
241
|
+
UIDLUtils.traverseNodes(componentClone.node, (childNode, parentNode) => {
|
|
204
242
|
if (childNode.type === 'slot' && parentNode.type === 'element') {
|
|
205
243
|
const nonSlotNodes = parentNode.content?.children?.filter((n) => n.type !== 'slot')
|
|
206
244
|
parentNode.content.children = [
|
|
@@ -229,25 +267,27 @@ const generateComponentContent = async (
|
|
|
229
267
|
node.content.children = []
|
|
230
268
|
}
|
|
231
269
|
|
|
232
|
-
const combinedProps = { ...propDefinitions, ...(
|
|
270
|
+
const combinedProps = { ...propDefinitions, ...(componentClone?.propDefinitions || {}) }
|
|
271
|
+
const propsForInstance: Record<string, UIDLPropDefinition> = {}
|
|
233
272
|
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
} else {
|
|
242
|
-
acc[propKey] = combinedProps[propKey]
|
|
273
|
+
for (const propKey of Object.keys(combinedProps)) {
|
|
274
|
+
// If the attribute is a named-slot, then we can directly pass the value instead of just the content
|
|
275
|
+
if (attrs[propKey]?.type === 'element') {
|
|
276
|
+
propsForInstance[propKey] = {
|
|
277
|
+
...combinedProps[propKey],
|
|
278
|
+
defaultValue: attrs[propKey],
|
|
243
279
|
}
|
|
280
|
+
} else if (attrs[propKey]) {
|
|
281
|
+
propsForInstance[propKey] = {
|
|
282
|
+
...combinedProps[propKey],
|
|
283
|
+
defaultValue: attrs[propKey]?.content || combinedProps[propKey]?.defaultValue,
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
propsForInstance[propKey] = combinedProps[propKey]
|
|
287
|
+
}
|
|
288
|
+
}
|
|
244
289
|
|
|
245
|
-
|
|
246
|
-
},
|
|
247
|
-
{}
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
const combinedStates = { ...stateDefinitions, ...(comp?.stateDefinitions || {}) }
|
|
290
|
+
const combinedStates = { ...stateDefinitions, ...(componentClone?.stateDefinitions || {}) }
|
|
251
291
|
const statesForInstance = Object.keys(combinedStates).reduce(
|
|
252
292
|
(acc: Record<string, UIDLStateDefinition>, propKey) => {
|
|
253
293
|
if (attrs[propKey]) {
|
|
@@ -264,43 +304,48 @@ const generateComponentContent = async (
|
|
|
264
304
|
{}
|
|
265
305
|
)
|
|
266
306
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
307
|
+
let componentWrapper = StringUtils.camelCaseToDashCase(`${compName}-wrapper`)
|
|
308
|
+
const isExistingNode = nodesLookup[componentWrapper]
|
|
309
|
+
if (isExistingNode !== undefined) {
|
|
310
|
+
componentWrapper = `${componentWrapper}-${StringUtils.generateRandomString()}`
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const componentInstanceToGenerate: UIDLElementNode = {
|
|
314
|
+
type: 'element',
|
|
315
|
+
content: {
|
|
316
|
+
elementType: componentWrapper,
|
|
317
|
+
key: componentWrapper,
|
|
318
|
+
children: [componentClone.node],
|
|
319
|
+
style: {
|
|
320
|
+
display: {
|
|
321
|
+
type: 'static',
|
|
322
|
+
content: 'contents',
|
|
281
323
|
},
|
|
282
324
|
},
|
|
283
325
|
},
|
|
284
|
-
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const compTag = await generateHtmlSyntax(
|
|
329
|
+
componentInstanceToGenerate,
|
|
330
|
+
nodesLookup,
|
|
285
331
|
propsForInstance,
|
|
286
332
|
statesForInstance,
|
|
287
|
-
|
|
288
|
-
routeDefinitions,
|
|
333
|
+
subComponentOptions,
|
|
289
334
|
structure
|
|
290
|
-
)
|
|
335
|
+
)
|
|
291
336
|
|
|
292
337
|
const cssPlugin = createCSSPlugin({
|
|
293
338
|
templateStyle: 'html',
|
|
294
|
-
templateChunkName:
|
|
339
|
+
templateChunkName: DEFAULT_COMPONENT_CHUNK_NAME,
|
|
295
340
|
declareDependency: 'import',
|
|
296
341
|
forceScoping: true,
|
|
297
|
-
chunkName:
|
|
342
|
+
chunkName: componentClone.name,
|
|
298
343
|
staticPropReferences: true,
|
|
299
344
|
})
|
|
300
345
|
|
|
301
|
-
const
|
|
346
|
+
const initialStructure: ComponentStructure = {
|
|
302
347
|
uidl: {
|
|
303
|
-
...
|
|
348
|
+
...componentClone,
|
|
304
349
|
propDefinitions: propsForInstance,
|
|
305
350
|
stateDefinitions: statesForInstance,
|
|
306
351
|
},
|
|
@@ -312,13 +357,21 @@ const generateComponentContent = async (
|
|
|
312
357
|
linkAfter: [],
|
|
313
358
|
content: compTag,
|
|
314
359
|
meta: {
|
|
315
|
-
nodesLookup
|
|
360
|
+
nodesLookup,
|
|
316
361
|
},
|
|
317
362
|
},
|
|
318
363
|
],
|
|
319
364
|
dependencies,
|
|
320
365
|
options,
|
|
321
|
-
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const result = await [cssPlugin, ...plugins].reduce(
|
|
369
|
+
async (previousPluginOperation: Promise<ComponentStructure>, plugin) => {
|
|
370
|
+
const modifiedStructure = await previousPluginOperation
|
|
371
|
+
return plugin(modifiedStructure)
|
|
372
|
+
},
|
|
373
|
+
Promise.resolve(initialStructure)
|
|
374
|
+
)
|
|
322
375
|
|
|
323
376
|
if (compHasSlots) {
|
|
324
377
|
result.chunks.forEach((chunk) => {
|
|
@@ -327,29 +380,57 @@ const generateComponentContent = async (
|
|
|
327
380
|
}
|
|
328
381
|
})
|
|
329
382
|
} else {
|
|
330
|
-
const chunk = chunks.find((item) => item.name ===
|
|
383
|
+
const chunk = chunks.find((item) => item.name === componentClone.name)
|
|
331
384
|
if (!chunk) {
|
|
332
|
-
const styleChunk = result.chunks.find(
|
|
385
|
+
const styleChunk = result.chunks.find(
|
|
386
|
+
(item: ChunkDefinition) => item.fileType === FileType.CSS
|
|
387
|
+
)
|
|
388
|
+
if (!styleChunk) {
|
|
389
|
+
return
|
|
390
|
+
}
|
|
333
391
|
chunks.push(styleChunk)
|
|
334
392
|
}
|
|
335
393
|
}
|
|
336
394
|
|
|
395
|
+
nodesLookup[key] = compTag
|
|
337
396
|
return compTag
|
|
338
397
|
}
|
|
339
398
|
|
|
340
|
-
const generateDynamicNode: NodeToHTML<UIDLDynamicReference, HastNode
|
|
399
|
+
const generateDynamicNode: NodeToHTML<UIDLDynamicReference, Promise<HastNode>> = async (
|
|
341
400
|
node,
|
|
342
|
-
|
|
401
|
+
nodesLookup,
|
|
343
402
|
propDefinitions,
|
|
344
|
-
stateDefinitions
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
403
|
+
stateDefinitions,
|
|
404
|
+
subComponentOptions,
|
|
405
|
+
structure
|
|
406
|
+
): Promise<HastNode> => {
|
|
407
|
+
const usedReferenceValue = getValueFromReference(
|
|
408
|
+
node.content.id,
|
|
409
|
+
node.content.referenceType === 'prop' ? propDefinitions : stateDefinitions
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue) {
|
|
413
|
+
const slotNode = await generateElementNode(
|
|
414
|
+
usedReferenceValue.defaultValue as UIDLElementNode,
|
|
415
|
+
nodesLookup,
|
|
416
|
+
propDefinitions,
|
|
417
|
+
stateDefinitions,
|
|
418
|
+
subComponentOptions,
|
|
419
|
+
structure
|
|
420
|
+
)
|
|
351
421
|
|
|
352
|
-
|
|
422
|
+
return slotNode as HastNode
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue === undefined) {
|
|
426
|
+
const spanTagWrapper = HASTBuilders.createHTMLNode('span')
|
|
427
|
+
const commentNode = HASTBuilders.createComment(`Content for slot ${node.content.id}`)
|
|
428
|
+
HASTUtils.addChildNode(spanTagWrapper, commentNode)
|
|
429
|
+
return spanTagWrapper
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const spanTag = HASTBuilders.createHTMLNode('span')
|
|
433
|
+
HASTUtils.addTextNode(spanTag, String(usedReferenceValue.defaultValue))
|
|
353
434
|
return spanTag
|
|
354
435
|
}
|
|
355
436
|
|
|
@@ -362,10 +443,12 @@ const handleStyles = (
|
|
|
362
443
|
Object.keys(styles).forEach((styleKey) => {
|
|
363
444
|
let style: string | UIDLStyleValue = styles[styleKey]
|
|
364
445
|
if (style.type === 'dynamic' && style.content?.referenceType !== 'token') {
|
|
365
|
-
|
|
366
|
-
style
|
|
367
|
-
|
|
368
|
-
|
|
446
|
+
const referencedValue = getValueFromReference(
|
|
447
|
+
style.content.id,
|
|
448
|
+
style.content.referenceType === 'prop' ? propDefinitions : stateDefinitions
|
|
449
|
+
)
|
|
450
|
+
if (referencedValue.type === 'string' || referencedValue.type === 'number') {
|
|
451
|
+
style = String(referencedValue.defaultValue)
|
|
369
452
|
}
|
|
370
453
|
node.content.style[styleKey] = typeof style === 'string' ? staticNode(style) : style
|
|
371
454
|
}
|
|
@@ -373,61 +456,119 @@ const handleStyles = (
|
|
|
373
456
|
}
|
|
374
457
|
|
|
375
458
|
const handleAttributes = (
|
|
459
|
+
elementType: UIDLElement['elementType'],
|
|
376
460
|
htmlNode: HastNode,
|
|
377
461
|
attrs: Record<string, UIDLAttributeValue>,
|
|
378
462
|
propDefinitions: Record<string, UIDLPropDefinition>,
|
|
379
463
|
stateDefinitions: Record<string, UIDLStateDefinition>,
|
|
380
|
-
routeDefinitions: UIDLRouteDefinitions
|
|
464
|
+
routeDefinitions: UIDLRouteDefinitions,
|
|
465
|
+
outputOptions: UIDLComponentOutputOptions
|
|
381
466
|
) => {
|
|
382
|
-
Object.keys(attrs)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
467
|
+
for (const attrKey of Object.keys(attrs)) {
|
|
468
|
+
const attrValue = attrs[attrKey]
|
|
469
|
+
const { type, content } = attrValue
|
|
470
|
+
|
|
471
|
+
switch (type) {
|
|
472
|
+
case 'static': {
|
|
473
|
+
if (attrKey === 'href' && typeof content === 'string' && content.startsWith('/')) {
|
|
474
|
+
let targetLink
|
|
475
|
+
|
|
476
|
+
const targetRoute = (routeDefinitions?.values || []).find(
|
|
477
|
+
(route) => route.pageOptions.navLink === content
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
if (targetRoute) {
|
|
481
|
+
targetLink = targetRoute.pageOptions.navLink
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (!targetRoute && content === '/home') {
|
|
485
|
+
targetLink = '/'
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (!targetLink && !targetRoute) {
|
|
489
|
+
targetLink = content
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const currentPageRoute = join(...(outputOptions?.folderPath || []), './')
|
|
493
|
+
const localPrefix = relative(
|
|
494
|
+
`/${currentPageRoute}`,
|
|
495
|
+
`/${targetLink === '/' ? 'index' : targetLink}`
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
HASTUtils.addAttributeToNode(htmlNode, attrKey, `${localPrefix}.html`)
|
|
499
|
+
break
|
|
500
|
+
}
|
|
402
501
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
attrValue.content
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
502
|
+
if (typeof content === 'boolean') {
|
|
503
|
+
htmlNode.properties[attrKey] = content === true ? 'true' : 'false'
|
|
504
|
+
} else if (typeof content === 'string' || typeof attrValue.content === 'number') {
|
|
505
|
+
let value = StringUtils.encode(String(attrValue.content))
|
|
506
|
+
|
|
507
|
+
/*
|
|
508
|
+
elementType of image is always mapped to img.
|
|
509
|
+
For reference, check `html-mapping` file.
|
|
510
|
+
*/
|
|
511
|
+
if (elementType === 'img' && attrKey === 'src' && !isValidURL(value)) {
|
|
512
|
+
/*
|
|
513
|
+
By default we just prefix all the asset paths with just the
|
|
514
|
+
assetPrefix that is configured in the project. But for `html` generators
|
|
515
|
+
we need to prefix that with the current file location.
|
|
516
|
+
|
|
517
|
+
Because, all the other frameworks have a build setup. which serves all the
|
|
518
|
+
assets from the `public` folder. But in the case of `html` here is how it works
|
|
519
|
+
|
|
520
|
+
We load a file from `index.html` the request for the image goes from
|
|
521
|
+
'...url.../public/...image...'
|
|
522
|
+
If it's a nested url, then the request goes from
|
|
523
|
+
'...url/nested/public/...image..'
|
|
524
|
+
|
|
525
|
+
But the nested folder is available only on the root. With this
|
|
526
|
+
The url changes prefixes to
|
|
527
|
+
|
|
528
|
+
../public/playground_assets/..image.. etc depending on the dept the file is in.
|
|
529
|
+
*/
|
|
530
|
+
value = join(relative(join(...outputOptions.folderPath), './'), value)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
HASTUtils.addAttributeToNode(htmlNode, attrKey, value)
|
|
534
|
+
}
|
|
411
535
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
536
|
+
break
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
case 'dynamic': {
|
|
540
|
+
const value = getValueFromReference(
|
|
541
|
+
content.id,
|
|
542
|
+
content.referenceType === 'prop' ? propDefinitions : stateDefinitions
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value.defaultValue))
|
|
546
|
+
break
|
|
547
|
+
}
|
|
416
548
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
549
|
+
case 'raw': {
|
|
550
|
+
HASTUtils.addAttributeToNode(htmlNode, attrKey, content)
|
|
551
|
+
break
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
case 'element':
|
|
555
|
+
case 'import':
|
|
556
|
+
case 'expr':
|
|
557
|
+
break
|
|
558
|
+
|
|
559
|
+
default: {
|
|
560
|
+
throw new HTMLComponentGeneratorError(
|
|
561
|
+
`Received ${JSON.stringify(attrValue, null, 2)} \n in handleAttributes for html`
|
|
562
|
+
)
|
|
563
|
+
}
|
|
423
564
|
}
|
|
424
|
-
}
|
|
565
|
+
}
|
|
425
566
|
}
|
|
426
567
|
|
|
427
568
|
const getValueFromReference = (
|
|
428
569
|
key: string,
|
|
429
570
|
definitions: Record<string, UIDLPropDefinition>
|
|
430
|
-
):
|
|
571
|
+
): UIDLPropDefinition | undefined => {
|
|
431
572
|
const usedReferenceValue = definitions[key.includes('.') ? key.split('.')[0] : key]
|
|
432
573
|
|
|
433
574
|
if (!usedReferenceValue) {
|
|
@@ -436,9 +577,9 @@ const getValueFromReference = (
|
|
|
436
577
|
)
|
|
437
578
|
}
|
|
438
579
|
|
|
439
|
-
if (
|
|
580
|
+
if (['string', 'number', 'object', 'element'].includes(usedReferenceValue?.type) === false) {
|
|
440
581
|
throw new HTMLComponentGeneratorError(
|
|
441
|
-
`
|
|
582
|
+
`Attribute is using dynamic value, but received of type ${JSON.stringify(
|
|
442
583
|
usedReferenceValue,
|
|
443
584
|
null,
|
|
444
585
|
2
|
|
@@ -446,9 +587,12 @@ const getValueFromReference = (
|
|
|
446
587
|
)
|
|
447
588
|
}
|
|
448
589
|
|
|
449
|
-
if (
|
|
590
|
+
if (
|
|
591
|
+
usedReferenceValue.type !== 'element' &&
|
|
592
|
+
usedReferenceValue.hasOwnProperty('defaultValue') === false
|
|
593
|
+
) {
|
|
450
594
|
throw new HTMLComponentGeneratorError(
|
|
451
|
-
`
|
|
595
|
+
`Default value is missing from dynamic reference - ${JSON.stringify(
|
|
452
596
|
usedReferenceValue,
|
|
453
597
|
null,
|
|
454
598
|
2
|
|
@@ -456,5 +600,5 @@ const getValueFromReference = (
|
|
|
456
600
|
)
|
|
457
601
|
}
|
|
458
602
|
|
|
459
|
-
return
|
|
603
|
+
return usedReferenceValue
|
|
460
604
|
}
|