@zeropress/build-core 0.5.0 → 0.5.2
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 +8 -2
- package/package.json +4 -4
- package/src/api/build-site.js +654 -47
- package/src/assets/asset-processor.js +1 -6
- package/src/index.d.ts +7 -1
- package/src/render/content-renderer.js +201 -3
- package/src/theme/load-theme-dir.js +0 -1
|
@@ -11,12 +11,7 @@ export class AssetProcessor {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async processJavaScript(js) {
|
|
14
|
-
return js
|
|
15
|
-
.replace(/\/\/(?![^\r\n]*https?:)[^\r\n]*/g, '')
|
|
16
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
17
|
-
.replace(/\s+/g, ' ')
|
|
18
|
-
.replace(/\s*([{}();,=+\-*/<>!&|])\s*/g, '$1')
|
|
19
|
-
.trim();
|
|
14
|
+
return js;
|
|
20
15
|
}
|
|
21
16
|
|
|
22
17
|
generateAssetHash(content) {
|
package/src/index.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { ThemeManifest } from '@zeropress/theme-validator';
|
|
|
4
4
|
export interface ThemePackage {
|
|
5
5
|
metadata: ThemeManifest & {
|
|
6
6
|
thumbnail?: string;
|
|
7
|
-
settings?: Record<string, unknown>;
|
|
8
7
|
};
|
|
9
8
|
templates: Map<string, string>;
|
|
10
9
|
partials: Map<string, string>;
|
|
@@ -23,7 +22,14 @@ export interface BuildWriter {
|
|
|
23
22
|
|
|
24
23
|
export interface BuildOptions {
|
|
25
24
|
assetHashing?: boolean;
|
|
25
|
+
favicon?: {
|
|
26
|
+
icon?: string;
|
|
27
|
+
svg?: string;
|
|
28
|
+
png?: string;
|
|
29
|
+
apple_touch_icon?: string;
|
|
30
|
+
};
|
|
26
31
|
generateSpecialFiles?: boolean;
|
|
32
|
+
generateRobotsTxt?: boolean;
|
|
27
33
|
writeManifest?: boolean;
|
|
28
34
|
}
|
|
29
35
|
|
|
@@ -3,6 +3,13 @@ import hljs from 'highlight.js';
|
|
|
3
3
|
import anchor from 'markdown-it-anchor';
|
|
4
4
|
|
|
5
5
|
const TOC_LEVELS = new Set([2, 3, 4]);
|
|
6
|
+
const ALERT_TYPES = new Map([
|
|
7
|
+
['NOTE', { className: 'note', title: 'Note' }],
|
|
8
|
+
['TIP', { className: 'tip', title: 'Tip' }],
|
|
9
|
+
['IMPORTANT', { className: 'important', title: 'Important' }],
|
|
10
|
+
['WARNING', { className: 'warning', title: 'Warning' }],
|
|
11
|
+
['CAUTION', { className: 'caution', title: 'Caution' }],
|
|
12
|
+
]);
|
|
6
13
|
|
|
7
14
|
export function renderDocumentContent(content, documentType = 'markdown') {
|
|
8
15
|
return renderDocument(content, documentType).html;
|
|
@@ -62,6 +69,8 @@ function createMarkdownRenderer(toc) {
|
|
|
62
69
|
},
|
|
63
70
|
});
|
|
64
71
|
|
|
72
|
+
markdown.use(markdownTaskLists);
|
|
73
|
+
markdown.use(markdownAlerts);
|
|
65
74
|
markdown.use(anchor, {
|
|
66
75
|
slugify: slugify,
|
|
67
76
|
callback(token, { slug, title }) {
|
|
@@ -82,6 +91,189 @@ function createMarkdownRenderer(toc) {
|
|
|
82
91
|
return markdown;
|
|
83
92
|
}
|
|
84
93
|
|
|
94
|
+
function markdownTaskLists(markdown) {
|
|
95
|
+
markdown.core.ruler.after('inline', 'zeropress_task_lists', (state) => {
|
|
96
|
+
const listStack = [];
|
|
97
|
+
const listItemStack = [];
|
|
98
|
+
|
|
99
|
+
for (let index = 0; index < state.tokens.length; index += 1) {
|
|
100
|
+
const token = state.tokens[index];
|
|
101
|
+
|
|
102
|
+
if (token.type === 'bullet_list_open' || token.type === 'ordered_list_open') {
|
|
103
|
+
listStack.push(index);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (token.type === 'bullet_list_close' || token.type === 'ordered_list_close') {
|
|
108
|
+
listStack.pop();
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (token.type === 'list_item_open') {
|
|
113
|
+
listItemStack.push({
|
|
114
|
+
index,
|
|
115
|
+
listIndex: listStack[listStack.length - 1],
|
|
116
|
+
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (token.type === 'list_item_close') {
|
|
121
|
+
listItemStack.pop();
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (token.type !== 'inline' || listItemStack.length === 0) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const markerMatch = token.content.match(/^\s*\[([ xX])\](?:\s+|$)/);
|
|
130
|
+
if (!markerMatch) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const listItem = listItemStack[listItemStack.length - 1];
|
|
135
|
+
const listToken = state.tokens[listItem.listIndex];
|
|
136
|
+
const listItemToken = state.tokens[listItem.index];
|
|
137
|
+
if (!listToken || !listItemToken) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
addTokenClass(listToken, 'contains-task-list');
|
|
142
|
+
addTokenClass(listItemToken, 'task-list-item');
|
|
143
|
+
|
|
144
|
+
const checkboxToken = new state.Token('html_inline', '', 0);
|
|
145
|
+
const checked = markerMatch[1].toLowerCase() === 'x' ? ' checked' : '';
|
|
146
|
+
checkboxToken.content = `<input class="task-list-item-checkbox" type="checkbox"${checked} disabled> `;
|
|
147
|
+
|
|
148
|
+
const strippedContent = token.content.slice(markerMatch[0].length);
|
|
149
|
+
token.content = strippedContent;
|
|
150
|
+
token.children = [];
|
|
151
|
+
state.md.inline.parse(strippedContent, state.md, state.env, token.children);
|
|
152
|
+
token.children.unshift(checkboxToken);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function markdownAlerts(markdown) {
|
|
158
|
+
markdown.core.ruler.after('inline', 'zeropress_alerts', (state) => {
|
|
159
|
+
for (let index = 0; index < state.tokens.length; index += 1) {
|
|
160
|
+
const token = state.tokens[index];
|
|
161
|
+
if (token.type !== 'blockquote_open') {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const closeIndex = findMatchingBlockquoteClose(state.tokens, index);
|
|
166
|
+
if (closeIndex === -1) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const firstParagraph = findFirstParagraph(state.tokens, index + 1, closeIndex);
|
|
171
|
+
if (!firstParagraph) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const inlineToken = state.tokens[firstParagraph.inlineIndex];
|
|
176
|
+
const alert = stripAlertMarker(inlineToken, state);
|
|
177
|
+
if (!alert) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
token.tag = 'aside';
|
|
182
|
+
token.attrSet('class', `zp-alert zp-alert--${alert.className}`);
|
|
183
|
+
token.attrSet('role', 'note');
|
|
184
|
+
state.tokens[closeIndex].tag = 'aside';
|
|
185
|
+
|
|
186
|
+
if (inlineToken.content.trim() === '') {
|
|
187
|
+
state.tokens.splice(firstParagraph.openIndex, 3);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
state.tokens.splice(index + 1, 0, ...createAlertTitleTokens(state, alert.title));
|
|
191
|
+
index += 3;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function findMatchingBlockquoteClose(tokens, openIndex) {
|
|
197
|
+
let depth = 0;
|
|
198
|
+
|
|
199
|
+
for (let index = openIndex; index < tokens.length; index += 1) {
|
|
200
|
+
if (tokens[index].type === 'blockquote_open') {
|
|
201
|
+
depth += 1;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (tokens[index].type === 'blockquote_close') {
|
|
206
|
+
depth -= 1;
|
|
207
|
+
if (depth === 0) {
|
|
208
|
+
return index;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return -1;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function findFirstParagraph(tokens, startIndex, endIndex) {
|
|
217
|
+
if (
|
|
218
|
+
startIndex + 2 < endIndex
|
|
219
|
+
&& tokens[startIndex].type === 'paragraph_open'
|
|
220
|
+
&& tokens[startIndex + 1].type === 'inline'
|
|
221
|
+
&& tokens[startIndex + 2].type === 'paragraph_close'
|
|
222
|
+
) {
|
|
223
|
+
return {
|
|
224
|
+
openIndex: startIndex,
|
|
225
|
+
inlineIndex: startIndex + 1,
|
|
226
|
+
closeIndex: startIndex + 2,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function stripAlertMarker(inlineToken, state) {
|
|
234
|
+
const markerMatch = inlineToken.content.match(/^\s*\[!([A-Za-z]+)\][ \t]*(?:\n)?/);
|
|
235
|
+
if (!markerMatch) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const alertType = ALERT_TYPES.get(markerMatch[1].toUpperCase());
|
|
240
|
+
if (!alertType) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const strippedContent = inlineToken.content.slice(markerMatch[0].length);
|
|
245
|
+
inlineToken.content = strippedContent;
|
|
246
|
+
inlineToken.children = [];
|
|
247
|
+
state.md.inline.parse(strippedContent, state.md, state.env, inlineToken.children);
|
|
248
|
+
|
|
249
|
+
return alertType;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function createAlertTitleTokens(state, title) {
|
|
253
|
+
const openToken = new state.Token('paragraph_open', 'p', 1);
|
|
254
|
+
openToken.attrSet('class', 'zp-alert__title');
|
|
255
|
+
|
|
256
|
+
const inlineToken = new state.Token('inline', '', 0);
|
|
257
|
+
inlineToken.content = title;
|
|
258
|
+
const textToken = new state.Token('text', '', 0);
|
|
259
|
+
textToken.content = title;
|
|
260
|
+
inlineToken.children = [textToken];
|
|
261
|
+
|
|
262
|
+
const closeToken = new state.Token('paragraph_close', 'p', -1);
|
|
263
|
+
|
|
264
|
+
return [openToken, inlineToken, closeToken];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function addTokenClass(token, className) {
|
|
268
|
+
const existingValue = token.attrGet('class') || '';
|
|
269
|
+
const existingClasses = existingValue.split(/\s+/).filter(Boolean);
|
|
270
|
+
if (existingClasses.includes(className)) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
token.attrJoin('class', className);
|
|
275
|
+
}
|
|
276
|
+
|
|
85
277
|
function normalizeDocumentType(value) {
|
|
86
278
|
return value === 'plaintext' || value === 'html' ? value : 'markdown';
|
|
87
279
|
}
|
|
@@ -113,16 +305,18 @@ function sanitizeHtml(html) {
|
|
|
113
305
|
'strong', 'em', 'u', 's', 'code', 'pre',
|
|
114
306
|
'a', 'img',
|
|
115
307
|
'ul', 'ol', 'li',
|
|
116
|
-
'blockquote',
|
|
308
|
+
'blockquote', 'aside',
|
|
117
309
|
'table', 'thead', 'tbody', 'tr', 'th', 'td',
|
|
118
310
|
'div', 'span', 'nav',
|
|
119
|
-
'iframe',
|
|
311
|
+
'iframe', 'input',
|
|
120
312
|
]);
|
|
121
313
|
|
|
122
314
|
const allowedAttributes = {
|
|
123
315
|
a: new Set(['href', 'title', 'class', 'id']),
|
|
316
|
+
aside: new Set(['role', 'class', 'id']),
|
|
124
317
|
img: new Set(['src', 'alt', 'title', 'class', 'id', 'width', 'height']),
|
|
125
318
|
iframe: new Set(['src', 'width', 'height', 'frameborder', 'allowfullscreen', 'class']),
|
|
319
|
+
input: new Set(['type', 'checked', 'disabled', 'class', 'id']),
|
|
126
320
|
'*': new Set(['class', 'id']),
|
|
127
321
|
};
|
|
128
322
|
|
|
@@ -163,11 +357,15 @@ function sanitizeHtml(html) {
|
|
|
163
357
|
continue;
|
|
164
358
|
}
|
|
165
359
|
|
|
360
|
+
if (normalizedTag === 'input' && attributeName === 'type' && attributeValue !== 'checkbox') {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
166
364
|
filteredAttributes.push(`${attributeName}="${attributeValue}"`);
|
|
167
365
|
}
|
|
168
366
|
}
|
|
169
367
|
|
|
170
|
-
const isSelfClosing = match.endsWith('/>') || normalizedTag === 'br' || normalizedTag === 'hr' || normalizedTag === 'img';
|
|
368
|
+
const isSelfClosing = match.endsWith('/>') || normalizedTag === 'br' || normalizedTag === 'hr' || normalizedTag === 'img' || normalizedTag === 'input';
|
|
171
369
|
const attributeSuffix = filteredAttributes.length > 0 ? ` ${filteredAttributes.join(' ')}` : '';
|
|
172
370
|
return isSelfClosing ? `<${normalizedTag}${attributeSuffix} />` : `<${normalizedTag}${attributeSuffix}>`;
|
|
173
371
|
});
|