@anydigital/eleventy-bricks 1.0.0-alpha.16 → 1.0.0-alpha.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anydigital/eleventy-bricks",
3
- "version": "1.0.0-alpha.16",
3
+ "version": "1.0.0-alpha.18",
4
4
  "description": "A collection of helpful utilities and filters for Eleventy (11ty)",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -2,19 +2,14 @@
2
2
  "name": "@anydigital/eleventy-bricks-do",
3
3
  "private": true,
4
4
  "scripts": {
5
- "build": "npm run tw -- --minify && npm run 11ty --",
6
-
7
- "start": "npm run tw -- --watch & npm run 11ty -- --serve",
8
-
5
+ "build": "npm run 11ty -- $ELTY_OPTIONS && npm run tw -- --minify",
6
+ "start": "npm run 11ty -- $ELTY_OPTIONS --serve & npm run tw -- --watch",
9
7
  "prerestart": "npm run 11ty:clean",
10
-
11
- "stage": "npm run 11ty:clean; serve ../_site & npm run build --",
12
-
8
+ "stage": "npm run 11ty:clean; npm run build && serve ../_site",
13
9
  "11ty": "cd ../ && NODE_OPTIONS='--preserve-symlinks' eleventy",
14
10
  "11ty:clean": "rm -r ../_site",
15
11
  "11ty:debug": "DEBUG=* npm run 11ty --",
16
-
17
- "tw": "tailwindcss -i ../src/_template/styles.css -o ../_site/styles.css",
12
+ "tw": "tailwindcss -i ../src/_theme/styles.css -o ../_site/styles.css",
18
13
  "tw:debug": "DEBUG=* npm run tw --"
19
14
  }
20
15
  }
@@ -15,7 +15,7 @@ import yaml from "js-yaml";
15
15
  * @param {Object} eleventyConfig - The Eleventy configuration object
16
16
  * @returns {Object} The Eleventy configuration object
17
17
  */
