@jsnchn/buntastic 0.0.12 → 0.1.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 +20 -0
- package/package.json +1 -1
- package/src/index.ts +41 -7
- package/src/layouts/base.html +1 -0
- package/src/layouts/post.html +2 -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,25 @@ 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 | safe }}`. Content BEFORE the marker goes into `<head>`, content AFTER the marker is treated as the layout body:
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<!-- layouts/post.html -->
|
|
92
|
+
---
|
|
93
|
+
extends: base.html
|
|
94
|
+
---
|
|
95
|
+
<link rel="stylesheet" href="/post.css">
|
|
96
|
+
{{ head | safe }}
|
|
97
|
+
<article class="post">
|
|
98
|
+
<h1>{{ title }}</h1>
|
|
99
|
+
{{ content | safe }}
|
|
100
|
+
</article>
|
|
101
|
+
```
|
|
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).
|
|
104
|
+
|
|
85
105
|
## Development
|
|
86
106
|
|
|
87
107
|
1. Add content to `content/` folder
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -94,21 +94,55 @@ 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 = template;
|
|
105
|
+
|
|
102
106
|
if (extendsMatch) {
|
|
103
107
|
const parentLayout = extendsMatch[1].match(/extends:\s*(\w+)/);
|
|
104
108
|
if (parentLayout) {
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
109
|
+
childBody = extendsMatch[2];
|
|
110
|
+
const headPlaceholderMatch = childBody.match(/(\{\{\s*head\s*\|\s*safe\s*\}\})/);
|
|
111
|
+
|
|
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;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const parentResult = await resolveLayout({ extends: parentLayout[1] } as Frontmatter);
|
|
121
|
+
|
|
122
|
+
let contentReplaced = parentResult.template.replace(
|
|
123
|
+
/\{\{\s*content\s*\|\s*safe\s*\}\}/g,
|
|
124
|
+
childBody
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const mergedHead = parentResult.head + (childHead ? "\n" + childHead : "");
|
|
128
|
+
|
|
129
|
+
contentReplaced = contentReplaced.replace(
|
|
130
|
+
/\{\{\s*head\s*\|\s*safe\s*\}\}/g,
|
|
131
|
+
mergedHead
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
return { template: contentReplaced, head: mergedHead };
|
|
108
135
|
}
|
|
109
136
|
}
|
|
110
137
|
|
|
111
|
-
|
|
138
|
+
let ownHead = "";
|
|
139
|
+
if (extendsMatch) {
|
|
140
|
+
const bodyMatch = extendsMatch[2];
|
|
141
|
+
const headMatch = bodyMatch.match(/\{\{\s*head\s*\|\s*safe\s*\}\}/);
|
|
142
|
+
ownHead = headMatch ? headMatch[0] : "";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { template, head: ownHead };
|
|
112
146
|
}
|
|
113
147
|
|
|
114
148
|
function applyTemplate(template: string, page: Page, collection?: CollectionItem[]): string {
|
|
@@ -213,7 +247,7 @@ async function build(includeDrafts = false): Promise<void> {
|
|
|
213
247
|
}
|
|
214
248
|
|
|
215
249
|
for (const page of pages) {
|
|
216
|
-
let template = await resolveLayout(page.frontmatter);
|
|
250
|
+
let { template } = await resolveLayout(page.frontmatter);
|
|
217
251
|
template = template.replace(/\{\{\s*content\s*\|\s*safe\s*\}\}/g, page.html);
|
|
218
252
|
|
|
219
253
|
let collection: CollectionItem[] | undefined;
|
|
@@ -266,7 +300,7 @@ async function build(includeDrafts = false): Promise<void> {
|
|
|
266
300
|
if (await exists(join(CONTENT_DIR, "404.md"))) {
|
|
267
301
|
const page404 = await buildPage(join(CONTENT_DIR, "404.md"), includeDrafts);
|
|
268
302
|
if (page404) {
|
|
269
|
-
let template = await resolveLayout(page404.frontmatter);
|
|
303
|
+
let { template } = await resolveLayout(page404.frontmatter);
|
|
270
304
|
template = template.replace(/\{\{\s*content\s*\|\s*safe\s*\}\}/g, page404.html);
|
|
271
305
|
const outputHtml = applyTemplate(template, page404);
|
|
272
306
|
await writeFile(join(DIST_DIR, "404.html"), outputHtml);
|
package/src/layouts/base.html
CHANGED