coralite 0.2.1 → 0.3.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/.github/workflows/publish.yml +1 -2
- package/bin/coralite.js +5 -5
- package/lib/parse.js +88 -99
- package/package.json +1 -1
- package/types/index.js +6 -2
- package/.github/docs/continue-commit-convention.md +0 -11
- package/.idea/coralite.iml +0 -12
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/.vscode/settings.json +0 -5
package/bin/coralite.js
CHANGED
|
@@ -43,14 +43,14 @@ const htmlPages = await getHTML({
|
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
/** @type {Object.<string, CoraliteModule>} */
|
|
46
|
-
const
|
|
46
|
+
const coraliteModules = {}
|
|
47
47
|
|
|
48
48
|
// create components
|
|
49
49
|
for (let i = 0; i < htmlComponents.length; i++) {
|
|
50
50
|
const html = htmlComponents[i]
|
|
51
|
-
const
|
|
51
|
+
const coraliteModule = parseModule(html.content)
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
coraliteModules[coraliteModule.id] = coraliteModule
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
for (let i = 0; i < htmlPages.length; i++) {
|
|
@@ -65,8 +65,8 @@ for (let i = 0; i < htmlPages.length; i++) {
|
|
|
65
65
|
const component = await createComponent({
|
|
66
66
|
id: customElement.name,
|
|
67
67
|
values: customElement.attribs,
|
|
68
|
-
|
|
69
|
-
components,
|
|
68
|
+
element: customElement,
|
|
69
|
+
components: coraliteModules,
|
|
70
70
|
document
|
|
71
71
|
})
|
|
72
72
|
|
package/lib/parse.js
CHANGED
|
@@ -13,10 +13,10 @@ import { invalidCustomTags, validTags } from './tags.js'
|
|
|
13
13
|
* CoraliteModule,
|
|
14
14
|
* CoraliteTextNode,
|
|
15
15
|
* CoraliteElement,
|
|
16
|
-
* CoraliteSlotElement,
|
|
17
16
|
* CoraliteModuleSlotElement,
|
|
18
17
|
* CoraliteDocumentTokens,
|
|
19
18
|
* CoraliteDocumentRoot,
|
|
19
|
+
* CoraliteAnyNode,
|
|
20
20
|
* } from '#types'
|
|
21
21
|
*/
|
|
22
22
|
|
|
@@ -32,7 +32,7 @@ const customElementTagTokenRegExp = /^[^-].*[-._a-z0-9\u00B7\u00C0-\u00D6\u00D8-
|
|
|
32
32
|
* @returns {CoraliteDocument} An object representing the parsed document structure
|
|
33
33
|
*
|
|
34
34
|
* @example
|
|
35
|
-
* ```
|
|
35
|
+
* ```
|
|
36
36
|
* // Example usage:
|
|
37
37
|
* const html = {
|
|
38
38
|
* name: 'index.html',
|
|
@@ -57,10 +57,8 @@ export function parseHTMLDocument (html, path) {
|
|
|
57
57
|
children: []
|
|
58
58
|
}
|
|
59
59
|
// stack to keep track of current element hierarchy
|
|
60
|
-
const stack = []
|
|
60
|
+
const stack = [root]
|
|
61
61
|
const customElements = []
|
|
62
|
-
/** @type {Object.<string, CoraliteSlotElement[]>} */
|
|
63
|
-
const customElementSlots = {}
|
|
64
62
|
|
|
65
63
|
const parser = new Parser({
|
|
66
64
|
onprocessinginstruction (name, data) {
|
|
@@ -71,25 +69,7 @@ export function parseHTMLDocument (html, path) {
|
|
|
71
69
|
})
|
|
72
70
|
},
|
|
73
71
|
onopentag (originalName, attributes) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (attributes.slot) {
|
|
77
|
-
const customElement = customElements[customElements.length - 1]
|
|
78
|
-
const slot = {
|
|
79
|
-
name: attributes.slot,
|
|
80
|
-
customElement,
|
|
81
|
-
element
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (!customElementSlots[customElement.name]) {
|
|
85
|
-
customElementSlots[customElement.name] = [slot]
|
|
86
|
-
} else {
|
|
87
|
-
customElementSlots[customElement.name].unshift(slot)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// remove slot attribute
|
|
91
|
-
delete attributes.slot
|
|
92
|
-
}
|
|
72
|
+
createElement(originalName, attributes, customElements, stack)
|
|
93
73
|
},
|
|
94
74
|
ontext (text) {
|
|
95
75
|
if (text.trim()) {
|
|
@@ -100,10 +80,10 @@ export function parseHTMLDocument (html, path) {
|
|
|
100
80
|
// remove current element from stack as we're done with its children
|
|
101
81
|
stack.pop()
|
|
102
82
|
},
|
|
103
|
-
oncomment (
|
|
83
|
+
oncomment (data) {
|
|
104
84
|
stack[stack.length - 1].children.push({
|
|
105
85
|
type: 'comment',
|
|
106
|
-
data
|
|
86
|
+
data,
|
|
107
87
|
parent: stack[stack.length - 1]
|
|
108
88
|
})
|
|
109
89
|
}
|
|
@@ -112,12 +92,32 @@ export function parseHTMLDocument (html, path) {
|
|
|
112
92
|
parser.write(html.content)
|
|
113
93
|
parser.end()
|
|
114
94
|
|
|
95
|
+
for (let i = 0; i < customElements.length; i++) {
|
|
96
|
+
const customElement = customElements[i]
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < customElement.children.length; i++) {
|
|
99
|
+
const childNode = customElement.children[i]
|
|
100
|
+
let slotName = 'default'
|
|
101
|
+
|
|
102
|
+
if (childNode.attribs && childNode.attribs.slot) {
|
|
103
|
+
slotName = childNode.attribs.slot
|
|
104
|
+
|
|
105
|
+
// clean up slot attribute
|
|
106
|
+
delete childNode.attribs.slot
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
customElement.slots.push({
|
|
110
|
+
name: slotName,
|
|
111
|
+
node: childNode
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
115
116
|
return {
|
|
116
117
|
name: html.name,
|
|
117
118
|
parentPath: html.parentPath,
|
|
118
119
|
root,
|
|
119
120
|
customElements,
|
|
120
|
-
customElementSlots,
|
|
121
121
|
path
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -145,6 +145,7 @@ export function parseHTMLMeta (string) {
|
|
|
145
145
|
const stack = []
|
|
146
146
|
/** @type {Object.<string, CoraliteToken[]>} */
|
|
147
147
|
const meta = {}
|
|
148
|
+
let hasHead = false
|
|
148
149
|
|
|
149
150
|
const parser = new Parser({
|
|
150
151
|
onopentag (name, attributes) {
|
|
@@ -162,6 +163,7 @@ export function parseHTMLMeta (string) {
|
|
|
162
163
|
},
|
|
163
164
|
onclosetag (name) {
|
|
164
165
|
if (name === 'head') {
|
|
166
|
+
hasHead = true
|
|
165
167
|
// end on closing head tag
|
|
166
168
|
return parser.end()
|
|
167
169
|
}
|
|
@@ -173,6 +175,10 @@ export function parseHTMLMeta (string) {
|
|
|
173
175
|
parser.write(string)
|
|
174
176
|
parser.end()
|
|
175
177
|
|
|
178
|
+
if (!hasHead) {
|
|
179
|
+
throw new Error('Document requires a head element')
|
|
180
|
+
}
|
|
181
|
+
|
|
176
182
|
return meta
|
|
177
183
|
}
|
|
178
184
|
|
|
@@ -184,7 +190,7 @@ export function parseHTMLMeta (string) {
|
|
|
184
190
|
* @returns {CoraliteModule} - Parsed module information, including template, script, tokens, and slot configurations
|
|
185
191
|
*
|
|
186
192
|
* @example
|
|
187
|
-
* ```
|
|
193
|
+
* ```
|
|
188
194
|
* // Example usage:
|
|
189
195
|
* const html = `<template id="home">
|
|
190
196
|
* <slot name="default">Hello</slot>
|
|
@@ -216,7 +222,7 @@ export function parseModule (string) {
|
|
|
216
222
|
children: []
|
|
217
223
|
}
|
|
218
224
|
// stack to keep track of current element hierarchy
|
|
219
|
-
const stack = []
|
|
225
|
+
const stack = [root]
|
|
220
226
|
const customElements = []
|
|
221
227
|
/** @type {Object.<string, Object.<string,CoraliteModuleSlotElement>>} */
|
|
222
228
|
const slotElements = {}
|
|
@@ -230,7 +236,7 @@ export function parseModule (string) {
|
|
|
230
236
|
|
|
231
237
|
const parser = new Parser({
|
|
232
238
|
onopentag (originalName, attributes) {
|
|
233
|
-
const element = createElement(originalName, attributes, customElements, stack
|
|
239
|
+
const element = createElement(originalName, attributes, customElements, stack)
|
|
234
240
|
const attributeNames = Object.keys(attributes)
|
|
235
241
|
|
|
236
242
|
// collect tokens
|
|
@@ -315,10 +321,10 @@ export function parseModule (string) {
|
|
|
315
321
|
console.log(csb)
|
|
316
322
|
},
|
|
317
323
|
|
|
318
|
-
oncomment (
|
|
324
|
+
oncomment (data) {
|
|
319
325
|
stack[stack.length - 1].children.push({
|
|
320
326
|
type: 'comment',
|
|
321
|
-
data
|
|
327
|
+
data,
|
|
322
328
|
parent: stack[stack.length - 1]
|
|
323
329
|
})
|
|
324
330
|
}
|
|
@@ -374,11 +380,12 @@ export function parseModule (string) {
|
|
|
374
380
|
* @param {Object.<string, (string | (CoraliteTextNode | CoraliteElement)[])>} options.values - Token values available for replacement
|
|
375
381
|
* @param {Object.<string, CoraliteModuleSlotElement[]>} [options.customElementSlots = {}] - Custom slots and their configurations for the component
|
|
376
382
|
* @param {Object.<string, CoraliteModule>} options.components - Mapping of component IDs to their module definitions
|
|
383
|
+
* @param {CoraliteElement} options.element - Mapping of component IDs to their module definitions
|
|
377
384
|
* @param {CoraliteDocument} options.document - Current document being processed
|
|
378
385
|
* @returns {Promise<CoraliteElement>}
|
|
379
386
|
*
|
|
380
387
|
* @example
|
|
381
|
-
* ```
|
|
388
|
+
* ```
|
|
382
389
|
* // Example usage:
|
|
383
390
|
* const component = await createComponent({
|
|
384
391
|
* id: 'my-component',
|
|
@@ -405,7 +412,7 @@ export function parseModule (string) {
|
|
|
405
412
|
export async function createComponent ({
|
|
406
413
|
id,
|
|
407
414
|
values,
|
|
408
|
-
|
|
415
|
+
element,
|
|
409
416
|
components,
|
|
410
417
|
document
|
|
411
418
|
}) {
|
|
@@ -429,8 +436,9 @@ export async function createComponent ({
|
|
|
429
436
|
if (value == null) {
|
|
430
437
|
if (component.script) {
|
|
431
438
|
computedTokens.push({
|
|
432
|
-
type: '
|
|
439
|
+
type: 'attribute',
|
|
433
440
|
node: item.element,
|
|
441
|
+
attribute: item.name,
|
|
434
442
|
name: token.name,
|
|
435
443
|
content: token.content
|
|
436
444
|
})
|
|
@@ -487,8 +495,7 @@ export async function createComponent ({
|
|
|
487
495
|
const value = values[computedToken.name]
|
|
488
496
|
|
|
489
497
|
if (computedToken.type === 'attribute') {
|
|
490
|
-
|
|
491
|
-
node.attribs[computedToken.name] = node.attribs[computedToken.name].replace(computedToken.content, value)
|
|
498
|
+
node.attribs[computedToken.attribute] = node.attribs[computedToken.attribute].replace(computedToken.content, value)
|
|
492
499
|
} else {
|
|
493
500
|
if (Array.isArray(value)) {
|
|
494
501
|
// inject nodes
|
|
@@ -508,7 +515,7 @@ export async function createComponent ({
|
|
|
508
515
|
parent: node.parent
|
|
509
516
|
}
|
|
510
517
|
)
|
|
511
|
-
} else
|
|
518
|
+
} else {
|
|
512
519
|
// replace token string
|
|
513
520
|
node.data = node.data.replace(computedToken.content, value)
|
|
514
521
|
}
|
|
@@ -525,7 +532,7 @@ export async function createComponent ({
|
|
|
525
532
|
const component = await createComponent({
|
|
526
533
|
id: customElement.name,
|
|
527
534
|
values: Object.assign(values, customElement.attribs),
|
|
528
|
-
|
|
535
|
+
element: customElement,
|
|
529
536
|
components,
|
|
530
537
|
document
|
|
531
538
|
})
|
|
@@ -541,57 +548,46 @@ export async function createComponent ({
|
|
|
541
548
|
children.splice(childIndex, 1, ...component.children)
|
|
542
549
|
}
|
|
543
550
|
|
|
544
|
-
const slots =
|
|
551
|
+
const slots = component.slotElements[id]
|
|
545
552
|
|
|
553
|
+
// replace slot content
|
|
546
554
|
if (slots) {
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
const slotIndexes = {}
|
|
550
|
-
|
|
551
|
-
for (let i = 0; i < slots.length; i++) {
|
|
552
|
-
const slot = slots[i]
|
|
553
|
-
const name = slot.name
|
|
554
|
-
|
|
555
|
-
if (componentSlots[name]) {
|
|
556
|
-
const componentSlot = componentSlots[name]
|
|
557
|
-
const parentChildren = componentSlot.element.parent.children
|
|
558
|
-
let slotIndex = slotIndexes[name]
|
|
559
|
-
let deleteCount = 0
|
|
560
|
-
|
|
561
|
-
if (slotIndex == null) {
|
|
562
|
-
slotIndex = parentChildren.indexOf(componentSlot.element, componentSlot.element.parentChildIndex)
|
|
563
|
-
deleteCount = 1
|
|
564
|
-
slotIndexes[name] = slotIndex
|
|
565
|
-
}
|
|
555
|
+
const slotChildren = {}
|
|
556
|
+
const slotNames = Object.keys(slots)
|
|
566
557
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
558
|
+
// sort slot content by name
|
|
559
|
+
for (let i = 0; i < slotNames.length; i++) {
|
|
560
|
+
const slotName = slotNames[i]
|
|
561
|
+
slotChildren[slotName] = []
|
|
570
562
|
}
|
|
571
563
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const children = element.children || [{
|
|
579
|
-
type: 'text',
|
|
580
|
-
data: ''
|
|
581
|
-
}]
|
|
582
|
-
const slotIndex = parent.children.indexOf(componentSlot.element, componentSlot.element.parentChildIndex)
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
for (let i = 0; i < children.length; i++) {
|
|
586
|
-
const childNode = children[i]
|
|
587
|
-
childNode.parent = parent
|
|
588
|
-
}
|
|
564
|
+
// insert slot content by name
|
|
565
|
+
if (element) {
|
|
566
|
+
for (let i = 0; i < element.slots.length; i++) {
|
|
567
|
+
const elementSlotContent = element.slots[i]
|
|
568
|
+
const slotName = elementSlotContent.name
|
|
569
|
+
const slot = slots[slotName]
|
|
589
570
|
|
|
590
|
-
|
|
591
|
-
|
|
571
|
+
if (slot) {
|
|
572
|
+
slotChildren[slotName].push(elementSlotContent.node)
|
|
592
573
|
}
|
|
593
574
|
}
|
|
594
575
|
}
|
|
576
|
+
|
|
577
|
+
for (let i = 0; i < slotNames.length; i++) {
|
|
578
|
+
const slotName = slotNames[i]
|
|
579
|
+
let slotNodes = slotChildren[slotName]
|
|
580
|
+
const slot = slots[slotName]
|
|
581
|
+
const slotIndex = slot.element.parent.children.indexOf(slot.element)
|
|
582
|
+
|
|
583
|
+
if (!slotNodes.length) {
|
|
584
|
+
// set default content
|
|
585
|
+
slotNodes = slot.element.children || []
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// replace slot element with content
|
|
589
|
+
slot.element.parent.children.splice(slotIndex, 1, ...slotNodes)
|
|
590
|
+
}
|
|
595
591
|
}
|
|
596
592
|
|
|
597
593
|
return template
|
|
@@ -607,7 +603,7 @@ export async function createComponent ({
|
|
|
607
603
|
* @returns {Promise<Object.<string,(string|(CoraliteElement|CoraliteTextNode)[])>>}
|
|
608
604
|
*
|
|
609
605
|
* @example
|
|
610
|
-
* ```
|
|
606
|
+
* ```
|
|
611
607
|
* // Example usage:
|
|
612
608
|
* const parsedResult = await parseScript({
|
|
613
609
|
* id: 'my-component',
|
|
@@ -750,18 +746,20 @@ function addMetadata (meta, name, content) {
|
|
|
750
746
|
* @param {string} name
|
|
751
747
|
* @param {Object.<string, string>} attributes
|
|
752
748
|
* @param {CoraliteElement[]} customElements
|
|
753
|
-
* @param {
|
|
754
|
-
* @param {CoraliteDocumentRoot} root
|
|
749
|
+
* @param {CoraliteAnyNode[]} stack
|
|
755
750
|
*/
|
|
756
|
-
function createElement (name, attributes, customElements, stack
|
|
751
|
+
function createElement (name, attributes, customElements, stack) {
|
|
757
752
|
const sanitisedName = name.toLowerCase()
|
|
758
|
-
const
|
|
753
|
+
const parent = stack[stack.length - 1]
|
|
754
|
+
|
|
759
755
|
/** @type {CoraliteElement} */
|
|
760
756
|
const element = {
|
|
761
757
|
type: 'tag',
|
|
762
758
|
name: sanitisedName,
|
|
763
759
|
attribs: attributes,
|
|
764
|
-
children: []
|
|
760
|
+
children: [],
|
|
761
|
+
parent,
|
|
762
|
+
parentChildIndex: parent.children.length
|
|
765
763
|
}
|
|
766
764
|
|
|
767
765
|
if (!validTags[sanitisedName]) {
|
|
@@ -772,23 +770,14 @@ function createElement (name, attributes, customElements, stack, root) {
|
|
|
772
770
|
if (customElementTagRegExp.test(sanitisedName)) {
|
|
773
771
|
// store custom elements
|
|
774
772
|
customElements.push(element)
|
|
773
|
+
element.slots = []
|
|
775
774
|
} else {
|
|
776
775
|
throw new Error('Invalid custom element tag name: "' + sanitisedName + '" https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name')
|
|
777
776
|
}
|
|
778
777
|
}
|
|
779
778
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
element.parent = root
|
|
783
|
-
} else {
|
|
784
|
-
const parent = stack[parentIndex]
|
|
785
|
-
|
|
786
|
-
element.parent = parent
|
|
787
|
-
element.parentChildIndex = parent.children.length
|
|
788
|
-
|
|
789
|
-
// add element to its parent's children
|
|
790
|
-
parent.children.push(element)
|
|
791
|
-
}
|
|
779
|
+
// add element to its parent's children
|
|
780
|
+
parent.children.push(element)
|
|
792
781
|
|
|
793
782
|
// push element to stack as it may have children
|
|
794
783
|
stack.push(element)
|
package/package.json
CHANGED
package/types/index.js
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
* @property {'tag'} type - Element type
|
|
67
67
|
* @property {string} name - Tag name
|
|
68
68
|
* @property {Object.<string, string>} attribs - Element attributes
|
|
69
|
-
* @property {
|
|
69
|
+
* @property {CoraliteAnyNode[]} children - Child nodes of the element
|
|
70
70
|
* @property {CoraliteElement | CoraliteDocumentRoot} parent - Parent element
|
|
71
71
|
* @property {number} [parentChildIndex] - Position in parent's child list
|
|
72
72
|
*/
|
|
@@ -79,6 +79,10 @@
|
|
|
79
79
|
* @property {CoraliteElement} parent - Parent element of the text node
|
|
80
80
|
*/
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* @typedef {CoraliteDirective | CoraliteElement | CoraliteTextNode} CoraliteAnyNode
|
|
84
|
+
*/
|
|
85
|
+
|
|
82
86
|
/**
|
|
83
87
|
* @typedef {Object} CoraliteSlotElement
|
|
84
88
|
* @property {string} name - Slot's unique identifier
|
|
@@ -96,7 +100,7 @@
|
|
|
96
100
|
/**
|
|
97
101
|
* @typedef {Object} CoraliteDocumentRoot
|
|
98
102
|
* @property {'root'} type - Node type
|
|
99
|
-
* @property {
|
|
103
|
+
* @property {CoraliteAnyNode[]} children - Document list
|
|
100
104
|
*/
|
|
101
105
|
|
|
102
106
|
/**
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
Based on the provided Git diff above, create a git commit message by strictly following these rules:
|
|
2
|
-
|
|
3
|
-
1. Use one of the predefined prefixes (feat, fix, perf, build, chore, ci, docs, style, refactor, test, types, wip) or a custom prefix for non-changelog related tasks.
|
|
4
|
-
2. The commit message must have a header, which includes a type and subject, separated by a colon.
|
|
5
|
-
3. Follow the imperative present tense for both the subject and body of the commit message:
|
|
6
|
-
- Use concise and descriptive subjects.
|
|
7
|
-
- Avoid including implementation details in the body unless necessary for understanding context.
|
|
8
|
-
4. Include an optional footer to provide information about breaking changes using the format "BREAKING CHANGE:" followed by additional details.
|
|
9
|
-
5. Limit line length to 72 characters and maintain consistent use of whitespace for better readability and uniformity.
|
|
10
|
-
6. If changes are about JSDoc, the prefix should be 'types'.
|
|
11
|
-
7. Use any suggestion below this to help with the context.
|
package/.idea/coralite.iml
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="WEB_MODULE" version="4">
|
|
3
|
-
<component name="NewModuleRootManager">
|
|
4
|
-
<content url="file://$MODULE_DIR$">
|
|
5
|
-
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
6
|
-
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
7
|
-
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
-
</content>
|
|
9
|
-
<orderEntry type="inheritedJdk" />
|
|
10
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
11
|
-
</component>
|
|
12
|
-
</module>
|
package/.idea/modules.xml
DELETED
package/.idea/vcs.xml
DELETED