@teleporthq/teleport-plugin-html-base-component 0.35.0-alpha.0 → 0.35.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 +294 -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,49 @@ 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,
|
|
349
|
+
node: componentInstanceToGenerate,
|
|
304
350
|
propDefinitions: propsForInstance,
|
|
305
351
|
stateDefinitions: statesForInstance,
|
|
306
352
|
},
|
|
@@ -312,13 +358,21 @@ const generateComponentContent = async (
|
|
|
312
358
|
linkAfter: [],
|
|
313
359
|
content: compTag,
|
|
314
360
|
meta: {
|
|
315
|
-
nodesLookup
|
|
361
|
+
nodesLookup,
|
|
316
362
|
},
|
|
317
363
|
},
|
|
318
364
|
],
|
|
319
365
|
dependencies,
|
|
320
366
|
options,
|
|
321
|
-
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const result = await [cssPlugin, ...plugins].reduce(
|
|
370
|
+
async (previousPluginOperation: Promise<ComponentStructure>, plugin) => {
|
|
371
|
+
const modifiedStructure = await previousPluginOperation
|
|
372
|
+
return plugin(modifiedStructure)
|
|
373
|
+
},
|
|
374
|
+
Promise.resolve(initialStructure)
|
|
375
|
+
)
|
|
322
376
|
|
|
323
377
|
if (compHasSlots) {
|
|
324
378
|
result.chunks.forEach((chunk) => {
|
|
@@ -327,29 +381,57 @@ const generateComponentContent = async (
|
|
|
327
381
|
}
|
|
328
382
|
})
|
|
329
383
|
} else {
|
|
330
|
-
const chunk = chunks.find((item) => item.name ===
|
|
384
|
+
const chunk = chunks.find((item) => item.name === componentClone.name)
|
|
331
385
|
if (!chunk) {
|
|
332
|
-
const styleChunk = result.chunks.find(
|
|
386
|
+
const styleChunk = result.chunks.find(
|
|
387
|
+
(item: ChunkDefinition) => item.fileType === FileType.CSS
|
|
388
|
+
)
|
|
389
|
+
if (!styleChunk) {
|
|
390
|
+
return
|
|
391
|
+
}
|
|
333
392
|
chunks.push(styleChunk)
|
|
334
393
|
}
|
|
335
394
|
}
|
|
336
395
|
|
|
396
|
+
nodesLookup[key] = compTag
|
|
337
397
|
return compTag
|
|
338
398
|
}
|
|
339
399
|
|
|
340
|
-
const generateDynamicNode: NodeToHTML<UIDLDynamicReference, HastNode
|
|
400
|
+
const generateDynamicNode: NodeToHTML<UIDLDynamicReference, Promise<HastNode>> = async (
|
|
341
401
|
node,
|
|
342
|
-
|
|
402
|
+
nodesLookup,
|
|
343
403
|
propDefinitions,
|
|
344
|
-
stateDefinitions
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
404
|
+
stateDefinitions,
|
|
405
|
+
subComponentOptions,
|
|
406
|
+
structure
|
|
407
|
+
): Promise<HastNode> => {
|
|
408
|
+
const usedReferenceValue = getValueFromReference(
|
|
409
|
+
node.content.id,
|
|
410
|
+
node.content.referenceType === 'prop' ? propDefinitions : stateDefinitions
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue) {
|
|
414
|
+
const slotNode = await generateElementNode(
|
|
415
|
+
usedReferenceValue.defaultValue as UIDLElementNode,
|
|
416
|
+
nodesLookup,
|
|
417
|
+
propDefinitions,
|
|
418
|
+
stateDefinitions,
|
|
419
|
+
subComponentOptions,
|
|
420
|
+
structure
|
|
421
|
+
)
|
|
351
422
|
|
|
352
|
-
|
|
423
|
+
return slotNode as HastNode
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (usedReferenceValue.type === 'element' && usedReferenceValue.defaultValue === undefined) {
|
|
427
|
+
const spanTagWrapper = HASTBuilders.createHTMLNode('span')
|
|
428
|
+
const commentNode = HASTBuilders.createComment(`Content for slot ${node.content.id}`)
|
|
429
|
+
HASTUtils.addChildNode(spanTagWrapper, commentNode)
|
|
430
|
+
return spanTagWrapper
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const spanTag = HASTBuilders.createHTMLNode('span')
|
|
434
|
+
HASTUtils.addTextNode(spanTag, String(usedReferenceValue.defaultValue))
|
|
353
435
|
return spanTag
|
|
354
436
|
}
|
|
355
437
|
|
|
@@ -362,10 +444,12 @@ const handleStyles = (
|
|
|
362
444
|
Object.keys(styles).forEach((styleKey) => {
|
|
363
445
|
let style: string | UIDLStyleValue = styles[styleKey]
|
|
364
446
|
if (style.type === 'dynamic' && style.content?.referenceType !== 'token') {
|
|
365
|
-
|
|
366
|
-
style
|
|
367
|
-
|
|
368
|
-
|
|
447
|
+
const referencedValue = getValueFromReference(
|
|
448
|
+
style.content.id,
|
|
449
|
+
style.content.referenceType === 'prop' ? propDefinitions : stateDefinitions
|
|
450
|
+
)
|
|
451
|
+
if (referencedValue.type === 'string' || referencedValue.type === 'number') {
|
|
452
|
+
style = String(referencedValue.defaultValue)
|
|
369
453
|
}
|
|
370
454
|
node.content.style[styleKey] = typeof style === 'string' ? staticNode(style) : style
|
|
371
455
|
}
|
|
@@ -373,61 +457,119 @@ const handleStyles = (
|
|
|
373
457
|
}
|
|
374
458
|
|
|
375
459
|
const handleAttributes = (
|
|
460
|
+
elementType: UIDLElement['elementType'],
|
|
376
461
|
htmlNode: HastNode,
|
|
377
462
|
attrs: Record<string, UIDLAttributeValue>,
|
|
378
463
|
propDefinitions: Record<string, UIDLPropDefinition>,
|
|
379
464
|
stateDefinitions: Record<string, UIDLStateDefinition>,
|
|
380
|
-
routeDefinitions: UIDLRouteDefinitions
|
|
465
|
+
routeDefinitions: UIDLRouteDefinitions,
|
|
466
|
+
outputOptions: UIDLComponentOutputOptions
|
|
381
467
|
) => {
|
|
382
|
-
Object.keys(attrs)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
468
|
+
for (const attrKey of Object.keys(attrs)) {
|
|
469
|
+
const attrValue = attrs[attrKey]
|
|
470
|
+
const { type, content } = attrValue
|
|
471
|
+
|
|
472
|
+
switch (type) {
|
|
473
|
+
case 'static': {
|
|
474
|
+
if (attrKey === 'href' && typeof content === 'string' && content.startsWith('/')) {
|
|
475
|
+
let targetLink
|
|
476
|
+
|
|
477
|
+
const targetRoute = (routeDefinitions?.values || []).find(
|
|
478
|
+
(route) => route.pageOptions.navLink === content
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
if (targetRoute) {
|
|
482
|
+
targetLink = targetRoute.pageOptions.navLink
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (!targetRoute && content === '/home') {
|
|
486
|
+
targetLink = '/'
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (!targetLink && !targetRoute) {
|
|
490
|
+
targetLink = content
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const currentPageRoute = join(...(outputOptions?.folderPath || []), './')
|
|
494
|
+
const localPrefix = relative(
|
|
495
|
+
`/${currentPageRoute}`,
|
|
496
|
+
`/${targetLink === '/' ? 'index' : targetLink}`
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
HASTUtils.addAttributeToNode(htmlNode, attrKey, `${localPrefix}.html`)
|
|
500
|
+
break
|
|
501
|
+
}
|
|
402
502
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
attrValue.content
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
503
|
+
if (typeof content === 'boolean') {
|
|
504
|
+
htmlNode.properties[attrKey] = content === true ? 'true' : 'false'
|
|
505
|
+
} else if (typeof content === 'string' || typeof attrValue.content === 'number') {
|
|
506
|
+
let value = StringUtils.encode(String(attrValue.content))
|
|
507
|
+
|
|
508
|
+
/*
|
|
509
|
+
elementType of image is always mapped to img.
|
|
510
|
+
For reference, check `html-mapping` file.
|
|
511
|
+
*/
|
|
512
|
+
if (elementType === 'img' && attrKey === 'src' && !isValidURL(value)) {
|
|
513
|
+
/*
|
|
514
|
+
By default we just prefix all the asset paths with just the
|
|
515
|
+
assetPrefix that is configured in the project. But for `html` generators
|
|
516
|
+
we need to prefix that with the current file location.
|
|
517
|
+
|
|
518
|
+
Because, all the other frameworks have a build setup. which serves all the
|
|
519
|
+
assets from the `public` folder. But in the case of `html` here is how it works
|
|
520
|
+
|
|
521
|
+
We load a file from `index.html` the request for the image goes from
|
|
522
|
+
'...url.../public/...image...'
|
|
523
|
+
If it's a nested url, then the request goes from
|
|
524
|
+
'...url/nested/public/...image..'
|
|
525
|
+
|
|
526
|
+
But the nested folder is available only on the root. With this
|
|
527
|
+
The url changes prefixes to
|
|
528
|
+
|
|
529
|
+
../public/playground_assets/..image.. etc depending on the dept the file is in.
|
|
530
|
+
*/
|
|
531
|
+
value = join(relative(join(...outputOptions.folderPath), './'), value)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
HASTUtils.addAttributeToNode(htmlNode, attrKey, value)
|
|
535
|
+
}
|
|
411
536
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
537
|
+
break
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
case 'dynamic': {
|
|
541
|
+
const value = getValueFromReference(
|
|
542
|
+
content.id,
|
|
543
|
+
content.referenceType === 'prop' ? propDefinitions : stateDefinitions
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
HASTUtils.addAttributeToNode(htmlNode, attrKey, String(value.defaultValue))
|
|
547
|
+
break
|
|
548
|
+
}
|
|
416
549
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
550
|
+
case 'raw': {
|
|
551
|
+
HASTUtils.addAttributeToNode(htmlNode, attrKey, content)
|
|
552
|
+
break
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
case 'element':
|
|
556
|
+
case 'import':
|
|
557
|
+
case 'expr':
|
|
558
|
+
break
|
|
559
|
+
|
|
560
|
+
default: {
|
|
561
|
+
throw new HTMLComponentGeneratorError(
|
|
562
|
+
`Received ${JSON.stringify(attrValue, null, 2)} \n in handleAttributes for html`
|
|
563
|
+
)
|
|
564
|
+
}
|
|
423
565
|
}
|
|
424
|
-
}
|
|
566
|
+
}
|
|
425
567
|
}
|
|
426
568
|
|
|
427
569
|
const getValueFromReference = (
|
|
428
570
|
key: string,
|
|
429
571
|
definitions: Record<string, UIDLPropDefinition>
|
|
430
|
-
):
|
|
572
|
+
): UIDLPropDefinition | undefined => {
|
|
431
573
|
const usedReferenceValue = definitions[key.includes('.') ? key.split('.')[0] : key]
|
|
432
574
|
|
|
433
575
|
if (!usedReferenceValue) {
|
|
@@ -436,9 +578,9 @@ const getValueFromReference = (
|
|
|
436
578
|
)
|
|
437
579
|
}
|
|
438
580
|
|
|
439
|
-
if (
|
|
581
|
+
if (['string', 'number', 'object', 'element'].includes(usedReferenceValue?.type) === false) {
|
|
440
582
|
throw new HTMLComponentGeneratorError(
|
|
441
|
-
`
|
|
583
|
+
`Attribute is using dynamic value, but received of type ${JSON.stringify(
|
|
442
584
|
usedReferenceValue,
|
|
443
585
|
null,
|
|
444
586
|
2
|
|
@@ -446,9 +588,12 @@ const getValueFromReference = (
|
|
|
446
588
|
)
|
|
447
589
|
}
|
|
448
590
|
|
|
449
|
-
if (
|
|
591
|
+
if (
|
|
592
|
+
usedReferenceValue.type !== 'element' &&
|
|
593
|
+
usedReferenceValue.hasOwnProperty('defaultValue') === false
|
|
594
|
+
) {
|
|
450
595
|
throw new HTMLComponentGeneratorError(
|
|
451
|
-
`
|
|
596
|
+
`Default value is missing from dynamic reference - ${JSON.stringify(
|
|
452
597
|
usedReferenceValue,
|
|
453
598
|
null,
|
|
454
599
|
2
|
|
@@ -456,5 +601,5 @@ const getValueFromReference = (
|
|
|
456
601
|
)
|
|
457
602
|
}
|
|
458
603
|
|
|
459
|
-
return
|
|
604
|
+
return usedReferenceValue
|
|
460
605
|
}
|