coralite 0.6.4 → 0.6.6

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/change-logs.md CHANGED
@@ -1,3 +1,39 @@
1
+ # 🎁 Release notes (`v0.6.6`)
2
+
3
+ ## Changes
4
+ - 790c585 (HEAD -> main, tag: v0.6.6, origin/main) fix: computedSlots token param includes attrib values - (*Thomas David*)
5
+ - 505d4f0 feat: export parseHTML util - (*Thomas David*)
6
+ - d0fc52d docs: update CoraliteModuleValues type - (*Thomas David*)
7
+ - 6ad27ce feat: tokens parse HTML - (*Thomas David*)
8
+ - 9497c25 feat: export current document to template context - (*Thomas David*)
9
+ - df9a1a5 refactor: create reusable parseHTML function - (*Thomas David*)
10
+ - ebb388c feat: append rendered token to custom element slots - (*Thomas David*)
11
+ - 1f6ffd0 feat: add filtering to aggregate function - (*Thomas David*)
12
+
13
+ ## Metadata
14
+ ```
15
+ This version -------- v0.6.6
16
+ Previous version ---- v0.6.5
17
+ Total commits ------- 8
18
+ ```
19
+ # 🎁 Release notes (`v0.6.5`)
20
+
21
+ ## Changes
22
+ - 4610baa (HEAD -> main, tag: v0.6.5, origin/main) chore: version bump - (*Thomas David*)
23
+ - be2511d ci: remove windows due to unavailability - (*Thomas David*)
24
+ - 821f212 test: cover aggregate filter option - (*Thomas David*)
25
+ - 48eaa76 docs: add CoraliteAggregate to aggregate param - (*Thomas David*)
26
+ - 39ab5ba docs: add CoraliteAggregate typedef - (*Thomas David*)
27
+ - 148dee9 feat: add filter to aggregate function - (*Thomas David*)
28
+ - 15cf0fd docs: add CoraliteAggregate typedef - (*Thomas David*)
29
+ - dbb5b8f feat: remove unused parseHTMLMeta ignoreByAttribute param - (*Thomas David*)
30
+
31
+ ## Metadata
32
+ ```
33
+ This version -------- v0.6.5
34
+ Previous version ---- v0.6.4
35
+ Total commits ------- 8
36
+ ```
1
37
  # 🎁 Release notes (`v0.6.4`)
2
38
 
3
39
  ## Changes
package/lib/coralite.js CHANGED
@@ -5,10 +5,10 @@ import render from 'dom-serializer'
5
5
  * @import {
6
6
  * CoraliteElement,
7
7
  * CoraliteTextNode,
8
- * CoraliteAggregateTemplate,
9
8
  * CoraliteAnyNode,
10
9
  * CoraliteModule,
11
- * CoraliteResult
10
+ * CoraliteResult,
11
+ * CoraliteAggregate
12
12
  * } from '#types'
13
13
  */
14
14
 
