@jsnchn/buntastic 0.0.12 → 0.2.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 +23 -0
- package/README.md +24 -1
- package/package.json +1 -1
- package/src/index.ts +63 -6
- package/src/layouts/base.html +4 -1
- package/src/layouts/page.html +2 -0
- package/src/layouts/post.html +5 -0
package/AGENTS.md
CHANGED
|
@@ -64,6 +64,7 @@ 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) |
|
|
67
68
|
|
|
68
69
|
## Layout System
|
|
69
70
|
|
|
@@ -82,6 +83,28 @@ extends: base.html
|
|
|
82
83
|
|
|
83
84
|
The `{{ content | safe }}` placeholder is where child content gets injected.
|
|
84
85
|
|
|
86
|
+
## Head Block
|
|
87
|
+
|
|
88
|
+
Layouts can inject content into the `<head>` section using `[head]...[/head]` blocks:
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<!-- layouts/post.html -->
|
|
92
|
+
---
|
|
93
|
+
extends: base.html
|
|
94
|
+
---
|
|
95
|
+
[head]
|
|
96
|
+
<link rel="stylesheet" href="/post.css">
|
|
97
|
+
[/head]
|
|
98
|
+
[content]
|
|
99
|
+
<article class="post">
|
|
100
|
+
<h1>{{ title }}</h1>
|
|
101
|
+
{{ content | safe }}
|
|
102
|
+
</article>
|
|
103
|
+
[/content]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Content inside `[head]` blocks is automatically placed in the parent's `<head>` section. Child layout head content is appended to parent head content.
|
|
107
|
+
|
|
85
108
|
## Development
|
|
86
109
|
|
|
87
110
|
1. Add content to `content/` folder
|
package/README.md
CHANGED
|
@@ -164,11 +164,12 @@ extends: base.html
|
|
|
164
164
|
| Variable | Description |
|
|
165
165
|
|----------|-------------|
|
|
166
166
|
| `{{ title }}` | Page title |
|
|
167
|
-
| `{{ content
|
|
167
|
+
| `{{ content | safe }}` | Rendered markdown HTML |
|
|
168
168
|
| `{{ date }}` | Page date |
|
|
169
169
|
| `{{ description }}` | Meta description |
|
|
170
170
|
| `{{ url }}` | Current page URL |
|
|
171
171
|
| `{{ collection }}` | List of posts in current folder (for index pages) |
|
|
172
|
+
| `{{ head | safe }}` | Head content from child layouts (appended to parent) |
|
|
172
173
|
|
|
173
174
|
### Layout Inheritance
|
|
174
175
|
|
|
@@ -187,6 +188,28 @@ Use `extends:` to build on top of other layouts:
|
|
|
187
188
|
</html>
|
|
188
189
|
```
|
|
189
190
|
|
|
191
|
+
### Head Block
|
|
192
|
+
|
|
193
|
+
Layouts can inject content into the `<head>` section using `[head]...[/head]` blocks:
|
|
194
|
+
|
|
195
|
+
```html
|
|
196
|
+
<!-- layouts/post.html -->
|
|
197
|
+
---
|
|
198
|
+
extends: base.html
|
|
199
|
+
---
|
|
200
|
+
[head]
|
|
201
|
+
<link rel="stylesheet" href="/post.css">
|
|
202
|
+
[/head]
|
|
203
|
+
[content]
|
|
204
|
+
<article class="post">
|
|
205
|
+
<h1>{{ title }}</h1>
|
|
206
|
+
{{ content | safe }}
|
|
207
|
+
</article>
|
|
208
|
+
[/content]
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Content inside `[head]` blocks is automatically placed in the parent's `<head>` section. Child layout head content is appended to parent head content.
|
|
212
|
+
|
|
190
213
|
## Collections
|
|
191
214
|
|
|
192
215
|
For folder index pages (e.g., `content/posts/index.md`), use `{{ collection }}` to list all pages in that folder:
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -94,21 +94,78 @@ async function readLayout(layoutName: string): Promise<string> {
|
|
|
94
94
|
return await readFile(layoutPath, "utf-8");
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
async function resolveLayout(frontmatter: Frontmatter): Promise<string> {
|
|
97
|
+
async function resolveLayout(frontmatter: Frontmatter): Promise<{ template: string; head: string }> {
|
|
98
98
|
const layoutName = frontmatter.layout || frontmatter.extends || "page";
|
|
99
99
|
let template = await readLayout(layoutName);
|
|
100
100
|
|
|
101
101
|
const extendsMatch = template.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
102
|
+
|
|
103
|
+
let childHead = "";
|
|
104
|
+
let childBody = "";
|
|
105
|
+
|
|
102
106
|
if (extendsMatch) {
|
|
103
107
|
const parentLayout = extendsMatch[1].match(/extends:\s*(\w+)/);
|
|
104
108
|
if (parentLayout) {
|
|
105
|
-
const parentTemplate = await resolveLayout({ extends: parentLayout[1] } as Frontmatter);
|
|
106
109
|
const childContent = extendsMatch[2];
|
|
107
|
-
|
|
110
|
+
|
|
111
|
+
const headMatch = childContent.match(/\[head\]([\s\S]*?)\[\/head\]/);
|
|
112
|
+
if (headMatch) {
|
|
113
|
+
childHead = headMatch[1].trim();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const bodyMatch = childContent.match(/\[content\]([\s\S]*?)\[\/content\]/);
|
|
117
|
+
if (bodyMatch) {
|
|
118
|
+
childBody = bodyMatch[1];
|
|
119
|
+
} else {
|
|
120
|
+
childBody = childContent;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const parentResult = await resolveLayout({ extends: parentLayout[1] } as Frontmatter);
|
|
124
|
+
|
|
125
|
+
let contentReplaced = parentResult.template;
|
|
126
|
+
|
|
127
|
+
if (bodyMatch) {
|
|
128
|
+
contentReplaced = contentReplaced.replace(
|
|
129
|
+
/\[content\]([\s\S]*?)\[\/content\]/g,
|
|
130
|
+
childBody
|
|
131
|
+
);
|
|
132
|
+
} else {
|
|
133
|
+
contentReplaced = contentReplaced.replace(
|
|
134
|
+
/\{\{\s*content\s*\|\s*safe\s*\}\}/g,
|
|
135
|
+
childBody
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const mergedHead = parentResult.head + (childHead ? "\n" + childHead : "");
|
|
140
|
+
|
|
141
|
+
if (headMatch) {
|
|
142
|
+
contentReplaced = contentReplaced.replace(
|
|
143
|
+
/\[head\]([\s\S]*?)\[\/head\]/g,
|
|
144
|
+
mergedHead
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
contentReplaced = contentReplaced.replace(
|
|
148
|
+
/\[head\]([\s\S]*?)\[\/head\]/g,
|
|
149
|
+
mergedHead
|
|
150
|
+
);
|
|
151
|
+
contentReplaced = contentReplaced.replace(
|
|
152
|
+
/\{\{\s*head\s*\|\s*safe\s*\}\}/g,
|
|
153
|
+
mergedHead
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { template: contentReplaced, head: mergedHead };
|
|
108
158
|
}
|
|
109
159
|
}
|
|
110
160
|
|
|
111
|
-
|
|
161
|
+
let ownHead = "";
|
|
162
|
+
if (extendsMatch) {
|
|
163
|
+
const bodyMatch = extendsMatch[2];
|
|
164
|
+
const headMatch = bodyMatch.match(/\[head\]([\s\S]*?)\[\/head\]/);
|
|
165
|
+
ownHead = headMatch ? headMatch[1].trim() : "";
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return { template, head: ownHead };
|
|
112
169
|
}
|
|
113
170
|
|
|
114
171
|
function applyTemplate(template: string, page: Page, collection?: CollectionItem[]): string {
|
|
@@ -213,7 +270,7 @@ async function build(includeDrafts = false): Promise<void> {
|
|
|
213
270
|
}
|
|
214
271
|
|
|
215
272
|
for (const page of pages) {
|
|
216
|
-
let template = await resolveLayout(page.frontmatter);
|
|
273
|
+
let { template } = await resolveLayout(page.frontmatter);
|
|
217
274
|
template = template.replace(/\{\{\s*content\s*\|\s*safe\s*\}\}/g, page.html);
|
|
218
275
|
|
|
219
276
|
let collection: CollectionItem[] | undefined;
|
|
@@ -266,7 +323,7 @@ async function build(includeDrafts = false): Promise<void> {
|
|
|
266
323
|
if (await exists(join(CONTENT_DIR, "404.md"))) {
|
|
267
324
|
const page404 = await buildPage(join(CONTENT_DIR, "404.md"), includeDrafts);
|
|
268
325
|
if (page404) {
|
|
269
|
-
let template = await resolveLayout(page404.frontmatter);
|
|
326
|
+
let { template } = await resolveLayout(page404.frontmatter);
|
|
270
327
|
template = template.replace(/\{\{\s*content\s*\|\s*safe\s*\}\}/g, page404.html);
|
|
271
328
|
const outputHtml = applyTemplate(template, page404);
|
|
272
329
|
await writeFile(join(DIST_DIR, "404.html"), outputHtml);
|
package/src/layouts/base.html
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
<title>{{ title }}</title>
|
|
7
7
|
<meta name="description" content="{{ description }}">
|
|
8
8
|
<link rel="stylesheet" href="/style.css">
|
|
9
|
+
[head]
|
|
10
|
+
[/head]
|
|
9
11
|
</head>
|
|
10
12
|
<body>
|
|
11
13
|
<header>
|
|
@@ -16,7 +18,8 @@
|
|
|
16
18
|
</nav>
|
|
17
19
|
</header>
|
|
18
20
|
<main>
|
|
19
|
-
|
|
21
|
+
[content]
|
|
22
|
+
[/content]
|
|
20
23
|
</main>
|
|
21
24
|
<footer>
|
|
22
25
|
<p>Built with BunPress</p>
|
package/src/layouts/page.html
CHANGED
package/src/layouts/post.html
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
extends: base.html
|
|
3
3
|
---
|
|
4
|
+
[head]
|
|
5
|
+
<link rel="stylesheet" href="/post.css">
|
|
6
|
+
[/head]
|
|
7
|
+
[content]
|
|
4
8
|
<article class="post">
|
|
5
9
|
<header>
|
|
6
10
|
<h1>{{ title }}</h1>
|
|
@@ -10,3 +14,4 @@ extends: base.html
|
|
|
10
14
|
{{ content | safe }}
|
|
11
15
|
</div>
|
|
12
16
|
</article>
|
|
17
|
+
[/content]
|