@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/README.md CHANGED
@@ -17,27 +17,35 @@ You can use this library in two ways:
17
17
  Import and use the entire plugin. You can configure which helpers to enable using the options parameter:
18
18
 
19
19
  **ES Modules:**
20
+
20
21
  ```javascript
21
22
  import eleventyBricks from "@anydigital/eleventy-bricks";
22
23
 
23
- export default function(eleventyConfig) {
24
+ export default function (eleventyConfig) {
24
25
  eleventyConfig.addPlugin(eleventyBricks, {
25
- mdAutoRawTags: true // Enable mdAutoRawTags preprocessor (default: false)
26
+ mdAutoRawTags: true,
27
+ mdAutoNl2br: true,
28
+ siteData: true,
29
+ filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
26
30
  });
27
-
31
+
28
32
  // Your other configuration...
29
33
  }
30
34
  ```
31
35
 
32
36
  **CommonJS:**
37
+
33
38
  ```javascript
34
39
  const eleventyBricks = require("@anydigital/eleventy-bricks");
35
40
 
36
- module.exports = function(eleventyConfig) {
41
+ module.exports = function (eleventyConfig) {
37
42
  eleventyConfig.addPlugin(eleventyBricks, {
38
- mdAutoRawTags: true // Enable mdAutoRawTags preprocessor (default: false)
43
+ mdAutoRawTags: true,
44
+ mdAutoNl2br: true,
45
+ siteData: true,
46
+ filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
39
47
  });
40
-
48
+
41
49
  // Your other configuration...
42
50
  };
43
51
  ```
