@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.
@@ -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
  });
@@ -51,7 +51,6 @@ export async function loadThemePackageFromDir(themeDir) {
51
51
  metadata: {
52
52
  ...manifest,
53
53
  thumbnail: themeJson.thumbnail,
54
- settings: themeJson.settings || {},
55
54
  },
56
55
  templates,
57
56
  partials,