@jsnchn/buntastic 0.1.0 → 0.3.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/AGENTS.md CHANGED
@@ -64,7 +64,6 @@ draft: false # Set true to exclude from production build
64
64
  | `{{ description }}` | Page description |
65
65
  | `{{ url }}` | Current page URL |
66
66
  | `{{ collection }}` | Array of posts in current folder (for index pages) |
67
- | `{{ head \| safe }}` | Head content from child layouts (appended to parent) |
68
67
 
69
68
  ## Layout System
70
69
 
@@ -85,22 +84,23 @@ The `{{ content | safe }}` placeholder is where child content gets injected.
85
84
 
86
85
  ## Head Block
87
86
 
88
- Layouts can inject content into the `<head>` section using `{{ head | safe }}`. Content BEFORE the marker goes into `<head>`, content AFTER the marker is treated as the layout body:
87
+ Layouts can inject content into the `<head>` section using `[head]...[/head]` blocks. Content inside these blocks is automatically merged and appended to the `<head>` element in the parent layout:
89
88
 
90
89
  ```html
91
90
  <!-- layouts/post.html -->
92
91
  ---
93
92
  extends: base.html
94
93
  ---
94
+ [head]
95
95
  <link rel="stylesheet" href="/post.css">
96
- {{ head | safe }}
96
+ [/head]
97
97
  <article class="post">
98
98
  <h1>{{ title }}</h1>
99
99
  {{ content | safe }}
100
100
  </article>
101
101
  ```
102
102
 
103
- The `{{ head | safe }}` marker is where additional head content can be injected (from child layouts or page frontmatter). Content before the marker (e.g., the post.css link) is automatically placed in the parent's `<head>` section. Child layout head content is appended to parent head content (parent's head first, then child's).
103
+ Head content from child layouts is appended to parent head content.
104
104
 
105
105
  ## Development
106
106
 
package/README.md CHANGED
@@ -164,7 +164,7 @@ extends: base.html
164
164
  | Variable | Description |
165
165
  |----------|-------------|
166
166
  | `{{ title }}` | Page title |
167
- | `{{ content \| safe }}` | Rendered markdown HTML |
167
+ | `{{ content | safe }}` | Rendered markdown HTML |
168
168
  | `{{ date }}` | Page date |
169
169
  | `{{ description }}` | Meta description |
170
170
  | `{{ url }}` | Current page URL |
@@ -187,6 +187,26 @@ Use `extends:` to build on top of other layouts:
187
187
  </html>
188
188
  ```
189
189
 
190
+ ### Head Block
191
+
192
+ Layouts can inject content into the `<head>` section using `[head]...[/head]` blocks. Content inside these blocks is automatically merged and appended to the `<head>` element in the parent layout:
193
+
194
+ ```html
195
+ <!-- layouts/post.html -->
196
+ ---
197
+ extends: base.html
198
+ ---
199
+ [head]
200
+ <link rel="stylesheet" href="/post.css">
201
+ [/head]
202
+ <article class="post">
203
+ <h1>{{ title }}</h1>
204
+ {{ content | safe }}
205
+ </article>
206
+ ```
207
+
208
+ Head content from child layouts is appended to parent head content.
209
+
190
210
  ## Collections
191
211
 
192
212
  For folder index pages (e.g., `content/posts/index.md`), use `{{ collection }}` to list all pages in that folder:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsnchn/buntastic",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "A simple static site generator built with Bun",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -101,25 +101,25 @@ async function resolveLayout(frontmatter: Frontmatter): Promise<{ template: stri
101
101
  const extendsMatch = template.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
102
102
 
103
103
  let childHead = "";
104
- let childBody = template;
104
+ let childBody = "";
105
105
 
106
106
  if (extendsMatch) {
107
107
  const parentLayout = extendsMatch[1].match(/extends:\s*(\w+)/);
108
108
  if (parentLayout) {
109
- childBody = extendsMatch[2];
110
- const headPlaceholderMatch = childBody.match(/(\{\{\s*head\s*\|\s*safe\s*\}\})/);
109
+ const childContent = extendsMatch[2];
111
110
 
112
- if (headPlaceholderMatch) {
113
- const placeholderIdx = headPlaceholderMatch.index || 0;
114
- const beforePlaceholder = childBody.substring(0, placeholderIdx).trim();
115
- const afterPlaceholder = childBody.substring(placeholderIdx + headPlaceholderMatch[0].length).trim();
116
- childHead = beforePlaceholder;
117
- childBody = afterPlaceholder;
111
+ const headMatch = childContent.match(/\[head\]([\s\S]*?)\[\/head\]/);
112
+ if (headMatch) {
113
+ childHead = headMatch[1].trim();
118
114
  }
115
+
116
+ childBody = childContent;
119
117
 
120
118
  const parentResult = await resolveLayout({ extends: parentLayout[1] } as Frontmatter);
121
119
 
122
- let contentReplaced = parentResult.template.replace(
120
+ let contentReplaced = parentResult.template;
121
+
122
+ contentReplaced = contentReplaced.replace(
123
123
  /\{\{\s*content\s*\|\s*safe\s*\}\}/g,
124
124
  childBody
125
125
  );
@@ -127,8 +127,8 @@ async function resolveLayout(frontmatter: Frontmatter): Promise<{ template: stri
127
127
  const mergedHead = parentResult.head + (childHead ? "\n" + childHead : "");
128
128
 
129
129
  contentReplaced = contentReplaced.replace(
130
- /\{\{\s*head\s*\|\s*safe\s*\}\}/g,
131
- mergedHead
130
+ /<\/head>/i,
131
+ `\n${mergedHead}\n</head>`
132
132
  );
133
133
 
134
134
  return { template: contentReplaced, head: mergedHead };
@@ -137,9 +137,8 @@ async function resolveLayout(frontmatter: Frontmatter): Promise<{ template: stri
137
137
 
138
138
  let ownHead = "";
139
139
  if (extendsMatch) {
140
- const bodyMatch = extendsMatch[2];
141
- const headMatch = bodyMatch.match(/\{\{\s*head\s*\|\s*safe\s*\}\}/);
142
- ownHead = headMatch ? headMatch[0] : "";
140
+ const headMatch = extendsMatch[2].match(/\[head\]([\s\S]*?)\[\/head\]/);
141
+ ownHead = headMatch ? headMatch[1].trim() : "";
143
142
  }
144
143
 
145
144
  return { template, head: ownHead };
@@ -6,7 +6,6 @@
6
6
  <title>{{ title }}</title>
7
7
  <meta name="description" content="{{ description }}">
8
8
  <link rel="stylesheet" href="/style.css">
9
- {{ head | safe }}
10
9
  </head>
11
10
  <body>
12
11
  <header>
@@ -1,8 +1,9 @@
1
1
  ---
2
2
  extends: base.html
3
3
  ---
4
+ [head]
4
5
  <link rel="stylesheet" href="/post.css">
5
- {{ head | safe }}
6
+ [/head]
6
7
  <article class="post">
7
8
  <header>
8
9
  <h1>{{ title }}</h1>