@@ -49,33 +57,61 @@ module.exports = function(eleventyConfig) {
49
57
  Import only the specific helpers you need without using the plugin:
50
58
 
51
59
  **ES Modules:**
52
- ```javascript
53
- import { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, siteData } from "@anydigital/eleventy-bricks";
54
60
 
55
- export default function(eleventyConfig) {
56
- bricks(eleventyConfig);
61
+ ```javascript
62
+ import {
63
+ mdAutoRawTags,
64
+ mdAutoNl2br,
65
+ setAttrFilter,
66
+ whereInFilter,
67
+ mergeFilter,
68
+ removeTagFilter,
69
+ ifFilter,
70
+ attrConcatFilter,
71
+ siteData,
72
+ } from "@anydigital/eleventy-bricks";
73
+
74
+ export default function (eleventyConfig) {
57
75
  mdAutoRawTags(eleventyConfig);
58
76
  mdAutoNl2br(eleventyConfig);
59
77
  setAttrFilter(eleventyConfig);
60
- byAttrFilter(eleventyConfig);
78
+ whereInFilter(eleventyConfig);
79
+ mergeFilter(eleventyConfig);
80
+ removeTagFilter(eleventyConfig);
81
+ ifFilter(eleventyConfig);
82
+ attrConcatFilter(eleventyConfig);
61
83
  siteData(eleventyConfig);
62
-
84
+
63
85
  // Your other configuration...
64
86
  }
65
87
  ```
66
88
 
67
89
  **CommonJS:**
68
- ```javascript
69
- const { bricks, mdAutoRawTags, mdAutoNl2br, setAttrFilter, byAttrFilter, siteData } = require("@anydigital/eleventy-bricks");
70
90
 
71
- module.exports = async function(eleventyConfig) {
72
- await bricks(eleventyConfig);
91
+ ```javascript
92
+ const {
93
+ mdAutoRawTags,
94
+ mdAutoNl2br,
95
+ setAttrFilter,
96
+ whereInFilter,
97
+ mergeFilter,
98
+ removeTagFilter,
99
+ ifFilter,
100
+ attrConcatFilter,
101
+ siteData,
102
+ } = require("@anydigital/eleventy-bricks");
103
+
104
+ module.exports = async function (eleventyConfig) {
73
105
  await mdAutoRawTags(eleventyConfig);
74
106
  await mdAutoNl2br(eleventyConfig);
75
107
  await setAttrFilter(eleventyConfig);
76
- await byAttrFilter(eleventyConfig);
108
+ await whereInFilter(eleventyConfig);
109
+ await mergeFilter(eleventyConfig);
110
+ await removeTagFilter(eleventyConfig);
111
+ await ifFilter(eleventyConfig);
112
+ await attrConcatFilter(eleventyConfig);
77
113
  await siteData(eleventyConfig);
78
-
114
+
79
115
  // Your other configuration...
80
116
  };
81
117
  ```
@@ -86,132 +122,35 @@ module.exports = async function(eleventyConfig) {
86
122
 
87
123
  When using the plugin (Option 1), you can configure which helpers to enable:
88
124
 
89
- | Option | Type | Default | Description |
90
- |--------|------|---------|-------------|
91
- | `bricks` | boolean | `false` | Enable the bricks system for dependency management |
92
- | `mdAutoRawTags` | boolean | `false` | Enable the mdAutoRawTags preprocessor for Markdown files |
93
- | `mdAutoNl2br` | boolean | `false` | Enable the mdAutoNl2br preprocessor to convert \n to `<br>` tags |
94
- | `setAttrFilter` | boolean | `false` | Enable the setAttr filter for overriding object attributes |
95
- | `byAttrFilter` | boolean | `false` | Enable the byAttr filter for filtering collections by attribute values |
96
- | `siteData` | boolean | `false` | Enable site.year and site.isProd global data |
125
+ | Option | Type | Default | Description |
126
+ | --------------- | --------------- | ------- | ---------------------------------------------------------------- |
127
+ | `mdAutoRawTags` | boolean | `false` | Enable the mdAutoRawTags preprocessor for Markdown files |
128
+ | `mdAutoNl2br` | boolean | `false` | Enable the mdAutoNl2br preprocessor to convert \n to `<br>` tags |
129
+ | `siteData` | boolean | `false` | Enable site.year and site.prod global data |
130
+ | `filters` | array of string | `[]` | Array of filter names to enable (see Available Filters section) |
131
+
132
+ **Available filter names for the `filters` array:**
133
+
134
+ - `'attr'` - Override object attributes
135
+ - `'where_in'` - Filter collections by attribute values
136
+ - `'merge'` - Merge arrays or objects
137
+ - `'remove_tag'` - Remove HTML elements from content
138
+ - `'if'` - Inline conditional/ternary operator
139
+ - `'attr_concat'` - Concatenate values to an attribute array
97
140
 
98
141
  **Example:**
142
+
99
143
  ```javascript
100
144
  eleventyConfig.addPlugin(eleventyBricks, {
101
- bricks: true,
102
145
  mdAutoRawTags: true,
103
- byAttrFilter: true,
104
- siteData: true
146
+ mdAutoNl2br: true,
147
+ siteData: true,
148
+ filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
105
149
  });
106
150
  ```
107
151
 
108
152
  ## Available 11ty Helpers
109
153
 
110
- ### bricks
111
-
112
- A dependency management system for Eleventy that automatically collects and injects CSS and JavaScript dependencies (both external and inline) per page. This allows brick components to declare their dependencies, and the system will inject them in the correct location in your HTML.
113
-
114
- **Why use this?**
115
-
116
- When building reusable components (bricks) in Eleventy, you often need to include CSS and JavaScript dependencies. Instead of manually adding these to every page, `bricks` automatically:
117
- - Collects dependencies from all bricks used on a page
118
- - Categorizes them (external CSS, external JS, inline styles, inline scripts)
119
- - Injects them in the correct location in your HTML output
120
-
121
- **How it works:**
122
-
123
- 1. Use the `bricksDependencies` shortcode in your base template to mark where dependencies should be injected
124
- 2. Use the `brick` shortcode to register and render brick components that declare their dependencies
125
- 3. The system automatically collects all dependencies and injects them when the page is built
126
-
127
- **Usage:**
128
-
129
- 1. Enable `bricks` in your Eleventy config:
130
-
131
- ```javascript
132
- import { bricks } from "@anydigital/eleventy-bricks";
133
-
134
- export default function(eleventyConfig) {
135
- bricks(eleventyConfig);
136
- // Or use as plugin:
137
- // eleventyConfig.addPlugin(eleventyBricks, { bricks: true });
138
- }
139
- ```
140
-
141
- 2. Add the `bricksDependencies` shortcode in your base template (typically in the `<head>` section):
142
-
143
- ```njk
144
- <head>
145
- <meta charset="UTF-8">
146
- <title>My Site</title>
147
- {% bricksDependencies [
148
- ... (global dependencies can be set here) ...
149
- ] %}
150
- <!-- Other head content -->
151
- </head>
152
- ```
153
-
154
- 3. Create brick components that declare their dependencies:
155
-
156
- ```javascript
157
- // myBrick.js
158
- export default {
159
- dependencies: [
160
- 'https://cdn.example.com/library.css',
161
- 'https://cdn.example.com/library.js'
162
- ],
163
- style: `
164
- .my-component { color: blue; }
165
- `,
166
- script: `
167
- console.log('Component initialized');
168
- `,
169
- render: function() {
170
- return '<div class="my-component">Hello World</div>';
171
- }
172
- };
173
- ```
174
-
175
- 4. Use the `brick` shortcode in your templates:
176
-
177
- ```njk
178
- {% set myBrick = require('./myBrick.js') %}
179
- {% brick myBrick %}
180
- ```
181
-
182
- **Brick Component Structure:**
183
-
184
- A brick component is a JavaScript object with the following optional properties:
185
-
186
- - `dependencies`: Array of URLs to external CSS or JavaScript files (e.g., `['https://cdn.example.com/style.css', 'https://cdn.example.com/script.js']`)
187
- - `style`: String containing inline CSS
188
- - `script`: String containing inline JavaScript
189
- - `render`: Function that returns the HTML markup for the component
190
-
191
- **Output:**
192
-
193
- The system will automatically inject all dependencies in the order they were registered:
194
-
195
- ```html
196
- <head>
197
- <meta charset="UTF-8">
198
- <title>My Site</title>
199
- <link rel="stylesheet" href="https://cdn.example.com/library.css">
200
- <style>.my-component { color: blue; }</style>
201
- <script src="https://cdn.example.com/library.js"></script>
202
- <script>console.log('Component initialized');</script>
203
- <!-- Other head content -->
204
- </head>
205
- ```
206
-
207
- **Features:**
208
-
209
- - Automatic dependency collection per page
210
- - Categorizes dependencies (CSS vs JS, external vs inline)
211
- - Deduplicates dependencies (using Sets internally)
212
- - Works with both external URLs and inline code
213
- - Clears registry before each build to prevent stale data
214
-
215
154
  ### mdAutoRawTags
216
155
 
217
156
  Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping `{{`, `}}`, `{%`, and `%}` with `{% raw %}` tags.
@@ -227,7 +166,7 @@ When writing documentation or tutorials about templating in Markdown files, you
227
166
  ```javascript
228
167
  import { mdAutoRawTags } from "@anydigital/eleventy-bricks";
229
168
 
230
- export default function(eleventyConfig) {
169
+ export default function (eleventyConfig) {
231
170
  mdAutoRawTags(eleventyConfig);
232
171
  // Or use as plugin:
233
172
  // eleventyConfig.addPlugin(eleventyBricks, { mdAutoRawTags: true });
@@ -237,6 +176,7 @@ export default function(eleventyConfig) {
237
176
  **Example:**
238
177
 
239
178
  Before `mdAutoRawTags`, writing this in Markdown:
179
+
240
180
  ```markdown
241
181
  Use {{ variable }} to output variables.
242
182
  ```
@@ -258,7 +198,7 @@ Markdown tables don't support multi-line content in cells. By using `\n` in your
258
198
  ```javascript
259
199
  import { mdAutoNl2br } from "@anydigital/eleventy-bricks";
260
200
 
261
- export default function(eleventyConfig) {
201
+ export default function (eleventyConfig) {
262
202
  mdAutoNl2br(eleventyConfig);
263
203
  // Or use as plugin:
264
204
  // eleventyConfig.addPlugin(eleventyBricks, { mdAutoNl2br: true });
@@ -268,39 +208,41 @@ export default function(eleventyConfig) {
268
208
  **Example:**
269
209
 
270
210
  In your Markdown file:
211
+
271
212
  ```markdown
272
- | Column 1 | Column 2 |
273
- |----------|----------|
213
+ | Column 1 | Column 2 |
214
+ | ---------------------- | --------------------------------- |
274
215
  | Line 1\nLine 2\nLine 3 | Another cell\nWith multiple lines |
275
216
  ```
276
217
 
277
218
  Will render as:
219
+
278
220
  ```html
279
- <td>Line 1<br>Line 2<br>Line 3</td>
280
- <td>Another cell<br>With multiple lines</td>
221
+ <td>Line 1<br />Line 2<br />Line 3</td>
222
+ <td>Another cell<br />With multiple lines</td>
281
223
  ```
282
224
 
283
225
  **Note:** This processes literal `\n` sequences (backslash followed by 'n'), not actual newline characters. Type `\n` in your source files where you want line breaks.
284
226
 
285
- ### setAttr
227
+ ### attr
286
228
 
287
229
  A filter that creates a new object with an overridden attribute value. This is useful for modifying data objects in templates without mutating the original.
288
230
 
289
231
  **Why use this?**
290
232
 
291
- When working with Eleventy data, you sometimes need to modify an object's properties for a specific use case. The `setAttr` filter provides a clean way to create a modified copy of an object without affecting the original.
233
+ When working with Eleventy data, you sometimes need to modify an object's properties for a specific use case. The `attr` filter provides a clean way to create a modified copy of an object without affecting the original.
292
234
 
293
235
  **Usage:**
294
236
 
295
- 1. Enable `setAttr` in your Eleventy config:
237
+ 1. Enable the `attr` filter in your Eleventy config:
296
238
 
297
239
  ```javascript
298
240
  import { setAttrFilter } from "@anydigital/eleventy-bricks";
299
241
 
300
- export default function(eleventyConfig) {
242
+ export default function (eleventyConfig) {
301
243
  setAttrFilter(eleventyConfig);
302
244
  // Or use as plugin:
303
- // eleventyConfig.addPlugin(eleventyBricks, { setAttrFilter: true });
245
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr'] });
304
246
  }
305
247
  ```
306
248
 
@@ -308,7 +250,7 @@ export default function(eleventyConfig) {
308
250
 
309
251
  ```njk
310
252
  {# Create a modified version of a page object #}
311
- {% set modifiedPage = page | setAttr('title', 'New Title') %}
253
+ {% set modifiedPage = page | attr('title', 'New Title') %}
312
254
 
313
255
  <h1>{{ modifiedPage.title }}</h1>
314
256
  <p>Original title: {{ page.title }}</p>
@@ -335,49 +277,50 @@ A new object with the specified attribute set to the given value. The original o
335
277
 
336
278
  ```njk
337
279
  {# Override a single attribute #}
338
- {% set updatedPost = post | setAttr('featured', true) %}
280
+ {% set updatedPost = post | attr('featured', true) %}
339
281
 
340
- {# Chain multiple setAttr filters #}
341
- {% set modifiedPost = post
342
- | setAttr('category', 'blog')
343
- | setAttr('priority', 1)
282
+ {# Chain multiple attr filters #}
283
+ {% set modifiedPost = post
284
+ | attr('category', 'blog')
285
+ | attr('priority', 1)
344
286
  %}
345
287
 
346
288
  {# Use in loops #}
347
289
  {% for item in collection %}
348
- {% set enhancedItem = item | setAttr('processed', true) %}
290
+ {% set enhancedItem = item | attr('processed', true) %}
349
291
  {# ... use enhancedItem ... #}
350
292
  {% endfor %}
351
293
  ```
352
294
 
353
- ### byAttr
295
+ ### where_in
354
296
 
355
- A filter that filters collection items by attribute value. It checks if an item's attribute matches a target value. If the attribute is an array, it checks if the array includes the target value.
297
+ A filter that filters collection items by attribute value. It checks if an item's attribute matches a target value. If the attribute is an array, it checks if the array includes the target value. Supports nested attribute names using dot notation.
356
298
 
357
299
  **Why use this?**
358
300
 
359
- When working with Eleventy collections, you often need to filter items based on front matter data. The `byAttr` filter provides a flexible way to filter by any attribute, with special handling for array attributes (like tags).
301
+ When working with Eleventy collections, you often need to filter items based on front matter data. The `where_in` filter provides a flexible way to filter by any attribute, with special handling for array attributes (like tags) and support for nested properties using dot notation.
360
302
 
361
303
  **Usage:**
362
304
 
363
- 1. Enable `byAttr` in your Eleventy config:
305
+ 1. Enable the `where_in` filter in your Eleventy config:
364
306
 
365
307
  ```javascript
366
- import { byAttrFilter } from "@anydigital/eleventy-bricks";
308
+ import { whereInFilter } from "@anydigital/eleventy-bricks";
367
309
 
368
- export default function(eleventyConfig) {
369
- byAttrFilter(eleventyConfig);
310
+ export default function (eleventyConfig) {
311
+ whereInFilter(eleventyConfig);
370
312
  // Or use as plugin:
371
- // eleventyConfig.addPlugin(eleventyBricks, { byAttrFilter: true });
313
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['where_in'] });
372
314
  }
373
315
  ```
374
316
 
375
317
  2. Use the filter in your templates:
376
318
 
377
319
  **Filter by exact attribute match:**
320
+
378
321
  ```njk
379
322
  {# Get all posts with category 'blog' #}
380
- {% set blogPosts = collections.all | byAttr('category', 'blog') %}
323
+ {% set blogPosts = collections.all | where_in('data.category', 'blog') %}
381
324
 
382
325
  {% for post in blogPosts %}
383
326
  <h2>{{ post.data.title }}</h2>
@@ -385,9 +328,10 @@ export default function(eleventyConfig) {
385
328
  ```
386
329
 
387
330
  **Filter by array attribute (tags):**
331
+
388
332
  ```njk
389
333
  {# Get all posts that include 'javascript' tag #}
390
- {% set jsPosts = collections.all | byAttr('tags', 'javascript') %}
334
+ {% set jsPosts = collections.all | where_in('data.tags', 'javascript') %}
391
335
 
392
336
  {% for post in jsPosts %}
393
337
  <h2>{{ post.data.title }}</h2>
@@ -397,13 +341,13 @@ export default function(eleventyConfig) {
397
341
  **Parameters:**
398
342
 
399
343
  - `collection`: The collection to filter (array of items)
400
- - `attrName`: The attribute name to check (string)
344
+ - `attrName`: The attribute name to check (string, supports dot notation for nested properties)
401
345
  - `targetValue`: The value to match against (any type)
402
346
 
403
347
  **Features:**
404
348
 
405
349
  - Works with any attribute in front matter
406
- - Handles both `item.data.attrName` and `item.attrName` patterns
350
+ - Supports dot notation for nested properties (e.g., `'data.tags'`, `'data.author.name'`)
407
351
  - Special handling for array attributes (uses `includes()` check)
408
352
  - Returns empty array if collection is invalid
409
353
  - Filters out items without the specified attribute
@@ -411,6 +355,7 @@ export default function(eleventyConfig) {
411
355
  **Examples:**
412
356
 
413
357
  Front matter:
358
+
414
359
  ```yaml
415
360
  ---
416
361
  title: My Post
@@ -421,18 +366,337 @@ priority: 1
421
366
  ```
422
367
 
423
368
  Template usage:
369
+
424
370
  ```njk
425
- {# Filter by category #}
426
- {% set blogPosts = collections.all | byAttr('category', 'blog') %}
371
+ {# Filter by category (using dot notation for nested properties) #}
372
+ {% set blogPosts = collections.all | where_in('data.category', 'blog') %}
427
373
 
428
374
  {# Filter by tag (array) #}
429
- {% set jsTutorials = collections.all | byAttr('tags', 'javascript') %}
375
+ {% set jsTutorials = collections.all | where_in('data.tags', 'javascript') %}
430
376
 
431
377
  {# Filter by numeric value #}
432
- {% set highPriority = collections.all | byAttr('priority', 1) %}
378
+ {% set highPriority = collections.all | where_in('data.priority', 1) %}
433
379
 
434
380
  {# Chain filters #}
435
- {% set recentBlogPosts = collections.all | byAttr('category', 'blog') | reverse | limit(5) %}
381
+ {% set recentBlogPosts = collections.all | where_in('data.category', 'blog') | reverse | limit(5) %}
382
+ ```
383
+
384
+ ### merge
385
+
386
+ 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.
387
+
388
+ **Why use this?**
389
+
390
+ 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.
391
+
392
+ **Usage:**
393
+
394
+ 1. Enable the `merge` filter in your Eleventy config:
395
+
396
+ ```javascript
397
+ import { mergeFilter } from "@anydigital/eleventy-bricks";
398
+
399
+ export default function (eleventyConfig) {
400
+ mergeFilter(eleventyConfig);
401
+ // Or use as plugin:
402
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['merge'] });
403
+ }
404
+ ```
405
+
406
+ 2. Use the filter in your templates:
407
+
408
+ **Merge arrays:**
409
+
410
+ ```njk
411
+ {# Combine two arrays #}
412
+ {% set allItems = featured | merge(regular) %}
413
+
414
+ {# Merge multiple arrays #}
415
+ {% set combined = array1 | merge(array2, array3, array4) %}
416
+
417
+ {% for item in allItems %}
418
+ <p>{{ item }}</p>
419
+ {% endfor %}
420
+ ```
421
+
422
+ **Merge objects:**
423
+
424
+ ```njk
425
+ {# Merge configuration objects #}
426
+ {% set defaultConfig = { theme: 'light', lang: 'en' } %}
427
+ {% set userConfig = { theme: 'dark' } %}
428
+ {% set finalConfig = defaultConfig | merge(userConfig) %}
429
+
430
+ {# Result: { theme: 'dark', lang: 'en' } #}
431
+ ```
432
+
433
+ **Parameters:**
434
+
435
+ - `first`: The first array or object (the base to merge into)
436
+ - `...rest`: One or more arrays or objects to merge in
437
+
438
+ **Features:**
439
+
440
+ - Works with both arrays and objects
441
+ - Supports merging multiple items at once
442
+ - Non-mutating: Creates new arrays/objects, leaving originals unchanged
443
+ - For objects: Later values override earlier ones (shallow merge)
444
+ - For arrays: Concatenates all arrays together
445
+ - Handles null/undefined gracefully
446
+
447
+ **Examples:**
448
+
449
+ ```njk
450
+ {# Combine featured and regular posts #}
451
+ {% set featuredPosts = collections.all | where_in('data.featured', true) %}
452
+ {% set regularPosts = collections.all | where_in('data.featured', false) %}
453
+ {% set allPosts = featuredPosts | merge(regularPosts) %}
454
+
455
+ {# Merge page metadata with defaults #}
456
+ {% set defaultMeta = {
457
+ author: 'Site Admin',
458
+ category: 'general',
459
+ comments: false
460
+ } %}
461
+ {% set pageMeta = defaultMeta | merge(page.data) %}
462
+
463
+ {# Combine arrays of tags #}
464
+ {% set commonTags = ['javascript', 'html', 'css'] %}
465
+ {% set specialTags = page.data.tags or [] %}
466
+ {% set allTags = commonTags | merge(specialTags) %}
467
+
468
+ {# Merge multiple configuration sources #}
469
+ {% set config = defaults | merge(siteConfig, pageConfig, userPrefs) %}
470
+ ```
471
+
472
+ ### remove_tag
473
+
474
+ A filter that removes a specified HTML element from provided HTML content. It removes the tag along with its content, including self-closing tags.
475
+
476
+ **Why use this?**
477
+
478
+ 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 `remove_tag` filter provides a simple way to remove unwanted tags like `<script>`, `<style>`, or any other HTML elements from your content.
479
+
480
+ **Usage:**
481
+
482
+ 1. Enable the `remove_tag` filter in your Eleventy config:
483
+
484
+ ```javascript
485
+ import { removeTagFilter } from "@anydigital/eleventy-bricks";
486
+
487
+ export default function (eleventyConfig) {
488
+ removeTagFilter(eleventyConfig);
489
+ // Or use as plugin:
490
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['remove_tag'] });
491
+ }
492
+ ```
493
+
494
+ 2. Use the filter in your templates:
495
+
496
+ ```njk
497
+ {# Remove all script tags from content #}
498
+ {% set cleanContent = htmlContent | remove_tag('script') %}
499
+
500
+ {{ cleanContent | safe }}
501
+ ```
502
+
503
+ **Parameters:**
504
+
505
+ - `html`: The HTML content to process (string)
506
+ - `tagName`: The tag name to remove (string)
507
+
508
+ **Features:**
509
+
510
+ - Removes both opening and closing tags along with their content
511
+ - Handles self-closing tags (e.g., `<br />`, `<img />`)
512
+ - Handles tags with attributes
513
+ - Case-insensitive matching
514
+ - Non-destructive: Returns new string, doesn't modify original
515
+
516
+ **Examples:**
517
+
518
+ ```njk
519
+ {# Remove scripts from user-generated content #}
520
+ {% set userContent = '<p>Hello</p><script>alert("XSS")</script><p>World</p>' %}
521
+ {% set safeContent = userContent | remove_tag('script') %}
522
+ {# Result: '<p>Hello</p><p>World</p>' #}
523
+
524
+ {# Strip specific formatting tags #}
525
+ {% set formatted = '<div><strong>Bold</strong> and <em>italic</em> text</div>' %}
526
+ {% set noStrong = formatted | remove_tag('strong') %}
527
+ {# Result: '<div>Bold and <em>italic</em> text</div>' #}
528
+
529
+ {# Chain multiple remove_tag filters for multiple tags #}
530
+ {% set richContent = page.content %}
531
+ {% set stripped = richContent
532
+ | remove_tag('script')
533
+ | remove_tag('style')
534
+ | remove_tag('iframe')
535
+ %}
536
+
537
+ {# Remove images for text-only preview #}
538
+ {% set textOnly = htmlContent | remove_tag('img') %}
539
+ ```
540
+
541
+ **Security Note:**
542
+
543
+ 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.
544
+
545
+ ### if
546
+
547
+ An inline conditional/ternary operator filter that returns one value if a condition is truthy, and another if it's falsy. Similar to Nunjucks' inline if syntax.
548
+
549
+ **Why use this?**
550
+
551
+ When you need simple conditional values in templates without verbose if/else blocks, the `if` filter provides a clean inline solution. It's especially useful for class names, attributes, or displaying alternate text based on conditions.
552
+
553
+ **Usage:**
554
+
555
+ 1. Enable the `if` filter in your Eleventy config:
556
+
557
+ ```javascript
558
+ import { ifFilter } from "@anydigital/eleventy-bricks";
559
+
560
+ export default function (eleventyConfig) {
561
+ ifFilter(eleventyConfig);
562
+ // Or use as plugin:
563
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['if'] });
564
+ }
565
+ ```
566
+
567
+ 2. Use the filter in your templates:
568
+
569
+ ```njk
570
+ {# Basic usage #}
571
+ <div class="{{ 'active' | if: isActive, 'inactive' }}">Status</div>
572
+
573
+ {# Without falsy value (defaults to empty string) #}
574
+ <span class="{{ 'highlight' | if: shouldHighlight }}">Text</span>
575
+
576
+ {# With variable values #}
577
+ {% set status = 'Published' | if: post.published, 'Draft' %}
578
+ ```
579
+
580
+ **Parameters:**
581
+
582
+ - `trueValue`: The value to return if condition is truthy
583
+ - `condition`: The condition to evaluate
584
+ - `falseValue`: The value to return if condition is falsy (optional, defaults to empty string)
585
+
586
+ **Features:**
587
+
588
+ - Returns `trueValue` if condition is truthy, otherwise returns `falseValue`
589
+ - Treats empty objects `{}` as falsy
590
+ - Default `falseValue` is an empty string if not provided
591
+ - Works with any data type for values
592
+
593
+ **Examples:**
594
+
595
+ ```njk
596
+ {# Toggle CSS classes #}
597
+ <button class="{{ 'btn-primary' | if: isPrimary, 'btn-secondary' }}">
598
+ Click me
599
+ </button>
600
+
601
+ {# Display different text #}
602
+ <p>{{ 'Online' | if: user.isOnline, 'Offline' }}</p>
603
+
604
+ {# Use with boolean values #}
605
+ {% set isEnabled = true %}
606
+ <div>{{ 'Enabled' | if: isEnabled, 'Disabled' }}</div>
607
+
608
+ {# Conditional attribute values #}
609
+ <input type="checkbox" {{ 'checked' | if: isChecked }}>
610
+
611
+ {# With numeric values #}
612
+ <span class="{{ 'has-items' | if: items.length }}">
613
+ {{ items.length }} items
614
+ </span>
615
+
616
+ {# Chain with other filters #}
617
+ {% set cssClass = 'featured' | if: post.featured | upper %}
618
+ ```
619
+
620
+ ### attr_concat
621
+
622
+ A filter that concatenates values to an attribute array, returning a new object with the combined array. Useful for adding items to arrays like tags, classes, or other list-based attributes.
623
+
624
+ **Why use this?**
625
+
626
+ When working with objects that have array attributes (like tags), you often need to add additional values without mutating the original object. The `attr_concat` filter provides a clean way to combine existing array values with new ones, automatically handling duplicates.
627
+
628
+ **Usage:**
629
+
630
+ 1. Enable the `attr_concat` filter in your Eleventy config:
631
+
632
+ ```javascript
633
+ import { attrConcatFilter } from "@anydigital/eleventy-bricks";
634
+
635
+ export default function (eleventyConfig) {
636
+ attrConcatFilter(eleventyConfig);
637
+ // Or use as plugin:
638
+ // eleventyConfig.addPlugin(eleventyBricks, { filters: ['attr_concat'] });
639
+ }
640
+ ```
641
+
642
+ 2. Use the filter in your templates:
643
+
644
+ ```njk
645
+ {# Add tags to a post object #}
646
+ {% set enhancedPost = post | attr_concat('tags', ['featured', 'popular']) %}
647
+
648
+ {# Add a single value #}
649
+ {% set updatedPost = post | attr_concat('tags', 'important') %}
650
+
651
+ {# Add values from a JSON string #}
652
+ {% set modifiedPost = post | attr_concat('tags', '["new", "trending"]') %}
653
+ ```
654
+
655
+ **Parameters:**
656
+
657
+ - `obj`: The object to modify
658
+ - `attr`: The attribute name (must be an array or will be treated as one)
659
+ - `values`: Values to concatenate (can be an array, JSON string array, or single value)
660
+
661
+ **Returns:**
662
+
663
+ A new object with the specified attribute containing the combined unique array. The original object is not modified.
664
+
665
+ **Features:**
666
+
667
+ - Non-mutating: Creates a new object, leaving the original unchanged
668
+ - Automatically removes duplicates using Set
669
+ - Handles multiple input types: arrays, JSON string arrays, or single values
670
+ - Creates the attribute as an empty array if it doesn't exist
671
+ - Logs an error if the existing attribute is not an array
672
+
673
+ **Examples:**
674
+
675
+ ```njk
676
+ {# Add multiple tags #}
677
+ {% set post = { title: 'My Post', tags: ['javascript'] } %}
678
+ {% set enhancedPost = post | attr_concat('tags', ['tutorial', 'beginner']) %}
679
+ {# Result: { title: 'My Post', tags: ['javascript', 'tutorial', 'beginner'] } #}
680
+
681
+ {# Add single value #}
682
+ {% set updatedPost = post | attr_concat('tags', 'featured') %}
683
+ {# Result: { title: 'My Post', tags: ['javascript', 'tutorial', 'beginner', 'featured'] } #}
684
+
685
+ {# No duplicates #}
686
+ {% set deduped = post | attr_concat('tags', ['javascript', 'advanced']) %}
687
+ {# Result: Only 'advanced' is added, 'javascript' already exists #}
688
+
689
+ {# Chain multiple attr_concat filters #}
690
+ {% set finalPost = post
691
+ | attr_concat('tags', 'popular')
692
+ | attr_concat('categories', ['tech', 'programming'])
693
+ %}
694
+
695
+ {# Use in loops to enhance collection items #}
696
+ {% for item in collections.posts %}
697
+ {% set enhancedItem = item | attr_concat('data.tags', 'blog') %}
698
+ {# ... use enhancedItem ... #}
699
+ {% endfor %}
436
700
  ```
437
701
 
438
702
  ### siteData
@@ -450,7 +714,7 @@ Many websites need access to the current year (for copyright notices) and enviro
450
714
  ```javascript
451
715
  import { siteData } from "@anydigital/eleventy-bricks";
452
716
 
453
- export default function(eleventyConfig) {
717
+ export default function (eleventyConfig) {
454
718
  siteData(eleventyConfig);
455
719
  // Or use as plugin:
456
720
  // eleventyConfig.addPlugin(eleventyBricks, { siteData: true });
@@ -460,6 +724,7 @@ export default function(eleventyConfig) {
460
724
  2. Use the global data in your templates:
461
725
 
462
726
  **Current Year:**
727
+
463
728
  ```njk
464
729
  <footer>
465
730
  <p>&copy; {{ site.year }} Your Company Name. All rights reserved.</p>
@@ -467,8 +732,9 @@ export default function(eleventyConfig) {
467
732
  ```
468
733
 
469
734
  **Environment Check:**
735
+
470
736
  ```njk
471
- {% if site.isProd %}
737
+ {% if site.prod %}
472
738
  <!-- Production-only features -->
473
739
  <script async src="https://www.googletagmanager.com/gtag/js?id=GA_TRACKING_ID"></script>
474
740
  {% else %}
@@ -480,7 +746,7 @@ export default function(eleventyConfig) {
480
746
  **Available Data:**
481
747
 
482
748
  - `site.year`: The current year as a number (e.g., `2026`)
483
- - `site.isProd`: Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`)
749
+ - `site.prod`: Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`)
484
750
 
485
751
  **Features:**
486
752
 
@@ -496,12 +762,12 @@ export default function(eleventyConfig) {
496
762
  <p>Copyright &copy; {{ site.year }} My Site</p>
497
763
 
498
764
  {# Conditional loading of analytics #}
499
- {% if site.isProd %}
765
+ {% if site.prod %}
500
766
  <script src="/analytics.js"></script>
501
767
  {% endif %}
502
768
 
503
769
  {# Different behavior in dev vs prod #}
504
- {% if site.isProd %}
770
+ {% if site.prod %}
505
771
  <link rel="stylesheet" href="/css/styles.min.css">
506
772
  {% else %}
507
773
  <link rel="stylesheet" href="/css/styles.css">
@@ -511,10 +777,14 @@ export default function(eleventyConfig) {
511
777
 
512
778
  ### Additional Exports
513
779
 
514
- The plugin also exports the following for advanced usage:
780
+ The plugin also exports the following utility functions for advanced usage:
515
781
 
516
782
  - `transformAutoRaw(content)`: The transform function used by `mdAutoRawTags` preprocessor. Can be used programmatically to wrap Nunjucks syntax with raw tags.
517
783
  - `transformNl2br(content)`: The transform function used by `mdAutoNl2br` preprocessor. Can be used programmatically to convert `\n` sequences to `<br>` tags.
784
+ - `merge(first, ...rest)`: The core merge function used by the `merge` filter. Can be used programmatically to merge arrays or objects.
785
+ - `removeTag(html, tagName)`: The core function used by the `remove_tag` filter. Can be used programmatically to remove HTML tags from content.
786
+ - `iff(trueValue, condition, falseValue)`: The core conditional function used by the `if` filter. Can be used programmatically as a ternary operator.
787
+ - `attrConcat(obj, attr, values)`: The core function used by the `attr_concat` filter. Can be used programmatically to concatenate values to an attribute array.
518
788
 
519
789
  ## Starter Configuration Files
520
790
 
@@ -525,6 +795,7 @@ The package includes pre-configured starter files in `node_modules/@anydigital/e
525
795
  #### eleventy.config.js
526
796
 
527
797
  A fully-configured Eleventy config file with:
798
+
528
799
  - All eleventy-bricks plugins enabled
529
800
  - Eleventy Navigation plugin
530
801
  - Markdown-it with anchors
@@ -533,11 +804,13 @@ A fully-configured Eleventy config file with:
533
804
  - Symlink support for development
534
805
 
535
806
  **Required dependencies:**
807
+
536
808
  ```bash
537
809
  npm install @11ty/eleventy-navigation markdown-it markdown-it-anchor js-yaml minimist
538
810
  ```
539
811
 
540
812
  **Symlink to your project:**
813
+
541
814
  ```bash
542
815
  ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js eleventy.config.js
543
816
  ```
@@ -547,6 +820,7 @@ ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js eleventy.c
547
820
  A ready-to-use Sveltia CMS admin interface for content management.
548
821
 
549
822
  **Symlink to your project:**
823
+
550
824
  ```bash
551
825
  mkdir -p admin
552
826
  ln -s ../node_modules/@anydigital/eleventy-bricks/src/admin/index.html admin/index.html