18
- export default function(eleventyConfig) {
18
+ export default function (eleventyConfig) {
19
19
  /* CLI support */
20
20
  const argv = minimist(process.argv.slice(2));
21
21
  const inputDir = argv.input || "src";
@@ -24,31 +24,41 @@ export default function(eleventyConfig) {
24
24
  eleventyConfig.addPlugin(RenderPlugin);
25
25
  eleventyConfig.addPlugin(eleventyNavigationPlugin);
26
26
  eleventyConfig.addPlugin(eleventyBricksPlugin, {
27
- bricks: true,
28
27
  mdAutoNl2br: true,
29
28
  mdAutoRawTags: true,
30
- setAttrFilter: true,
31
- byAttrFilter: true,
32
- siteData: true
29
+ siteData: true,
30
+ filters: [
31
+ "attr",
32
+ "where_in",
33
+ "merge",
34
+ "remove_tag",
35
+ "if",
36
+ "attr_concat",
37
+ ],
33
38
  });
34
39
 
35
40
  /* Libraries */
36
- eleventyConfig.setLibrary("md", markdownIt({
37
- html: true,
38
- breaks: true,
39
- linkify: true
40
- }).use(markdownItAnchor, {
41
- permalink: markdownItAnchor.permalink.headerLink()
42
- }));
41
+ eleventyConfig.setLibrary(
42
+ "md",
43
+ markdownIt({
44
+ html: true,
45
+ linkify: true,
46
+ }).use(markdownItAnchor, {
47
+ permalink: markdownItAnchor.permalink.headerLink(),
48
+ })
49
+ );
43
50
 
44
51
  /* Data */
45
52
  eleventyConfig.addDataExtension("yml", (contents) => yaml.load(contents));
46
53
 
47
54
  /* Build */
48
- eleventyConfig.addPassthroughCopy({
49
- "src/_public": ".",
50
- ...(inputDir !== "src" && { [`${inputDir}/_public`]: "." })
51
- }, { expand: true }); // This follows/resolves symbolic links
55
+ eleventyConfig.addPassthroughCopy(
56
+ {
57
+ "src/_public": ".",
58
+ ...(inputDir !== "src" && { [`${inputDir}/_public`]: "." }),
59
+ },
60
+ { expand: true } // This follows/resolves symbolic links
61
+ );
52
62
 
53
63
  /* Dev tools */
54
64
  // Follow symlinks in Chokidar used by 11ty to watch files
@@ -56,11 +66,9 @@ export default function(eleventyConfig) {
56
66
 
57
67
  /* Config */
58
68
  return {
59
- templateFormats: ["md", "njk"],
60
- htmlTemplateEngine: "njk",
61
69
  dir: {
62
70
  input: inputDir,
63
- includes: "_template"
64
- }
71
+ includes: "_theme",
72
+ },
65
73
  };
66
- };
74
+ }
@@ -1,17 +1,16 @@
1
1
  /**
2
- * setAttr filter - Override an attribute and return the object
3
- *
2
+ * attr filter - Override an attribute and return the object
3
+ *
4
4
  * This filter takes an object, a key, and a value, and returns a new object
5
5
  * with the specified attribute set to the given value.
6
- *
6
+ *
7
7
  * @param {Object} eleventyConfig - The Eleventy configuration object
8
8
  */
9
9
  export function setAttrFilter(eleventyConfig) {
10
- eleventyConfig.addFilter("setAttr", function(obj, key, value) {
10
+ eleventyConfig.addFilter("attr", function (obj, key, value) {
11
11
  return {
12
12
  ...obj,
13
- [key]: value
13
+ [key]: value,
14
14
  };
15
15
  });
16
16
  }
17
-
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Concatenate values to an attribute array
3
+ *
4
+ * This function takes an object, an attribute name, and values to append.
5
+ * It returns a new object with the attribute as a combined array of unique items.
6
+ *
7
+ * @param {Object} obj - The object to modify
8
+ * @param {string} attr - The attribute name
9
+ * @param {Array|string|*} values - Values to concatenate (array, JSON string array, or single value)
10
+ * @returns {Object} A new object with the combined unique array
11
+ */
12
+ export function attrConcat(obj, attr, values) {
13
+ // Get the existing attribute value, default to empty array if not present
14
+ const existingArray = obj?.[attr] || [];
15
+
16
+ // Check if existing value is an array, convert if not
17
+ if (!Array.isArray(existingArray)) {
18
+ console.error(
19
+ `attrConcat: Expected ${attr} to be an array, got ${typeof existingArray}`
20
+ );
21
+ }
22
+
23
+ // Process the values argument
24
+ let valuesToAdd = [];
25
+ if (Array.isArray(values)) {
26
+ valuesToAdd = values;
27
+ } else if (typeof values === "string") {
28
+ // Try to parse as JSON array
29
+ try {
30
+ const parsed = JSON.parse(values);
31
+ if (Array.isArray(parsed)) {
32
+ valuesToAdd = parsed;
33
+ } else {
34
+ valuesToAdd = [values];
35
+ }
36
+ } catch {
37
+ // Not valid JSON, treat as single value
38
+ valuesToAdd = [values];
39
+ }
40
+ } else {
41
+ // If it's a single value, wrap it in an array
42
+ valuesToAdd = [values];
43
+ }
44
+
45
+ // Combine arrays and remove duplicates using Set
46
+ const combinedArray = [...new Set([...existingArray, ...valuesToAdd])];
47
+
48
+ // Return a new object with the combined array
49
+ return {
50
+ ...obj,
51
+ [attr]: combinedArray,
52
+ };
53
+ }
54
+
55
+ /**
56
+ * attr_concat filter - Concatenate values to an attribute array
57
+ *
58
+ * This filter takes an object, an attribute name, and values to append.
59
+ * It returns a new object with the attribute as a combined array of unique items.
60
+ *
61
+ * @param {Object} eleventyConfig - The Eleventy configuration object
62
+ */
63
+ export function attrConcatFilter(eleventyConfig) {
64
+ eleventyConfig.addFilter("attr_concat", attrConcat);
65
+ }
@@ -0,0 +1,205 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert";
3
+ import { attrConcat } from "./attr_concat.js";
4
+
5
+ describe("attrConcat", () => {
6
+ it("should concatenate array values to existing array attribute", () => {
7
+ const obj = { classes: ["foo", "bar"] };
8
+ const result = attrConcat(obj, "classes", ["baz", "qux"]);
9
+
10
+ assert.deepStrictEqual(result, {
11
+ classes: ["foo", "bar", "baz", "qux"],
12
+ });
13
+ });
14
+
15
+ it("should parse JSON array string", () => {
16
+ const obj = { classes: ["foo", "bar"] };
17
+ const result = attrConcat(obj, "classes", '["baz", "qux", "quux"]');
18
+
19
+ assert.deepStrictEqual(result, {
20
+ classes: ["foo", "bar", "baz", "qux", "quux"],
21
+ });
22
+ });
23
+
24
+ it("should handle single string value (not JSON)", () => {
25
+ const obj = { classes: ["foo"] };
26
+ const result = attrConcat(obj, "classes", "bar baz");
27
+
28
+ assert.deepStrictEqual(result, {
29
+ classes: ["foo", "bar baz"],
30
+ });
31
+ });
32
+
33
+ it("should create new array if attribute doesn't exist", () => {
34
+ const obj = { name: "test" };
35
+ const result = attrConcat(obj, "classes", ["foo", "bar"]);
36
+
37
+ assert.deepStrictEqual(result, {
38
+ name: "test",
39
+ classes: ["foo", "bar"],
40
+ });
41
+ });
42
+
43
+ it("should handle empty object", () => {
44
+ const obj = {};
45
+ const result = attrConcat(obj, "classes", ["foo", "bar"]);
46
+
47
+ assert.deepStrictEqual(result, {
48
+ classes: ["foo", "bar"],
49
+ });
50
+ });
51
+
52
+ it("should handle null object", () => {
53
+ const result = attrConcat(null, "classes", ["foo", "bar"]);
54
+
55
+ assert.deepStrictEqual(result, {
56
+ classes: ["foo", "bar"],
57
+ });
58
+ });
59
+
60
+ it("should handle single value (non-array, non-string)", () => {
61
+ const obj = { ids: [1, 2] };
62
+ const result = attrConcat(obj, "ids", 3);
63
+
64
+ assert.deepStrictEqual(result, {
65
+ ids: [1, 2, 3],
66
+ });
67
+ });
68
+
69
+ it("should not mutate original object", () => {
70
+ const obj = { classes: ["foo", "bar"] };
71
+ const result = attrConcat(obj, "classes", ["baz"]);
72
+
73
+ assert.deepStrictEqual(obj, { classes: ["foo", "bar"] });
74
+ assert.deepStrictEqual(result, { classes: ["foo", "bar", "baz"] });
75
+ assert.notStrictEqual(obj, result);
76
+ });
77
+
78
+ it("should preserve other attributes", () => {
79
+ const obj = { classes: ["foo"], id: "test", data: { value: 42 } };
80
+ const result = attrConcat(obj, "classes", ["bar"]);
81
+
82
+ assert.deepStrictEqual(result, {
83
+ classes: ["foo", "bar"],
84
+ id: "test",
85
+ data: { value: 42 },
86
+ });
87
+ });
88
+
89
+ it("should handle empty array values", () => {
90
+ const obj = { classes: ["foo"] };
91
+ const result = attrConcat(obj, "classes", []);
92
+
93
+ assert.deepStrictEqual(result, {
94
+ classes: ["foo"],
95
+ });
96
+ });
97
+
98
+ it("should handle empty string values", () => {
99
+ const obj = { classes: ["foo"] };
100
+ const result = attrConcat(obj, "classes", "");
101
+
102
+ assert.deepStrictEqual(result, {
103
+ classes: ["foo", ""],
104
+ });
105
+ });
106
+
107
+ it("should handle multiline strings as single value (not JSON)", () => {
108
+ const obj = { classes: ["foo"] };
109
+ const result = attrConcat(obj, "classes", "bar\n\nbaz\n\n\nqux");
110
+
111
+ assert.deepStrictEqual(result, {
112
+ classes: ["foo", "bar\n\nbaz\n\n\nqux"],
113
+ });
114
+ });
115
+
116
+ it("should remove duplicate values from existing array", () => {
117
+ const obj = { classes: ["foo", "bar"] };
118
+ const result = attrConcat(obj, "classes", ["bar", "baz"]);
119
+
120
+ assert.deepStrictEqual(result, {
121
+ classes: ["foo", "bar", "baz"],
122
+ });
123
+ });
124
+
125
+ it("should remove duplicate values from new array", () => {
126
+ const obj = { classes: ["foo"] };
127
+ const result = attrConcat(obj, "classes", ["bar", "bar", "baz", "baz"]);
128
+
129
+ assert.deepStrictEqual(result, {
130
+ classes: ["foo", "bar", "baz"],
131
+ });
132
+ });
133
+
134
+ it("should handle duplicates in JSON array strings", () => {
135
+ const obj = { classes: ["foo", "bar"] };
136
+ const result = attrConcat(obj, "classes", '["bar", "baz", "bar", "qux"]');
137
+
138
+ assert.deepStrictEqual(result, {
139
+ classes: ["foo", "bar", "baz", "qux"],
140
+ });
141
+ });
142
+
143
+ it("should preserve order and remove only duplicates", () => {
144
+ const obj = { classes: ["a", "b", "c"] };
145
+ const result = attrConcat(obj, "classes", ["b", "d", "e", "a"]);
146
+
147
+ assert.deepStrictEqual(result, {
148
+ classes: ["a", "b", "c", "d", "e"],
149
+ });
150
+ });
151
+
152
+ it("should parse valid JSON array string", () => {
153
+ const obj = { classes: ["foo"] };
154
+ const result = attrConcat(obj, "classes", '["bar", "baz", "qux"]');
155
+
156
+ assert.deepStrictEqual(result, {
157
+ classes: ["foo", "bar", "baz", "qux"],
158
+ });
159
+ });
160
+
161
+ it("should treat non-JSON string as single value", () => {
162
+ const obj = { classes: ["foo"] };
163
+ const result = attrConcat(obj, "classes", "bar\nbaz\nqux");
164
+
165
+ assert.deepStrictEqual(result, {
166
+ classes: ["foo", "bar\nbaz\nqux"],
167
+ });
168
+ });
169
+
170
+ it("should keep plain string as single value", () => {
171
+ const obj = { tags: ["existing"] };
172
+ const result = attrConcat(obj, "tags", "single value");
173
+
174
+ assert.deepStrictEqual(result, {
175
+ tags: ["existing", "single value"],
176
+ });
177
+ });
178
+
179
+ it("should parse JSON with numbers", () => {
180
+ const obj = { ids: [1, 2] };
181
+ const result = attrConcat(obj, "ids", "[3, 4, 5]");
182
+
183
+ assert.deepStrictEqual(result, {
184
+ ids: [1, 2, 3, 4, 5],
185
+ });
186
+ });
187
+
188
+ it("should treat invalid JSON as single string value", () => {
189
+ const obj = { classes: ["foo"] };
190
+ const result = attrConcat(obj, "classes", '["bar", "baz"'); // Invalid JSON
191
+
192
+ assert.deepStrictEqual(result, {
193
+ classes: ["foo", '["bar", "baz"'],
194
+ });
195
+ });
196
+
197
+ it("should treat JSON non-array as single value", () => {
198
+ const obj = { tags: ["foo"] };
199
+ const result = attrConcat(obj, "tags", '{"key": "value"}');
200
+
201
+ assert.deepStrictEqual(result, {
202
+ tags: ["foo", '{"key": "value"}'],
203
+ });
204
+ });
205
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * if utility function - Ternary/conditional helper
3
+ *
4
+ * Returns trueValue if condition is truthy, otherwise returns falseValue.
5
+ * Similar to Nunjucks' inline if: `value if condition else other_value`
6
+ *
7
+ * @param {*} trueValue - The value to return if condition is truthy
8
+ * @param {*} condition - The condition to evaluate
9
+ * @param {*} falseValue - The value to return if condition is falsy (default: empty string)
10
+ * @returns {*} Either trueValue or falseValue based on condition
11
+ */
12
+ export function iff(trueValue, condition, falseValue = "") {
13
+ // Treat empty objects {} as falsy
14
+ if (
15
+ condition &&
16
+ typeof condition === "object" &&
17
+ !Array.isArray(condition) &&
18
+ Object.keys(condition).length === 0
19
+ ) {
20
+ return falseValue;
21
+ }
22
+ return !!condition ? trueValue : falseValue;
23
+ }
24
+
25
+ /**
26
+ * if filter - Inline conditional/ternary operator for templates
27
+ *
28
+ * This filter provides a simple inline if/else similar to Nunjucks.
29
+ *
30
+ * Usage in Liquid templates:
31
+ * {{ "Active" | if: isActive, "Inactive" }}
32
+ * {{ "Yes" | if: condition }}
33
+ * {{ someValue | if: test, otherValue }}
34
+ *
35
+ * @param {Object} eleventyConfig - The Eleventy configuration object
36
+ */
37
+ export function ifFilter(eleventyConfig) {
38
+ eleventyConfig.addFilter("if", iff);
39
+ }
@@ -0,0 +1,63 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert";
3
+ import { iff } from "./if.js";
4
+
5
+ test("iff returns trueValue when condition is truthy", () => {
6
+ assert.strictEqual(iff("yes", true, "no"), "yes");
7
+ assert.strictEqual(iff("yes", 1, "no"), "yes");
8
+ assert.strictEqual(iff("yes", "truthy", "no"), "yes");
9
+ assert.strictEqual(iff("yes", { a: 1 }, "no"), "yes"); // non-empty object is truthy
10
+ assert.strictEqual(iff("yes", [], "no"), "yes");
11
+ });
12
+
13
+ test("iff returns falseValue when condition is falsy", () => {
14
+ assert.strictEqual(iff("yes", false, "no"), "no");
15
+ assert.strictEqual(iff("yes", 0, "no"), "no");
16
+ assert.strictEqual(iff("yes", "", "no"), "no");
17
+ assert.strictEqual(iff("yes", null, "no"), "no");
18
+ assert.strictEqual(iff("yes", undefined, "no"), "no");
19
+ });
20
+
21
+ test("iff treats empty objects as falsy", () => {
22
+ assert.strictEqual(iff("yes", {}, "no"), "no");
23
+ assert.strictEqual(iff("yes", {}, ""), "");
24
+ assert.strictEqual(iff(100, {}, 200), 200);
25
+
26
+ // Non-empty objects should still be truthy
27
+ assert.strictEqual(iff("yes", { a: 1 }, "no"), "yes");
28
+ assert.strictEqual(iff("yes", { nested: {} }, "no"), "yes");
29
+ });
30
+
31
+ test("iff returns falseValue when condition is undefined", () => {
32
+ assert.strictEqual(iff("yes", undefined, "no"), "no");
33
+ assert.strictEqual(iff("yes", undefined), "");
34
+ assert.strictEqual(iff(100, undefined, 200), 200);
35
+ });
36
+
37
+ test("iff returns empty string as default falseValue", () => {
38
+ assert.strictEqual(iff("yes", false), "");
39
+ assert.strictEqual(iff("yes", 0), "");
40
+ assert.strictEqual(iff("yes", null), "");
41
+ });
42
+
43
+ test("iff works with various value types", () => {
44
+ assert.strictEqual(iff(100, true, 200), 100);
45
+ assert.strictEqual(iff(100, false, 200), 200);
46
+
47
+ const obj1 = { a: 1 };
48
+ const obj2 = { b: 2 };
49
+ assert.strictEqual(iff(obj1, true, obj2), obj1);
50
+ assert.strictEqual(iff(obj1, false, obj2), obj2);
51
+
52
+ const arr1 = [1, 2];
53
+ const arr2 = [3, 4];
54
+ assert.strictEqual(iff(arr1, true, arr2), arr1);
55
+ assert.strictEqual(iff(arr1, false, arr2), arr2);
56
+ });
57
+
58
+ test("iff evaluates condition correctly without type coercion confusion", () => {
59
+ // Common edge cases
60
+ assert.strictEqual(iff("yes", "false", "no"), "yes"); // string 'false' is truthy
61
+ assert.strictEqual(iff("yes", "0", "no"), "yes"); // string '0' is truthy
62
+ assert.strictEqual(iff("yes", NaN, "no"), "no"); // NaN is falsy
63
+ });
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Merge objects together
3
+ *
4
+ * Shallow merges objects (later values override earlier ones)
5
+ *
6
+ * @param {Object} first - The first object
7
+ * @param {...Object} rest - Additional objects to merge
8
+ * @returns {Object} The merged result
9
+ */
10
+ export function merge(first, ...rest) {
11
+ // If first argument is null or undefined, treat as empty object
12
+ if (first === null || first === undefined) {
13
+ first = {};
14
+ }
15
+
16
+ // Only support objects
17
+ if (typeof first === "object" && !Array.isArray(first)) {
18
+ // Merge objects using spread operator (shallow merge)
19
+ return rest.reduce(
20
+ (acc, item) => {
21
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
22
+ return { ...acc, ...item };
23
+ }
24
+ return acc;
25
+ },
26
+ { ...first }
27
+ );
28
+ }
29
+
30
+ // If first is not an object, return empty object
31
+ return {};
32
+ }
33
+
34
+ /**
35
+ * merge filter - Merge objects together
36
+ *
37
+ * This filter merges objects, similar to Twig's merge filter.
38
+ *
39
+ * Usage in templates:
40
+ * {{ obj1 | merge(obj2) }}
41
+ * {{ obj1 | merge(obj2, obj3) }}
42
+ *
43
+ * @param {Object} eleventyConfig - The Eleventy configuration object
44
+ */
45
+ export function mergeFilter(eleventyConfig) {
46
+ eleventyConfig.addFilter("merge", merge);
47
+ }
@@ -0,0 +1,51 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { merge } from './merge.js';
4
+
5
+ test('merge - merges two objects', () => {
6
+ const result = merge({ a: 1, b: 2 }, { c: 3, d: 4 });
7
+ assert.deepStrictEqual(result, { a: 1, b: 2, c: 3, d: 4 });
8
+ });
9
+
10
+ test('merge - merges objects with override', () => {
11
+ const result = merge({ a: 1, b: 2 }, { b: 3, c: 4 });
12
+ assert.deepStrictEqual(result, { a: 1, b: 3, c: 4 });
13
+ });
14
+
15
+ test('merge - merges multiple objects', () => {
16
+ const result = merge({ a: 1 }, { b: 2 }, { c: 3 });
17
+ assert.deepStrictEqual(result, { a: 1, b: 2, c: 3 });
18
+ });
19
+
20
+ test('merge - handles null first argument', () => {
21
+ const result = merge(null, { a: 1 });
22
+ assert.deepStrictEqual(result, { a: 1 });
23
+ });
24
+
25
+ test('merge - handles undefined first argument', () => {
26
+ const result = merge(undefined, { a: 1 });
27
+ assert.deepStrictEqual(result, { a: 1 });
28
+ });
29
+
30
+ test('merge - does not modify original objects', () => {
31
+ const original = { a: 1 };
32
+ const result = merge(original, { b: 2 });
33
+
34
+ assert.deepStrictEqual(original, { a: 1 });
35
+ assert.deepStrictEqual(result, { a: 1, b: 2 });
36
+ });
37
+
38
+ test('merge - returns empty object for arrays', () => {
39
+ const result = merge([1, 2], [3, 4]);
40
+ assert.deepStrictEqual(result, {});
41
+ });
42
+
43
+ test('merge - returns empty object for primitives', () => {
44
+ const result = merge('string', { a: 1 });
45
+ assert.deepStrictEqual(result, {});
46
+ });
47
+
48
+ test('merge - ignores non-object arguments in rest', () => {
49
+ const result = merge({ a: 1 }, 'string', { b: 2 }, null, { c: 3 });
50
+ assert.deepStrictEqual(result, { a: 1, b: 2, c: 3 });
51
+ });
@@ -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
+ * remove_tag filter - Remove specified HTML element from provided HTML
34
+ *
35
+ * Usage in templates:
36
+ * {{ htmlContent | remove_tag('script') }}
37
+ *
38
+ * @param {Object} eleventyConfig - The Eleventy configuration object
39
+ */
40
+ export function removeTagFilter(eleventyConfig) {
41
+ eleventyConfig.addFilter("remove_tag", removeTag);
42
+ }