@anydigital/11ty-bricks 1.0.0-alpha.5 → 1.0.0-alpha.7
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 +209 -49
- package/package.json +1 -1
- package/src/bricksRegistry.js +6 -6
- package/src/byAttr.js +35 -0
- package/src/byAttr.test.js +105 -0
- package/src/fragment.js +34 -0
- package/src/index.cjs +12 -0
- package/src/index.js +22 -4
- package/src/setAttr.js +17 -0
- package/src/bricks/_gtm.njk +0 -16
- package/src/bricks/_nav.njk +0 -10
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ import eleventyBricks from "@anydigital/11ty-bricks";
|
|
|
22
22
|
|
|
23
23
|
export default function(eleventyConfig) {
|
|
24
24
|
eleventyConfig.addPlugin(eleventyBricks, {
|
|
25
|
-
|
|
25
|
+
autoRawPreprocessor: true // Enable autoRaw preprocessor (default: false)
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
// Your other configuration...
|
|
@@ -35,7 +35,7 @@ const eleventyBricks = require("@anydigital/11ty-bricks");
|
|
|
35
35
|
|
|
36
36
|
module.exports = function(eleventyConfig) {
|
|
37
37
|
eleventyConfig.addPlugin(eleventyBricks, {
|
|
38
|
-
|
|
38
|
+
autoRawPreprocessor: true // Enable autoRaw preprocessor (default: false)
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
// Your other configuration...
|
|
@@ -76,14 +76,18 @@ When using the plugin (Option 1), you can configure which helpers to enable:
|
|
|
76
76
|
|
|
77
77
|
| Option | Type | Default | Description |
|
|
78
78
|
|--------|------|---------|-------------|
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
79
|
+
| `bricks` | boolean | `false` | Enable the bricks system for dependency management |
|
|
80
|
+
| `autoRawPreprocessor` | boolean | `false` | Enable the autoRaw preprocessor for Markdown files |
|
|
81
|
+
| `fragments` | boolean | `false` | Enable the fragment shortcode for including content from fragments |
|
|
82
|
+
| `setAttrFilter` | boolean | `false` | Enable the setAttr filter for overriding object attributes |
|
|
83
|
+
| `byAttrFilter` | boolean | `false` | Enable the byAttr filter for filtering collections by attribute values |
|
|
81
84
|
|
|
82
85
|
**Example:**
|
|
83
86
|
```javascript
|
|
84
87
|
eleventyConfig.addPlugin(eleventyBricks, {
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
bricks: true,
|
|
89
|
+
autoRawPreprocessor: true,
|
|
90
|
+
byAttrFilter: true
|
|
87
91
|
});
|
|
88
92
|
```
|
|
89
93
|
|
|
@@ -102,7 +106,7 @@ When building reusable components (bricks) in Eleventy, you often need to includ
|
|
|
102
106
|
|
|
103
107
|
**How it works:**
|
|
104
108
|
|
|
105
|
-
1. Use the `
|
|
109
|
+
1. Use the `bricksRegistry` shortcode in your base template to mark where dependencies should be injected
|
|
106
110
|
2. Use the `brick` shortcode to register and render brick components that declare their dependencies
|
|
107
111
|
3. The system automatically collects all dependencies and injects them when the page is built
|
|
108
112
|
|
|
@@ -120,13 +124,15 @@ export default function(eleventyConfig) {
|
|
|
120
124
|
}
|
|
121
125
|
```
|
|
122
126
|
|
|
123
|
-
2. Add the `
|
|
127
|
+
2. Add the `bricksDependencies` shortcode in your base template (typically in the `<head>` section):
|
|
124
128
|
|
|
125
129
|
```njk
|
|
126
130
|
<head>
|
|
127
131
|
<meta charset="UTF-8">
|
|
128
132
|
<title>My Site</title>
|
|
129
|
-
{%
|
|
133
|
+
{% bricksDependencies [
|
|
134
|
+
... (global dependencies can be set here) ...
|
|
135
|
+
] %}
|
|
130
136
|
<!-- Other head content -->
|
|
131
137
|
</head>
|
|
132
138
|
```
|
|
@@ -209,84 +215,238 @@ Use {{ variable }} to output variables.
|
|
|
209
215
|
|
|
210
216
|
Would try to process `{{ variable }}` as a template variable. With `autoRaw`, it displays exactly as written.
|
|
211
217
|
|
|
212
|
-
|
|
218
|
+
### fragment
|
|
213
219
|
|
|
214
|
-
|
|
220
|
+
A shortcode that includes content from fragment files stored in the `_fragments` directory. The content will be processed by the template engine.
|
|
215
221
|
|
|
216
|
-
|
|
222
|
+
**Why use this?**
|
|
223
|
+
|
|
224
|
+
Fragments allow you to organize reusable content snippets in a dedicated directory and include them in your templates. This is useful for:
|
|
225
|
+
- Reusable content blocks
|
|
226
|
+
- Shared template sections
|
|
227
|
+
- Component-like content organization
|
|
217
228
|
|
|
218
229
|
**Usage:**
|
|
219
230
|
|
|
220
|
-
1.
|
|
231
|
+
1. Enable `fragment` in your Eleventy config:
|
|
221
232
|
|
|
222
|
-
```
|
|
223
|
-
{
|
|
233
|
+
```javascript
|
|
234
|
+
import { fragment } from "@anydigital/11ty-bricks";
|
|
235
|
+
|
|
236
|
+
export default function(eleventyConfig) {
|
|
237
|
+
fragment(eleventyConfig);
|
|
238
|
+
// Or use as plugin:
|
|
239
|
+
// eleventyConfig.addPlugin(eleventyBricks, { fragments: true });
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
2. Create fragment files in the `_fragments` directory (relative to your input directory):
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
your-project/
|
|
247
|
+
_fragments/
|
|
248
|
+
header.njk
|
|
249
|
+
footer.njk
|
|
250
|
+
callout.md
|
|
224
251
|
```
|
|
225
252
|
|
|
226
|
-
|
|
253
|
+
3. Use the `fragment` shortcode in your templates:
|
|
227
254
|
|
|
228
255
|
```njk
|
|
229
|
-
{
|
|
256
|
+
{% fragment "header.njk" %}
|
|
257
|
+
|
|
258
|
+
<main>
|
|
259
|
+
<!-- Your content -->
|
|
260
|
+
</main>
|
|
261
|
+
|
|
262
|
+
{% fragment "footer.njk" %}
|
|
230
263
|
```
|
|
231
264
|
|
|
232
265
|
**Parameters:**
|
|
233
266
|
|
|
234
|
-
- `
|
|
235
|
-
- `curPage`: Current page object (use Eleventy's `page` variable)
|
|
267
|
+
- `path`: The path to the fragment file relative to the `_fragments` directory
|
|
236
268
|
|
|
237
269
|
**Features:**
|
|
238
270
|
|
|
239
|
-
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
242
|
-
-
|
|
271
|
+
- Reads files from `_fragments` directory in your input directory
|
|
272
|
+
- Content is processed by the template engine
|
|
273
|
+
- Supports any template language that Eleventy supports
|
|
274
|
+
- Shows helpful error comment if fragment is not found
|
|
275
|
+
|
|
276
|
+
**Example:**
|
|
243
277
|
|
|
244
|
-
|
|
278
|
+
Create `_fragments/callout.njk`:
|
|
279
|
+
```njk
|
|
280
|
+
<div class="callout callout-{{ type | default('info') }}">
|
|
281
|
+
{{ content }}
|
|
282
|
+
</div>
|
|
283
|
+
```
|
|
245
284
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
</nav>
|
|
285
|
+
Use it in your template:
|
|
286
|
+
```njk
|
|
287
|
+
{% set type = "warning" %}
|
|
288
|
+
{% set content = "This is important!" %}
|
|
289
|
+
{% fragment "callout.njk" %}
|
|
252
290
|
```
|
|
253
291
|
|
|
254
|
-
###
|
|
292
|
+
### setAttr
|
|
255
293
|
|
|
256
|
-
A
|
|
294
|
+
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.
|
|
295
|
+
|
|
296
|
+
**Why use this?**
|
|
297
|
+
|
|
298
|
+
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.
|
|
257
299
|
|
|
258
300
|
**Usage:**
|
|
259
301
|
|
|
260
|
-
1.
|
|
302
|
+
1. Enable `setAttr` in your Eleventy config:
|
|
303
|
+
|
|
304
|
+
```javascript
|
|
305
|
+
import { setAttr } from "@anydigital/11ty-bricks";
|
|
306
|
+
|
|
307
|
+
export default function(eleventyConfig) {
|
|
308
|
+
setAttr(eleventyConfig);
|
|
309
|
+
// Or use as plugin:
|
|
310
|
+
// eleventyConfig.addPlugin(eleventyBricks, { setAttrFilter: true });
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
2. Use the filter in your templates:
|
|
261
315
|
|
|
262
316
|
```njk
|
|
263
|
-
{
|
|
317
|
+
{# Create a modified version of a page object #}
|
|
318
|
+
{% set modifiedPage = page | setAttr('title', 'New Title') %}
|
|
319
|
+
|
|
320
|
+
<h1>{{ modifiedPage.title }}</h1>
|
|
321
|
+
<p>Original title: {{ page.title }}</p>
|
|
264
322
|
```
|
|
265
323
|
|
|
266
|
-
|
|
324
|
+
**Parameters:**
|
|
325
|
+
|
|
326
|
+
- `obj`: The object to modify
|
|
327
|
+
- `key`: The attribute name to set (string)
|
|
328
|
+
- `value`: The value to set for the attribute (any type)
|
|
329
|
+
|
|
330
|
+
**Returns:**
|
|
331
|
+
|
|
332
|
+
A new object with the specified attribute set to the given value. The original object is not modified.
|
|
333
|
+
|
|
334
|
+
**Features:**
|
|
335
|
+
|
|
336
|
+
- Non-mutating: Creates a new object, leaving the original unchanged
|
|
337
|
+
- Works with any object type
|
|
338
|
+
- Supports any attribute name and value type
|
|
339
|
+
- Can be chained with other filters
|
|
340
|
+
|
|
341
|
+
**Examples:**
|
|
267
342
|
|
|
268
343
|
```njk
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
344
|
+
{# Override a single attribute #}
|
|
345
|
+
{% set updatedPost = post | setAttr('featured', true) %}
|
|
346
|
+
|
|
347
|
+
{# Chain multiple setAttr filters #}
|
|
348
|
+
{% set modifiedPost = post
|
|
349
|
+
| setAttr('category', 'blog')
|
|
350
|
+
| setAttr('priority', 1)
|
|
351
|
+
%}
|
|
352
|
+
|
|
353
|
+
{# Use in loops #}
|
|
354
|
+
{% for item in collection %}
|
|
355
|
+
{% set enhancedItem = item | setAttr('processed', true) %}
|
|
356
|
+
{# ... use enhancedItem ... #}
|
|
357
|
+
{% endfor %}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### byAttr
|
|
361
|
+
|
|
362
|
+
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.
|
|
363
|
+
|
|
364
|
+
**Why use this?**
|
|
365
|
+
|
|
366
|
+
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).
|
|
367
|
+
|
|
368
|
+
**Usage:**
|
|
369
|
+
|
|
370
|
+
1. Enable `byAttr` in your Eleventy config:
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
import { byAttr } from "@anydigital/11ty-bricks";
|
|
374
|
+
|
|
375
|
+
export default function(eleventyConfig) {
|
|
376
|
+
byAttr(eleventyConfig);
|
|
377
|
+
// Or use as plugin:
|
|
378
|
+
// eleventyConfig.addPlugin(eleventyBricks, { byAttrFilter: true });
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
2. Use the filter in your templates:
|
|
383
|
+
|
|
384
|
+
**Filter by exact attribute match:**
|
|
385
|
+
```njk
|
|
386
|
+
{# Get all posts with category 'blog' #}
|
|
387
|
+
{% set blogPosts = collections.all | byAttr('category', 'blog') %}
|
|
388
|
+
|
|
389
|
+
{% for post in blogPosts %}
|
|
390
|
+
<h2>{{ post.data.title }}</h2>
|
|
391
|
+
{% endfor %}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**Filter by array attribute (tags):**
|
|
395
|
+
```njk
|
|
396
|
+
{# Get all posts that include 'javascript' tag #}
|
|
397
|
+
{% set jsPosts = collections.all | byAttr('tags', 'javascript') %}
|
|
398
|
+
|
|
399
|
+
{% for post in jsPosts %}
|
|
400
|
+
<h2>{{ post.data.title }}</h2>
|
|
401
|
+
{% endfor %}
|
|
277
402
|
```
|
|
278
403
|
|
|
279
404
|
**Parameters:**
|
|
280
405
|
|
|
281
|
-
|
|
282
|
-
- `
|
|
406
|
+
- `collection`: The collection to filter (array of items)
|
|
407
|
+
- `attrName`: The attribute name to check (string)
|
|
408
|
+
- `targetValue`: The value to match against (any type)
|
|
283
409
|
|
|
284
410
|
**Features:**
|
|
285
411
|
|
|
286
|
-
-
|
|
287
|
-
-
|
|
288
|
-
-
|
|
289
|
-
-
|
|
412
|
+
- Works with any attribute in front matter
|
|
413
|
+
- Handles both `item.data.attrName` and `item.attrName` patterns
|
|
414
|
+
- Special handling for array attributes (uses `includes()` check)
|
|
415
|
+
- Returns empty array if collection is invalid
|
|
416
|
+
- Filters out items without the specified attribute
|
|
417
|
+
|
|
418
|
+
**Examples:**
|
|
419
|
+
|
|
420
|
+
Front matter:
|
|
421
|
+
```yaml
|
|
422
|
+
---
|
|
423
|
+
title: My Post
|
|
424
|
+
category: blog
|
|
425
|
+
tags: [javascript, tutorial, beginner]
|
|
426
|
+
priority: 1
|
|
427
|
+
---
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Template usage:
|
|
431
|
+
```njk
|
|
432
|
+
{# Filter by category #}
|
|
433
|
+
{% set blogPosts = collections.all | byAttr('category', 'blog') %}
|
|
434
|
+
|
|
435
|
+
{# Filter by tag (array) #}
|
|
436
|
+
{% set jsTutorials = collections.all | byAttr('tags', 'javascript') %}
|
|
437
|
+
|
|
438
|
+
{# Filter by numeric value #}
|
|
439
|
+
{% set highPriority = collections.all | byAttr('priority', 1) %}
|
|
440
|
+
|
|
441
|
+
{# Chain filters #}
|
|
442
|
+
{% set recentBlogPosts = collections.all | byAttr('category', 'blog') | reverse | limit(5) %}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Additional Exports
|
|
446
|
+
|
|
447
|
+
The plugin also exports the following for advanced usage:
|
|
448
|
+
|
|
449
|
+
- `transformAutoRaw(content)`: The transform function used by `autoRaw` preprocessor. Can be used programmatically to wrap Nunjucks syntax with raw tags.
|
|
290
450
|
|
|
291
451
|
## CLI Helper Commands
|
|
292
452
|
|
package/package.json
CHANGED
package/src/bricksRegistry.js
CHANGED
|
@@ -23,7 +23,7 @@ export function bricksRegistry(eleventyConfig) {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
// brick shortcode: registers and renders a brick component
|
|
26
|
-
eleventyConfig.addShortcode("brick", function(brickModule) {
|
|
26
|
+
eleventyConfig.addShortcode("brick", function(brickModule, ...args) {
|
|
27
27
|
const registry = getPageRegistry(this.page);
|
|
28
28
|
|
|
29
29
|
if (!brickModule) return '';
|
|
@@ -47,19 +47,19 @@ export function bricksRegistry(eleventyConfig) {
|
|
|
47
47
|
|
|
48
48
|
// Render the brick using render() macro
|
|
49
49
|
if (brickModule.render && typeof brickModule.render === 'function') {
|
|
50
|
-
return brickModule.render();
|
|
50
|
+
return brickModule.render(...args);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
return '';
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
//
|
|
57
|
-
eleventyConfig.addShortcode("
|
|
56
|
+
// bricksRegistry shortcode: outputs placeholder and base dependencies
|
|
57
|
+
eleventyConfig.addShortcode("bricksDependencies", function(dependencies = []) {
|
|
58
58
|
const registry = getPageRegistry(this.page);
|
|
59
59
|
|
|
60
60
|
// Register root dependencies if provided (categorized later in transform)
|
|
61
|
-
if (
|
|
62
|
-
|
|
61
|
+
if (dependencies && Array.isArray(dependencies)) {
|
|
62
|
+
dependencies.forEach(dep => {
|
|
63
63
|
registry.dependencies.add(dep);
|
|
64
64
|
});
|
|
65
65
|
}
|
package/src/byAttr.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* byAttr filter - Filter collection items by attribute value
|
|
3
|
+
*
|
|
4
|
+
* This filter takes a collection, an attribute name, and a target value,
|
|
5
|
+
* and returns items where the attribute matches the target value.
|
|
6
|
+
* If the attribute is an array, it checks if the array includes the target value.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
9
|
+
*/
|
|
10
|
+
export function byAttr(eleventyConfig) {
|
|
11
|
+
eleventyConfig.addFilter("byAttr", function(collection, attrName, targetValue) {
|
|
12
|
+
if (!collection || !Array.isArray(collection)) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return collection.filter(item => {
|
|
17
|
+
// Get the attribute value from the item's data
|
|
18
|
+
const attrValue = item?.data?.[attrName] ?? item?.[attrName];
|
|
19
|
+
|
|
20
|
+
// If attribute doesn't exist, skip this item
|
|
21
|
+
if (attrValue === undefined || attrValue === null) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// If the attribute is an array, check if it includes the target value
|
|
26
|
+
if (Array.isArray(attrValue)) {
|
|
27
|
+
return attrValue.includes(targetValue);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Otherwise, do a direct comparison
|
|
31
|
+
return attrValue === targetValue;
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { byAttr } from './byAttr.js';
|
|
4
|
+
|
|
5
|
+
describe('byAttr filter', () => {
|
|
6
|
+
let filterFn;
|
|
7
|
+
|
|
8
|
+
// Mock eleventyConfig to capture the filter function
|
|
9
|
+
const mockEleventyConfig = {
|
|
10
|
+
addFilter(name, fn) {
|
|
11
|
+
if (name === 'byAttr') {
|
|
12
|
+
filterFn = fn;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Register the filter
|
|
18
|
+
byAttr(mockEleventyConfig);
|
|
19
|
+
|
|
20
|
+
it('should filter items by exact attribute match', () => {
|
|
21
|
+
const collection = [
|
|
22
|
+
{ data: { category: 'blog' }, title: 'Post 1' },
|
|
23
|
+
{ data: { category: 'news' }, title: 'Post 2' },
|
|
24
|
+
{ data: { category: 'blog' }, title: 'Post 3' }
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const result = filterFn(collection, 'category', 'blog');
|
|
28
|
+
assert.strictEqual(result.length, 2);
|
|
29
|
+
assert.strictEqual(result[0].title, 'Post 1');
|
|
30
|
+
assert.strictEqual(result[1].title, 'Post 3');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should filter items when attribute is an array (includes check)', () => {
|
|
34
|
+
const collection = [
|
|
35
|
+
{ data: { tags: ['javascript', 'tutorial'] }, title: 'Post 1' },
|
|
36
|
+
{ data: { tags: ['python', 'tutorial'] }, title: 'Post 2' },
|
|
37
|
+
{ data: { tags: ['javascript', 'advanced'] }, title: 'Post 3' }
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const result = filterFn(collection, 'tags', 'javascript');
|
|
41
|
+
assert.strictEqual(result.length, 2);
|
|
42
|
+
assert.strictEqual(result[0].title, 'Post 1');
|
|
43
|
+
assert.strictEqual(result[1].title, 'Post 3');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return empty array when collection is not an array', () => {
|
|
47
|
+
const result = filterFn(null, 'category', 'blog');
|
|
48
|
+
assert.strictEqual(result.length, 0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should filter out items without the specified attribute', () => {
|
|
52
|
+
const collection = [
|
|
53
|
+
{ data: { category: 'blog' }, title: 'Post 1' },
|
|
54
|
+
{ data: {}, title: 'Post 2' },
|
|
55
|
+
{ data: { category: 'blog' }, title: 'Post 3' }
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const result = filterFn(collection, 'category', 'blog');
|
|
59
|
+
assert.strictEqual(result.length, 2);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should work with attribute directly on item (not in data)', () => {
|
|
63
|
+
const collection = [
|
|
64
|
+
{ category: 'blog', title: 'Post 1' },
|
|
65
|
+
{ category: 'news', title: 'Post 2' },
|
|
66
|
+
{ category: 'blog', title: 'Post 3' }
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const result = filterFn(collection, 'category', 'blog');
|
|
70
|
+
assert.strictEqual(result.length, 2);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle mixed data structures', () => {
|
|
74
|
+
const collection = [
|
|
75
|
+
{ data: { category: 'blog' }, title: 'Post 1' },
|
|
76
|
+
{ category: 'blog', title: 'Post 2' },
|
|
77
|
+
{ data: { category: 'news' }, title: 'Post 3' }
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const result = filterFn(collection, 'category', 'blog');
|
|
81
|
+
assert.strictEqual(result.length, 2);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should handle array that does not include target value', () => {
|
|
85
|
+
const collection = [
|
|
86
|
+
{ data: { tags: ['python', 'tutorial'] }, title: 'Post 1' },
|
|
87
|
+
{ data: { tags: ['ruby', 'guide'] }, title: 'Post 2' }
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
const result = filterFn(collection, 'tags', 'javascript');
|
|
91
|
+
assert.strictEqual(result.length, 0);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should handle different value types', () => {
|
|
95
|
+
const collection = [
|
|
96
|
+
{ data: { priority: 1 }, title: 'Post 1' },
|
|
97
|
+
{ data: { priority: 2 }, title: 'Post 2' },
|
|
98
|
+
{ data: { priority: 1 }, title: 'Post 3' }
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
const result = filterFn(collection, 'priority', 1);
|
|
102
|
+
assert.strictEqual(result.length, 2);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
package/src/fragment.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* fragment shortcode - Include content from fragments
|
|
6
|
+
*
|
|
7
|
+
* This shortcode reads a file from the _fragments directory and includes
|
|
8
|
+
* its content. The content will be processed by the template engine.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
11
|
+
*/
|
|
12
|
+
export function fragment(eleventyConfig) {
|
|
13
|
+
eleventyConfig.addShortcode("fragment", function(path) {
|
|
14
|
+
// Get the input directory from Eleventy's context
|
|
15
|
+
const inputDir = this.page?.inputPath
|
|
16
|
+
? join(process.cwd(), eleventyConfig.dir?.input || ".")
|
|
17
|
+
: process.cwd();
|
|
18
|
+
|
|
19
|
+
// Construct the full path to the fragment file
|
|
20
|
+
const fragmentPath = join(inputDir, "_fragments", path);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Read the fragment file
|
|
24
|
+
const content = readFileSync(fragmentPath, "utf8");
|
|
25
|
+
|
|
26
|
+
// Return content to be processed by the template engine
|
|
27
|
+
return content;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(`Error reading fragment at ${fragmentPath}:`, error.message);
|
|
30
|
+
return `<!-- Fragment not found: ${path} -->`;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
package/src/index.cjs
CHANGED
|
@@ -18,3 +18,15 @@ module.exports.autoRaw = async function(eleventyConfig) {
|
|
|
18
18
|
const { autoRaw } = await import('./index.js');
|
|
19
19
|
return autoRaw(eleventyConfig);
|
|
20
20
|
};
|
|
21
|
+
module.exports.fragment = async function(eleventyConfig) {
|
|
22
|
+
const { fragment } = await import('./index.js');
|
|
23
|
+
return fragment(eleventyConfig);
|
|
24
|
+
};
|
|
25
|
+
module.exports.setAttr = async function(eleventyConfig) {
|
|
26
|
+
const { setAttr } = await import('./index.js');
|
|
27
|
+
return setAttr(eleventyConfig);
|
|
28
|
+
};
|
|
29
|
+
module.exports.byAttr = async function(eleventyConfig) {
|
|
30
|
+
const { byAttr } = await import('./index.js');
|
|
31
|
+
return byAttr(eleventyConfig);
|
|
32
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { bricksRegistry } from "./bricksRegistry.js";
|
|
2
2
|
import { autoRaw } from "./autoRaw.js";
|
|
3
|
+
import { fragment } from "./fragment.js";
|
|
4
|
+
import { setAttr } from "./setAttr.js";
|
|
5
|
+
import { byAttr } from "./byAttr.js";
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* 11ty Bricks Plugin
|
|
@@ -9,18 +12,33 @@ import { autoRaw } from "./autoRaw.js";
|
|
|
9
12
|
*
|
|
10
13
|
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
11
14
|
* @param {Object} options - Plugin options
|
|
12
|
-
* @param {boolean} options.
|
|
13
|
-
* @param {boolean} options.
|
|
15
|
+
* @param {boolean} options.bricks - Enable bricks system with dependencies injection (default: false)
|
|
16
|
+
* @param {boolean} options.autoRawPreprocessor - Enable autoRaw preprocessor (default: false)
|
|
17
|
+
* @param {boolean} options.fragments - Enable fragment shortcode (default: false)
|
|
18
|
+
* @param {boolean} options.setAttrFilter - Enable setAttr filter (default: false)
|
|
19
|
+
* @param {boolean} options.byAttrFilter - Enable byAttr filter (default: false)
|
|
14
20
|
*/
|
|
15
21
|
export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
|
|
16
|
-
if (options.
|
|
22
|
+
if (options.bricks) {
|
|
17
23
|
bricksRegistry(eleventyConfig);
|
|
18
24
|
}
|
|
19
|
-
if (options.
|
|
25
|
+
if (options.autoRawPreprocessor) {
|
|
20
26
|
autoRaw(eleventyConfig);
|
|
21
27
|
}
|
|
28
|
+
if (options.fragments) {
|
|
29
|
+
fragment(eleventyConfig);
|
|
30
|
+
}
|
|
31
|
+
if (options.setAttrFilter) {
|
|
32
|
+
setAttr(eleventyConfig);
|
|
33
|
+
}
|
|
34
|
+
if (options.byAttrFilter) {
|
|
35
|
+
byAttr(eleventyConfig);
|
|
36
|
+
}
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
// Export individual helpers for granular usage
|
|
25
40
|
export { bricksRegistry };
|
|
26
41
|
export { autoRaw };
|
|
42
|
+
export { fragment };
|
|
43
|
+
export { setAttr };
|
|
44
|
+
export { byAttr };
|
package/src/setAttr.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* setAttr filter - Override an attribute and return the object
|
|
3
|
+
*
|
|
4
|
+
* This filter takes an object, a key, and a value, and returns a new object
|
|
5
|
+
* with the specified attribute set to the given value.
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
8
|
+
*/
|
|
9
|
+
export function setAttr(eleventyConfig) {
|
|
10
|
+
eleventyConfig.addFilter("setAttr", function(obj, key, value) {
|
|
11
|
+
return {
|
|
12
|
+
...obj,
|
|
13
|
+
[key]: value
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
package/src/bricks/_gtm.njk
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{% macro renderHead(gtmId) %}
|
|
2
|
-
<!-- Google Tag Manager -->
|
|
3
|
-
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
|
4
|
-
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
|
5
|
-
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
|
6
|
-
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
|
7
|
-
})(window,document,'script','dataLayer','{{ gtmId }}');</script>
|
|
8
|
-
<!-- End Google Tag Manager -->
|
|
9
|
-
{% endmacro %}
|
|
10
|
-
|
|
11
|
-
{% macro renderBody(gtmId) %}
|
|
12
|
-
<!-- Google Tag Manager (noscript) -->
|
|
13
|
-
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ gtmId }}"
|
|
14
|
-
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
|
15
|
-
<!-- End Google Tag Manager (noscript) -->
|
|
16
|
-
{% endmacro %}
|
package/src/bricks/_nav.njk
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{% macro render(navPages, curPage) %}
|
|
2
|
-
{# https://www.11ty.dev/docs/plugins/navigation/#bring-your-own-html-render-the-menu-items-manually #}
|
|
3
|
-
<nav>
|
|
4
|
-
{%- for entry in navPages %}
|
|
5
|
-
<a href="{{ entry.url }}" {{ 'aria-current="page"' | safe if entry.url == curPage.url }}>
|
|
6
|
-
{{- entry.title -}}
|
|
7
|
-
</a>
|
|
8
|
-
{%- endfor %}
|
|
9
|
-
</nav>
|
|
10
|
-
{% endmacro %}
|