@anydigital/eleventy-bricks 0.27.0 → 0.28.0-alpha

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anydigital/eleventy-bricks",
3
- "version": "0.27.0",
3
+ "version": "0.28.0-alpha",
4
4
  "description": "A collection of helpful utilities and filters for Eleventy (11ty)",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -2,9 +2,14 @@
2
2
  import minimist from "minimist";
3
3
  /* Plugins */
4
4
  import { RenderPlugin } from "@11ty/eleventy";
5
- import eleventyNavigationPlugin from "@11ty/eleventy-navigation";
6
5
  import eleventyBricksPlugin from "@anydigital/eleventy-bricks";
7
- /* Conditional imports */
6
+ /* Dynamic plugins */
7
+ let eleventyNavigationPlugin;
8
+ try {
9
+ eleventyNavigationPlugin = (await import("@11ty/eleventy-navigation")).default;
10
+ } catch (e) {
11
+ // @11ty/eleventy-navigation not installed
12
+ }
8
13
  let pluginTOC;
9
14
  try {
10
15
  pluginTOC = (await import("@uncenter/eleventy-plugin-toc")).default;
@@ -13,8 +18,25 @@ try {
13
18
  }
14
19
  /* Libraries */
15
20
  import markdownIt from "markdown-it";
16
- import markdownItAnchor from "markdown-it-anchor";
17
- import markdownItAttrs from "markdown-it-attrs";
21
+ /* Dynamic libraries */
22
+ let slugify;
23
+ try {
24
+ slugify = (await import("@sindresorhus/slugify")).default;
25
+ } catch (e) {
26
+ // @sindresorhus/slugify not installed
27
+ }
28
+ let markdownItAnchor;
29
+ try {
30
+ markdownItAnchor = (await import("markdown-it-anchor")).default;
31
+ } catch (e) {
32
+ // markdown-it-anchor not installed
33
+ }
34
+ let markdownItAttrs;
35
+ try {
36
+ markdownItAttrs = (await import("markdown-it-attrs")).default;
37
+ } catch (e) {
38
+ // markdown-it-attrs not installed
39
+ }
18
40
  /* Data */
19
41
  import yaml from "js-yaml";
20
42
 
@@ -26,17 +48,17 @@ import yaml from "js-yaml";
26
48
  export default function (eleventyConfig) {
27
49
  /* CLI support */
28
50
  const argv = minimist(process.argv.slice(2));
29
- const inputDir = argv.input || "src";
51
+ const inputDir = argv.input || ".";
30
52
 
31
53
  /* Plugins */
32
54
  eleventyConfig.addPlugin(RenderPlugin);
33
- eleventyConfig.addPlugin(eleventyNavigationPlugin);
55
+ if (eleventyNavigationPlugin) eleventyConfig.addPlugin(eleventyNavigationPlugin);
34
56
  eleventyConfig.addPlugin(eleventyBricksPlugin, {
35
57
  mdAutoNl2br: true,
36
58
  mdAutoRawTags: true,
37
59
  autoLinkFavicons: true,
38
60
  siteData: true,
39
- filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "fetch", "section"],
61
+ filters: ["attr_set", "attr_includes", "merge", "remove_tag", "if", "attr_concat", "fetch", "section", "strip_tag"],
40
62
  });
41
63
  if (pluginTOC) {
42
64
  eleventyConfig.addPlugin(pluginTOC, {
@@ -46,21 +68,25 @@ export default function (eleventyConfig) {
46
68
  }
47
69
 
48
70
  /* Libraries */
49
- eleventyConfig.setLibrary(
50
- "md",
51
- markdownIt({
52
- html: true,
53
- // breaks: true,
54
- linkify: true,
55
- })
56
- .use(markdownItAnchor, {
57
- permalink: markdownItAnchor.permalink.ariaHidden(),
58
- })
59
- .use(markdownItAttrs),
60
- );
71
+ eleventyConfig.setLiquidOptions({
72
+ dynamicPartials: false, // This allows unquoted includes like Jekyll
73
+ });
74
+ let md = markdownIt({
75
+ html: true,
76
+ linkify: true,
77
+ });
78
+ if (markdownItAnchor) {
79
+ md = md.use(markdownItAnchor, {
80
+ slugify: slugify, // @TODO: TRICKS
81
+ permalink: markdownItAnchor.permalink.ariaHidden(),
82
+ });
83
+ }
84
+ if (markdownItAttrs) md = md.use(markdownItAttrs);
85
+ eleventyConfig.setLibrary("md", md);
86
+ eleventyConfig.addFilter("markdownify", (content) => md.render(String(content ?? "")));
61
87
 
62
88
  /* Data */
63
- eleventyConfig.addGlobalData("layout", "__layout");
89
+ // eleventyConfig.addGlobalData("layout", "__layout");
64
90
  eleventyConfig.addDataExtension("yml", (contents) => yaml.load(contents));
65
91
 
66
92
  /* Build */
@@ -80,7 +106,7 @@ export default function (eleventyConfig) {
80
106
  return {
81
107
  dir: {
82
108
  input: inputDir,
83
- includes: "_theme",
109
+ // includes: "_theme",
84
110
  },
85
111
  };
86
112
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Strip a specified HTML element from provided HTML, keeping its inner content
3
+ *
4
+ * @param {string} html - The HTML content to process
5
+ * @param {string} tagName - The tag name to strip (opening/closing tags removed, inner content kept)
6
+ * @returns {string} The HTML with the specified tag stripped but its inner content preserved
7
+ */
8
+ export function stripTag(html, tagName) {
9
+ if (!html || typeof html !== "string") {
10
+ return html;
11
+ }
12
+
13
+ if (typeof tagName !== "string" || !tagName) {
14
+ return html;
15
+ }
16
+
17
+ // Escape special regex characters in tag name
18
+ const escapedTag = tagName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
19
+
20
+ // Remove opening tags (with optional attributes): <tag> or <tag attr="val">
21
+ const openingRegex = new RegExp(`<${escapedTag}(?:\\s[^>]*)?>`, "gi");
22
+ let result = html.replace(openingRegex, "");
23
+
24
+ // Remove closing tags: </tag>
25
+ const closingRegex = new RegExp(`<\\/${escapedTag}>`, "gi");
26
+ result = result.replace(closingRegex, "");
27
+
28
+ return result;
29
+ }
30
+
31
+ /**
32
+ * strip_tag filter - Strip a specified HTML element, keeping its inner content
33
+ *
34
+ * Usage in templates:
35
+ * {{ htmlContent | strip_tag('div') }}
36
+ *
37
+ * @param {Object} eleventyConfig - The Eleventy configuration object
38
+ */
39
+ export function stripTagFilter(eleventyConfig) {
40
+ eleventyConfig.addFilter("strip_tag", stripTag);
41
+ }
@@ -0,0 +1,74 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert";
3
+ import { stripTag } from "./strip_tag.js";
4
+
5
+ describe("stripTag", () => {
6
+ it("should strip a tag but keep its inner content", () => {
7
+ const html = "<div><p>Keep this</p></div>";
8
+ const result = stripTag(html, "div");
9
+
10
+ assert.strictEqual(result, "<p>Keep this</p>");
11
+ });
12
+
13
+ it("should strip multiple instances of the same tag", () => {
14
+ const html = "<div>First</div><p>Middle</p><div>Second</div>";
15
+ const result = stripTag(html, "div");
16
+
17
+ assert.strictEqual(result, "First<p>Middle</p>Second");
18
+ });
19
+
20
+ it("should handle tags with attributes", () => {
21
+ const html = '<div class="wrapper" id="main">Content</div>';
22
+ const result = stripTag(html, "div");
23
+
24
+ assert.strictEqual(result, "Content");
25
+ });
26
+
27
+ it("should only strip the specified tag, leaving others intact", () => {
28
+ const html = "<div><span>Keep span</span><em>Keep em</em></div>";
29
+ const result = stripTag(html, "div");
30
+
31
+ assert.strictEqual(result, "<span>Keep span</span><em>Keep em</em>");
32
+ });
33
+
34
+ it("should handle nested content with the same tag", () => {
35
+ const html = "<div>outer <div>inner</div> text</div>";
36
+ const result = stripTag(html, "div");
37
+
38
+ assert.strictEqual(result, "outer inner text");
39
+ });
40
+
41
+ it("should return original HTML if tag does not exist", () => {
42
+ const html = "<p>Some text</p>";
43
+ const result = stripTag(html, "div");
44
+
45
+ assert.strictEqual(result, html);
46
+ });
47
+
48
+ it("should handle empty or null input", () => {
49
+ assert.strictEqual(stripTag("", "div"), "");
50
+ assert.strictEqual(stripTag(null, "div"), null);
51
+ assert.strictEqual(stripTag(undefined, "div"), undefined);
52
+ });
53
+
54
+ it("should handle missing or invalid tagName", () => {
55
+ const html = "<div>Content</div>";
56
+ assert.strictEqual(stripTag(html, ""), html);
57
+ assert.strictEqual(stripTag(html, null), html);
58
+ assert.strictEqual(stripTag(html, undefined), html);
59
+ });
60
+
61
+ it("should be case-insensitive", () => {
62
+ const html = '<DIV class="foo">Content</DIV>';
63
+ const result = stripTag(html, "div");
64
+
65
+ assert.strictEqual(result, "Content");
66
+ });
67
+
68
+ it("should preserve whitespace and newlines inside the tag", () => {
69
+ const html = "<div>\n <p>Line 1</p>\n <p>Line 2</p>\n</div>";
70
+ const result = stripTag(html, "div");
71
+
72
+ assert.strictEqual(result, "\n <p>Line 1</p>\n <p>Line 2</p>\n");
73
+ });
74
+ });
package/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { mdAutoRawTags, mdAutoNl2br, transformAutoRaw, transformNl2br } from "./transforms/markdown.js";
1
+ import { mdAutoRawTags, mdAutoNl2br, transformAutoRaw, transformNl2br } from "./processors/markdown.js";
2
2
  import {
3
3
  autoLinkFavicons,
4
4
  isPlainUrlText,
@@ -6,11 +6,12 @@ import {
6
6
  buildFaviconLink,
7
7
  transformLink,
8
8
  replaceLinksInHtml,
9
- } from "./transforms/autoLinkFavicons.js";
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
+ import { stripTagFilter, stripTag } from "./filters/strip_tag.js";
14
15
  import { ifFilter, iff } from "./filters/if.js";
15
16
  import { attrConcatFilter, attrConcat } from "./filters/attr_concat.js";
16
17
  import { sectionFilter, section as sectionFn } from "./filters/section.js";
@@ -37,7 +38,7 @@ try {
37
38
  * @param {boolean} options.mdAutoRawTags - Enable mdAutoRawTags preprocessor (default: false)
38
39
  * @param {boolean} options.mdAutoNl2br - Enable mdAutoNl2br for \n to <br> conversion (default: false)
39
40
  * @param {boolean} options.autoLinkFavicons - Enable autoLinkFavicons to add favicons to plain text links (default: false)
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: [])
41
+ * @param {Array<string>} options.filters - Array of filter names to enable: 'attr_set', 'attr_includes', 'merge', 'remove_tag', 'strip_tag', 'if', 'attr_concat', 'section', 'fetch' (default: [])
41
42
  * @param {boolean} options.siteData - Enable site.year and site.prod global data (default: false)
42
43
  */
43
44
  export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
@@ -53,6 +54,7 @@ export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
53
54
  attr_includes: attrIncludesFilter,
54
55
  merge: mergeFilter,
55
56
  remove_tag: removeTagFilter,
57
+ strip_tag: stripTagFilter,
56
58
  if: ifFilter,
57
59
  attr_concat: attrConcatFilter,
58
60
  section: sectionFilter,
@@ -86,6 +88,7 @@ export {
86
88
  attrIncludesFilter,
87
89
  mergeFilter,
88
90
  removeTagFilter,
91
+ stripTagFilter,
89
92
  ifFilter,
90
93
  attrConcatFilter,
91
94
  sectionFilter,
@@ -104,6 +107,7 @@ export {
104
107
  replaceLinksInHtml,
105
108
  merge,
106
109
  removeTag,
110
+ stripTag,
107
111
  iff,
108
112
  attrConcat,
109
113
  attrSet,
@@ -1,96 +0,0 @@
1
- # Section Filter Examples
2
-
3
- ## Basic Usage
4
-
5
- ```markdown
6
- <!--section:intro-->
7
-
8
- This is the introduction section.
9
-
10
- <!--section:main-->
11
-
12
- This is the main content.
13
-
14
- <!--section:footer-->
15
-
16
- This is the footer.
17
- ```
18
-
19
- In your template:
20
-
21
- ```nunjucks
22
- {{ content | section('intro') }}
23
- ```
24
-
25
- Output:
26
-
27
- ```
28
- This is the introduction section.
29
- ```
30
-
31
- ## Multiple Names per Section
32
-
33
- ```markdown
34
- <!--section:summary,abstract-->
35
-
36
- This content appears in both 'summary' and 'abstract' sections.
37
-
38
- <!--section:conclusion-->
39
-
40
- Final thoughts.
41
- ```
42
-
43
- Both of these work:
44
-
45
- ```nunjucks
46
- {{ content | section('summary') }}
47
- {{ content | section('abstract') }}
48
- ```
49
-
50
- ## Real-World Example
51
-
52
- ```markdown
53
- # Research Paper
54
-
55
- <!--section:abstract,summary-->
56
-
57
- A brief overview of the research findings.
58
-
59
- <!--section:introduction-->
60
-
61
- Background and context for the research.
62
-
63
- <!--section:methodology-->
64
-
65
- How the research was conducted.
66
-
67
- <!--section:results-->
68
-
69
- Key findings from the study.
70
-
71
- <!--section:conclusion,summary-->
72
-
73
- Summary and implications of the research.
74
- ```
75
-
76
- Usage:
77
-
78
- ```nunjucks
79
- <!-- Get full summary (abstract + conclusion) -->
80
- <div class="summary">
81
- {{ content | section('summary') }}
82
- </div>
83
-
84
- <!-- Get just introduction -->
85
- <div class="intro">
86
- {{ content | section('introduction') }}
87
- </div>
88
- ```
89
-
90
- ## Notes
91
-
92
- - Section names are **case-insensitive**: `intro`, `INTRO`, and `Intro` are all the same
93
- - Multiple names can be comma-separated: `<!--section:name1,name2,name3-->`
94
- - Whitespace around names is trimmed
95
- - Content extends from the section marker to the next `<!--section*>` or EOF
96
- - If a section name appears multiple times, all matching sections are concatenated
package/src/index.cjs DELETED
@@ -1,51 +0,0 @@
1
- /**
2
- * CommonJS wrapper for 11ty Bricks Plugin
3
- * Provides compatibility for projects using require()
4
- */
5
-
6
- // Dynamic import for ES modules
7
- module.exports = async function eleventyBricksPlugin(eleventyConfig, options) {
8
- const { default: plugin } = await import("./index.js");
9
- return plugin(eleventyConfig, options);
10
- };
11
-
12
- // Export individual helpers for granular usage
13
- [
14
- "mdAutoRawTags",
15
- "mdAutoNl2br",
16
- "autoLinkFavicons",
17
- "attrSetFilter",
18
- "attrIncludesFilter",
19
- "mergeFilter",
20
- "removeTagFilter",
21
- "ifFilter",
22
- "attrConcatFilter",
23
- "fetchFilter",
24
- "siteData",
25
- ].forEach((name) => {
26
- module.exports[name] = async (...args) => {
27
- const module = await import("./index.js");
28
- return module[name](...args);
29
- };
30
- });
31
-
32
- // Export transform/utility functions for advanced usage
33
- [
34
- "transformAutoRaw",
35
- "transformNl2br",
36
- "isPlainUrlText",
37
- "cleanLinkText",
38
- "buildFaviconLink",
39
- "transformLink",
40
- "replaceLinksInHtml",
41
- "merge",
42
- "removeTag",
43
- "iff",
44
- "attrConcat",
45
- "attrSet",
46
- ].forEach((name) => {
47
- module.exports[name] = async (...args) => {
48
- const module = await import("./index.js");
49
- return module[name](...args);
50
- };
51
- });
File without changes
File without changes