@anydigital/eleventy-bricks 0.26.0 → 0.27.1
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 +314 -241
- package/package.json +1 -1
- package/src/eleventy.config.js +1 -1
- package/src/filters/section.js +62 -0
- package/src/filters/section.test.js +174 -0
- package/src/index.js +7 -3
- /package/src/{transforms → processors}/autoLinkFavicons.js +0 -0
- /package/src/{transforms → processors}/autoLinkFavicons.test.js +0 -0
- /package/src/{transforms → processors}/markdown.js +0 -0
- /package/src/{transforms → processors}/markdown.test.js +0 -0
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ export default function (eleventyConfig) {
|
|
|
27
27
|
mdAutoNl2br: true,
|
|
28
28
|
autoLinkFavicons: true,
|
|
29
29
|
siteData: true,
|
|
30
|
-
filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "fetch"],
|
|
30
|
+
filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "section", "fetch"],
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
// Your other configuration...
|
|
@@ -45,7 +45,7 @@ module.exports = function (eleventyConfig) {
|
|
|
45
45
|
mdAutoNl2br: true,
|
|
46
46
|
autoLinkFavicons: true,
|
|
47
47
|
siteData: true,
|
|
48
|
-
filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "fetch"],
|
|
48
|
+
filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "section", "fetch"],
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
// Your other configuration...
|
|
@@ -71,6 +71,7 @@ import {
|
|
|
71
71
|
removeTagFilter,
|
|
72
72
|
ifFilter,
|
|
73
73
|
attrConcatFilter,
|
|
74
|
+
sectionFilter,
|
|
74
75
|
fetchFilter,
|
|
75
76
|
siteData,
|
|
76
77
|
} from "@anydigital/eleventy-bricks";
|
|
@@ -85,6 +86,7 @@ export default function (eleventyConfig) {
|
|
|
85
86
|
removeTagFilter(eleventyConfig);
|
|
86
87
|
ifFilter(eleventyConfig);
|
|
87
88
|
attrConcatFilter(eleventyConfig);
|
|
89
|
+
sectionFilter(eleventyConfig);
|
|
88
90
|
// fetchFilter is only available if @11ty/eleventy-fetch is installed
|
|
89
91
|
if (fetchFilter) {
|
|
90
92
|
fetchFilter(eleventyConfig);
|
|
@@ -108,6 +110,7 @@ const {
|
|
|
108
110
|
removeTagFilter,
|
|
109
111
|
ifFilter,
|
|
110
112
|
attrConcatFilter,
|
|
113
|
+
sectionFilter,
|
|
111
114
|
fetchFilter,
|
|
112
115
|
siteData,
|
|
113
116
|
} = require("@anydigital/eleventy-bricks");
|
|
@@ -122,6 +125,7 @@ module.exports = async function (eleventyConfig) {
|
|
|
122
125
|
await removeTagFilter(eleventyConfig);
|
|
123
126
|
await ifFilter(eleventyConfig);
|
|
124
127
|
await attrConcatFilter(eleventyConfig);
|
|
128
|
+
await sectionFilter(eleventyConfig);
|
|
125
129
|
// fetchFilter is only available if @11ty/eleventy-fetch is installed
|
|
126
130
|
if (fetchFilter) {
|
|
127
131
|
await fetchFilter(eleventyConfig);
|
|
@@ -154,6 +158,7 @@ When using the plugin (Option 1), you can configure which helpers to enable:
|
|
|
154
158
|
- `'remove_tag'` - Remove HTML elements from content
|
|
155
159
|
- `'if'` - Inline conditional/ternary operator
|
|
156
160
|
- `'attr_concat'` - Concatenate values to an attribute array
|
|
161
|
+
- `'section'` - Extract named sections from content marked with HTML comments
|
|
157
162
|
- `'fetch'` - Fetch remote URLs or local files (requires `@11ty/eleventy-fetch`)
|
|
158
163
|
|
|
159
164
|
**Example:**
|
|
@@ -164,7 +169,7 @@ eleventyConfig.addPlugin(eleventyBricks, {
|
|
|
164
169
|
mdAutoNl2br: true,
|
|
165
170
|
autoLinkFavicons: true,
|
|
166
171
|
siteData: true,
|
|
167
|
-
filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "fetch"],
|
|
172
|
+
filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "section", "fetch"],
|
|
168
173
|
});
|
|
169
174
|
```
|
|
170
175
|
|
|
@@ -172,50 +177,40 @@ eleventyConfig.addPlugin(eleventyBricks, {
|
|
|
172
177
|
|
|
173
178
|
The plugin also exports the following utility functions for advanced usage:
|
|
174
179
|
|
|
175
|
-
- `transformAutoRaw(content)`: The
|
|
176
|
-
- `transformNl2br(content)`: The
|
|
180
|
+
- `transformAutoRaw(content)`: The processor function used by `mdAutoRawTags` preprocessor. Can be used programmatically to wrap Nunjucks syntax with raw tags.
|
|
181
|
+
- `transformNl2br(content)`: The processor function used by `mdAutoNl2br` preprocessor. Can be used programmatically to convert `\\n` sequences to `\u003cbr\u003e` tags.
|
|
177
182
|
- `isPlainUrlText(linkText, domain)`: Helper function that checks if link text looks like a plain URL or domain.
|
|
178
183
|
- `cleanLinkText(linkText, domain)`: Helper function that cleans link text by removing protocol, domain, and leading slash.
|
|
179
184
|
- `buildFaviconLink(attrs, domain, text)`: Helper function that builds HTML for a link with favicon.
|
|
180
|
-
- `transformLink(match, attrs, url, linkText)`: The
|
|
181
|
-
- `replaceLinksInHtml(content,
|
|
185
|
+
- `transformLink(match, attrs, url, linkText)`: The processor function used by `autoLinkFavicons` that processes a single link to include a favicon.
|
|
186
|
+
- `replaceLinksInHtml(content, processor)`: Helper function that replaces all anchor links in HTML content with processed versions.
|
|
182
187
|
- `attrIncludes(collection, attrName, targetValue)`: The core logic for filtering collection items by checking if an attribute array includes a target value. Can be used programmatically to filter collections.
|
|
183
188
|
- `merge(first, ...rest)`: The core merge function used by the `merge` filter. Can be used programmatically to merge arrays or objects.
|
|
184
189
|
- `removeTag(html, tagName)`: The core function used by the `remove_tag` filter. Can be used programmatically to remove HTML tags from content.
|
|
185
190
|
- `iff(trueValue, condition, falseValue)`: The core conditional function used by the `if` filter. Can be used programmatically as a ternary operator.
|
|
186
191
|
- `attrConcat(obj, attr, values)`: The core function used by the `attr_concat` filter. Can be used programmatically to concatenate values to an attribute array.
|
|
187
192
|
- `attrSet(obj, key, value)`: The core function used by the `attr_set` filter. Can be used programmatically to override object attributes.
|
|
193
|
+
- `section(content, sectionName)`: The core function used by the `section` filter. Can be used programmatically to extract named sections from content.
|
|
188
194
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
###
|
|
194
|
-
|
|
195
|
-
| Input |
|
|
196
|
-
| ---------: |
|
|
197
|
-
| {.divider} | Logical
|
|
198
|
-
|
|
|
199
|
-
|
|
|
200
|
-
|
|
|
201
|
-
| `OBJ \|` | [`
|
|
202
|
-
|
|
|
203
|
-
| `OBJ \|` | `
|
|
204
|
-
|
|
|
205
|
-
| `
|
|
206
|
-
|
|
|
207
|
-
|
|
|
208
|
-
| {.divider} | Textual |
|
|
209
|
-
| `HTML \|` | `striptags` | `strip_html` |
|
|
210
|
-
| `HTML \|` | [`remove_tag(TAG)`](#remove_tag) | [`remove_tag: TAG`](#remove_tag) |
|
|
211
|
-
| `STR \|` | `remove: STR2` | `remove: STR2` |
|
|
212
|
-
| {.divider} | Other |
|
|
213
|
-
| `URL \|` | [`fetch`](#fetch) | [`fetch`](#fetch) |
|
|
214
|
-
|
|
215
|
-
Ref:
|
|
216
|
-
|
|
217
|
-
- https://mozilla.github.io/nunjucks/templating.html#builtin-filters
|
|
218
|
-
- https://shopify.github.io/liquid/
|
|
195
|
+
## Features
|
|
196
|
+
|
|
197
|
+
<!--section:filters-h3-->
|
|
198
|
+
|
|
199
|
+
### Universal 11ty filters <small>for `.njk` & `.liquid`</small> <sub>by https://github.com/anydigital/eleventy-bricks</sub>
|
|
200
|
+
|
|
201
|
+
| Input | Filter | Arguments |
|
|
202
|
+
| ---------: | --------------------------------- | -------------------------------------------------- |
|
|
203
|
+
| {.divider} | Logical filters: |
|
|
204
|
+
| `ANY \|` | [`if`](#if) | `TEST, OP, VALUE` <sub>currently only `TEST`</sub> |
|
|
205
|
+
| {.divider} | Filters for objects: |
|
|
206
|
+
| `OBJ \|` | [`merge`](#merge) | `OBJ2` |
|
|
207
|
+
| `OBJ \|` | [`attr_set`](#attr_set) | `ATTR, VALUE` |
|
|
208
|
+
| `OBJ \|` | [`attr_concat`](#attr_concat) | `ATTR, ARRAY2` |
|
|
209
|
+
| `OBJ \|` | [`attr_includes`](#attr_includes) | `ATTR, VALUE` |
|
|
210
|
+
| {.divider} | Other filters: |
|
|
211
|
+
| `URL \|` | [`fetch`](#fetch) | |
|
|
212
|
+
| `HTML \|` | [`section`](#section) | `NAME` |
|
|
213
|
+
| `HTML \|` | [`remove_tag`](#remove_tag) | `TAG` |
|
|
219
214
|
|
|
220
215
|
#### `attr_set`
|
|
221
216
|
|
|
@@ -520,6 +515,103 @@ export default function (eleventyConfig) {
|
|
|
520
515
|
|
|
521
516
|
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.
|
|
522
517
|
|
|
518
|
+
#### `section`
|
|
519
|
+
|
|
520
|
+
A filter that extracts a named section from content marked with HTML comments. This is useful for splitting a single content file (like a Markdown post) into multiple parts that can be displayed and styled independently in your templates.
|
|
521
|
+
|
|
522
|
+
**Why use this?**
|
|
523
|
+
|
|
524
|
+
When working with Markdown content in Eleventy, you're usually limited to a single `content` variable. The `section` filter allows you to define multiple named sections within your content using simple HTML comments, giving you granular control over where different parts of your content appear in your layout.
|
|
525
|
+
|
|
526
|
+
**Usage:**
|
|
527
|
+
|
|
528
|
+
1. Enable the `section` filter in your Eleventy config:
|
|
529
|
+
|
|
530
|
+
```javascript
|
|
531
|
+
import { sectionFilter } from "@anydigital/eleventy-bricks";
|
|
532
|
+
|
|
533
|
+
export default function (eleventyConfig) {
|
|
534
|
+
sectionFilter(eleventyConfig);
|
|
535
|
+
// Or use as plugin:
|
|
536
|
+
// eleventyConfig.addPlugin(eleventyBricks, { filters: ['section'] });
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
2. Mark sections in your content file (e.g., `post.md`):
|
|
541
|
+
|
|
542
|
+
```markdown
|
|
543
|
+
# My Post
|
|
544
|
+
|
|
545
|
+
<¡--section:intro-->
|
|
546
|
+
|
|
547
|
+
This is the introduction that appears at the top of the page.
|
|
548
|
+
|
|
549
|
+
<¡--section:main-->
|
|
550
|
+
|
|
551
|
+
This is the main body of the post with all the details.
|
|
552
|
+
|
|
553
|
+
<¡--section:summary,sidebar-->
|
|
554
|
+
|
|
555
|
+
This content appears in both the summary and the sidebar!
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
3. Use the filter in your templates:
|
|
559
|
+
|
|
560
|
+
```njk
|
|
561
|
+
{# Get the intro section #}
|
|
562
|
+
<div class="page-intro">
|
|
563
|
+
{{ content | section('intro') | safe }}
|
|
564
|
+
</div>
|
|
565
|
+
|
|
566
|
+
{# Get the main section #}
|
|
567
|
+
<article>
|
|
568
|
+
{{ content | section('main') | safe }}
|
|
569
|
+
</article>
|
|
570
|
+
|
|
571
|
+
{# Get the sidebar section #}
|
|
572
|
+
<aside>
|
|
573
|
+
{{ content | section('sidebar') | safe }}
|
|
574
|
+
</aside>
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Parameters:**
|
|
578
|
+
|
|
579
|
+
- `content`: The string content to process (usually `content` variable)
|
|
580
|
+
- `sectionName`: The name(s) of the section to extract (string)
|
|
581
|
+
|
|
582
|
+
**Features:**
|
|
583
|
+
|
|
584
|
+
- **Multiple names**: A single section can have multiple names separated by commas: `<¡--section:name1,name2-->`
|
|
585
|
+
- **Case-insensitive**: Section names are matched without regard to case
|
|
586
|
+
- **Multiple occurrences**: If a section name appears multiple times, the filter concatenates all matching sections
|
|
587
|
+
- **Non-destructive**: Returns extracted content without modifying the original input
|
|
588
|
+
- **EOF support**: Sections continue until the next `<¡--section*-->` marker or the end of the file
|
|
589
|
+
|
|
590
|
+
**Examples:**
|
|
591
|
+
|
|
592
|
+
```njk
|
|
593
|
+
{# Extract multiple sections with same name #}
|
|
594
|
+
{# Example content has two <!--section:note--> blocks #}
|
|
595
|
+
<div class="notes-box">
|
|
596
|
+
{{ content | section('note') | safe }}
|
|
597
|
+
</div>
|
|
598
|
+
|
|
599
|
+
{# Use case-insensitive names #}
|
|
600
|
+
{{ content | section('INTRO') | safe }}
|
|
601
|
+
|
|
602
|
+
{# Handle missing sections gracefully (returns empty string) #}
|
|
603
|
+
{% set footer = content | section('non-existent-section') %}
|
|
604
|
+
{% if footer %}
|
|
605
|
+
<footer>{{ footer | safe }}</footer>
|
|
606
|
+
{% endif %}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
**Syntax Rules:**
|
|
610
|
+
|
|
611
|
+
- Sections start with: `<¡--section:NAME-->` or `<¡--section:NAME1,NAME2-->`
|
|
612
|
+
- Sections end at the next `<¡--section*-->` marker or end of file
|
|
613
|
+
- Whitespace around names and inside comments is automatically trimmed
|
|
614
|
+
|
|
523
615
|
#### `if`
|
|
524
616
|
|
|
525
617
|
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.
|
|
@@ -810,61 +902,108 @@ your-project/
|
|
|
810
902
|
|
|
811
903
|
**Note:** The filter returns raw text content. Use Eleventy's built-in filters like `| safe`, `| markdown`, or `| fromJson` to process the content as needed.
|
|
812
904
|
|
|
813
|
-
|
|
905
|
+
<!--section:data&processors-h3-->
|
|
814
906
|
|
|
815
|
-
|
|
907
|
+
### Global `site` data helpers
|
|
816
908
|
|
|
817
|
-
|
|
909
|
+
Adds global `site` data to your Eleventy project, providing commonly needed values that can be accessed in all templates:
|
|
818
910
|
|
|
819
|
-
|
|
911
|
+
| Variable | Value |
|
|
912
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
913
|
+
| `{{ site.year }}` | The current year as a number (e.g., `2026`) |
|
|
914
|
+
| `{{ site.prod }}` | Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`) |
|
|
820
915
|
|
|
821
|
-
|
|
916
|
+
<details>
|
|
917
|
+
<summary>Quick setup</summary>
|
|
822
918
|
|
|
823
|
-
|
|
919
|
+
```sh
|
|
920
|
+
npm install @anydigital/eleventy-bricks
|
|
921
|
+
```
|
|
824
922
|
|
|
825
|
-
|
|
923
|
+
Then choose one of the following options:
|
|
826
924
|
|
|
827
|
-
```
|
|
828
|
-
import
|
|
925
|
+
1. ```js {data-caption="As a plugin in eleventy.config.js (balanced)"}
|
|
926
|
+
import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
|
|
829
927
|
|
|
830
|
-
export default function (eleventyConfig) {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
```
|
|
928
|
+
export default function (eleventyConfig) {
|
|
929
|
+
eleventyConfig.addPlugin(eleventyBricksPlugin, { siteData: true });
|
|
930
|
+
}
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
2. ```js {data-caption="Individual import in eleventy.config.js (minimal)"}
|
|
934
|
+
import { siteData } from "@anydigital/eleventy-bricks";
|
|
935
|
+
|
|
936
|
+
export default function (eleventyConfig) {
|
|
937
|
+
siteData(eleventyConfig);
|
|
938
|
+
}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
3. ```sh {data-caption="Symlink entire eleventy.config.js (easiest)"}
|
|
942
|
+
ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
{.list-[upper-roman]}
|
|
946
|
+
|
|
947
|
+
</details>
|
|
948
|
+
|
|
949
|
+
### `mdAutoRawTags` preprocessor
|
|
950
|
+
|
|
951
|
+
Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping `{{`, `}}`, `{%`, and `%}` with `{% raw %}` tags.
|
|
952
|
+
|
|
953
|
+
**Why use this?**
|
|
954
|
+
|
|
955
|
+
When writing documentation or tutorials about templating in Markdown files, you often want to show Nunjucks/Liquid syntax as literal text. This preprocessor automatically escapes these special characters so they display as-is instead of being processed by the template engine.
|
|
836
956
|
|
|
837
957
|
**Example:**
|
|
838
958
|
|
|
839
959
|
Before `mdAutoRawTags`, writing this in Markdown:
|
|
840
960
|
|
|
841
961
|
```markdown
|
|
842
|
-
|
|
962
|
+
### Using {{ variable }} to output variables
|
|
843
963
|
```
|
|
844
964
|
|
|
845
965
|
Would try to process `{{ variable }}` as a template variable. With `mdAutoRawTags`, it displays exactly as written.
|
|
846
966
|
|
|
847
|
-
|
|
967
|
+
<details>
|
|
968
|
+
<summary>Quick setup</summary>
|
|
848
969
|
|
|
849
|
-
|
|
970
|
+
```sh
|
|
971
|
+
npm install @anydigital/eleventy-bricks
|
|
972
|
+
```
|
|
850
973
|
|
|
851
|
-
|
|
974
|
+
Then choose one of the following options:
|
|
852
975
|
|
|
853
|
-
|
|
976
|
+
1. ```js {data-caption="As a plugin in eleventy.config.js (balanced)"}
|
|
977
|
+
import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
|
|
854
978
|
|
|
855
|
-
|
|
979
|
+
export default function (eleventyConfig) {
|
|
980
|
+
eleventyConfig.addPlugin(eleventyBricksPlugin, { mdAutoRawTags: true });
|
|
981
|
+
}
|
|
982
|
+
```
|
|
856
983
|
|
|
857
|
-
|
|
984
|
+
2. ```js {data-caption="Individual import in eleventy.config.js (minimal)"}
|
|
985
|
+
import { mdAutoRawTags } from "@anydigital/eleventy-bricks";
|
|
858
986
|
|
|
859
|
-
|
|
860
|
-
|
|
987
|
+
export default function (eleventyConfig) {
|
|
988
|
+
mdAutoRawTags(eleventyConfig);
|
|
989
|
+
}
|
|
990
|
+
```
|
|
861
991
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
}
|
|
867
|
-
|
|
992
|
+
3. ```sh {data-caption="Symlink entire eleventy.config.js (easiest)"}
|
|
993
|
+
ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
{.list-[upper-roman]}
|
|
997
|
+
|
|
998
|
+
</details>
|
|
999
|
+
|
|
1000
|
+
### `mdAutoNl2br` converter
|
|
1001
|
+
|
|
1002
|
+
Automatically converts `\n` sequences to `<br>` tags in Markdown content. This is particularly useful for adding line breaks inside Markdown tables where standard newlines don't work.
|
|
1003
|
+
|
|
1004
|
+
**Why use this?**
|
|
1005
|
+
|
|
1006
|
+
Markdown tables don't support multi-line content in cells. By using `\n` in your content, this preprocessor will convert it to `<br>` tags, allowing you to display line breaks within table cells and other content.
|
|
868
1007
|
|
|
869
1008
|
**Example:**
|
|
870
1009
|
|
|
@@ -885,31 +1024,48 @@ Will render as:
|
|
|
885
1024
|
|
|
886
1025
|
**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.
|
|
887
1026
|
|
|
888
|
-
|
|
1027
|
+
<details>
|
|
1028
|
+
<summary>Quick setup</summary>
|
|
889
1029
|
|
|
890
|
-
|
|
1030
|
+
```sh
|
|
1031
|
+
npm install @anydigital/eleventy-bricks
|
|
1032
|
+
```
|
|
891
1033
|
|
|
892
|
-
|
|
1034
|
+
Then choose one of the following options:
|
|
893
1035
|
|
|
894
|
-
|
|
1036
|
+
1. ```js {data-caption="As a plugin in eleventy.config.js (balanced)"}
|
|
1037
|
+
import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
|
|
895
1038
|
|
|
896
|
-
|
|
1039
|
+
export default function (eleventyConfig) {
|
|
1040
|
+
eleventyConfig.addPlugin(eleventyBricksPlugin, { mdAutoNl2br: true });
|
|
1041
|
+
}
|
|
1042
|
+
```
|
|
897
1043
|
|
|
898
|
-
|
|
1044
|
+
2. ```js {data-caption="Individual import in eleventy.config.js (minimal)"}
|
|
1045
|
+
import { mdAutoNl2br } from "@anydigital/eleventy-bricks";
|
|
899
1046
|
|
|
900
|
-
|
|
901
|
-
|
|
1047
|
+
export default function (eleventyConfig) {
|
|
1048
|
+
mdAutoNl2br(eleventyConfig);
|
|
1049
|
+
}
|
|
1050
|
+
```
|
|
902
1051
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
// eleventyConfig.addPlugin(eleventyBricks, { autoLinkFavicons: true });
|
|
907
|
-
}
|
|
908
|
-
```
|
|
1052
|
+
3. ```sh {data-caption="Symlink entire eleventy.config.js (easiest)"}
|
|
1053
|
+
ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
|
|
1054
|
+
```
|
|
909
1055
|
|
|
910
|
-
|
|
1056
|
+
{.list-[upper-roman]}
|
|
1057
|
+
|
|
1058
|
+
</details>
|
|
1059
|
+
|
|
1060
|
+
### `autoLinkFavicons` processor
|
|
1061
|
+
|
|
1062
|
+
Automatically adds favicon images from Google's favicon service to links that display plain URLs or domain names. This processor processes all HTML output files and adds inline favicon images next to link text that appears to be a plain URL.
|
|
1063
|
+
|
|
1064
|
+
**Why use this?**
|
|
1065
|
+
|
|
1066
|
+
When you have links in your content that display raw URLs or domain names (like `https://example.com/page`), adding favicons provides a visual indicator of the external site. This processor automatically detects these plain-text URL links and enhances them with favicon images, making them more visually appealing and easier to recognize.
|
|
911
1067
|
|
|
912
|
-
|
|
1068
|
+
**How it works:**
|
|
913
1069
|
|
|
914
1070
|
1. Scans all HTML output files for `<a>` tags
|
|
915
1071
|
2. Checks if the link text appears to be a plain URL or domain
|
|
@@ -919,13 +1075,13 @@ The transform:
|
|
|
919
1075
|
|
|
920
1076
|
**Example:**
|
|
921
1077
|
|
|
922
|
-
Before
|
|
1078
|
+
Before processing:
|
|
923
1079
|
|
|
924
1080
|
```html
|
|
925
1081
|
<a href="https://github.com/anydigital/eleventy-bricks">https://github.com/anydigital/eleventy-bricks</a>
|
|
926
1082
|
```
|
|
927
1083
|
|
|
928
|
-
After
|
|
1084
|
+
After processing:
|
|
929
1085
|
|
|
930
1086
|
```html
|
|
931
1087
|
<a href="https://github.com/anydigital/eleventy-bricks" class="whitespace-nowrap" target="_blank">
|
|
@@ -941,124 +1097,49 @@ After transformation:
|
|
|
941
1097
|
- Removes the trailing slash from the display text
|
|
942
1098
|
- Only applies if at least 3 characters remain after removing the domain (to avoid showing favicons for bare domain links)
|
|
943
1099
|
- Uses Google's favicon service at `https://www.google.com/s2/favicons?domain=DOMAIN&sz=32`
|
|
944
|
-
- Adds `target="_blank"` to the
|
|
1100
|
+
- Adds `target="_blank"` to the processed links (only if not already present)
|
|
945
1101
|
- Adds `whitespace-nowrap` class to the link
|
|
946
1102
|
- Wraps the link text in a `<span>` element
|
|
947
1103
|
- The favicon is wrapped in an `<i>` tag for easy styling
|
|
948
1104
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
You can style the favicon icons with CSS:
|
|
952
|
-
|
|
953
|
-
```css
|
|
954
|
-
/* Style the favicon wrapper */
|
|
955
|
-
a i {
|
|
956
|
-
display: inline-block;
|
|
957
|
-
margin-right: 0.25em;
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
/* Style the favicon image */
|
|
961
|
-
a i img {
|
|
962
|
-
width: 16px;
|
|
963
|
-
height: 16px;
|
|
964
|
-
vertical-align: middle;
|
|
965
|
-
}
|
|
966
|
-
```
|
|
967
|
-
|
|
968
|
-
**Note:** This transform only processes HTML output files (those ending in `.html`). It does not modify the original content files.
|
|
969
|
-
|
|
970
|
-
### Global Data
|
|
971
|
-
|
|
972
|
-
Adds global site data to your Eleventy project, providing commonly needed values that can be accessed in all templates.
|
|
973
|
-
|
|
974
|
-
**Why use this?**
|
|
975
|
-
|
|
976
|
-
Many websites need access to the current year (for copyright notices) and environment information (to conditionally enable features based on production vs development). This helper provides these as global `site` data without manually setting them up.
|
|
977
|
-
|
|
978
|
-
**Usage:**
|
|
979
|
-
|
|
980
|
-
1. Enable `siteData` in your Eleventy config:
|
|
1105
|
+
<details>
|
|
1106
|
+
<summary>Quick setup</summary>
|
|
981
1107
|
|
|
982
|
-
```
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
export default function (eleventyConfig) {
|
|
986
|
-
siteData(eleventyConfig);
|
|
987
|
-
// Or use as plugin:
|
|
988
|
-
// eleventyConfig.addPlugin(eleventyBricks, { siteData: true });
|
|
989
|
-
}
|
|
990
|
-
```
|
|
991
|
-
|
|
992
|
-
2. Use the global data in your templates:
|
|
993
|
-
|
|
994
|
-
**Current Year:**
|
|
995
|
-
|
|
996
|
-
```njk
|
|
997
|
-
<footer>
|
|
998
|
-
<p>© {{ site.year }} Your Company Name. All rights reserved.</p>
|
|
999
|
-
</footer>
|
|
1000
|
-
```
|
|
1001
|
-
|
|
1002
|
-
**Environment Check:**
|
|
1003
|
-
|
|
1004
|
-
```njk
|
|
1005
|
-
{% if site.prod %}
|
|
1006
|
-
<!-- Production-only features -->
|
|
1007
|
-
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_TRACKING_ID"></script>
|
|
1008
|
-
{% else %}
|
|
1009
|
-
<!-- Development-only features -->
|
|
1010
|
-
<div class="dev-toolbar">Development Mode</div>
|
|
1011
|
-
{% endif %}
|
|
1108
|
+
```sh
|
|
1109
|
+
npm install @anydigital/eleventy-bricks
|
|
1012
1110
|
```
|
|
1013
1111
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
- `site.year`: The current year as a number (e.g., `2026`)
|
|
1017
|
-
- `site.prod`: Boolean indicating if running in production mode (`true` for `eleventy build`, `false` for `eleventy serve`)
|
|
1112
|
+
Then choose one of the following options:
|
|
1018
1113
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
- Automatically updates the year value
|
|
1022
|
-
- Detects production vs development mode based on `ELEVENTY_RUN_MODE` environment variable
|
|
1023
|
-
- Available globally in all templates without manual setup
|
|
1024
|
-
- No configuration required
|
|
1025
|
-
|
|
1026
|
-
**Examples:**
|
|
1027
|
-
|
|
1028
|
-
```njk
|
|
1029
|
-
{# Copyright notice #}
|
|
1030
|
-
<p>Copyright © {{ site.year }} My Site</p>
|
|
1114
|
+
1. ```js {data-caption="As a plugin in eleventy.config.js (balanced)"}
|
|
1115
|
+
import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
|
|
1031
1116
|
|
|
1032
|
-
|
|
1033
|
-
{
|
|
1034
|
-
|
|
1035
|
-
|
|
1117
|
+
export default function (eleventyConfig) {
|
|
1118
|
+
eleventyConfig.addPlugin(eleventyBricksPlugin, { autoLinkFavicons: true });
|
|
1119
|
+
}
|
|
1120
|
+
```
|
|
1036
1121
|
|
|
1037
|
-
{
|
|
1038
|
-
{
|
|
1039
|
-
<link rel="stylesheet" href="/css/styles.min.css">
|
|
1040
|
-
{% else %}
|
|
1041
|
-
<link rel="stylesheet" href="/css/styles.css">
|
|
1042
|
-
<script src="/live-reload.js"></script>
|
|
1043
|
-
{% endif %}
|
|
1044
|
-
```
|
|
1122
|
+
2. ```js {data-caption="Individual import in eleventy.config.js (minimal)"}
|
|
1123
|
+
import { autoLinkFavicons } from "@anydigital/eleventy-bricks";
|
|
1045
1124
|
|
|
1046
|
-
|
|
1125
|
+
export default function (eleventyConfig) {
|
|
1126
|
+
autoLinkFavicons(eleventyConfig);
|
|
1127
|
+
}
|
|
1128
|
+
```
|
|
1047
1129
|
|
|
1048
|
-
|
|
1130
|
+
3. ```sh {data-caption="Symlink entire eleventy.config.js (easiest)"}
|
|
1131
|
+
ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
|
|
1132
|
+
```
|
|
1049
1133
|
|
|
1050
|
-
|
|
1134
|
+
{.list-[upper-roman]}
|
|
1051
1135
|
|
|
1052
|
-
|
|
1053
|
-
- **Less maintenance**: No need to manually sync configuration changes
|
|
1054
|
-
- **Quick setup**: Get started immediately with best-practice configurations
|
|
1055
|
-
- **Easy customization**: Override specific settings by creating your own config that imports from the symlinked version
|
|
1136
|
+
</details>
|
|
1056
1137
|
|
|
1057
|
-
|
|
1138
|
+
<!--section:config-h3-->
|
|
1058
1139
|
|
|
1059
|
-
|
|
1140
|
+
### Symlinked `eleventy.config.js` <sub>from https://github.com/anydigital/eleventy-bricks</sub>
|
|
1060
1141
|
|
|
1061
|
-
|
|
1142
|
+
The package includes a fully-configured Eleventy config file `eleventy.config.js` that you can symlink to your project to get:
|
|
1062
1143
|
|
|
1063
1144
|
- All eleventy-bricks plugins enabled
|
|
1064
1145
|
- Eleventy Navigation plugin
|
|
@@ -1067,30 +1148,27 @@ A fully-configured Eleventy config file with:
|
|
|
1067
1148
|
- YAML data support
|
|
1068
1149
|
- CLI input directory support
|
|
1069
1150
|
- Symlink support for development
|
|
1151
|
+
- _and more_
|
|
1070
1152
|
|
|
1071
|
-
**
|
|
1072
|
-
|
|
1073
|
-
```bash
|
|
1074
|
-
npm install @11ty/eleventy-navigation markdown-it markdown-it-anchor markdown-it-attrs js-yaml minimist
|
|
1075
|
-
```
|
|
1153
|
+
**Benefits of symlinking:**
|
|
1076
1154
|
|
|
1077
|
-
**
|
|
1155
|
+
- **Always up-to-date**: Configuration automatically updates when you upgrade the package
|
|
1156
|
+
- **Less maintenance**: No need to manually sync configuration changes
|
|
1157
|
+
- **Quick setup**: Get started immediately with best-practice configurations
|
|
1158
|
+
- **Easy customization**: Override specific settings by creating your own config that imports from the symlinked version
|
|
1078
1159
|
|
|
1079
|
-
|
|
1080
|
-
# For the fetch filter
|
|
1081
|
-
npm install @11ty/eleventy-fetch
|
|
1160
|
+
**Quick setup:**
|
|
1082
1161
|
|
|
1083
|
-
|
|
1084
|
-
npm install @
|
|
1162
|
+
```sh
|
|
1163
|
+
npm install @anydigital/eleventy-bricks
|
|
1164
|
+
ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js
|
|
1085
1165
|
```
|
|
1086
1166
|
|
|
1087
|
-
|
|
1167
|
+
Done! 🎉
|
|
1088
1168
|
|
|
1089
|
-
|
|
1090
|
-
ln -s node_modules/@anydigital/eleventy-bricks/src/eleventy.config.js eleventy.config.js
|
|
1091
|
-
```
|
|
1169
|
+
<!--section:cms-h4-->
|
|
1092
1170
|
|
|
1093
|
-
|
|
1171
|
+
### Symlinked CMS
|
|
1094
1172
|
|
|
1095
1173
|
A ready-to-use Sveltia CMS admin interface for content management.
|
|
1096
1174
|
|
|
@@ -1101,59 +1179,54 @@ mkdir -p admin
|
|
|
1101
1179
|
ln -s ../node_modules/@anydigital/eleventy-bricks/src/admin/index.html admin/index.html
|
|
1102
1180
|
```
|
|
1103
1181
|
|
|
1104
|
-
|
|
1182
|
+
<!--section:npm-h3-->
|
|
1183
|
+
|
|
1184
|
+
### Reusable 11ty npm scripts <small>via npm workspace</small> <sub>from https://github.com/anydigital/eleventy-bricks</sub>
|
|
1105
1185
|
|
|
1106
1186
|
This package provides a pre-configured `do` folder setup that helps organize your development workflow using npm workspaces. The `do` folder contains scripts for building and running your Eleventy project.
|
|
1107
1187
|
|
|
1108
|
-
**
|
|
1188
|
+
**Quick setup:**
|
|
1109
1189
|
|
|
1110
|
-
1. Create a
|
|
1190
|
+
1. Create a simple folder, which will hold reusable npm scripts:
|
|
1111
1191
|
|
|
1112
|
-
```
|
|
1113
|
-
mkdir do
|
|
1114
|
-
```
|
|
1192
|
+
```sh
|
|
1193
|
+
mkdir do
|
|
1194
|
+
```
|
|
1115
1195
|
|
|
1116
|
-
2.
|
|
1196
|
+
2. Install https://github.com/anydigital/eleventy-bricks to reuse default 11ty scripts from there:
|
|
1117
1197
|
|
|
1118
|
-
```
|
|
1119
|
-
|
|
1120
|
-
```
|
|
1198
|
+
```sh
|
|
1199
|
+
npm install @anydigital/eleventy-bricks
|
|
1200
|
+
```
|
|
1121
1201
|
|
|
1122
|
-
3.
|
|
1202
|
+
3. Symlink the `do/package.json` containing scripts into your project's `do` folder:
|
|
1123
1203
|
|
|
1124
|
-
```
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
"scripts": {
|
|
1129
|
-
"build": "npm -w do run build",
|
|
1130
|
-
"start": "npm -w do run start"
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
```
|
|
1204
|
+
```sh
|
|
1205
|
+
cd do
|
|
1206
|
+
ln -s node_modules/@anydigital/eleventy-bricks/src/do/package.json
|
|
1207
|
+
```
|
|
1134
1208
|
|
|
1135
|
-
|
|
1209
|
+
4. Finally register `do` folder as npm workspace in your `package.json`, and enjoy default 11ty scripts as simple as:
|
|
1136
1210
|
|
|
1137
|
-
|
|
1211
|
+
```json {data-caption="YOUR project's package.json"}
|
|
1212
|
+
{
|
|
1213
|
+
"workspaces": ["do"],
|
|
1214
|
+
"scripts": {
|
|
1215
|
+
"start": "npm -w do run start",
|
|
1216
|
+
"stage": "npm -w do run stage",
|
|
1217
|
+
"build": "npm -w do run build"
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
```
|
|
1138
1221
|
|
|
1139
|
-
|
|
1140
|
-
npm start
|
|
1141
|
-
```
|
|
1142
|
-
|
|
1143
|
-
Build for production:
|
|
1144
|
-
|
|
1145
|
-
```bash
|
|
1146
|
-
npm run build
|
|
1147
|
-
```
|
|
1222
|
+
**Done!** 🎉 Now you can run:
|
|
1148
1223
|
|
|
1149
|
-
|
|
1224
|
+
- `npm start` to start 11ty dev server with live reload and Tailwind watch mode
|
|
1225
|
+
- `npm run stage` to build and serve production-like site locally
|
|
1226
|
+
- `npm run build` to finally build the site for production
|
|
1227
|
+
- all available scripts: https://github.com/anydigital/eleventy-bricks/blob/main/src/do/package.json
|
|
1150
1228
|
|
|
1151
|
-
|
|
1152
|
-
- `start` - Start Eleventy dev server with live reload and Tailwind watch mode
|
|
1153
|
-
- `stage` - Clean build and serve locally for preview
|
|
1154
|
-
- `11ty` - Run Eleventy commands directly
|
|
1155
|
-
- `11ty:clean` - Remove the `_site` output directory
|
|
1156
|
-
- `tw` - Run Tailwind CSS commands
|
|
1229
|
+
**Example setup:** https://github.com/anydigital/sveleven
|
|
1157
1230
|
|
|
1158
1231
|
**Benefits:**
|
|
1159
1232
|
|
package/package.json
CHANGED
package/src/eleventy.config.js
CHANGED
|
@@ -36,7 +36,7 @@ export default function (eleventyConfig) {
|
|
|
36
36
|
mdAutoRawTags: true,
|
|
37
37
|
autoLinkFavicons: true,
|
|
38
38
|
siteData: true,
|
|
39
|
-
filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "fetch"],
|
|
39
|
+
filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "fetch", "section"],
|
|
40
40
|
});
|
|
41
41
|
if (pluginTOC) {
|
|
42
42
|
eleventyConfig.addPlugin(pluginTOC, {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract a named section from content marked with HTML comments
|
|
3
|
+
*
|
|
4
|
+
* @param {string} content - The content to process
|
|
5
|
+
* @param {string} sectionName - The section name(s) to extract
|
|
6
|
+
* @returns {string} The extracted section content
|
|
7
|
+
*/
|
|
8
|
+
export function section(content, sectionName) {
|
|
9
|
+
if (!content || typeof content !== "string") {
|
|
10
|
+
return content;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (typeof sectionName !== "string" || !sectionName) {
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Normalize section name for comparison (trim whitespace)
|
|
18
|
+
const targetName = sectionName.trim().toLowerCase();
|
|
19
|
+
|
|
20
|
+
// Regex to match section markers with content up to the next section or end of string
|
|
21
|
+
// Captures: (1) section names, (2) content until next section marker or end
|
|
22
|
+
const sectionRegex = /<!--section:([^>]+)-->([\s\S]*?)(?=<!--section|$)/g;
|
|
23
|
+
|
|
24
|
+
let results = [];
|
|
25
|
+
let match;
|
|
26
|
+
|
|
27
|
+
// Find all sections
|
|
28
|
+
while ((match = sectionRegex.exec(content)) !== null) {
|
|
29
|
+
const namesStr = match[1];
|
|
30
|
+
const sectionContent = match[2];
|
|
31
|
+
const names = namesStr.split(",").map((n) => n.trim().toLowerCase());
|
|
32
|
+
|
|
33
|
+
// Check if any of the names match the target
|
|
34
|
+
if (names.includes(targetName)) {
|
|
35
|
+
results.push(sectionContent);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Join all matching sections
|
|
40
|
+
return results.join("");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* section filter - Extract a named section from content
|
|
45
|
+
*
|
|
46
|
+
* Usage in templates:
|
|
47
|
+
* {{ content | section('intro') }}
|
|
48
|
+
* {{ content | section('footer') }}
|
|
49
|
+
*
|
|
50
|
+
* Content format:
|
|
51
|
+
* <!--section:intro-->
|
|
52
|
+
* This is the intro content
|
|
53
|
+
* <!--section:main-->
|
|
54
|
+
* This is the main content
|
|
55
|
+
* <!--section:footer,sidebar-->
|
|
56
|
+
* This appears in both footer and sidebar sections
|
|
57
|
+
*
|
|
58
|
+
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
59
|
+
*/
|
|
60
|
+
export function sectionFilter(eleventyConfig) {
|
|
61
|
+
eleventyConfig.addFilter("section", section);
|
|
62
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { section } from "./section.js";
|
|
4
|
+
|
|
5
|
+
describe("section", () => {
|
|
6
|
+
it("should extract a single named section", () => {
|
|
7
|
+
const content = `Before
|
|
8
|
+
<!--section:intro-->
|
|
9
|
+
This is the intro
|
|
10
|
+
<!--section:main-->
|
|
11
|
+
This is the main`;
|
|
12
|
+
|
|
13
|
+
const result = section(content, "intro");
|
|
14
|
+
assert.strictEqual(result, "\nThis is the intro\n");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should extract section up to the next section marker", () => {
|
|
18
|
+
const content = `<!--section:first-->
|
|
19
|
+
First content
|
|
20
|
+
<!--section:second-->
|
|
21
|
+
Second content
|
|
22
|
+
<!--section:third-->
|
|
23
|
+
Third content`;
|
|
24
|
+
|
|
25
|
+
const result = section(content, "second");
|
|
26
|
+
assert.strictEqual(result, "\nSecond content\n");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should extract section up to EOF when no next marker", () => {
|
|
30
|
+
const content = `<!--section:intro-->
|
|
31
|
+
Intro content
|
|
32
|
+
<!--section:main-->
|
|
33
|
+
Main content that goes to the end`;
|
|
34
|
+
|
|
35
|
+
const result = section(content, "main");
|
|
36
|
+
assert.strictEqual(result, "\nMain content that goes to the end");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should handle section with multiple names", () => {
|
|
40
|
+
const content = `<!--section:header,nav-->
|
|
41
|
+
Shared content
|
|
42
|
+
<!--section:main-->
|
|
43
|
+
Main content`;
|
|
44
|
+
|
|
45
|
+
const resultHeader = section(content, "header");
|
|
46
|
+
const resultNav = section(content, "nav");
|
|
47
|
+
|
|
48
|
+
assert.strictEqual(resultHeader, "\nShared content\n");
|
|
49
|
+
assert.strictEqual(resultNav, "\nShared content\n");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should handle section with multiple names (spaces around commas)", () => {
|
|
53
|
+
const content = `<!--section:header, nav , top-->
|
|
54
|
+
Shared content
|
|
55
|
+
<!--section:main-->
|
|
56
|
+
Main content`;
|
|
57
|
+
|
|
58
|
+
const resultHeader = section(content, "header");
|
|
59
|
+
const resultNav = section(content, "nav");
|
|
60
|
+
const resultTop = section(content, "top");
|
|
61
|
+
|
|
62
|
+
assert.strictEqual(resultHeader, "\nShared content\n");
|
|
63
|
+
assert.strictEqual(resultNav, "\nShared content\n");
|
|
64
|
+
assert.strictEqual(resultTop, "\nShared content\n");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should return empty string for non-existent section", () => {
|
|
68
|
+
const content = `<!--section:intro-->
|
|
69
|
+
Content here
|
|
70
|
+
<!--section:main-->
|
|
71
|
+
More content`;
|
|
72
|
+
|
|
73
|
+
const result = section(content, "footer");
|
|
74
|
+
assert.strictEqual(result, "");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should handle empty or null input", () => {
|
|
78
|
+
assert.strictEqual(section("", "test"), "");
|
|
79
|
+
assert.strictEqual(section(null, "test"), null);
|
|
80
|
+
assert.strictEqual(section(undefined, "test"), undefined);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should handle missing section name", () => {
|
|
84
|
+
const content = `<!--section:intro-->Content`;
|
|
85
|
+
|
|
86
|
+
assert.strictEqual(section(content, ""), "");
|
|
87
|
+
assert.strictEqual(section(content, null), "");
|
|
88
|
+
assert.strictEqual(section(content, undefined), "");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should be case-insensitive for section names", () => {
|
|
92
|
+
const content = `<!--section:INTRO-->
|
|
93
|
+
Content here
|
|
94
|
+
<!--section:Main-->
|
|
95
|
+
More content`;
|
|
96
|
+
|
|
97
|
+
const result1 = section(content, "intro");
|
|
98
|
+
const result2 = section(content, "INTRO");
|
|
99
|
+
const result3 = section(content, "main");
|
|
100
|
+
const result4 = section(content, "MAIN");
|
|
101
|
+
|
|
102
|
+
assert.strictEqual(result1, "\nContent here\n");
|
|
103
|
+
assert.strictEqual(result2, "\nContent here\n");
|
|
104
|
+
assert.strictEqual(result3, "\nMore content");
|
|
105
|
+
assert.strictEqual(result4, "\nMore content");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should handle multiple sections with the same name", () => {
|
|
109
|
+
const content = `<!--section:note-->
|
|
110
|
+
First note
|
|
111
|
+
<!--section:main-->
|
|
112
|
+
Main content
|
|
113
|
+
<!--section:note-->
|
|
114
|
+
Second note
|
|
115
|
+
<!--section:footer-->
|
|
116
|
+
Footer`;
|
|
117
|
+
|
|
118
|
+
const result = section(content, "note");
|
|
119
|
+
assert.strictEqual(result, "\nFirst note\n\nSecond note\n");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should handle sections with no content", () => {
|
|
123
|
+
const content = `<!--section:empty--><!--section:main-->
|
|
124
|
+
Main content`;
|
|
125
|
+
|
|
126
|
+
const result = section(content, "empty");
|
|
127
|
+
assert.strictEqual(result, "");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should handle content before first section", () => {
|
|
131
|
+
const content = `Some preamble
|
|
132
|
+
<!--section:intro-->
|
|
133
|
+
Intro content`;
|
|
134
|
+
|
|
135
|
+
const result = section(content, "intro");
|
|
136
|
+
assert.strictEqual(result, "\nIntro content");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should handle complex real-world example", () => {
|
|
140
|
+
const content = `# Document Title
|
|
141
|
+
|
|
142
|
+
<!--section:summary,abstract-->
|
|
143
|
+
This is a summary that can be used as an abstract.
|
|
144
|
+
<!--section:introduction-->
|
|
145
|
+
This is the introduction.
|
|
146
|
+
<!--section:methods-->
|
|
147
|
+
These are the methods.
|
|
148
|
+
<!--section:conclusion,summary-->
|
|
149
|
+
This is the conclusion and also part of summary.`;
|
|
150
|
+
|
|
151
|
+
const summary = section(content, "summary");
|
|
152
|
+
const introduction = section(content, "introduction");
|
|
153
|
+
const methods = section(content, "methods");
|
|
154
|
+
const conclusion = section(content, "conclusion");
|
|
155
|
+
|
|
156
|
+
assert.strictEqual(
|
|
157
|
+
summary,
|
|
158
|
+
"\nThis is a summary that can be used as an abstract.\n\nThis is the conclusion and also part of summary.",
|
|
159
|
+
);
|
|
160
|
+
assert.strictEqual(introduction, "\nThis is the introduction.\n");
|
|
161
|
+
assert.strictEqual(methods, "\nThese are the methods.\n");
|
|
162
|
+
assert.strictEqual(conclusion, "\nThis is the conclusion and also part of summary.");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should handle section markers with extra whitespace", () => {
|
|
166
|
+
const content = `<!--section: intro -->
|
|
167
|
+
Content
|
|
168
|
+
<!--section: main -->
|
|
169
|
+
More`;
|
|
170
|
+
|
|
171
|
+
const result = section(content, "intro");
|
|
172
|
+
assert.strictEqual(result, "\nContent\n");
|
|
173
|
+
});
|
|
174
|
+
});
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mdAutoRawTags, mdAutoNl2br, transformAutoRaw, transformNl2br } from "./
|
|
1
|
+
import { mdAutoRawTags, mdAutoNl2br, transformAutoRaw, transformNl2br } from "./processors/markdown.js";
|
|
2
2
|
import {
|
|
3
3
|
autoLinkFavicons,
|
|
4
4
|
isPlainUrlText,
|
|
@@ -6,13 +6,14 @@ import {
|
|
|
6
6
|
buildFaviconLink,
|
|
7
7
|
transformLink,
|
|
8
8
|
replaceLinksInHtml,
|
|
9
|
-
} from "./
|
|
9
|
+
} from "./processors/autoLinkFavicons.js";
|
|
10
10
|
import { attrSetFilter, attrSet } from "./filters/attr_set.js";
|
|
11
11
|
import { attrIncludesFilter } from "./filters/attr_includes.js";
|
|
12
12
|
import { mergeFilter, merge } from "./filters/merge.js";
|
|
13
13
|
import { removeTagFilter, removeTag } from "./filters/remove_tag.js";
|
|
14
14
|
import { ifFilter, iff } from "./filters/if.js";
|
|
15
15
|
import { attrConcatFilter, attrConcat } from "./filters/attr_concat.js";
|
|
16
|
+
import { sectionFilter, section as sectionFn } from "./filters/section.js";
|
|
16
17
|
import { siteData } from "./siteData.js";
|
|
17
18
|
|
|
18
19
|
// Conditionally import fetchFilter only if @11ty/eleventy-fetch is available
|
|
@@ -36,7 +37,7 @@ try {
|
|
|
36
37
|
* @param {boolean} options.mdAutoRawTags - Enable mdAutoRawTags preprocessor (default: false)
|
|
37
38
|
* @param {boolean} options.mdAutoNl2br - Enable mdAutoNl2br for \n to <br> conversion (default: false)
|
|
38
39
|
* @param {boolean} options.autoLinkFavicons - Enable autoLinkFavicons to add favicons to plain text links (default: false)
|
|
39
|
-
* @param {Array<string>} options.filters - Array of filter names to enable: 'attr_set', 'attr_includes', 'merge', 'remove_tag', 'if', 'attr_concat', 'fetch' (default: [])
|
|
40
|
+
* @param {Array<string>} options.filters - Array of filter names to enable: 'attr_set', 'attr_includes', 'merge', 'remove_tag', 'if', 'attr_concat', 'section', 'fetch' (default: [])
|
|
40
41
|
* @param {boolean} options.siteData - Enable site.year and site.prod global data (default: false)
|
|
41
42
|
*/
|
|
42
43
|
export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
|
|
@@ -54,6 +55,7 @@ export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
|
|
|
54
55
|
remove_tag: removeTagFilter,
|
|
55
56
|
if: ifFilter,
|
|
56
57
|
attr_concat: attrConcatFilter,
|
|
58
|
+
section: sectionFilter,
|
|
57
59
|
...(fetchFilter && { fetch: fetchFilter }),
|
|
58
60
|
};
|
|
59
61
|
|
|
@@ -86,6 +88,7 @@ export {
|
|
|
86
88
|
removeTagFilter,
|
|
87
89
|
ifFilter,
|
|
88
90
|
attrConcatFilter,
|
|
91
|
+
sectionFilter,
|
|
89
92
|
fetchFilter,
|
|
90
93
|
siteData,
|
|
91
94
|
};
|
|
@@ -104,4 +107,5 @@ export {
|
|
|
104
107
|
iff,
|
|
105
108
|
attrConcat,
|
|
106
109
|
attrSet,
|
|
110
|
+
sectionFn as section,
|
|
107
111
|
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|