@anydigital/11ty-bricks 1.0.0-alpha.3 → 1.0.0-alpha.5

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 CHANGED
@@ -48,9 +48,10 @@ Import only the specific helpers you need without using the plugin:
48
48
 
49
49
  **ES Modules:**
50
50
  ```javascript
51
- import { autoRaw } from "@anydigital/11ty-bricks";
51
+ import { bricksRegistry, autoRaw } from "@anydigital/11ty-bricks";
52
52
 
53
53
  export default function(eleventyConfig) {
54
+ bricksRegistry(eleventyConfig);
54
55
  autoRaw(eleventyConfig);
55
56
 
56
57
  // Your other configuration...
@@ -59,9 +60,10 @@ export default function(eleventyConfig) {
59
60
 
60
61
  **CommonJS:**
61
62
  ```javascript
62
- const { autoRaw } = require("@anydigital/11ty-bricks");
63
+ const { bricksRegistry, autoRaw } = require("@anydigital/11ty-bricks");
63
64
 
64
65
  module.exports = function(eleventyConfig) {
66
+ bricksRegistry(eleventyConfig);
65
67
  autoRaw(eleventyConfig);
66
68
 
67
69
  // Your other configuration...
@@ -74,17 +76,122 @@ When using the plugin (Option 1), you can configure which helpers to enable:
74
76
 
75
77
  | Option | Type | Default | Description |
76
78
  |--------|------|---------|-------------|
79
+ | `bricksRegistry` | boolean | `false` | Enable the bricksRegistry system for dependency management |
77
80
  | `autoRaw` | boolean | `false` | Enable the autoRaw preprocessor for Markdown files |
78
81
 
79
82
  **Example:**
80
83
  ```javascript
81
84
  eleventyConfig.addPlugin(eleventyBricks, {
85
+ bricksRegistry: true,
82
86
  autoRaw: true
83
87
  });
84
88
  ```
85
89
 
86
90
  ## Available 11ty Helpers
87
91
 
92
+ ### bricksRegistry
93
+
94
+ A dependency management system for Eleventy that automatically collects and injects CSS and JavaScript dependencies (both external and inline) per page. This allows brick components to declare their dependencies, and the system will inject them in the correct location in your HTML.
95
+
96
+ **Why use this?**
97
+
98
+ When building reusable components (bricks) in Eleventy, you often need to include CSS and JavaScript dependencies. Instead of manually adding these to every page, `bricksRegistry` automatically:
99
+ - Collects dependencies from all bricks used on a page
100
+ - Categorizes them (external CSS, external JS, inline styles, inline scripts)
101
+ - Injects them in the correct location in your HTML output
102
+
103
+ **How it works:**
104
+
105
+ 1. Use the `rootBrick` shortcode in your base template to mark where dependencies should be injected
106
+ 2. Use the `brick` shortcode to register and render brick components that declare their dependencies
107
+ 3. The system automatically collects all dependencies and injects them when the page is built
108
+
109
+ **Usage:**
110
+
111
+ 1. Enable `bricksRegistry` in your Eleventy config:
112
+
113
+ ```javascript
114
+ import { bricksRegistry } from "@anydigital/11ty-bricks";
115
+
116
+ export default function(eleventyConfig) {
117
+ bricksRegistry(eleventyConfig);
118
+ // Or use as plugin:
119
+ // eleventyConfig.addPlugin(eleventyBricks, { bricksRegistry: true });
120
+ }
121
+ ```
122
+
123
+ 2. Add the `rootBrick` shortcode in your base template (typically in the `<head>` section):
124
+
125
+ ```njk
126
+ <head>
127
+ <meta charset="UTF-8">
128
+ <title>My Site</title>
129
+ {% rootBrick %}
130
+ <!-- Other head content -->
131
+ </head>
132
+ ```
133
+
134
+ 3. Create brick components that declare their dependencies:
135
+
136
+ ```javascript
137
+ // myBrick.js
138
+ export default {
139
+ dependencies: [
140
+ 'https://cdn.example.com/library.css',
141
+ 'https://cdn.example.com/library.js'
142
+ ],
143
+ style: `
144
+ .my-component { color: blue; }
145
+ `,
146
+ script: `
147
+ console.log('Component initialized');
148
+ `,
149
+ render: function() {
150
+ return '<div class="my-component">Hello World</div>';
151
+ }
152
+ };
153
+ ```
154
+
155
+ 4. Use the `brick` shortcode in your templates:
156
+
157
+ ```njk
158
+ {% set myBrick = require('./myBrick.js') %}
159
+ {% brick myBrick %}
160
+ ```
161
+
162
+ **Brick Component Structure:**
163
+
164
+ A brick component is a JavaScript object with the following optional properties:
165
+
166
+ - `dependencies`: Array of URLs to external CSS or JavaScript files (e.g., `['https://cdn.example.com/style.css', 'https://cdn.example.com/script.js']`)
167
+ - `style`: String containing inline CSS
168
+ - `script`: String containing inline JavaScript
169
+ - `render`: Function that returns the HTML markup for the component
170
+
171
+ **Output:**
172
+
173
+ The system will automatically inject all dependencies in the order they were registered:
174
+
175
+ ```html
176
+ <head>
177
+ <meta charset="UTF-8">
178
+ <title>My Site</title>
179
+ <link rel="stylesheet" href="https://cdn.example.com/library.css">
180
+ <style>.my-component { color: blue; }</style>
181
+ <script src="https://cdn.example.com/library.js"></script>
182
+ <script>console.log('Component initialized');</script>
183
+ <!-- Other head content -->
184
+ </head>
185
+ ```
186
+
187
+ **Features:**
188
+
189
+ - Automatic dependency collection per page
190
+ - Categorizes dependencies (CSS vs JS, external vs inline)
191
+ - Deduplicates dependencies (using Sets internally)
192
+ - Works with both external URLs and inline code
193
+ - Clears registry before each build to prevent stale data
194
+
88
195
  ### autoRaw
89
196
 
90
197
  Prevents Nunjucks syntax from being processed in Markdown files by automatically wrapping `{{`, `}}`, `{%`, and `%}` with `{% raw %}` tags.
@@ -102,6 +209,85 @@ Use {{ variable }} to output variables.
102
209
 
103
210
  Would try to process `{{ variable }}` as a template variable. With `autoRaw`, it displays exactly as written.
104
211
 
212
+ ## Available Bricks (Components)
213
+
214
+ ### Navigation Macro (`_nav.njk`)
215
+
216
+ A reusable Nunjucks macro for rendering navigation menus with proper accessibility attributes. This macro works seamlessly with the [11ty Navigation Plugin](https://www.11ty.dev/docs/plugins/navigation/).
217
+
218
+ **Usage:**
219
+
220
+ 1. Import the macro in your template:
221
+
222
+ ```njk
223
+ {% from "bricks/_nav.njk" import render as renderNav %}
224
+ ```
225
+
226
+ 2. Call the macro with your navigation data:
227
+
228
+ ```njk
229
+ {{ renderNav(collections.all | eleventyNavigation, page) }}
230
+ ```
231
+
232
+ **Parameters:**
233
+
234
+ - `navPages`: Array of navigation entries (typically from `eleventyNavigation` filter)
235
+ - `curPage`: Current page object (use Eleventy's `page` variable)
236
+
237
+ **Features:**
238
+
239
+ - Renders a semantic `<nav>` element
240
+ - Automatically adds `aria-current="page"` to the current page link for accessibility
241
+ - Clean, minimal markup ready for styling
242
+ - Works with nested navigation structures from the 11ty Navigation Plugin
243
+
244
+ **Example Output:**
245
+
246
+ ```html
247
+ <nav>
248
+ <a href="/">Home</a>
249
+ <a href="/about/">About</a>
250
+ <a href="/contact/" aria-current="page">Contact</a>
251
+ </nav>
252
+ ```
253
+
254
+ ### Google Tag Manager Macro (`_gtm.njk`)
255
+
256
+ A reusable Nunjucks macro for integrating Google Tag Manager (GTM) into your site. Provides separate macros for the head and body GTM snippets.
257
+
258
+ **Usage:**
259
+
260
+ 1. Import the macros in your base template:
261
+
262
+ ```njk
263
+ {% import 'bricks/_gtm.njk' as gtm %}
264
+ ```
265
+
266
+ 2. Call the macros in the appropriate locations:
267
+
268
+ ```njk
269
+ <head>
270
+ <!-- Other head content -->
271
+ {{ gtm.renderHead('GTM-XXXXXXX') }}
272
+ </head>
273
+ <body>
274
+ {{ gtm.renderBody('GTM-XXXXXXX') }}
275
+ <!-- Rest of body content -->
276
+ </body>
277
+ ```
278
+
279
+ **Parameters:**
280
+
281
+ Both macros accept the same parameter:
282
+ - `gtmId`: Your Google Tag Manager container ID (e.g., `'GTM-XXXXXXX'`)
283
+
284
+ **Features:**
285
+
286
+ - Separate macros for head and body placement as recommended by Google
287
+ - Clean, standard GTM implementation
288
+ - Easy to maintain and update across your site
289
+ - Works with all GTM features including noscript fallback
290
+
105
291
  ## CLI Helper Commands
106
292
 
107
293
  After installing this package, the `download-files` command becomes available:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anydigital/11ty-bricks",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.5",
4
4
  "description": "A collection of helpful utilities and filters for Eleventy (11ty)",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -11,7 +11,7 @@
11
11
  }
12
12
  },
13
13
  "bin": {
14
- "download-files": "./src/cli/download-files.js"
14
+ "download-files": "src/cli/download-files.js"
15
15
  },
16
16
  "files": [
17
17
  "src"
@@ -0,0 +1,16 @@
1
+ {% macro renderHead(gtmId) %}
2
+ <!-- Google Tag Manager -->
3
+ <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
4
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
5
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
6
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
7
+ })(window,document,'script','dataLayer','{{ gtmId }}');</script>
8
+ <!-- End Google Tag Manager -->
9
+ {% endmacro %}
10
+
11
+ {% macro renderBody(gtmId) %}
12
+ <!-- Google Tag Manager (noscript) -->
13
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ gtmId }}"
14
+ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
15
+ <!-- End Google Tag Manager (noscript) -->
16
+ {% endmacro %}
@@ -0,0 +1,10 @@
1
+ {% macro render(navPages, curPage) %}
2
+ {# https://www.11ty.dev/docs/plugins/navigation/#bring-your-own-html-render-the-menu-items-manually #}
3
+ <nav>
4
+ {%- for entry in navPages %}
5
+ <a href="{{ entry.url }}" {{ 'aria-current="page"' | safe if entry.url == curPage.url }}>
6
+ {{- entry.title -}}
7
+ </a>
8
+ {%- endfor %}
9
+ </nav>
10
+ {% endmacro %}
@@ -0,0 +1,125 @@
1
+ export function bricksRegistry(eleventyConfig) {
2
+
3
+ // Brick Registry System
4
+ // Global registry to track dependencies per page
5
+ const brickRegistry = new Map();
6
+
7
+ // Helper to get or create page registry
8
+ function getPageRegistry(page) {
9
+ const pageUrl = page.url || page.outputPath || 'default';
10
+ if (!brickRegistry.has(pageUrl)) {
11
+ brickRegistry.set(pageUrl, {
12
+ dependencies: new Set(), // Raw dependencies (URLs) - categorized later
13
+ inlineStyles: new Set(),
14
+ inlineScripts: new Set()
15
+ });
16
+ }
17
+ return brickRegistry.get(pageUrl);
18
+ }
19
+
20
+ // Clear registry before each build
21
+ eleventyConfig.on("eleventy.before", async () => {
22
+ brickRegistry.clear();
23
+ });
24
+
25
+ // brick shortcode: registers and renders a brick component
26
+ eleventyConfig.addShortcode("brick", function(brickModule) {
27
+ const registry = getPageRegistry(this.page);
28
+
29
+ if (!brickModule) return '';
30
+
31
+ // Register external dependencies (categorized later in transform)
32
+ if (brickModule.dependencies) {
33
+ brickModule.dependencies.forEach(dep => {
34
+ registry.dependencies.add(dep);
35
+ });
36
+ }
37
+
38
+ // Register inline styles directly from style variable
39
+ if (brickModule.style && brickModule.style.trim()) {
40
+ registry.inlineStyles.add(brickModule.style);
41
+ }
42
+
43
+ // Register inline scripts directly from script variable
44
+ if (brickModule.script && brickModule.script.trim()) {
45
+ registry.inlineScripts.add(brickModule.script);
46
+ }
47
+
48
+ // Render the brick using render() macro
49
+ if (brickModule.render && typeof brickModule.render === 'function') {
50
+ return brickModule.render();
51
+ }
52
+
53
+ return '';
54
+ });
55
+
56
+ // rootBrick shortcode: outputs placeholder and base dependencies
57
+ eleventyConfig.addShortcode("rootBrick", function(options = {}) {
58
+ const registry = getPageRegistry(this.page);
59
+
60
+ // Register root dependencies if provided (categorized later in transform)
61
+ if (options.dependencies) {
62
+ options.dependencies.forEach(dep => {
63
+ registry.dependencies.add(dep);
64
+ });
65
+ }
66
+
67
+ // Return placeholder comment that will be replaced by transform
68
+ return '<!-- BRICK_DEPENDENCIES_PLACEHOLDER -->';
69
+ });
70
+
71
+ // Transform to inject collected dependencies
72
+ eleventyConfig.addTransform("injectBrickDependencies", function(content, outputPath) {
73
+ if (!outputPath || !outputPath.endsWith(".html")) {
74
+ return content;
75
+ }
76
+
77
+ const pageUrl = this.page?.url || this.page?.outputPath || outputPath;
78
+ const registry = brickRegistry.get(pageUrl);
79
+
80
+ if (!registry || !content.includes('<!-- BRICK_DEPENDENCIES_PLACEHOLDER -->')) {
81
+ return content;
82
+ }
83
+
84
+ // Categorize dependencies by type
85
+ const externalStyles = [];
86
+ const externalScripts = [];
87
+
88
+ registry.dependencies.forEach(dep => {
89
+ // Categorize by type
90
+ if (dep.endsWith('.css') || dep.includes('.css?')) {
91
+ externalStyles.push(dep);
92
+ } else if (dep.endsWith('.js') || dep.includes('.js?')) {
93
+ externalScripts.push(dep);
94
+ }
95
+ });
96
+
97
+ // Build HTML for dependencies
98
+ let dependenciesHtml = '\n';
99
+
100
+ // Add external CSS links
101
+ externalStyles.forEach(href => {
102
+ dependenciesHtml += ` <link rel="stylesheet" href="${href}">\n`;
103
+ });
104
+
105
+ // Add inline styles
106
+ registry.inlineStyles.forEach(style => {
107
+ dependenciesHtml += ` <style>${style}</style>\n`;
108
+ });
109
+
110
+ // Add external script links
111
+ externalScripts.forEach(src => {
112
+ dependenciesHtml += ` <script src="${src}"></script>\n`;
113
+ });
114
+
115
+ // Add inline scripts
116
+ registry.inlineScripts.forEach(script => {
117
+ dependenciesHtml += ` <script>${script}</script>\n`;
118
+ });
119
+
120
+ dependenciesHtml += ' ';
121
+
122
+ // Replace placeholder with actual dependencies
123
+ return content.replace('<!-- BRICK_DEPENDENCIES_PLACEHOLDER -->', dependenciesHtml);
124
+ });
125
+ }
package/src/index.cjs CHANGED
@@ -10,8 +10,11 @@ module.exports = async function eleventyBricksPlugin(eleventyConfig, options) {
10
10
  };
11
11
 
12
12
  // Export individual helpers
13
+ module.exports.bricksRegistry = async function(eleventyConfig) {
14
+ const { bricksRegistry } = await import('./index.js');
15
+ return bricksRegistry(eleventyConfig);
16
+ };
13
17
  module.exports.autoRaw = async function(eleventyConfig) {
14
18
  const { autoRaw } = await import('./index.js');
15
19
  return autoRaw(eleventyConfig);
16
20
  };
17
-
package/src/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { bricksRegistry } from "./bricksRegistry.js";
1
2
  import { autoRaw } from "./autoRaw.js";
2
3
 
3
4
  /**
@@ -8,17 +9,18 @@ import { autoRaw } from "./autoRaw.js";
8
9
  *
9
10
  * @param {Object} eleventyConfig - The Eleventy configuration object
10
11
  * @param {Object} options - Plugin options
12
+ * @param {boolean} options.bricksRegistry - Enable bricksRegistry (default: false)
11
13
  * @param {boolean} options.autoRaw - Enable autoRaw preprocessor (default: false)
12
14
  */
13
15
  export default function eleventyBricksPlugin(eleventyConfig, options = {}) {
14
- const { autoRaw: enableAutoRaw = false } = options;
15
-
16
- // Register helpers based on options
17
- if (enableAutoRaw) {
16
+ if (options.bricksRegistry) {
17
+ bricksRegistry(eleventyConfig);
18
+ }
19
+ if (options.autoRaw) {
18
20
  autoRaw(eleventyConfig);
19
21
  }
20
22
  }
21
23
 
22
24
  // Export individual helpers for granular usage
25
+ export { bricksRegistry };
23
26
  export { autoRaw };
24
-