@anydigital/eleventy-bricks 0.23.2 → 0.25.0
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/.prettierrc.json +10 -1
- package/README.md +158 -3
- package/package.json +2 -9
- package/src/eleventy.config.js +14 -1
- package/src/filters/attr_concat.js +2 -4
- package/src/filters/fetch.js +46 -0
- package/src/filters/where_in.js +5 -9
- package/src/index.js +14 -1
- package/src/markdown.js +5 -9
- package/src/cli/download-files.js +0 -136
package/.prettierrc.json
CHANGED
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ export default function (eleventyConfig) {
|
|
|
27
27
|
mdAutoNl2br: true,
|
|
28
28
|
mdAutoLinkFavicons: true,
|
|
29
29
|
siteData: true,
|
|
30
|
-
filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
|
|
30
|
+
filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat", "fetch"],
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
// Your other configuration...
|
|
@@ -45,7 +45,7 @@ module.exports = function (eleventyConfig) {
|
|
|
45
45
|
mdAutoNl2br: true,
|
|
46
46
|
mdAutoLinkFavicons: true,
|
|
47
47
|
siteData: true,
|
|
48
|
-
filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
|
|
48
|
+
filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat", "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
|
+
fetchFilter,
|
|
74
75
|
siteData,
|
|
75
76
|
} from "@anydigital/eleventy-bricks";
|
|
76
77
|
|
|
@@ -84,6 +85,10 @@ export default function (eleventyConfig) {
|
|
|
84
85
|
removeTagFilter(eleventyConfig);
|
|
85
86
|
ifFilter(eleventyConfig);
|
|
86
87
|
attrConcatFilter(eleventyConfig);
|
|
88
|
+
// fetchFilter is only available if @11ty/eleventy-fetch is installed
|
|
89
|
+
if (fetchFilter) {
|
|
90
|
+
fetchFilter(eleventyConfig);
|
|
91
|
+
}
|
|
87
92
|
siteData(eleventyConfig);
|
|
88
93
|
|
|
89
94
|
// Your other configuration...
|
|
@@ -103,6 +108,7 @@ const {
|
|
|
103
108
|
removeTagFilter,
|
|
104
109
|
ifFilter,
|
|
105
110
|
attrConcatFilter,
|
|
111
|
+
fetchFilter,
|
|
106
112
|
siteData,
|
|
107
113
|
} = require("@anydigital/eleventy-bricks");
|
|
108
114
|
|
|
@@ -116,6 +122,10 @@ module.exports = async function (eleventyConfig) {
|
|
|
116
122
|
await removeTagFilter(eleventyConfig);
|
|
117
123
|
await ifFilter(eleventyConfig);
|
|
118
124
|
await attrConcatFilter(eleventyConfig);
|
|
125
|
+
// fetchFilter is only available if @11ty/eleventy-fetch is installed
|
|
126
|
+
if (fetchFilter) {
|
|
127
|
+
await fetchFilter(eleventyConfig);
|
|
128
|
+
}
|
|
119
129
|
await siteData(eleventyConfig);
|
|
120
130
|
|
|
121
131
|
// Your other configuration...
|
|
@@ -144,6 +154,7 @@ When using the plugin (Option 1), you can configure which helpers to enable:
|
|
|
144
154
|
- `'remove_tag'` - Remove HTML elements from content
|
|
145
155
|
- `'if'` - Inline conditional/ternary operator
|
|
146
156
|
- `'attr_concat'` - Concatenate values to an attribute array
|
|
157
|
+
- `'fetch'` - Fetch remote URLs or local files (requires `@11ty/eleventy-fetch`)
|
|
147
158
|
|
|
148
159
|
**Example:**
|
|
149
160
|
|
|
@@ -153,7 +164,7 @@ eleventyConfig.addPlugin(eleventyBricks, {
|
|
|
153
164
|
mdAutoNl2br: true,
|
|
154
165
|
mdAutoLinkFavicons: true,
|
|
155
166
|
siteData: true,
|
|
156
|
-
filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
|
|
167
|
+
filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat", "fetch"],
|
|
157
168
|
});
|
|
158
169
|
```
|
|
159
170
|
|
|
@@ -789,6 +800,139 @@ A new object with the specified attribute containing the combined unique array.
|
|
|
789
800
|
{% endfor %}
|
|
790
801
|
```
|
|
791
802
|
|
|
803
|
+
### fetch
|
|
804
|
+
|
|
805
|
+
A filter that fetches content from remote URLs or local files. For remote URLs, it uses `@11ty/eleventy-fetch` to download and cache files. For local paths, it reads files relative to the input directory.
|
|
806
|
+
|
|
807
|
+
**Why use this?**
|
|
808
|
+
|
|
809
|
+
When building static sites, you often need to include content from external sources or reuse content from local files. The `fetch` filter provides a unified way to retrieve content from both remote URLs and local files, with automatic caching for remote resources to improve build performance.
|
|
810
|
+
|
|
811
|
+
**Requirements:**
|
|
812
|
+
|
|
813
|
+
This filter requires the `@11ty/eleventy-fetch` package to be installed:
|
|
814
|
+
|
|
815
|
+
```bash
|
|
816
|
+
npm install @11ty/eleventy-fetch
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
> **Note:** If `@11ty/eleventy-fetch` is not installed, this filter will not be available. The plugin automatically detects whether the package is installed and only enables the filter if it's present.
|
|
820
|
+
|
|
821
|
+
**Usage:**
|
|
822
|
+
|
|
823
|
+
1. Install the required dependency:
|
|
824
|
+
|
|
825
|
+
```bash
|
|
826
|
+
npm install @11ty/eleventy-fetch
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
2. Enable the `fetch` filter in your Eleventy config:
|
|
830
|
+
|
|
831
|
+
```javascript
|
|
832
|
+
import { fetchFilter } from "@anydigital/eleventy-bricks";
|
|
833
|
+
|
|
834
|
+
export default function (eleventyConfig) {
|
|
835
|
+
fetchFilter(eleventyConfig);
|
|
836
|
+
// Or use as plugin:
|
|
837
|
+
// eleventyConfig.addPlugin(eleventyBricks, { filters: ['fetch'] });
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
3. Use the filter in your templates:
|
|
842
|
+
|
|
843
|
+
**Fetch remote URLs:**
|
|
844
|
+
|
|
845
|
+
```njk
|
|
846
|
+
{# Fetch content from a remote URL #}
|
|
847
|
+
{% set externalContent = "https://example.com/data.json" | fetch %}
|
|
848
|
+
{{ externalContent }}
|
|
849
|
+
|
|
850
|
+
{# Fetch and parse JSON #}
|
|
851
|
+
{% set apiData = "https://api.example.com/posts" | fetch %}
|
|
852
|
+
{% set posts = apiData | fromJson %}
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
**Fetch local files:**
|
|
856
|
+
|
|
857
|
+
```njk
|
|
858
|
+
{# Fetch content from a local file (relative to input directory) #}
|
|
859
|
+
{% set localData = "_data/content.txt" | fetch %}
|
|
860
|
+
{{ localData }}
|
|
861
|
+
|
|
862
|
+
{# Include content from another file #}
|
|
863
|
+
{% set snippet = "_includes/snippets/example.md" | fetch %}
|
|
864
|
+
{{ snippet | markdown | safe }}
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
**Parameters:**
|
|
868
|
+
|
|
869
|
+
- `url`: A URL (starting with `http://` or `https://`) or a local file path (relative to the input directory)
|
|
870
|
+
|
|
871
|
+
**Features:**
|
|
872
|
+
|
|
873
|
+
- **Remote URLs**: Downloads and caches content using `@11ty/eleventy-fetch`
|
|
874
|
+
- Caches files for 1 day by default
|
|
875
|
+
- Stores cached files in `[input-dir]/_downloads/` directory
|
|
876
|
+
- Automatically revalidates after cache expires
|
|
877
|
+
- **Local files**: Reads files relative to the Eleventy input directory
|
|
878
|
+
- No caching needed for local files
|
|
879
|
+
- Supports any file type that can be read as text
|
|
880
|
+
- **Error handling**: Throws descriptive errors if fetching fails
|
|
881
|
+
- **Conditional loading**: Only available when `@11ty/eleventy-fetch` is installed
|
|
882
|
+
|
|
883
|
+
**Examples:**
|
|
884
|
+
|
|
885
|
+
```njk
|
|
886
|
+
{# Fetch and display remote content #}
|
|
887
|
+
{% set readme = "https://raw.githubusercontent.com/user/repo/main/README.md" | fetch %}
|
|
888
|
+
<div class="readme">
|
|
889
|
+
{{ readme | markdown | safe }}
|
|
890
|
+
</div>
|
|
891
|
+
|
|
892
|
+
{# Fetch JSON data from API #}
|
|
893
|
+
{% set data = "https://api.example.com/data.json" | fetch %}
|
|
894
|
+
{% set items = data | fromJson %}
|
|
895
|
+
{% for item in items %}
|
|
896
|
+
<p>{{ item.title }}</p>
|
|
897
|
+
{% endfor %}
|
|
898
|
+
|
|
899
|
+
{# Include local file content #}
|
|
900
|
+
{% set changelog = "CHANGELOG.md" | fetch %}
|
|
901
|
+
{{ changelog | markdown | safe }}
|
|
902
|
+
|
|
903
|
+
{# Fetch CSS from CDN and inline it #}
|
|
904
|
+
<style>
|
|
905
|
+
{{ "https://cdn.example.com/styles.css" | fetch }}
|
|
906
|
+
</style>
|
|
907
|
+
|
|
908
|
+
{# Reuse content across pages #}
|
|
909
|
+
{% set sharedContent = "_includes/shared/footer.html" | fetch %}
|
|
910
|
+
{{ sharedContent | safe }}
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
**Cache Directory:**
|
|
914
|
+
|
|
915
|
+
Remote files are cached in the `_downloads` folder within your input directory:
|
|
916
|
+
|
|
917
|
+
```
|
|
918
|
+
your-project/
|
|
919
|
+
├── src/ (or your input directory)
|
|
920
|
+
│ ├── _downloads/ (cached remote files)
|
|
921
|
+
│ ├── index.njk
|
|
922
|
+
│ └── ...
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
**Use Cases:**
|
|
926
|
+
|
|
927
|
+
- Fetch content from external APIs during build time
|
|
928
|
+
- Include README files from GitHub repositories
|
|
929
|
+
- Reuse content from local files across multiple pages
|
|
930
|
+
- Download and inline external CSS or JavaScript
|
|
931
|
+
- Fetch data from headless CMS or external data sources
|
|
932
|
+
- Include shared content snippets without using Eleventy's include syntax
|
|
933
|
+
|
|
934
|
+
**Note:** The filter returns raw text content. Use Eleventy's built-in filters like `| safe`, `| markdown`, or `| fromJson` to process the content as needed.
|
|
935
|
+
|
|
792
936
|
### siteData
|
|
793
937
|
|
|
794
938
|
Adds global site data to your Eleventy project, providing commonly needed values that can be accessed in all templates.
|
|
@@ -892,6 +1036,7 @@ A fully-configured Eleventy config file with:
|
|
|
892
1036
|
|
|
893
1037
|
- All eleventy-bricks plugins enabled
|
|
894
1038
|
- Eleventy Navigation plugin
|
|
1039
|
+
- Table of Contents plugin (conditionally loaded if installed)
|
|
895
1040
|
- Markdown-it with anchors and attributes
|
|
896
1041
|
- YAML data support
|
|
897
1042
|
- CLI input directory support
|
|
@@ -903,6 +1048,16 @@ A fully-configured Eleventy config file with:
|
|
|
903
1048
|
npm install @11ty/eleventy-navigation markdown-it markdown-it-anchor markdown-it-attrs js-yaml minimist
|
|
904
1049
|
```
|
|
905
1050
|
|
|
1051
|
+
**Optional dependencies:**
|
|
1052
|
+
|
|
1053
|
+
```bash
|
|
1054
|
+
# For the fetch filter
|
|
1055
|
+
npm install @11ty/eleventy-fetch
|
|
1056
|
+
|
|
1057
|
+
# For table of contents generation
|
|
1058
|
+
npm install @uncenter/eleventy-plugin-toc
|
|
1059
|
+
```
|
|
1060
|
+
|
|
906
1061
|
**Symlink to your project:**
|
|
907
1062
|
|
|
908
1063
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anydigital/eleventy-bricks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "A collection of helpful utilities and filters for Eleventy (11ty)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -10,11 +10,8 @@
|
|
|
10
10
|
"require": "./src/index.cjs"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
-
"bin": {
|
|
14
|
-
"download-files": "src/cli/download-files.js"
|
|
15
|
-
},
|
|
16
13
|
"scripts": {
|
|
17
|
-
"build": "
|
|
14
|
+
"build": "curl -O https://raw.githubusercontent.com/anydigital/bricks/refs/heads/main/.prettierrc.json",
|
|
18
15
|
"test": "node --test src/**/*.test.js"
|
|
19
16
|
},
|
|
20
17
|
"keywords": [
|
|
@@ -41,9 +38,5 @@
|
|
|
41
38
|
},
|
|
42
39
|
"engines": {
|
|
43
40
|
"node": ">=18.0.0"
|
|
44
|
-
},
|
|
45
|
-
"_downloadFiles": {
|
|
46
|
-
"https://raw.githubusercontent.com/anydigital/bricks/refs/heads/main/.prettierrc.json": ".prettierrc.json",
|
|
47
|
-
"https://raw.githubusercontent.com/danurbanowicz/eleventy-sveltia-cms-starter/refs/heads/master/admin/index.html": "src/admin/index.html"
|
|
48
41
|
}
|
|
49
42
|
}
|
package/src/eleventy.config.js
CHANGED
|
@@ -4,6 +4,13 @@ import minimist from "minimist";
|
|
|
4
4
|
import { RenderPlugin } from "@11ty/eleventy";
|
|
5
5
|
import eleventyNavigationPlugin from "@11ty/eleventy-navigation";
|
|
6
6
|
import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
|
|
7
|
+
/* Conditional imports */
|
|
8
|
+
let pluginTOC;
|
|
9
|
+
try {
|
|
10
|
+
pluginTOC = (await import('@uncenter/eleventy-plugin-toc')).default;
|
|
11
|
+
} catch (e) {
|
|
12
|
+
// @uncenter/eleventy-plugin-toc not installed
|
|
13
|
+
}
|
|
7
14
|
/* Libraries */
|
|
8
15
|
import markdownIt from "markdown-it";
|
|
9
16
|
import markdownItAnchor from "markdown-it-anchor";
|
|
@@ -29,8 +36,14 @@ export default function (eleventyConfig) {
|
|
|
29
36
|
mdAutoRawTags: true,
|
|
30
37
|
mdAutoLinkFavicons: true,
|
|
31
38
|
siteData: true,
|
|
32
|
-
filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat"],
|
|
39
|
+
filters: ["attr", "where_in", "merge", "remove_tag", "if", "attr_concat", "fetch"],
|
|
33
40
|
});
|
|
41
|
+
if (pluginTOC) {
|
|
42
|
+
eleventyConfig.addPlugin(pluginTOC, {
|
|
43
|
+
ignoredElements: ["sub", ".header-anchor"],
|
|
44
|
+
ul: true,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
34
47
|
|
|
35
48
|
/* Libraries */
|
|
36
49
|
eleventyConfig.setLibrary(
|
|
@@ -15,16 +15,14 @@ export function attrConcat(obj, attr, values) {
|
|
|
15
15
|
|
|
16
16
|
// Check if existing value is an array, convert if not
|
|
17
17
|
if (!Array.isArray(existingArray)) {
|
|
18
|
-
console.error(
|
|
19
|
-
`attrConcat: Expected ${attr} to be an array, got ${typeof existingArray}`
|
|
20
|
-
);
|
|
18
|
+
console.error(`attrConcat: Expected ${attr} to be an array, got ${typeof existingArray}`);
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
// Process the values argument
|
|
24
22
|
let valuesToAdd = [];
|
|
25
23
|
if (Array.isArray(values)) {
|
|
26
24
|
valuesToAdd = values;
|
|
27
|
-
} else if (typeof values === "string") {
|
|
25
|
+
} else if (typeof values === "string" && values.length >= 2 && values.at(0) == "[" && values.at(-1) == "]") {
|
|
28
26
|
// Try to parse as JSON array
|
|
29
27
|
try {
|
|
30
28
|
const parsed = JSON.parse(values);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import EleventyFetch from "@11ty/eleventy-fetch";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs/promises";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* fetch filter - Fetch a URL or local file and return its raw content
|
|
7
|
+
*
|
|
8
|
+
* This filter takes a URL or local file path. For URLs, it downloads them
|
|
9
|
+
* using eleventy-fetch to the input directory's _downloads folder.
|
|
10
|
+
* For local paths, it reads them relative to the input directory.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
13
|
+
*/
|
|
14
|
+
export function fetchFilter(eleventyConfig) {
|
|
15
|
+
eleventyConfig.addFilter("fetch", async function (url) {
|
|
16
|
+
if (!url) {
|
|
17
|
+
throw new Error("fetch filter requires a URL or path");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Get the input directory from Eleventy config
|
|
21
|
+
const inputDir = eleventyConfig.dir?.input || ".";
|
|
22
|
+
|
|
23
|
+
// Check if it's a URL or local path
|
|
24
|
+
const isUrl = url.startsWith("http://") || url.startsWith("https://");
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
if (isUrl) {
|
|
28
|
+
// Handle remote URLs with eleventy-fetch
|
|
29
|
+
const cacheDirectory = path.join(inputDir, "_downloads");
|
|
30
|
+
const content = await EleventyFetch(url, {
|
|
31
|
+
duration: "1d", // Cache for 1 day by default
|
|
32
|
+
type: "text", // Return as text
|
|
33
|
+
directory: cacheDirectory,
|
|
34
|
+
});
|
|
35
|
+
return content;
|
|
36
|
+
} else {
|
|
37
|
+
// Handle local file paths relative to input directory
|
|
38
|
+
const filePath = path.join(inputDir, url);
|
|
39
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
40
|
+
return content;
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw new Error(`Failed to fetch ${url}: ${error.message}`);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
package/src/filters/where_in.js
CHANGED
|
@@ -16,26 +16,22 @@ const { get } = lodash;
|
|
|
16
16
|
* @returns {Array} Filtered collection
|
|
17
17
|
*/
|
|
18
18
|
export function whereIn(collection, attrName, targetValue) {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// If no targetValue, return original collection
|
|
20
|
+
if (!targetValue) {
|
|
21
|
+
return collection;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
return collection.filter((item) => {
|
|
24
25
|
// Get the attribute value from the item (supports nested paths like "data.tags")
|
|
25
26
|
const attrValue = get(item, attrName);
|
|
26
27
|
|
|
27
|
-
// If attribute doesn't exist, skip this item
|
|
28
|
-
if (attrValue === undefined || attrValue === null) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
28
|
// If the attribute is an array, check if it includes the target value
|
|
33
29
|
if (Array.isArray(attrValue)) {
|
|
34
30
|
return attrValue.includes(targetValue);
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
// Otherwise
|
|
38
|
-
return
|
|
33
|
+
// Otherwise skip this item
|
|
34
|
+
return false;
|
|
39
35
|
});
|
|
40
36
|
}
|
|
41
37
|
|
package/src/index.js
CHANGED
|
@@ -17,6 +17,16 @@ import { ifFilter, iff } from "./filters/if.js";
|
|
|
17
17
|
import { attrConcatFilter, attrConcat } from "./filters/attr_concat.js";
|
|
18
18
|
import { siteData } from "./siteData.js";
|
|
19
19
|
|
|
20
|
+
// Conditionally import fetchFilter only if @11ty/eleventy-fetch is available
|
|
21
|
+
let fetchFilter = null;
|
|
22
|
+
try {
|
|
23
|
+
await import("@11ty/eleventy-fetch");
|
|
24
|
+
const fetchModule = await import("./filters/fetch.js");
|
|
25
|
+
fetchFilter = fetchModule.fetchFilter;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
// @11ty/eleventy-fetch not available, fetch filter will be disabled
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
/**
|
|
21
31
|
* 11ty Bricks Plugin
|
|
22
32
|
*
|
|
@@ -28,7 +38,7 @@ import { siteData } from "./siteData.js";
|
|
|
28
38
|
* @param {boolean} options.mdAutoRawTags - Enable mdAutoRawTags preprocessor (default: false)
|
|
29
39
|
* @param {boolean} options.mdAutoNl2br - Enable mdAutoNl2br for \n to <br> conversion (default: false)
|
|
30
40
|
* @param {boolean} options.mdAutoLinkFavicons - Enable mdAutoLinkFavicons to add favicons to plain text links (default: false)
|
|
31
|
-
* @param {Array<string>} options.filters - Array of filter names to enable: 'attr', 'where_in', 'merge', 'remove_tag', 'if', 'attr_concat' (default: [])
|
|
41
|
+
* @param {Array<string>} options.filters - Array of filter names to enable: 'attr', 'where_in', 'merge', 'remove_tag', 'if', 'attr_concat', 'fetch' (default: [])
|
|
32
42
|
* @param {boolean} options.siteData - Enable site.year and site.prod global data (default: false)
|
|
33
43
|
*/
|
|
34
44
|
export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
|
|
@@ -46,6 +56,7 @@ export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
|
|
|
46
56
|
remove_tag: removeTagFilter,
|
|
47
57
|
if: ifFilter,
|
|
48
58
|
attr_concat: attrConcatFilter,
|
|
59
|
+
...(fetchFilter && { fetch: fetchFilter }),
|
|
49
60
|
};
|
|
50
61
|
|
|
51
62
|
// Handle individual plugin options
|
|
@@ -66,6 +77,7 @@ export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
|
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
// Export individual helpers for granular usage
|
|
80
|
+
// Note: fetchFilter will be null/undefined if @11ty/eleventy-fetch is not installed
|
|
69
81
|
export {
|
|
70
82
|
mdAutoRawTags,
|
|
71
83
|
mdAutoNl2br,
|
|
@@ -76,6 +88,7 @@ export {
|
|
|
76
88
|
removeTagFilter,
|
|
77
89
|
ifFilter,
|
|
78
90
|
attrConcatFilter,
|
|
91
|
+
fetchFilter,
|
|
79
92
|
siteData,
|
|
80
93
|
};
|
|
81
94
|
|
package/src/markdown.js
CHANGED
|
@@ -76,11 +76,12 @@ export function isPlainUrlText(linkText, domain) {
|
|
|
76
76
|
* @returns {string} The cleaned text
|
|
77
77
|
*/
|
|
78
78
|
export function cleanLinkText(linkText, domain) {
|
|
79
|
-
|
|
79
|
+
const cleanedText = linkText
|
|
80
80
|
.trim()
|
|
81
81
|
.replace(/^https?:\/\//, "")
|
|
82
|
-
.replace(domain, "")
|
|
83
82
|
.replace(/\/$/, "");
|
|
83
|
+
const withoutDomain = cleanedText.replace(domain, "");
|
|
84
|
+
return withoutDomain.length > 2 ? withoutDomain : cleanedText;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
/**
|
|
@@ -128,15 +129,10 @@ export function transformLink(match, attrs, url, linkText) {
|
|
|
128
129
|
|
|
129
130
|
// Only add favicon if link text looks like a plain URL/domain
|
|
130
131
|
if (isPlainUrlText(linkText, domain)) {
|
|
131
|
-
// Remove domain from link text
|
|
132
132
|
const cleanedText = cleanLinkText(linkText, domain);
|
|
133
|
-
|
|
134
|
-
// Only apply if there are at least 2 letters remaining after domain
|
|
135
|
-
if (cleanedText.length > 2) {
|
|
136
|
-
return buildFaviconLink(attrs, domain, cleanedText);
|
|
137
|
-
}
|
|
133
|
+
return buildFaviconLink(attrs, domain, cleanedText);
|
|
138
134
|
}
|
|
139
|
-
return match;
|
|
135
|
+
return match; // @TODO: throw?
|
|
140
136
|
} catch (e) {
|
|
141
137
|
// If URL parsing fails, return original match
|
|
142
138
|
return match;
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
4
|
-
import { dirname, resolve, join } from 'path';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Parse command line arguments
|
|
8
|
-
*
|
|
9
|
-
* @returns {Object} Parsed arguments
|
|
10
|
-
*/
|
|
11
|
-
function parseArgs() {
|
|
12
|
-
const args = process.argv.slice(2);
|
|
13
|
-
const parsed = {
|
|
14
|
-
outputDir: null
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
for (let i = 0; i < args.length; i++) {
|
|
18
|
-
const arg = args[i];
|
|
19
|
-
|
|
20
|
-
if (arg === '--output' || arg === '-o') {
|
|
21
|
-
if (i + 1 < args.length) {
|
|
22
|
-
parsed.outputDir = args[i + 1];
|
|
23
|
-
i++; // Skip next argument
|
|
24
|
-
} else {
|
|
25
|
-
throw new Error(`${arg} requires a directory path`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return parsed;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Downloads files specified in package.json's _downloadFiles field
|
|
35
|
-
*
|
|
36
|
-
* @param {string|null} outputDir - Optional output directory to prepend to all paths
|
|
37
|
-
* @returns {Promise<boolean>} True if all downloads succeeded, false if any failed
|
|
38
|
-
*/
|
|
39
|
-
async function download(outputDir = null) {
|
|
40
|
-
try {
|
|
41
|
-
// Find and read package.json from the current working directory
|
|
42
|
-
const packageJsonPath = resolve(process.cwd(), 'package.json');
|
|
43
|
-
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
|
|
44
|
-
|
|
45
|
-
const downloadFiles = packageJson._downloadFiles;
|
|
46
|
-
|
|
47
|
-
if (!downloadFiles || typeof downloadFiles !== 'object') {
|
|
48
|
-
console.log('No _downloadFiles field found in package.json');
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const entries = Object.entries(downloadFiles);
|
|
53
|
-
|
|
54
|
-
if (entries.length === 0) {
|
|
55
|
-
console.log('No files to download (_downloadFiles is empty)');
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
console.log(`Starting download of ${entries.length} file(s)...\n`);
|
|
60
|
-
|
|
61
|
-
let hasErrors = false;
|
|
62
|
-
|
|
63
|
-
// Process all downloads
|
|
64
|
-
for (const entry of entries) {
|
|
65
|
-
const url = entry[0];
|
|
66
|
-
let localPath = entry[1];
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
console.log(`Downloading: ${url}`);
|
|
70
|
-
console.log(` To: ${localPath}`);
|
|
71
|
-
|
|
72
|
-
// Download the file
|
|
73
|
-
const response = await fetch(url);
|
|
74
|
-
|
|
75
|
-
if (!response.ok) {
|
|
76
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Get the file content
|
|
80
|
-
const content = await response.arrayBuffer();
|
|
81
|
-
const buffer = Buffer.from(content);
|
|
82
|
-
|
|
83
|
-
// Prepend output directory to local path if specified
|
|
84
|
-
if (outputDir) {
|
|
85
|
-
localPath = join(outputDir, localPath);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Resolve the full path
|
|
89
|
-
const fullPath = resolve(process.cwd(), localPath);
|
|
90
|
-
|
|
91
|
-
// Create directory if it doesn't exist
|
|
92
|
-
const dir = dirname(fullPath);
|
|
93
|
-
await mkdir(dir, { recursive: true });
|
|
94
|
-
|
|
95
|
-
// Write the file
|
|
96
|
-
await writeFile(fullPath, buffer);
|
|
97
|
-
|
|
98
|
-
console.log(` Success: ${localPath}\n`);
|
|
99
|
-
} catch (error) {
|
|
100
|
-
hasErrors = true;
|
|
101
|
-
console.error(` Error: ${error.message}`);
|
|
102
|
-
console.error(` URL: ${url}\n`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Summary
|
|
107
|
-
if (hasErrors) {
|
|
108
|
-
console.log('Download completed with errors');
|
|
109
|
-
return false;
|
|
110
|
-
} else {
|
|
111
|
-
console.log('All downloads completed successfully');
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error(`Fatal error: ${error.message}`);
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* CLI entry point
|
|
123
|
-
*/
|
|
124
|
-
async function main() {
|
|
125
|
-
try {
|
|
126
|
-
const args = parseArgs();
|
|
127
|
-
const success = await download(args.outputDir);
|
|
128
|
-
process.exit(success ? 0 : 1);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error(`Error: ${error.message}`);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
main();
|
|
136
|
-
|