coralite 0.14.0 → 0.14.2

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/lib/parse.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Parser } from 'htmlparser2'
2
- import { invalidCustomTags, validTags } from './tags.js'
2
+ import { isValidCustomElementName, VALID_TAGS } from './tags.js'
3
3
 
4
4
  /**
5
5
  * @import {
@@ -15,9 +15,6 @@ import { invalidCustomTags, validTags } from './tags.js'
15
15
  * ParseHTMLResult} from '#types'
16
16
  */
17
17
 
18
- const customElementTagRegExp = /^[^-].*[-._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
19
- 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
20
-
21
18
  /**
22
19
  * Parse HTML content and return a CoraliteDocument object representing the parsed document structure
23
20
  *
@@ -180,13 +177,13 @@ export function parseModule (string, { ignoreByAttribute }) {
180
177
  const customElements = []
181
178
  /** @type {Object.<string, Object.<string,CoraliteModuleSlotElement>>} */
182
179
  const slotElements = {}
183
- let isTemplate = false
184
-
185
180
  /** @type {CoraliteDocumentValues} */
186
181
  const documentValues = {
187
182
  attributes: [],
188
183
  textNodes: []
189
184
  }
185
+ let isScript = false
186
+ let isTemplate = false
190
187
  let templateId = ''
191
188
 
192
189
  const parser = new Parser({
@@ -206,7 +203,9 @@ export function parseModule (string, { ignoreByAttribute }) {
206
203
  // push element to stack as it may have children
207
204
  stack.push(element)
208
205
 
209
- if (element.name === 'template') {
206
+ if (element.name === 'script') {
207
+ isScript = true
208
+ } else if (element.name === 'template') {
210
209
  // enter template tag
211
210
  isTemplate = true
212
211
 
@@ -214,19 +213,7 @@ export function parseModule (string, { ignoreByAttribute }) {
214
213
  throw new Error('Template requires an "id"')
215
214
  }
216
215
 
217
- let idHasToken = false
218
- // check if template id contains a token
219
- for (let i = 0; i < documentValues.attributes.length; i++) {
220
- const token = documentValues.attributes[i]
221
-
222
- if (token.name === 'id') {
223
- idHasToken = true
224
- break
225
- }
226
- }
227
-
228
- if (!customElementTagRegExp.test(attributes.id)
229
- || (idHasToken && !customElementTagTokenRegExp.test(attributes.id))) {
216
+ if (!isValidCustomElementName(attributes.id)) {
230
217
  throw new Error('Invalid template id: "' + originalName + '" it must match following the pattern https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name')
231
218
  }
232
219
 
@@ -275,7 +262,7 @@ export function parseModule (string, { ignoreByAttribute }) {
275
262
  const parent = stack[stack.length - 1]
276
263
  const textNode = createTextNode(text, parent)
277
264
 
278
- if (text.trim()) {
265
+ if (isTemplate && !isScript && text.trim()) {
279
266
  const tokens = getTokensFromString(text)
280
267
 
281
268
  // store tokens
@@ -298,6 +285,8 @@ export function parseModule (string, { ignoreByAttribute }) {
298
285
  if (name === 'template') {
299
286
  // exit template tag
300
287
  isTemplate = false
288
+ } else if (name === 'script') {
289
+ isScript = false
301
290
  }
302
291
 
303
292
  // remove current element from stack as we're done with its children
@@ -435,12 +424,9 @@ export function createElement ({
435
424
  }
436
425
  }
437
426
 
438
- if (!validTags[sanitisedName]) {
439
- if (invalidCustomTags[sanitisedName]) {
440
- throw new Error('Element name is reserved: "'+ sanitisedName +'"')
441
- }
442
-
443
- if (customElementTagRegExp.test(sanitisedName)) {
427
+ if (!VALID_TAGS[sanitisedName]) {
428
+ // check if the tag name matches the regex for valid custom elements
429
+ if (isValidCustomElementName(sanitisedName)) {
444
430
  // store custom elements
445
431
  element.slots = []
446
432
  } else {
@@ -501,7 +487,11 @@ function findAttributesToIgnore (ignoreByAttribute, attributes) {
501
487
  * @returns {CoraliteToken[]}
502
488
  */
503
489
  function getTokensFromString (string) {
504
- const matches = string.matchAll(/\{{[^}]*\}}/g)
490
+ if (string.length > 100) {
491
+ console.warn(`Token "${string}" is too long`)
492
+ }
493
+
494
+ const matches = string.matchAll(/\{\{[^}]{0,100}?\}\}/g)
505
495
  const result = []
506
496
 
507
497
  for (const match of matches) {
package/lib/tags.js CHANGED
@@ -1,4 +1,4 @@
1
- export const validTags = {
1
+ export const VALID_TAGS = {
2
2
  a: true,
3
3
  abbr: true,
4
4
  acronym: true, // deprecated
@@ -193,8 +193,7 @@ export const validTags = {
193
193
  view: true
194
194
  }
195
195
 
196
-
197
- export const invalidCustomTags = {
196
+ export const RESERVED_ELEMENT_NAMES = {
198
197
  'annotation-xml': true,
199
198
  'color-profile': true,
200
199
  'font-face': true,
@@ -204,3 +203,37 @@ export const invalidCustomTags = {
204
203
  'font-face-name': true,
205
204
  'missing-glyph': true
206
205
  }
206
+
207
+ const CUSTOM_ELEMENT_TAG = /^[a-z](?:[-.\w\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD])*?-(?:[-.\w\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD])*$/;
208
+
209
+ /**
210
+ * Validates a custom element name
211
+ * @param {string} name - The custom element name to validate
212
+ * @param {number} [maxLength=100] - Max length of the tag name
213
+ * @returns {boolean} - True if valid, false otherwise
214
+ * @throws {Error} - If the element name is reserved
215
+ */
216
+ export function isValidCustomElementName(name, maxLength = 100) {
217
+ // Check if string is empty or not a string
218
+ if (!name || typeof name !== 'string') {
219
+ return false;
220
+ }
221
+
222
+ // Check against reserved names first (case-insensitive)
223
+ if (RESERVED_ELEMENT_NAMES[name.toLowerCase()]) {
224
+ throw new Error('Element name is reserved: "'+ name +'"')
225
+ }
226
+
227
+ // Length check to prevent ReDoS
228
+ if (name.length > maxLength) {
229
+ return false;
230
+ }
231
+
232
+ // check for obviously invalid patterns that could cause backtracking
233
+ if (name.includes('--') || name.startsWith('-') || name.endsWith('-')) {
234
+ return false;
235
+ }
236
+
237
+ // Test against the regex
238
+ return CUSTOM_ELEMENT_TAG.test(name);
239
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coralite",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "HTML modules static site generator",
5
5
  "main": "./lib/coralite.js",
6
6
  "type": "module",
@@ -1,5 +0,0 @@
1
- ---
2
- description: Javascript
3
- ---
4
-
5
- Ensure to use Javascript and JSDoc annotations to provide type information.