@@ -50,9 +50,7 @@ export async function defineComponent (options) {
50
50
  /**
51
51
  * Aggregates HTML content from specified paths into a single collection of components.
52
52
  *
53
- * @param {Object} options - Configuration object for the aggregation process
54
- * @param {CoraliteAggregateTemplate | string} options.template - Templates used to display the result
55
- * @param {string} options.path - The path to aggregate, relative to pages directory
53
+ * @param {CoraliteAggregate} options - Configuration object for the aggregation process
56
54
  * @returns {Promise<(CoraliteElement | CoraliteTextNode)[]>}
57
55
  */
58
56
  export async function aggregate (options) {
@@ -4,17 +4,13 @@ import { createComponent, parseHTMLMeta } from './parse.js'
4
4
  import { existsSync } from 'node:fs'
5
5
 
6
6
  /**
7
- * @import { CoraliteTokenOptions, CoraliteModule, CoraliteDocument, CoraliteModuleValues, CoraliteAggregateTemplate } from '#types'
7
+ * @import { CoraliteTokenOptions, CoraliteModule, CoraliteDocument, CoraliteModuleValues, CoraliteAggregateTemplate, CoraliteAggregate } from '#types'
8
8
  */
9
9
 
10
10
  /**
11
11
  * Aggregates HTML content from specified paths into a single collection of components.
12
12
  *
13
- * @param {Object} options - Configuration object for the aggregation process
14
- * @param {string} options.path - The path to aggregate, relative to pages directory
15
- * @param {CoraliteAggregateTemplate | string} options.template - Templates used to display the result
16
- * @param {boolean} [options.recursive] - Whether to recursively search subdirectories
17
- * @param {CoraliteTokenOptions} [options.tokens] - Token configuration options
13
+ * @param {CoraliteAggregate} options - Configuration object for the aggregation process
18
14
  * @param {CoraliteModuleValues} values - Default token values
19
15
  * @param {Object.<string, CoraliteModule>} components - Available components library
20
16
  * @param {CoraliteDocument} document - Current document being processed
@@ -66,26 +62,48 @@ export async function aggregate (options, values, components, document) {
66
62
 
67
63
  for (let i = 0; i < pages.length; i++) {
68
64
  const page = pages[i]
69
- const meta = parseHTMLMeta(page.content, document.ignoreByAttribute)
65
+ const meta = parseHTMLMeta(page.content)
70
66
  const pageValues = Object.assign({}, values)
67
+ let isFilter = !!options.filter
68
+ let ignorePage = false
71
69
 
72
70
  for (const key in meta) {
73
71
  if (Object.prototype.hasOwnProperty.call(meta, key)) {
74
72
  const data = meta[key]
73
+ const content = []
74
+ const firstItem = data[0]
75
75
 
76
- for (let i = 0; i < data.length; i++) {
77
- const item = data[i]
78
- let suffix = ''
76
+ if (data.length > 1) {
77
+ for (let i = 0; i < data.length; i++) {
78
+ const item = data[i]
79
+ let suffix = ''
79
80
 
80
- if (i > 0) {
81
+ if (isFilter && !ignorePage) {
82
+ ignorePage = options.filter(item)
83
+ }
81
84
  suffix = '_' + i
85
+
86
+ if (i === 0) {
87
+ pageValues[item.name] = item.content
88
+ }
89
+
90
+ pageValues[item.name + suffix] = item.content
91
+
92
+ content.push(item.content)
82
93
  }
83
94
 
84
- pageValues[item.name + suffix] = item.content
95
+ pageValues[firstItem.name + '_list'] = content
96
+ } else {
97
+ pageValues[firstItem.name] = firstItem.content
85
98
  }
86
99
  }
87
100
  }
88
101
 
102
+ // break if page is filtered
103
+ if (isFilter && !ignorePage) {
104
+ break
105
+ }
106
+
89
107
  const component = await createComponent({
90
108
  id: componentId,
91
109
  values: pageValues,
package/lib/parse.js CHANGED
@@ -3,7 +3,6 @@ import { aggregate } from './html-module.js'
3
3
  import vm from 'node:vm'
4
4
  import { invalidCustomTags, validTags } from './tags.js'
5
5
 
6
-
7
6
  /**
8
7
  * @import {Module} from 'node:vm'
9
8
  * @import {
@@ -19,8 +18,10 @@ import { invalidCustomTags, validTags } from './tags.js'
19
18
  * CoraliteDocumentRoot,
20
19
  * CoraliteContentNode,
21
20
  * CoraliteModuleValues,
22
- * CoraliteAggregateTemplate,
23
- * IgnoreByAttribute
21
+ * IgnoreByAttribute,
22
+ * CoraliteAggregate,
23
+ * CoraliteAnyNode,
24
+ * CoraliteDirective
24
25
  * } from '#types'
25
26
  */
26
27
 
@@ -28,33 +29,15 @@ const customElementTagRegExp = /^[^-].*[-._a-z0-9\u00B7\u00C0-\u00D6\u00D8-\u00F
28
29
 
29
30
  const customElementTagTokenRegExp = /^[^-].*[-._a-z0-9\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u{10000}-\u{EFFFF}\{\}]*$/ui
30
31
 
32
+
31
33
  /**
32
- * Parses HTML content into a Coralite document structure.
34
+ * Parse HTML content and return a CoraliteDocument object representing the parsed document structure
33
35
  *
34
- * @param {HTMLData} html - The HTML data containing the content to parse
35
- * @param {CoralitePath} path - The path object containing the file path information
36
+ * @param {string} string - HTML content to parse as string input type textual data
36
37
  * @param {IgnoreByAttribute} [ignoreByAttribute] - Ignore element with attribute name value pair
37
- * @returns {CoraliteDocument} An object representing the parsed document structure
38
- *
39
- * @example
40
- * ```
41
- * // Example usage:
42
- * const html = {
43
- * name: 'index.html',
44
- * parentPath: 'path/to/parent',
45
- * content: '<div>Content</div>'
46
- * };
47
- *
48
- * const path = {
49
- * pages: 'path/to/pages',
50
- * templates: 'path/to/templates'
51
- * };
52
- *
53
- * const document = parseHTMLDocument(html, path);
54
- * // document.root will contain parsed elements and text nodes
55
- * ```
38
+ * @example parseHTML('<h1>Hello world!</h1>')
56
39
  */
57
- export function parseHTMLDocument (html, path, ignoreByAttribute) {
40
+ export function parseHTML (string, ignoreByAttribute) {
58
41
  // root element reference
59
42
  /** @type {CoraliteDocumentRoot} */
60
43
  const root = {
@@ -115,7 +98,8 @@ export function parseHTMLDocument (html, path, ignoreByAttribute) {
115
98
  }
116
99
  })
117
100
 
118
- parser.write(html.content)
101
+
102
+ parser.write(string)
119
103
  parser.end()
120
104
 
121
105
  for (let i = 0; i < customElements.length; i++) {
@@ -139,11 +123,46 @@ export function parseHTMLDocument (html, path, ignoreByAttribute) {
139
123
  }
140
124
  }
141
125
 
126
+ return {
127
+ root,
128
+ customElements
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Parses HTML content into a Coralite document structure.
134
+ *
135
+ * @param {HTMLData} html - The HTML data containing the content to parse
136
+ * @param {CoralitePath} path - The path object containing the file path information
137
+ * @param {IgnoreByAttribute} [ignoreByAttribute] - Ignore element with attribute name value pair
138
+ * @returns {CoraliteDocument} An object representing the parsed document structure
139
+ *
140
+ * @example
141
+ * ```
142
+ * // Example usage:
143
+ * const html = {
144
+ * name: 'index.html',
145
+ * parentPath: 'path/to/parent',
146
+ * content: '<div>Content</div>'
147
+ * };
148
+ *
149
+ * const path = {
150
+ * pages: 'path/to/pages',
151
+ * templates: 'path/to/templates'
152
+ * };
153
+ *
154
+ * const document = parseHTMLDocument(html, path);
155
+ * // document.root will contain parsed elements and text nodes
156
+ * ```
157
+ */
158
+ export function parseHTMLDocument (html, path, ignoreByAttribute) {
159
+ const result = parseHTML(html.content, ignoreByAttribute)
160
+
142
161
  return {
143
162
  name: html.name,
144
163
  parentPath: html.parentPath,
145
- root,
146
- customElements,
164
+ root: result.root,
165
+ customElements: result.customElements,
147
166
  path,
148
167
  ignoreByAttribute
149
168
  }
@@ -153,7 +172,6 @@ export function parseHTMLDocument (html, path, ignoreByAttribute) {
153
172
  * Parses HTML string containing meta tags and extracts associated metadata.
154
173
  *
155
174
  * @param {string} string - HTML content containing meta tags
156
- * @param {IgnoreByAttribute} ignoreByAttribute - IgnoreByAttribute option (optional) - If provided and true then the HTML tags will be ignored by this attribute name(s)
157
175
  * @returns {Object.<string, CoraliteToken[]>}
158
176
  *
159
177
  * @example
@@ -168,7 +186,7 @@ export function parseHTMLDocument (html, path, ignoreByAttribute) {
168
186
  * //}
169
187
  * ```
170
188
  */
171
- export function parseHTMLMeta (string, ignoreByAttribute) {
189
+ export function parseHTMLMeta (string) {
172
190
  // stack to keep track of current element hierarchy
173
191
  const stack = []
174
192
  /** @type {Object.<string, CoraliteToken[]>} */
@@ -179,15 +197,6 @@ export function parseHTMLMeta (string, ignoreByAttribute) {
179
197
  onopentag (name, attributes) {
180
198
  if (name === 'meta') {
181
199
  if (attributes.content) {
182
- if (ignoreByAttribute) {
183
- // ignore meta tags by attribute name
184
- const ignore = findAttributesToIgnore(ignoreByAttribute, attributes)
185
-
186
- if (ignore) {
187
- return
188
- }
189
- }
190
-
191
200
  if (attributes.property) {
192
201
  addMetadata(meta, attributes.property, attributes.content)
193
202
  }
@@ -573,20 +582,32 @@ export async function createComponent ({
573
582
  // inject nodes
574
583
  const textSplit = node.data.split(computedToken.content)
575
584
  const childIndex = node.parent.children.indexOf(node)
585
+ const children = []
586
+
587
+ // append computed tokens in between token split
588
+ for (let i = 0; i < value.length; i++) {
589
+ const child = value[i]
590
+
591
+ if (typeof child !== 'string') {
592
+ // update child parent
593
+ child.parent = node.parent
594
+ children.push(child)
595
+ }
596
+ }
576
597
 
598
+ // replace computed token
577
599
  node.parent.children.splice(childIndex, 1,
578
600
  {
579
601
  type: 'text',
580
602
  data: textSplit[0],
581
603
  parent: node.parent
582
604
  },
583
- ...value,
605
+ ...children,
584
606
  {
585
607
  type: 'text',
586
608
  data: textSplit[1],
587
609
  parent: node.parent
588
- }
589
- )
610
+ })
590
611
  } else {
591
612
  // replace token string
592
613
  node.data = node.data.replace(computedToken.content, value)
@@ -601,6 +622,27 @@ export async function createComponent ({
601
622
 
602
623
  for (let i = 0; i < customElements.length; i++) {
603
624
  const customElement = customElements[i]
625
+
626
+ // update slot elements
627
+ if (customElement.children
628
+ && customElement.children.length
629
+ && !customElement.slots.length
630
+ ) {
631
+ for (let i = 0; i < customElement.children.length; i++) {
632
+ const node = customElement.children[i]
633
+ const slotElement = {
634
+ name: 'default',
635
+ node
636
+ }
637
+
638
+ if (node.attribs && node.attribs.slot) {
639
+ slotElement.name = node.attribs.slot
640
+ }
641
+
642
+ customElement.slots.push(slotElement)
643
+ }
644
+ }
645
+
604
646
  const component = await createComponent({
605
647
  id: customElement.name,
606
648
  values: Object.assign(values, customElement.attribs),
@@ -676,7 +718,7 @@ export async function createComponent ({
676
718
  * @param {CoraliteElement} data.element - Element
677
719
  * @param {Object.<string, CoraliteModule>} data.components - Mapping of other components that might be referenced
678
720
  * @param {CoraliteDocument} data.document - The current document being processed
679
- * @returns {Promise<Object.<string,(string|(CoraliteElement|CoraliteTextNode)[])>>}
721
+ * @returns {Promise<Object.<string,string|(CoraliteDirective|CoraliteAnyNode)[]>>}
680
722
  */
681
723
  export async function parseScript ({
682
724
  component,
@@ -688,16 +730,19 @@ export async function parseScript ({
688
730
  const contextifiedObject = vm.createContext({
689
731
  crypto: globalThis.crypto,
690
732
  coralite: {
733
+ document,
691
734
  tokens: values,
692
735
  /**
693
736
  * @param {Object} options
694
737
  * @param {Object.<string, (string | function)>} options.tokens
695
738
  * @param {Object.<string, Function>} options.slots
696
- * @returns {Promise<Object.<string, string>>}
739
+ * @returns {Promise<CoraliteModuleValues>}
697
740
  */
698
741
  async defineComponent (options) {
699
- /** @type {Object.<string, string>} */
700
- const tokens = {}
742
+ /** @type {CoraliteModuleValues} */
743
+ const tokens = { ...values }
744
+ const computedTokens = []
745
+ const computedTokenKey = []
701
746
 
702
747
  if (options.tokens) {
703
748
  for (const key in options.tokens) {
@@ -705,8 +750,48 @@ export async function parseScript ({
705
750
  const token = options.tokens[key]
706
751
 
707
752
  if (typeof token === 'function') {
708
- tokens[key] = await token(values)
753
+ const result = token(values) || ''
754
+
755
+ if (result instanceof Promise) {
756
+ computedTokens.push(result)
757
+ computedTokenKey.push(key)
758
+ } else {
759
+ tokens[key] = result
760
+ }
761
+ }
762
+
763
+ if (typeof tokens[key] === 'string') {
764
+ const result = parseHTML(tokens[key], document.ignoreByAttribute)
765
+ const children = result.root.children
766
+
767
+ if (children.length) {
768
+ if (children.length === 1 && children[0].type === 'text') {
769
+ tokens[key] = children[0].data
770
+ } else {
771
+ tokens[key] = children
772
+ }
773
+ }
774
+ }
775
+
776
+ }
777
+ }
778
+ }
779
+
780
+ if (computedTokens.length) {
781
+ const results = await Promise.all(computedTokens)
782
+
783
+ for (let i = 0; i < results.length; i++) {
784
+ const result = results[i]
785
+ const key = computedTokenKey[i]
786
+
787
+ if (typeof result === 'string') {
788
+ const html = parseHTML(results[i], document.ignoreByAttribute)
789
+
790
+ if (html.root.children.length) {
791
+ tokens[key] = html.root.children
709
792
  }
793
+ } else {
794
+ tokens[key] = result
710
795
  }
711
796
  }
712
797
  }
@@ -733,7 +818,7 @@ export async function parseScript ({
733
818
  }
734
819
 
735
820
  // compute slot nodes
736
- const result = computedSlot(slotContent, values) || slotContent
821
+ const result = computedSlot(slotContent, tokens) || slotContent
737
822
 
738
823
  // append new slot nodes
739
824
  for (let index = 0; index < result.length; index++) {
@@ -761,9 +846,7 @@ export async function parseScript ({
761
846
  */
762
847
 
763
848
  /**
764
- * @param {Object} options
765
- * @param {CoraliteAggregateTemplate} options.template - Templates used to display the result
766
- * @param {string} options.path
849
+ * @param {CoraliteAggregate} options
767
850
  */
768
851
  async aggregate (options) {
769
852
  /** @type {string} */
@@ -809,10 +892,11 @@ export async function parseScript ({
809
892
  async function moduleLinker (specifier, referencingModule) {
810
893
  if (specifier === 'coralite') {
811
894
  return new vm.SourceTextModule(`
895
+ export const document = coralite.document
812
896
  export const tokens = coralite.tokens
813
897
  export const defineComponent = coralite.defineComponent
814
898
  export const aggregate = coralite.aggregate
815
- export default { tokens, defineComponent, aggregate };
899
+ export default { tokens, defineComponent, aggregate, document };
816
900
  `, { context: referencingModule.context })
817
901
  }
818
902
  const module = await import(specifier)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coralite",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "HTML modules static site generator",
5
5
  "main": "./lib/coralite.js",
6
6
  "type": "module",
package/types/index.js CHANGED
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  /**
22
- * @typedef {Object.<string, (string | (CoraliteTextNode | CoraliteElement)[])>} CoraliteModuleValues
22
+ * @typedef {Object.<string, (string | string[] | (CoraliteDirective | CoraliteAnyNode)[])>} CoraliteModuleValues
23
23
  */
24
24
 
25
25
  /**
@@ -141,3 +141,17 @@
141
141
  /**
142
142
  * @typedef {Array<Array<string, string>>} IgnoreByAttribute - An array of attribute names and values to ignore by element type.
143
143
  */
144
+
145
+ /**
146
+ * @callback CoraliteAggregateFilter
147
+ * @param {Object.<string, string>} metadata - Aggregated HTML page metadata
148
+ */
149
+
150
+ /**
151
+ * @typedef {Object} CoraliteAggregate – Configuration object for the aggregation process
152
+ * @property {string} path - The path to aggregate, relative to pages directory
153
+ * @property {CoraliteAggregateTemplate | string} template - Templates used to display the result
154
+ * @property {CoraliteAggregateFilter} [filter] - Callback to filter out unwanted elements from the aggregated content.
155
+ * @property {boolean} [recursive] - Whether to recursively search subdirectories
156
+ * @property {CoraliteTokenOptions} [tokens] - Token configuration options
157
+ */