@anydigital/eleventy-bricks 1.0.0-alpha.15 → 1.0.0-alpha.17
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/README.md +167 -2
- package/package.json +1 -1
- package/src/do/package.json +7 -5
- package/src/eleventy.config.js +2 -0
- package/src/index.cjs +5 -5
- package/src/index.js +8 -4
- package/src/mergeFilter.js +57 -0
- package/src/mergeFilter.test.js +64 -0
- package/src/removeTagFilter.js +42 -0
- package/src/removeTagFilter.test.js +60 -0
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ Import only the specific helpers you need without using the plugin:
|
|
|
50
50
|
|
|
51
51
|
**ES Modules:**
|
|
52
52
|
```javascript
|
|
53
|
-
import { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, siteData } from "@anydigital/eleventy-bricks";
|
|
53
|
+
import { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, mergeFilter, removeTagFilter, siteData } from "@anydigital/eleventy-bricks";
|
|
54
54
|
|
|
55
55
|
export default function(eleventyConfig) {
|
|
56
56
|
bricks(eleventyConfig);
|
|
@@ -58,6 +58,8 @@ export default function(eleventyConfig) {
|
|
|
58
58
|
mdAutoNl2br(eleventyConfig);
|
|
59
59
|
setAttrFilter(eleventyConfig);
|
|
60
60
|
byAttrFilter(eleventyConfig);
|
|
61
|
+
mergeFilter(eleventyConfig);
|
|
62
|
+
removeTagFilter(eleventyConfig);
|
|
61
63
|
siteData(eleventyConfig);
|
|
62
64
|
|
|
63
65
|
// Your other configuration...
|
|
@@ -66,7 +68,7 @@ export default function(eleventyConfig) {
|
|
|
66
68
|
|
|
67
69
|
**CommonJS:**
|
|
68
70
|
```javascript
|
|
69
|
-
const { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, siteData } = require("@anydigital/eleventy-bricks");
|
|
71
|
+
const { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, mergeFilter, removeTagFilter, siteData } = require("@anydigital/eleventy-bricks");
|
|
70
72
|
|
|
71
73
|
module.exports = async function(eleventyConfig) {
|
|
72
74
|
await bricks(eleventyConfig);
|
|
@@ -74,6 +76,8 @@ module.exports = async function(eleventyConfig) {
|
|
|
74
76
|
await mdAutoNl2br(eleventyConfig);
|
|
75
77
|
await setAttrFilter(eleventyConfig);
|
|
76
78
|
await byAttrFilter(eleventyConfig);
|
|
79
|
+
await mergeFilter(eleventyConfig);
|
|
80
|
+
await removeTagFilter(eleventyConfig);
|
|
77
81
|
await siteData(eleventyConfig);
|
|
78
82
|
|
|
79
83
|
// Your other configuration...
|
|
@@ -93,6 +97,8 @@ When using the plugin (Option 1), you can configure which helpers to enable:
|
|
|
93
97
|
| `mdAutoNl2br` | boolean | `false` | Enable the mdAutoNl2br preprocessor to convert \n to `<br>` tags |
|
|
94
98
|
| `setAttrFilter` | boolean | `false` | Enable the setAttr filter for overriding object attributes |
|
|
95
99
|
| `byAttrFilter` | boolean | `false` | Enable the byAttr filter for filtering collections by attribute values |
|
|
100
|
+
| `mergeFilter` | boolean | `false` | Enable the merge filter for merging arrays or objects |
|
|
101
|
+
| `removeTagFilter` | boolean | `false` | Enable the removeTag filter for removing HTML elements from content |
|
|
96
102
|
| `siteData` | boolean | `false` | Enable site.year and site.isProd global data |
|
|
97
103
|
|
|
98
104
|
**Example:**
|
|
@@ -435,6 +441,165 @@ Template usage:
|
|
|
435
441
|
{% set recentBlogPosts = collections.all | byAttr('category', 'blog') | reverse | limit(5) %}
|
|
436
442
|
```
|
|
437
443
|
|
|
444
|
+
### merge
|
|
445
|
+
|
|
446
|
+
A filter that merges arrays or objects together, similar to Twig's merge filter. For arrays, it concatenates them. For objects, it performs a shallow merge where later values override earlier ones.
|
|
447
|
+
|
|
448
|
+
**Why use this?**
|
|
449
|
+
|
|
450
|
+
When working with data in templates, you often need to combine multiple arrays or objects. The `merge` filter provides a clean way to merge data structures without writing custom JavaScript, making it easy to combine collections, merge configuration objects, or aggregate data from multiple sources.
|
|
451
|
+
|
|
452
|
+
**Usage:**
|
|
453
|
+
|
|
454
|
+
1. Enable `merge` in your Eleventy config:
|
|
455
|
+
|
|
456
|
+
```javascript
|
|
457
|
+
import { mergeFilter } from "@anydigital/eleventy-bricks";
|
|
458
|
+
|
|
459
|
+
export default function(eleventyConfig) {
|
|
460
|
+
mergeFilter(eleventyConfig);
|
|
461
|
+
// Or use as plugin:
|
|
462
|
+
// eleventyConfig.addPlugin(eleventyBricks, { mergeFilter: true });
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
2. Use the filter in your templates:
|
|
467
|
+
|
|
468
|
+
**Merge arrays:**
|
|
469
|
+
```njk
|
|
470
|
+
{# Combine two arrays #}
|
|
471
|
+
{% set allItems = featured | merge(regular) %}
|
|
472
|
+
|
|
473
|
+
{# Merge multiple arrays #}
|
|
474
|
+
{% set combined = array1 | merge(array2, array3, array4) %}
|
|
475
|
+
|
|
476
|
+
{% for item in allItems %}
|
|
477
|
+
<p>{{ item }}</p>
|
|
478
|
+
{% endfor %}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Merge objects:**
|
|
482
|
+
```njk
|
|
483
|
+
{# Merge configuration objects #}
|
|
484
|
+
{% set defaultConfig = { theme: 'light', lang: 'en' } %}
|
|
485
|
+
{% set userConfig = { theme: 'dark' } %}
|
|
486
|
+
{% set finalConfig = defaultConfig | merge(userConfig) %}
|
|
487
|
+
|
|
488
|
+
{# Result: { theme: 'dark', lang: 'en' } #}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**Parameters:**
|
|
492
|
+
|
|
493
|
+
- `first`: The first array or object (the base to merge into)
|
|
494
|
+
- `...rest`: One or more arrays or objects to merge in
|
|
495
|
+
|
|
496
|
+
**Features:**
|
|
497
|
+
|
|
498
|
+
- Works with both arrays and objects
|
|
499
|
+
- Supports merging multiple items at once
|
|
500
|
+
- Non-mutating: Creates new arrays/objects, leaving originals unchanged
|
|
501
|
+
- For objects: Later values override earlier ones (shallow merge)
|
|
502
|
+
- For arrays: Concatenates all arrays together
|
|
503
|
+
- Handles null/undefined gracefully
|
|
504
|
+
|
|
505
|
+
**Examples:**
|
|
506
|
+
|
|
507
|
+
```njk
|
|
508
|
+
{# Combine featured and regular posts #}
|
|
509
|
+
{% set featuredPosts = collections.all | byAttr('featured', true) %}
|
|
510
|
+
{% set regularPosts = collections.all | byAttr('featured', false) %}
|
|
511
|
+
{% set allPosts = featuredPosts | merge(regularPosts) %}
|
|
512
|
+
|
|
513
|
+
{# Merge page metadata with defaults #}
|
|
514
|
+
{% set defaultMeta = {
|
|
515
|
+
author: 'Site Admin',
|
|
516
|
+
category: 'general',
|
|
517
|
+
comments: false
|
|
518
|
+
} %}
|
|
519
|
+
{% set pageMeta = defaultMeta | merge(page.data) %}
|
|
520
|
+
|
|
521
|
+
{# Combine arrays of tags #}
|
|
522
|
+
{% set commonTags = ['javascript', 'html', 'css'] %}
|
|
523
|
+
{% set specialTags = page.data.tags or [] %}
|
|
524
|
+
{% set allTags = commonTags | merge(specialTags) %}
|
|
525
|
+
|
|
526
|
+
{# Merge multiple configuration sources #}
|
|
527
|
+
{% set config = defaults | merge(siteConfig, pageConfig, userPrefs) %}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### removeTag
|
|
531
|
+
|
|
532
|
+
A filter that removes a specified HTML element from provided HTML content. It removes the tag along with its content, including self-closing tags.
|
|
533
|
+
|
|
534
|
+
**Why use this?**
|
|
535
|
+
|
|
536
|
+
When working with content from external sources or user-generated content, you may need to strip certain HTML tags for security or presentation purposes. The `removeTag` filter provides a simple way to remove unwanted tags like `<script>`, `<style>`, or any other HTML elements from your content.
|
|
537
|
+
|
|
538
|
+
**Usage:**
|
|
539
|
+
|
|
540
|
+
1. Enable `removeTag` in your Eleventy config:
|
|
541
|
+
|
|
542
|
+
```javascript
|
|
543
|
+
import { removeTagFilter } from "@anydigital/eleventy-bricks";
|
|
544
|
+
|
|
545
|
+
export default function(eleventyConfig) {
|
|
546
|
+
removeTagFilter(eleventyConfig);
|
|
547
|
+
// Or use as plugin:
|
|
548
|
+
// eleventyConfig.addPlugin(eleventyBricks, { removeTagFilter: true });
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
2. Use the filter in your templates:
|
|
553
|
+
|
|
554
|
+
```njk
|
|
555
|
+
{# Remove all script tags from content #}
|
|
556
|
+
{% set cleanContent = htmlContent | removeTag('script') %}
|
|
557
|
+
|
|
558
|
+
{{ cleanContent | safe }}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
**Parameters:**
|
|
562
|
+
|
|
563
|
+
- `html`: The HTML content to process (string)
|
|
564
|
+
- `tagName`: The tag name to remove (string)
|
|
565
|
+
|
|
566
|
+
**Features:**
|
|
567
|
+
|
|
568
|
+
- Removes both opening and closing tags along with their content
|
|
569
|
+
- Handles self-closing tags (e.g., `<br />`, `<img />`)
|
|
570
|
+
- Handles tags with attributes
|
|
571
|
+
- Case-insensitive matching
|
|
572
|
+
- Non-destructive: Returns new string, doesn't modify original
|
|
573
|
+
|
|
574
|
+
**Examples:**
|
|
575
|
+
|
|
576
|
+
```njk
|
|
577
|
+
{# Remove scripts from user-generated content #}
|
|
578
|
+
{% set userContent = '<p>Hello</p><script>alert("XSS")</script><p>World</p>' %}
|
|
579
|
+
{% set safeContent = userContent | removeTag('script') %}
|
|
580
|
+
{# Result: '<p>Hello</p><p>World</p>' #}
|
|
581
|
+
|
|
582
|
+
{# Strip specific formatting tags #}
|
|
583
|
+
{% set formatted = '<div><strong>Bold</strong> and <em>italic</em> text</div>' %}
|
|
584
|
+
{% set noStrong = formatted | removeTag('strong') %}
|
|
585
|
+
{# Result: '<div>Bold and <em>italic</em> text</div>' #}
|
|
586
|
+
|
|
587
|
+
{# Chain multiple removeTag filters for multiple tags #}
|
|
588
|
+
{% set richContent = page.content %}
|
|
589
|
+
{% set stripped = richContent
|
|
590
|
+
| removeTag('script')
|
|
591
|
+
| removeTag('style')
|
|
592
|
+
| removeTag('iframe')
|
|
593
|
+
%}
|
|
594
|
+
|
|
595
|
+
{# Remove images for text-only preview #}
|
|
596
|
+
{% set textOnly = htmlContent | removeTag('img') %}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**Security Note:**
|
|
600
|
+
|
|
601
|
+
While this filter can help sanitize HTML content, it should not be relied upon as the sole security measure. For critical security requirements, use a dedicated HTML sanitization library on the server side before content reaches your templates.
|
|
602
|
+
|
|
438
603
|
### siteData
|
|
439
604
|
|
|
440
605
|
Adds global site data to your Eleventy project, providing commonly needed values that can be accessed in all templates.
|
package/package.json
CHANGED
package/src/do/package.json
CHANGED
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
"name": "@anydigital/eleventy-bricks-do",
|
|
3
3
|
"private": true,
|
|
4
4
|
"scripts": {
|
|
5
|
-
"build": "npm run
|
|
6
|
-
|
|
5
|
+
"build": "npm run tw -- --minify && npm run 11ty --",
|
|
6
|
+
|
|
7
|
+
"start": "npm run tw -- --watch & npm run 11ty -- --serve",
|
|
7
8
|
|
|
8
9
|
"prerestart": "npm run 11ty:clean",
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
"stage": "npm run 11ty:clean; serve ../_site & npm run build --",
|
|
10
12
|
|
|
11
13
|
"11ty": "cd ../ && NODE_OPTIONS='--preserve-symlinks' eleventy",
|
|
12
14
|
"11ty:clean": "rm -r ../_site",
|
|
13
|
-
"11ty:debug": "DEBUG=* npm run 11ty",
|
|
15
|
+
"11ty:debug": "DEBUG=* npm run 11ty --",
|
|
14
16
|
|
|
15
17
|
"tw": "tailwindcss -i ../src/_template/styles.css -o ../_site/styles.css",
|
|
16
|
-
"tw:debug": "DEBUG=* npm run tw"
|
|
18
|
+
"tw:debug": "DEBUG=* npm run tw --"
|
|
17
19
|
}
|
|
18
20
|
}
|
package/src/eleventy.config.js
CHANGED
package/src/index.cjs
CHANGED
|
@@ -10,17 +10,17 @@ module.exports = async function eleventyBricksPlugin(eleventyConfig, options) {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// Export individual helpers for granular usage
|
|
13
|
-
['bricks', 'mdAutoRawTags', 'mdAutoNl2br', 'setAttrFilter', 'byAttrFilter', 'siteData'].forEach(name => {
|
|
13
|
+
['bricks', 'mdAutoRawTags', 'mdAutoNl2br', 'setAttrFilter', 'byAttrFilter', 'mergeFilter', 'removeTagFilter', 'siteData'].forEach(name => {
|
|
14
14
|
module.exports[name] = async (eleventyConfig) => {
|
|
15
15
|
const module = await import('./index.js');
|
|
16
16
|
return module[name](eleventyConfig);
|
|
17
17
|
};
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
// Export transform functions for advanced usage
|
|
21
|
-
['transformAutoRaw', 'transformNl2br'].forEach(name => {
|
|
22
|
-
module.exports[name] = async (
|
|
20
|
+
// Export transform/utility functions for advanced usage
|
|
21
|
+
['transformAutoRaw', 'transformNl2br', 'merge', 'removeTag'].forEach(name => {
|
|
22
|
+
module.exports[name] = async (...args) => {
|
|
23
23
|
const module = await import('./index.js');
|
|
24
|
-
return module[name](
|
|
24
|
+
return module[name](...args);
|
|
25
25
|
};
|
|
26
26
|
});
|
package/src/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import { bricks } from "./bricks.js";
|
|
|
2
2
|
import { mdAutoRawTags, mdAutoNl2br, transformAutoRaw, transformNl2br } from "./markdown.js";
|
|
3
3
|
import { setAttrFilter } from "./setAttrFilter.js";
|
|
4
4
|
import { byAttrFilter } from "./byAttrFilter.js";
|
|
5
|
+
import { mergeFilter, merge } from "./mergeFilter.js";
|
|
6
|
+
import { removeTagFilter, removeTag } from "./removeTagFilter.js";
|
|
5
7
|
import { siteData } from "./siteData.js";
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -17,15 +19,17 @@ import { siteData } from "./siteData.js";
|
|
|
17
19
|
* @param {boolean} options.mdAutoNl2br - Enable mdAutoNl2br for \n to <br> conversion (default: false)
|
|
18
20
|
* @param {boolean} options.setAttrFilter - Enable setAttr filter (default: false)
|
|
19
21
|
* @param {boolean} options.byAttrFilter - Enable byAttr filter (default: false)
|
|
22
|
+
* @param {boolean} options.mergeFilter - Enable merge filter (default: false)
|
|
23
|
+
* @param {boolean} options.removeTagFilter - Enable removeTag filter (default: false)
|
|
20
24
|
* @param {boolean} options.siteData - Enable site.year and site.isProd global data (default: false)
|
|
21
25
|
*/
|
|
22
26
|
export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
|
|
23
|
-
const plugins = { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, siteData };
|
|
27
|
+
const plugins = { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, mergeFilter, removeTagFilter, siteData };
|
|
24
28
|
Object.entries(options).forEach(([key, enabled]) => enabled && plugins[key]?.(eleventyConfig));
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
// Export individual helpers for granular usage
|
|
28
|
-
export { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, siteData };
|
|
32
|
+
export { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, mergeFilter, removeTagFilter, siteData };
|
|
29
33
|
|
|
30
|
-
// Export transform functions for advanced usage
|
|
31
|
-
export { transformAutoRaw, transformNl2br };
|
|
34
|
+
// Export transform/utility functions for advanced usage
|
|
35
|
+
export { transformAutoRaw, transformNl2br, merge, removeTag };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge arrays or objects together
|
|
3
|
+
*
|
|
4
|
+
* For arrays: concatenates them together
|
|
5
|
+
* For objects: shallow merges them (later values override earlier ones)
|
|
6
|
+
*
|
|
7
|
+
* @param {Array|Object} first - The first array or object
|
|
8
|
+
* @param {...Array|Object} rest - Additional arrays or objects to merge
|
|
9
|
+
* @returns {Array|Object} The merged result
|
|
10
|
+
*/
|
|
11
|
+
export function merge(first, ...rest) {
|
|
12
|
+
// If first argument is null or undefined, treat as empty
|
|
13
|
+
if (first === null || first === undefined) {
|
|
14
|
+
first = Array.isArray(rest[0]) ? [] : {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Determine if we're working with arrays or objects
|
|
18
|
+
const isArray = Array.isArray(first);
|
|
19
|
+
|
|
20
|
+
if (isArray) {
|
|
21
|
+
// Merge arrays by concatenating
|
|
22
|
+
return rest.reduce((acc, item) => {
|
|
23
|
+
if (Array.isArray(item)) {
|
|
24
|
+
return acc.concat(item);
|
|
25
|
+
}
|
|
26
|
+
// If item is not an array, add it as a single element
|
|
27
|
+
return acc.concat([item]);
|
|
28
|
+
}, [...first]);
|
|
29
|
+
} else if (typeof first === 'object') {
|
|
30
|
+
// Merge objects using spread operator (shallow merge)
|
|
31
|
+
return rest.reduce((acc, item) => {
|
|
32
|
+
if (item !== null && typeof item === 'object' && !Array.isArray(item)) {
|
|
33
|
+
return { ...acc, ...item };
|
|
34
|
+
}
|
|
35
|
+
return acc;
|
|
36
|
+
}, { ...first });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// If first is a primitive, return it as-is
|
|
40
|
+
return first;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* merge filter - Merge arrays or objects together
|
|
45
|
+
*
|
|
46
|
+
* This filter merges arrays or objects, similar to Twig's merge filter.
|
|
47
|
+
*
|
|
48
|
+
* Usage in templates:
|
|
49
|
+
* {{ array1 | merge(array2) }}
|
|
50
|
+
* {{ array1 | merge(array2, array3) }}
|
|
51
|
+
* {{ obj1 | merge(obj2) }}
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
54
|
+
*/
|
|
55
|
+
export function mergeFilter(eleventyConfig) {
|
|
56
|
+
eleventyConfig.addFilter("merge", merge);
|
|
57
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { merge } from './mergeFilter.js';
|
|
4
|
+
|
|
5
|
+
test('merge - merges two arrays', () => {
|
|
6
|
+
const result = merge([1, 2], [3, 4]);
|
|
7
|
+
assert.deepStrictEqual(result, [1, 2, 3, 4]);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('merge - merges multiple arrays', () => {
|
|
11
|
+
const result = merge([1, 2], [3, 4], [5, 6]);
|
|
12
|
+
assert.deepStrictEqual(result, [1, 2, 3, 4, 5, 6]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('merge - merges two objects', () => {
|
|
16
|
+
const result = merge({ a: 1, b: 2 }, { c: 3, d: 4 });
|
|
17
|
+
assert.deepStrictEqual(result, { a: 1, b: 2, c: 3, d: 4 });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('merge - merges objects with override', () => {
|
|
21
|
+
const result = merge({ a: 1, b: 2 }, { b: 3, c: 4 });
|
|
22
|
+
assert.deepStrictEqual(result, { a: 1, b: 3, c: 4 });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('merge - merges multiple objects', () => {
|
|
26
|
+
const result = merge({ a: 1 }, { b: 2 }, { c: 3 });
|
|
27
|
+
assert.deepStrictEqual(result, { a: 1, b: 2, c: 3 });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('merge - handles empty arrays', () => {
|
|
31
|
+
const result = merge([], [1, 2], [3]);
|
|
32
|
+
assert.deepStrictEqual(result, [1, 2, 3]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('merge - handles null first argument with arrays', () => {
|
|
36
|
+
const result = merge(null, [1, 2]);
|
|
37
|
+
assert.deepStrictEqual(result, [1, 2]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('merge - handles null first argument with objects', () => {
|
|
41
|
+
const result = merge(null, { a: 1 });
|
|
42
|
+
assert.deepStrictEqual(result, { a: 1 });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('merge - array merge with non-array adds as element', () => {
|
|
46
|
+
const result = merge([1, 2], 3);
|
|
47
|
+
assert.deepStrictEqual(result, [1, 2, 3]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('merge - does not modify original arrays', () => {
|
|
51
|
+
const original = [1, 2];
|
|
52
|
+
const result = merge(original, [3, 4]);
|
|
53
|
+
|
|
54
|
+
assert.deepStrictEqual(original, [1, 2]);
|
|
55
|
+
assert.deepStrictEqual(result, [1, 2, 3, 4]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('merge - does not modify original objects', () => {
|
|
59
|
+
const original = { a: 1 };
|
|
60
|
+
const result = merge(original, { b: 2 });
|
|
61
|
+
|
|
62
|
+
assert.deepStrictEqual(original, { a: 1 });
|
|
63
|
+
assert.deepStrictEqual(result, { a: 1, b: 2 });
|
|
64
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove specified HTML element from provided HTML
|
|
3
|
+
*
|
|
4
|
+
* @param {string} html - The HTML content to process
|
|
5
|
+
* @param {string} tagName - The tag name to remove
|
|
6
|
+
* @returns {string} The HTML with the specified tag removed
|
|
7
|
+
*/
|
|
8
|
+
export function removeTag(html, tagName) {
|
|
9
|
+
if (!html || typeof html !== 'string') {
|
|
10
|
+
return html;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (typeof tagName !== 'string' || !tagName) {
|
|
14
|
+
return html;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Escape special regex characters in tag name
|
|
18
|
+
const escapedTag = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
19
|
+
|
|
20
|
+
// Remove opening and closing tags along with their content
|
|
21
|
+
// This regex matches: <tag attributes>content</tag>
|
|
22
|
+
const regex = new RegExp(`<${escapedTag}(?:\\s[^>]*)?>.*?<\\/${escapedTag}>`, 'gis');
|
|
23
|
+
let result = html.replace(regex, '');
|
|
24
|
+
|
|
25
|
+
// Also remove self-closing tags: <tag />
|
|
26
|
+
const selfClosingRegex = new RegExp(`<${escapedTag}(?:\\s[^>]*)?\\s*\\/?>`, 'gi');
|
|
27
|
+
result = result.replace(selfClosingRegex, '');
|
|
28
|
+
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* removeTag filter - Remove specified HTML element from provided HTML
|
|
34
|
+
*
|
|
35
|
+
* Usage in templates:
|
|
36
|
+
* {{ htmlContent | removeTag('script') }}
|
|
37
|
+
*
|
|
38
|
+
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
39
|
+
*/
|
|
40
|
+
export function removeTagFilter(eleventyConfig) {
|
|
41
|
+
eleventyConfig.addFilter("removeTag", removeTag);
|
|
42
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { removeTag } from './removeTagFilter.js';
|
|
4
|
+
|
|
5
|
+
describe('removeTag', () => {
|
|
6
|
+
it('should remove a single tag with content', () => {
|
|
7
|
+
const html = '<div>Keep this</div><script>Remove this</script><p>Keep this too</p>';
|
|
8
|
+
const result = removeTag(html, 'script');
|
|
9
|
+
|
|
10
|
+
assert.strictEqual(result, '<div>Keep this</div><p>Keep this too</p>');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should remove multiple instances of the same tag', () => {
|
|
14
|
+
const html = '<p>First</p><script>One</script><p>Second</p><script>Two</script><p>Third</p>';
|
|
15
|
+
const result = removeTag(html, 'script');
|
|
16
|
+
|
|
17
|
+
assert.strictEqual(result, '<p>First</p><p>Second</p><p>Third</p>');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should handle tags with attributes', () => {
|
|
21
|
+
const html = '<div>Keep</div><script type="text/javascript" src="file.js">Code</script><p>Keep</p>';
|
|
22
|
+
const result = removeTag(html, 'script');
|
|
23
|
+
|
|
24
|
+
assert.strictEqual(result, '<div>Keep</div><p>Keep</p>');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should handle self-closing tags', () => {
|
|
28
|
+
const html = '<div>Keep</div><br /><p>Keep</p>';
|
|
29
|
+
const result = removeTag(html, 'br');
|
|
30
|
+
|
|
31
|
+
assert.strictEqual(result, '<div>Keep</div><p>Keep</p>');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return original HTML if tag does not exist', () => {
|
|
35
|
+
const html = '<div>Keep this</div><p>Keep this too</p>';
|
|
36
|
+
const result = removeTag(html, 'script');
|
|
37
|
+
|
|
38
|
+
assert.strictEqual(result, html);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle empty or null input', () => {
|
|
42
|
+
assert.strictEqual(removeTag('', 'script'), '');
|
|
43
|
+
assert.strictEqual(removeTag(null, 'script'), null);
|
|
44
|
+
assert.strictEqual(removeTag(undefined, 'script'), undefined);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should be case-insensitive', () => {
|
|
48
|
+
const html = '<div>Keep</div><SCRIPT>Remove</SCRIPT><Script>Remove</Script><p>Keep</p>';
|
|
49
|
+
const result = removeTag(html, 'script');
|
|
50
|
+
|
|
51
|
+
assert.strictEqual(result, '<div>Keep</div><p>Keep</p>');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should handle nested content', () => {
|
|
55
|
+
const html = '<div>Keep</div><script><div>Nested</div></script><p>Keep</p>';
|
|
56
|
+
const result = removeTag(html, 'script');
|
|
57
|
+
|
|
58
|
+
assert.strictEqual(result, '<div>Keep</div><p>Keep</p>');
|
|
59
|
+
});
|
|
60
|
+
});
|