@zeropress/build-core 0.7.5 → 0.7.6
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 +16 -1
- package/package.json +2 -2
- package/src/api/build-site.js +11 -6
- package/src/index.d.ts +10 -0
- package/src/render/menus.js +44 -0
package/README.md
CHANGED
|
@@ -118,7 +118,8 @@ Returns:
|
|
|
118
118
|
size: 1234,
|
|
119
119
|
sha256: '...'
|
|
120
120
|
}
|
|
121
|
-
]
|
|
121
|
+
],
|
|
122
|
+
warnings: []
|
|
122
123
|
}
|
|
123
124
|
```
|
|
124
125
|
|
|
@@ -145,6 +146,20 @@ This is useful for local tooling that wants filesystem theme loading but still u
|
|
|
145
146
|
Theme validation failures from either build entry point carry `error.code === 'THEME_VALIDATION_FAILED'`.
|
|
146
147
|
Their messages preserve report and hint line breaks while escaping terminal control characters in diagnostic values such as paths, messages, and source snippets. CLI consumers can preserve the layout of these reports while escaping arbitrary errors as single-line text.
|
|
147
148
|
|
|
149
|
+
## Menu depth
|
|
150
|
+
|
|
151
|
+
`theme.json.menu_slots.<menu_id>.max_depth` optionally limits rendered depth
|
|
152
|
+
(top-level items are depth 1). Omission leaves that menu unchanged. Items at the
|
|
153
|
+
limit remain, with empty `children`; deeper descendants are excluded, without
|
|
154
|
+
changing Preview Data. Direct `menus.<id>.items` loops and `{{menu:<id>}}` use
|
|
155
|
+
the same tree.
|
|
156
|
+
|
|
157
|
+
Both build APIs always return `warnings`. A depth overrun produces one
|
|
158
|
+
`MENU_MAX_DEPTH_EXCEEDED` warning per menu per build, containing `message`,
|
|
159
|
+
`menuId`, `maxDepth`, `actualDepth`, and `omittedItems` (all excluded
|
|
160
|
+
descendants). It does not fail the build. The library does not print warnings;
|
|
161
|
+
callers decide how to display them.
|
|
162
|
+
|
|
148
163
|
## Writers
|
|
149
164
|
|
|
150
165
|
### `MemoryWriter`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeropress/build-core",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"description": "Shared ZeroPress build core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@zeropress/slug-policy": "0.2.2",
|
|
41
41
|
"@zeropress/preview-data-validator": "0.7.4",
|
|
42
|
-
"@zeropress/theme-validator": "0.7.
|
|
42
|
+
"@zeropress/theme-validator": "0.7.4",
|
|
43
43
|
"highlight.js": "11.12.0",
|
|
44
44
|
"htmlparser2": "12.0.0",
|
|
45
45
|
"markdown-it": "15.0.2",
|
package/src/api/build-site.js
CHANGED
|
@@ -7,6 +7,7 @@ import { renderDocument, renderDocumentContent } from '../render/content-rendere
|
|
|
7
7
|
import { extractHtmlText } from '../render/html-text.js';
|
|
8
8
|
import { createThemeValidationError } from '../theme/format-theme-validation.js';
|
|
9
9
|
import { ZeroPressEngine } from '../render/zeropress-engine.js';
|
|
10
|
+
import { prepareRenderMenus } from '../render/menus.js';
|
|
10
11
|
|
|
11
12
|
const DEFAULT_OPTIONS = {
|
|
12
13
|
assetHashing: true,
|
|
@@ -136,6 +137,7 @@ export async function buildSite(input) {
|
|
|
136
137
|
|
|
137
138
|
return {
|
|
138
139
|
files: state.summaries,
|
|
140
|
+
warnings: state.warnings,
|
|
139
141
|
};
|
|
140
142
|
}
|
|
141
143
|
|
|
@@ -151,6 +153,7 @@ async function createBuildState(input, options) {
|
|
|
151
153
|
const assetProcessor = new AssetProcessor();
|
|
152
154
|
const summaries = [];
|
|
153
155
|
const previewData = normalizePreviewData(input.previewData, options);
|
|
156
|
+
const { menus, warnings } = prepareRenderMenus(previewData.menus, themePackage.metadata.menu_slots);
|
|
154
157
|
const renderData = createRenderData(previewData, themePackage, options);
|
|
155
158
|
|
|
156
159
|
engine.initialize(themePackage);
|
|
@@ -167,6 +170,8 @@ async function createBuildState(input, options) {
|
|
|
167
170
|
return {
|
|
168
171
|
writer: input.writer,
|
|
169
172
|
previewData,
|
|
173
|
+
menus,
|
|
174
|
+
warnings,
|
|
170
175
|
renderData,
|
|
171
176
|
widgets: resolveWidgetAreas(previewData, renderData),
|
|
172
177
|
engine,
|
|
@@ -214,7 +219,7 @@ async function renderRoute(state, templateName, route) {
|
|
|
214
219
|
let html = await state.engine.render(
|
|
215
220
|
templateName,
|
|
216
221
|
{
|
|
217
|
-
menus: state.
|
|
222
|
+
menus: state.menus,
|
|
218
223
|
widgets: state.widgets,
|
|
219
224
|
collections: state.renderData.collections,
|
|
220
225
|
taxonomies: state.renderData.taxonomies,
|
|
@@ -267,7 +272,7 @@ async function renderFrontPage(state, route) {
|
|
|
267
272
|
let html = await state.engine.render(
|
|
268
273
|
'page',
|
|
269
274
|
{
|
|
270
|
-
menus: state.
|
|
275
|
+
menus: state.menus,
|
|
271
276
|
widgets: state.widgets,
|
|
272
277
|
collections: state.renderData.collections,
|
|
273
278
|
taxonomies: state.renderData.taxonomies,
|
|
@@ -308,7 +313,7 @@ async function renderFrontPage(state, route) {
|
|
|
308
313
|
let html = await state.engine.render(
|
|
309
314
|
'index',
|
|
310
315
|
{
|
|
311
|
-
menus: state.
|
|
316
|
+
menus: state.menus,
|
|
312
317
|
widgets: state.widgets,
|
|
313
318
|
collections: state.renderData.collections,
|
|
314
319
|
taxonomies: state.renderData.taxonomies,
|
|
@@ -343,7 +348,7 @@ async function renderPost(state, post) {
|
|
|
343
348
|
let html = await state.engine.render(
|
|
344
349
|
'post',
|
|
345
350
|
{
|
|
346
|
-
menus: state.
|
|
351
|
+
menus: state.menus,
|
|
347
352
|
widgets: state.widgets,
|
|
348
353
|
collections: state.renderData.collections,
|
|
349
354
|
taxonomies: state.renderData.taxonomies,
|
|
@@ -391,7 +396,7 @@ async function renderPage(state, page) {
|
|
|
391
396
|
let html = await state.engine.render(
|
|
392
397
|
'page',
|
|
393
398
|
{
|
|
394
|
-
menus: state.
|
|
399
|
+
menus: state.menus,
|
|
395
400
|
widgets: state.widgets,
|
|
396
401
|
collections: state.renderData.collections,
|
|
397
402
|
taxonomies: state.renderData.taxonomies,
|
|
@@ -437,7 +442,7 @@ async function maybeRenderNotFoundPage(state) {
|
|
|
437
442
|
let html = await state.engine.render(
|
|
438
443
|
'404',
|
|
439
444
|
{
|
|
440
|
-
menus: state.
|
|
445
|
+
menus: state.menus,
|
|
441
446
|
widgets: state.widgets,
|
|
442
447
|
collections: state.renderData.collections,
|
|
443
448
|
taxonomies: state.renderData.taxonomies,
|
package/src/index.d.ts
CHANGED
|
@@ -80,8 +80,18 @@ export interface BuildSummaryFile {
|
|
|
80
80
|
sha256: string;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export interface BuildWarning {
|
|
84
|
+
code: 'MENU_MAX_DEPTH_EXCEEDED';
|
|
85
|
+
message: string;
|
|
86
|
+
menuId: string;
|
|
87
|
+
maxDepth: number;
|
|
88
|
+
actualDepth: number;
|
|
89
|
+
omittedItems: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
83
92
|
export interface BuildSiteResult {
|
|
84
93
|
files: BuildSummaryFile[];
|
|
94
|
+
warnings: BuildWarning[];
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
export function buildSite(input: {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Work on rendering copies only; validation and exported Preview Data keep the full tree.
|
|
2
|
+
export function prepareRenderMenus(menus, slots = {}) {
|
|
3
|
+
const rendered = { ...menus };
|
|
4
|
+
const warnings = [];
|
|
5
|
+
|
|
6
|
+
for (const [menuId, slot] of Object.entries(slots)) {
|
|
7
|
+
if (slot.max_depth === undefined || !Object.hasOwn(menus, menuId)) continue;
|
|
8
|
+
const maxDepth = slot.max_depth;
|
|
9
|
+
const items = [];
|
|
10
|
+
const stack = [{ source: menus[menuId].items, output: items, depth: 1 }];
|
|
11
|
+
let actualDepth = 0;
|
|
12
|
+
let omittedItems = 0;
|
|
13
|
+
|
|
14
|
+
while (stack.length) {
|
|
15
|
+
const { source, output, depth } = stack.pop();
|
|
16
|
+
for (const item of source) {
|
|
17
|
+
actualDepth = Math.max(actualDepth, depth);
|
|
18
|
+
const children = [];
|
|
19
|
+
if (depth <= maxDepth) {
|
|
20
|
+
output.push({ ...item, children });
|
|
21
|
+
} else {
|
|
22
|
+
omittedItems += 1;
|
|
23
|
+
}
|
|
24
|
+
if (item.children.length) {
|
|
25
|
+
stack.push({ source: item.children, output: children, depth: depth + 1 });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
rendered[menuId] = { ...menus[menuId], items };
|
|
31
|
+
if (omittedItems > 0) {
|
|
32
|
+
warnings.push({
|
|
33
|
+
code: 'MENU_MAX_DEPTH_EXCEEDED',
|
|
34
|
+
message: `Menu "${menuId}" reaches depth ${actualDepth}; the theme supports ${maxDepth}. ${omittedItems} deeper item(s) were omitted from rendering.`,
|
|
35
|
+
menuId,
|
|
36
|
+
maxDepth,
|
|
37
|
+
actualDepth,
|
|
38
|
+
omittedItems,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return { menus: rendered, warnings };
|
|
44
|
+
}
|