@anydigital/eleventy-bricks 0.22.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 +3 -0
- package/LICENSE +21 -0
- package/README.md +1064 -0
- package/package.json +49 -0
- package/src/admin/index.html +12 -0
- package/src/cli/download-files.js +136 -0
- package/src/do/package.json +15 -0
- package/src/eleventy.config.js +72 -0
- package/src/filters/attr.js +16 -0
- package/src/filters/attr_concat.js +65 -0
- package/src/filters/attr_concat.test.js +205 -0
- package/src/filters/if.js +39 -0
- package/src/filters/if.test.js +63 -0
- package/src/filters/merge.js +47 -0
- package/src/filters/merge.test.js +51 -0
- package/src/filters/remove_tag.js +42 -0
- package/src/filters/remove_tag.test.js +60 -0
- package/src/filters/where_in.js +49 -0
- package/src/filters/where_in.test.js +148 -0
- package/src/index.cjs +26 -0
- package/src/index.js +94 -0
- package/src/markdown.js +163 -0
- package/src/markdown.test.js +589 -0
- package/src/siteData.js +12 -0
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import {
|
|
4
|
+
transformAutoRaw,
|
|
5
|
+
transformNl2br,
|
|
6
|
+
isPlainUrlText,
|
|
7
|
+
cleanLinkText,
|
|
8
|
+
buildFaviconLink,
|
|
9
|
+
transformLink,
|
|
10
|
+
replaceLinksInHtml,
|
|
11
|
+
} from "./markdown.js";
|
|
12
|
+
|
|
13
|
+
describe("transformAutoRaw", () => {
|
|
14
|
+
it("should wrap opening double curly braces with raw tags", () => {
|
|
15
|
+
const input = "Use {{ variable }} to output.";
|
|
16
|
+
const expected = "Use {% raw %}{{{% endraw %} variable {% raw %}}}{% endraw %} to output.";
|
|
17
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should wrap closing double curly braces with raw tags", () => {
|
|
21
|
+
const input = "{{ name }}";
|
|
22
|
+
const expected = "{% raw %}{{{% endraw %} name {% raw %}}}{% endraw %}";
|
|
23
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should wrap opening template tags with raw tags", () => {
|
|
27
|
+
const input = "{% if condition %}";
|
|
28
|
+
const expected = "{% raw %}{%{% endraw %} if condition {% raw %}%}{% endraw %}";
|
|
29
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should wrap closing template tags with raw tags", () => {
|
|
33
|
+
const input = "{% endif %}";
|
|
34
|
+
const expected = "{% raw %}{%{% endraw %} endif {% raw %}%}{% endraw %}";
|
|
35
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should handle multiple Nunjucks patterns in one string", () => {
|
|
39
|
+
const input = "{{ var1 }} and {% if test %} something {% endif %}";
|
|
40
|
+
const expected =
|
|
41
|
+
"{% raw %}{{{% endraw %} var1 {% raw %}}}{% endraw %} and {% raw %}{%{% endraw %} if test {% raw %}%}{% endraw %} something {% raw %}{%{% endraw %} endif {% raw %}%}{% endraw %}";
|
|
42
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should handle multiline content with Nunjucks syntax", () => {
|
|
46
|
+
const input = `# Title
|
|
47
|
+
{{ variable }}
|
|
48
|
+
Some text
|
|
49
|
+
{% for item in items %}
|
|
50
|
+
{{ item }}
|
|
51
|
+
{% endfor %}`;
|
|
52
|
+
const expected = `# Title
|
|
53
|
+
{% raw %}{{{% endraw %} variable {% raw %}}}{% endraw %}
|
|
54
|
+
Some text
|
|
55
|
+
{% raw %}{%{% endraw %} for item in items {% raw %}%}{% endraw %}
|
|
56
|
+
{% raw %}{{{% endraw %} item {% raw %}}}{% endraw %}
|
|
57
|
+
{% raw %}{%{% endraw %} endfor {% raw %}%}{% endraw %}`;
|
|
58
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should return unchanged content when no Nunjucks syntax is present", () => {
|
|
62
|
+
const input = "This is just plain text with no templates.";
|
|
63
|
+
assert.equal(transformAutoRaw(input), input);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should handle empty string", () => {
|
|
67
|
+
assert.equal(transformAutoRaw(""), "");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should handle content with only Nunjucks syntax", () => {
|
|
71
|
+
const input = "{{}}";
|
|
72
|
+
const expected = "{% raw %}{{{% endraw %}{% raw %}}}{% endraw %}";
|
|
73
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should handle consecutive Nunjucks patterns", () => {
|
|
77
|
+
const input = "{{{{}}}}";
|
|
78
|
+
const expected = "{% raw %}{{{% endraw %}{% raw %}{{{% endraw %}{% raw %}}}{% endraw %}{% raw %}}}{% endraw %}";
|
|
79
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should wrap each delimiter individually", () => {
|
|
83
|
+
const input = "Show {{ and }} and {% and %}";
|
|
84
|
+
const expected =
|
|
85
|
+
"Show {% raw %}{{{% endraw %} and {% raw %}}}{% endraw %} and {% raw %}{%{% endraw %} and {% raw %}%}{% endraw %}";
|
|
86
|
+
assert.equal(transformAutoRaw(input), expected);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("transformNl2br", () => {
|
|
91
|
+
it("should convert single \\n to <br>", () => {
|
|
92
|
+
const input = "Line 1\\nLine 2";
|
|
93
|
+
const expected = "Line 1<br>Line 2";
|
|
94
|
+
assert.equal(transformNl2br(input), expected);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should convert double \\n\\n to <br>", () => {
|
|
98
|
+
const input = "Line 1\\n\\nLine 2";
|
|
99
|
+
const expected = "Line 1<br>Line 2";
|
|
100
|
+
assert.equal(transformNl2br(input), expected);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should convert multiple \\n sequences", () => {
|
|
104
|
+
const input = "Line 1\\nLine 2\\nLine 3";
|
|
105
|
+
const expected = "Line 1<br>Line 2<br>Line 3";
|
|
106
|
+
assert.equal(transformNl2br(input), expected);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should handle mixed single and double \\n", () => {
|
|
110
|
+
const input = "Line 1\\n\\nLine 2\\nLine 3";
|
|
111
|
+
const expected = "Line 1<br>Line 2<br>Line 3";
|
|
112
|
+
assert.equal(transformNl2br(input), expected);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should handle text without \\n", () => {
|
|
116
|
+
const input = "Just plain text";
|
|
117
|
+
assert.equal(transformNl2br(input), input);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should handle empty content", () => {
|
|
121
|
+
assert.equal(transformNl2br(""), "");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should handle content with only \\n", () => {
|
|
125
|
+
const input = "\\n\\n\\n";
|
|
126
|
+
const expected = "<br><br>";
|
|
127
|
+
assert.equal(transformNl2br(input), expected);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should handle markdown table cell content with \\n", () => {
|
|
131
|
+
const input = "Cell 1\\nCell 1 Line 2\\n\\nCell 1 Line 3";
|
|
132
|
+
const expected = "Cell 1<br>Cell 1 Line 2<br>Cell 1 Line 3";
|
|
133
|
+
assert.equal(transformNl2br(input), expected);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should handle multiple consecutive double \\n\\n", () => {
|
|
137
|
+
const input = "Line 1\\n\\n\\n\\nLine 2";
|
|
138
|
+
const expected = "Line 1<br><br>Line 2";
|
|
139
|
+
assert.equal(transformNl2br(input), expected);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should preserve actual newlines (not literal \\n)", () => {
|
|
143
|
+
const input = "Line 1\nLine 2";
|
|
144
|
+
const expected = "Line 1\nLine 2";
|
|
145
|
+
assert.equal(transformNl2br(input), expected);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should only convert literal backslash-n sequences", () => {
|
|
149
|
+
const input = "Text with\\nbackslash-n and\nreal newline";
|
|
150
|
+
const expected = "Text with<br>backslash-n and\nreal newline";
|
|
151
|
+
assert.equal(transformNl2br(input), expected);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("isPlainUrlText", () => {
|
|
156
|
+
it("should return true when linkText contains domain", () => {
|
|
157
|
+
assert.equal(isPlainUrlText("example.com", "example.com"), true);
|
|
158
|
+
assert.equal(isPlainUrlText("https://example.com/path", "example.com"), true);
|
|
159
|
+
assert.equal(isPlainUrlText("Visit example.com for more", "example.com"), true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should return true when linkText starts with http://", () => {
|
|
163
|
+
assert.equal(isPlainUrlText("http://example.com", "example.com"), true);
|
|
164
|
+
assert.equal(isPlainUrlText("http://other.com/path", "other.com"), true);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("should return true when linkText starts with https://", () => {
|
|
168
|
+
assert.equal(isPlainUrlText("https://example.com", "example.com"), true);
|
|
169
|
+
assert.equal(isPlainUrlText("https://other.com/path", "other.com"), true);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should return false for custom link text without domain", () => {
|
|
173
|
+
assert.equal(isPlainUrlText("Click here", "example.com"), false);
|
|
174
|
+
assert.equal(isPlainUrlText("Read more", "example.com"), false);
|
|
175
|
+
assert.equal(isPlainUrlText("Documentation", "example.com"), false);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("should handle whitespace in linkText", () => {
|
|
179
|
+
assert.equal(isPlainUrlText(" example.com ", "example.com"), true);
|
|
180
|
+
assert.equal(isPlainUrlText(" https://example.com ", "example.com"), true);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("should return false for empty linkText", () => {
|
|
184
|
+
assert.equal(isPlainUrlText("", "example.com"), false);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe("cleanLinkText", () => {
|
|
189
|
+
it("should remove protocol and domain", () => {
|
|
190
|
+
assert.equal(cleanLinkText("https://example.com/docs", "example.com"), "/docs");
|
|
191
|
+
assert.equal(cleanLinkText("http://example.com/docs", "example.com"), "/docs");
|
|
192
|
+
assert.equal(cleanLinkText("https://example.com/docs/guide", "example.com"), "/docs/guide");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should handle links without protocol", () => {
|
|
196
|
+
assert.equal(cleanLinkText("example.com/docs", "example.com"), "/docs");
|
|
197
|
+
assert.equal(cleanLinkText("example.com/path/to/page", "example.com"), "/path/to/page");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("should preserve leading slash after domain removal", () => {
|
|
201
|
+
assert.equal(cleanLinkText("https://example.com/docs", "example.com"), "/docs");
|
|
202
|
+
assert.equal(cleanLinkText("example.com/docs", "example.com"), "/docs");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("should return empty string for root domain", () => {
|
|
206
|
+
assert.equal(cleanLinkText("example.com/", "example.com"), "");
|
|
207
|
+
assert.equal(cleanLinkText("example.com", "example.com"), "");
|
|
208
|
+
assert.equal(cleanLinkText("https://example.com", "example.com"), "");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should handle whitespace", () => {
|
|
212
|
+
assert.equal(cleanLinkText(" https://example.com/docs ", "example.com"), "/docs");
|
|
213
|
+
assert.equal(cleanLinkText("\nhttps://example.com/docs\n", "example.com"), "/docs");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should preserve path after domain", () => {
|
|
217
|
+
assert.equal(cleanLinkText("https://example.com/api/v1/docs", "example.com"), "/api/v1/docs");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("should handle query parameters", () => {
|
|
221
|
+
const result = cleanLinkText("https://example.com/search?q=test", "example.com");
|
|
222
|
+
assert.equal(result, "/search?q=test");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("should handle hash fragments", () => {
|
|
226
|
+
const result = cleanLinkText("https://example.com/page#section", "example.com");
|
|
227
|
+
assert.equal(result, "/page#section");
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe("buildFaviconLink", () => {
|
|
232
|
+
it("should create correct HTML with favicon", () => {
|
|
233
|
+
const result = buildFaviconLink('href="https://example.com/docs"', "example.com", "/docs");
|
|
234
|
+
assert.equal(
|
|
235
|
+
result,
|
|
236
|
+
'<a href="https://example.com/docs" target="_blank"><i><img src="https://www.google.com/s2/favicons?domain=example.com&sz=32"></i>/docs</a>',
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("should handle complex attributes", () => {
|
|
241
|
+
const result = buildFaviconLink('href="https://example.com" class="link"', "example.com", "text");
|
|
242
|
+
assert.equal(
|
|
243
|
+
result,
|
|
244
|
+
'<a href="https://example.com" class="link" target="_blank"><i><img src="https://www.google.com/s2/favicons?domain=example.com&sz=32"></i>text</a>',
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("should use sz=32 parameter for favicon size", () => {
|
|
249
|
+
const result = buildFaviconLink('href="https://example.com"', "example.com", "text");
|
|
250
|
+
assert.match(result, /sz=32/);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("should wrap img in <i> tag", () => {
|
|
254
|
+
const result = buildFaviconLink('href="https://example.com"', "example.com", "text");
|
|
255
|
+
assert.match(result, /<i><img[^>]*><\/i>/);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("should handle different domains", () => {
|
|
259
|
+
const result = buildFaviconLink('href="https://github.com/repo"', "github.com", "/repo");
|
|
260
|
+
assert.equal(
|
|
261
|
+
result,
|
|
262
|
+
'<a href="https://github.com/repo" target="_blank"><i><img src="https://www.google.com/s2/favicons?domain=github.com&sz=32"></i>/repo</a>',
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should preserve link text as provided", () => {
|
|
267
|
+
const result = buildFaviconLink('href="https://example.com"', "example.com", "custom text");
|
|
268
|
+
assert.match(result, />custom text<\/a>$/);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe("transformLink", () => {
|
|
273
|
+
it("should transform plain URL links with sufficient length", () => {
|
|
274
|
+
const result = transformLink(
|
|
275
|
+
'<a href="https://example.com/docs">https://example.com/docs</a>',
|
|
276
|
+
'href="https://example.com/docs"',
|
|
277
|
+
"https://example.com/docs",
|
|
278
|
+
"https://example.com/docs",
|
|
279
|
+
);
|
|
280
|
+
assert.match(
|
|
281
|
+
result,
|
|
282
|
+
/<i><img src="https:\/\/www\.google\.com\/s2\/favicons\?domain=example\.com&sz=32"><\/i>\/docs/,
|
|
283
|
+
);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("should not transform if cleaned text is too short (2 chars or less)", () => {
|
|
287
|
+
const match = '<a href="https://example.com/a">https://example.com/a</a>';
|
|
288
|
+
const result = transformLink(
|
|
289
|
+
match,
|
|
290
|
+
'href="https://example.com/a"',
|
|
291
|
+
"https://example.com/a",
|
|
292
|
+
"https://example.com/a",
|
|
293
|
+
);
|
|
294
|
+
assert.equal(result, match);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("should not transform custom link text without URL", () => {
|
|
298
|
+
const match = '<a href="https://example.com/docs">Click here</a>';
|
|
299
|
+
const result = transformLink(match, 'href="https://example.com/docs"', "https://example.com/docs", "Click here");
|
|
300
|
+
assert.equal(result, match);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("should not transform root domain links", () => {
|
|
304
|
+
const match = '<a href="https://example.com">example.com</a>';
|
|
305
|
+
const result = transformLink(match, 'href="https://example.com"', "https://example.com", "example.com");
|
|
306
|
+
assert.equal(result, match);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("should not transform links ending with slash only", () => {
|
|
310
|
+
const match = '<a href="https://example.com/">https://example.com/</a>';
|
|
311
|
+
const result = transformLink(match, 'href="https://example.com/"', "https://example.com/", "https://example.com/");
|
|
312
|
+
assert.equal(result, match);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should handle invalid URLs gracefully", () => {
|
|
316
|
+
const match = '<a href="not-a-url">not-a-url</a>';
|
|
317
|
+
const result = transformLink(match, 'href="not-a-url"', "not-a-url", "not-a-url");
|
|
318
|
+
assert.equal(result, match);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("should work with http:// protocol", () => {
|
|
322
|
+
const result = transformLink(
|
|
323
|
+
'<a href="http://example.com/docs">http://example.com/docs</a>',
|
|
324
|
+
'href="http://example.com/docs"',
|
|
325
|
+
"http://example.com/docs",
|
|
326
|
+
"http://example.com/docs",
|
|
327
|
+
);
|
|
328
|
+
assert.match(result, /<i><img[^>]*><\/i>\/docs/);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("should work with https:// protocol", () => {
|
|
332
|
+
const result = transformLink(
|
|
333
|
+
'<a href="https://example.com/docs">https://example.com/docs</a>',
|
|
334
|
+
'href="https://example.com/docs"',
|
|
335
|
+
"https://example.com/docs",
|
|
336
|
+
"https://example.com/docs",
|
|
337
|
+
);
|
|
338
|
+
assert.match(result, /<i><img[^>]*><\/i>\/docs/);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("should handle longer paths correctly", () => {
|
|
342
|
+
const result = transformLink(
|
|
343
|
+
'<a href="https://example.com/path/to/document">https://example.com/path/to/document</a>',
|
|
344
|
+
'href="https://example.com/path/to/document"',
|
|
345
|
+
"https://example.com/path/to/document",
|
|
346
|
+
"https://example.com/path/to/document",
|
|
347
|
+
);
|
|
348
|
+
assert.match(result, /<i><img[^>]*><\/i>\/path\/to\/document/);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("should not transform when linkText doesn't look like URL", () => {
|
|
352
|
+
const match = '<a href="https://example.com/page">Read the documentation</a>';
|
|
353
|
+
const result = transformLink(
|
|
354
|
+
match,
|
|
355
|
+
'href="https://example.com/page"',
|
|
356
|
+
"https://example.com/page",
|
|
357
|
+
"Read the documentation",
|
|
358
|
+
);
|
|
359
|
+
assert.equal(result, match);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("should transform when linkText contains domain even without protocol", () => {
|
|
363
|
+
const result = transformLink(
|
|
364
|
+
'<a href="https://example.com/docs">example.com/docs</a>',
|
|
365
|
+
'href="https://example.com/docs"',
|
|
366
|
+
"https://example.com/docs",
|
|
367
|
+
"example.com/docs",
|
|
368
|
+
);
|
|
369
|
+
assert.match(result, /<i><img[^>]*><\/i>\/docs/);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should handle malformed URLs by returning original match", () => {
|
|
373
|
+
const match = '<a href="ht!tp://bad-url">ht!tp://bad-url</a>';
|
|
374
|
+
const result = transformLink(match, 'href="ht!tp://bad-url"', "ht!tp://bad-url", "ht!tp://bad-url");
|
|
375
|
+
assert.equal(result, match);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe("replaceLinksInHtml", () => {
|
|
380
|
+
it("should replace a single anchor link with transformer function", () => {
|
|
381
|
+
const html = '<a href="https://example.com">Click here</a>';
|
|
382
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
383
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
384
|
+
assert.equal(result, "[Click here](https://example.com)");
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("should replace multiple anchor links in HTML", () => {
|
|
388
|
+
const html = '<a href="https://example.com">Link 1</a> and <a href="https://other.com">Link 2</a>';
|
|
389
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
390
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
391
|
+
assert.equal(result, "[Link 1](https://example.com) and [Link 2](https://other.com)");
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("should handle links with single quotes in href", () => {
|
|
395
|
+
const html = "<a href='https://example.com'>Link</a>";
|
|
396
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
397
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
398
|
+
assert.equal(result, "[Link](https://example.com)");
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("should handle links with double quotes in href", () => {
|
|
402
|
+
const html = '<a href="https://example.com">Link</a>';
|
|
403
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
404
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
405
|
+
assert.equal(result, "[Link](https://example.com)");
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it("should capture all attributes in first group", () => {
|
|
409
|
+
const html = '<a href="https://example.com" class="link" target="_blank">Link</a>';
|
|
410
|
+
let capturedAttrs = "";
|
|
411
|
+
const transformer = (match, attrs, url, linkText) => {
|
|
412
|
+
capturedAttrs = attrs;
|
|
413
|
+
return match;
|
|
414
|
+
};
|
|
415
|
+
replaceLinksInHtml(html, transformer);
|
|
416
|
+
assert.equal(capturedAttrs, 'href="https://example.com" class="link" target="_blank"');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("should capture URL in second group", () => {
|
|
420
|
+
const html = '<a href="https://example.com/path">Link</a>';
|
|
421
|
+
let capturedUrl = "";
|
|
422
|
+
const transformer = (match, attrs, url, linkText) => {
|
|
423
|
+
capturedUrl = url;
|
|
424
|
+
return match;
|
|
425
|
+
};
|
|
426
|
+
replaceLinksInHtml(html, transformer);
|
|
427
|
+
assert.equal(capturedUrl, "https://example.com/path");
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("should capture link text in third group", () => {
|
|
431
|
+
const html = '<a href="https://example.com">Click here</a>';
|
|
432
|
+
let capturedText = "";
|
|
433
|
+
const transformer = (match, attrs, url, linkText) => {
|
|
434
|
+
capturedText = linkText;
|
|
435
|
+
return match;
|
|
436
|
+
};
|
|
437
|
+
replaceLinksInHtml(html, transformer);
|
|
438
|
+
assert.equal(capturedText, "Click here");
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it("should preserve content that is not an anchor link", () => {
|
|
442
|
+
const html = '<p>Some text</p><a href="https://example.com">Link</a><div>More text</div>';
|
|
443
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
444
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
445
|
+
assert.equal(result, "<p>Some text</p>[Link](https://example.com)<div>More text</div>");
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it("should handle empty HTML content", () => {
|
|
449
|
+
const html = "";
|
|
450
|
+
const transformer = (match) => match;
|
|
451
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
452
|
+
assert.equal(result, "");
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it("should handle HTML with no anchor links", () => {
|
|
456
|
+
const html = "<p>No links here</p><div>Just text</div>";
|
|
457
|
+
const transformer = (match) => "REPLACED";
|
|
458
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
459
|
+
assert.equal(result, html);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it("should handle links with query parameters", () => {
|
|
463
|
+
const html = '<a href="https://example.com/search?q=test&lang=en">Search</a>';
|
|
464
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
465
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
466
|
+
assert.equal(result, "[Search](https://example.com/search?q=test&lang=en)");
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it("should handle links with hash fragments", () => {
|
|
470
|
+
const html = '<a href="https://example.com/page#section">Section</a>';
|
|
471
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
472
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
473
|
+
assert.equal(result, "[Section](https://example.com/page#section)");
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should be case-insensitive for anchor tag", () => {
|
|
477
|
+
const html = '<A HREF="https://example.com">Link</A>';
|
|
478
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
479
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
480
|
+
assert.equal(result, "[Link](https://example.com)");
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it("should handle links with additional attributes before href", () => {
|
|
484
|
+
const html = '<a class="link" href="https://example.com" id="mylink">Link</a>';
|
|
485
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
486
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
487
|
+
assert.equal(result, "[Link](https://example.com)");
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("should handle links with additional attributes after href", () => {
|
|
491
|
+
const html = '<a href="https://example.com" class="link" id="mylink">Link</a>';
|
|
492
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
493
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
494
|
+
assert.equal(result, "[Link](https://example.com)");
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it("should handle relative URLs", () => {
|
|
498
|
+
const html = '<a href="/docs/guide">Guide</a>';
|
|
499
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
500
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
501
|
+
assert.equal(result, "[Guide](/docs/guide)");
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("should handle root-relative URLs", () => {
|
|
505
|
+
const html = '<a href="/">Home</a>';
|
|
506
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
507
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
508
|
+
assert.equal(result, "[Home](/)");
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it("should not match anchor tags with nested HTML in link text", () => {
|
|
512
|
+
const html = '<a href="https://example.com"><span>Link</span></a>';
|
|
513
|
+
const transformer = (match) => "REPLACED";
|
|
514
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
515
|
+
// Should not match because regex expects [^<]+ for link text (no nested tags)
|
|
516
|
+
assert.equal(result, html);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it("should handle transformer that returns original match unchanged", () => {
|
|
520
|
+
const html = '<a href="https://example.com">Link</a>';
|
|
521
|
+
const transformer = (match) => match;
|
|
522
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
523
|
+
assert.equal(result, html);
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it("should work with transformLink function for real-world usage", () => {
|
|
527
|
+
const html = '<a href="https://example.com/docs">https://example.com/docs</a>';
|
|
528
|
+
const result = replaceLinksInHtml(html, transformLink);
|
|
529
|
+
// transformLink should add favicon for plain URL links
|
|
530
|
+
assert.match(result, /<img src="https:\/\/www\.google\.com\/s2\/favicons\?domain=example\.com&sz=32">/);
|
|
531
|
+
assert.match(result, /\/docs<\/a>/);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it("should not transform custom link text when using transformLink", () => {
|
|
535
|
+
const html = '<a href="https://example.com/docs">Click here</a>';
|
|
536
|
+
const result = replaceLinksInHtml(html, transformLink);
|
|
537
|
+
// transformLink should not add favicon for custom text
|
|
538
|
+
assert.equal(result, html);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("should handle multiple links with mixed transformation results", () => {
|
|
542
|
+
const html =
|
|
543
|
+
'<a href="https://example.com/docs">https://example.com/docs</a> and <a href="https://other.com">Click</a>';
|
|
544
|
+
const result = replaceLinksInHtml(html, transformLink);
|
|
545
|
+
// First link should be transformed (plain URL), second should not (custom text)
|
|
546
|
+
assert.match(result, /img src=/);
|
|
547
|
+
assert.match(result, /Click<\/a>/);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it("should handle link text with special characters", () => {
|
|
551
|
+
const html = '<a href="https://example.com">Link & More!</a>';
|
|
552
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
553
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
554
|
+
assert.equal(result, "[Link & More!](https://example.com)");
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it("should handle URLs with ports", () => {
|
|
558
|
+
const html = '<a href="https://example.com:8080/page">Link</a>';
|
|
559
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
560
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
561
|
+
assert.equal(result, "[Link](https://example.com:8080/page)");
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("should handle http protocol links", () => {
|
|
565
|
+
const html = '<a href="http://example.com">Link</a>';
|
|
566
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
567
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
568
|
+
assert.equal(result, "[Link](http://example.com)");
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it("should handle links in multiline HTML", () => {
|
|
572
|
+
const html = `<div>
|
|
573
|
+
<a href="https://example.com">Link 1</a>
|
|
574
|
+
<p>Some text</p>
|
|
575
|
+
<a href="https://other.com">Link 2</a>
|
|
576
|
+
</div>`;
|
|
577
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
578
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
579
|
+
assert.match(result, /\[Link 1\]\(https:\/\/example\.com\)/);
|
|
580
|
+
assert.match(result, /\[Link 2\]\(https:\/\/other\.com\)/);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it("should handle adjacent anchor links", () => {
|
|
584
|
+
const html = '<a href="https://example.com">Link1</a><a href="https://other.com">Link2</a>';
|
|
585
|
+
const transformer = (match, attrs, url, linkText) => `[${linkText}](${url})`;
|
|
586
|
+
const result = replaceLinksInHtml(html, transformer);
|
|
587
|
+
assert.equal(result, "[Link1](https://example.com)[Link2](https://other.com)");
|
|
588
|
+
});
|
|
589
|
+
});
|
package/src/siteData.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add site.year and site.prod global data
|
|
3
|
+
* - site.prod: Boolean indicating if running in production mode (build) vs development (serve)
|
|
4
|
+
* - site.year: Sets the current year to be available in all templates as {{ site.year }}
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} eleventyConfig - The Eleventy configuration object
|
|
7
|
+
*/
|
|
8
|
+
export function siteData(eleventyConfig) {
|
|
9
|
+
eleventyConfig.addGlobalData("site.prod", () => process.env.ELEVENTY_RUN_MODE === "build");
|
|
10
|
+
eleventyConfig.addGlobalData("site.year", () => new Date().getFullYear());
|
|
11
|
+
}
|
|
12
|
+
|