@webmate-studio/builder 0.2.113 → 0.2.114
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/package.json +1 -1
- package/src/index.js +2 -1
- package/src/markdown.js +4 -2
- package/src/safe-html.js +30 -0
- package/src/template-evaluator.js +14 -4
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { bundleIsland, bundleComponentIslands } from './bundler.js';
|
|
|
5
5
|
import { deduplicateCSS } from './css-deduplicator.js';
|
|
6
6
|
import { markdownToHtml, processMarkdownProps } from './markdown.js';
|
|
7
7
|
import TemplateProcessor, { templateProcessor } from './template-processor.js';
|
|
8
|
+
import { SafeHtml } from './safe-html.js';
|
|
8
9
|
import { defaultDesignTokens, generateTailwindV4Theme, generateTailwindConfig, generateCSSFromTokens } from './design-tokens.js';
|
|
9
10
|
import { readFileSync } from 'fs';
|
|
10
11
|
import { fileURLToPath } from 'url';
|
|
@@ -18,4 +19,4 @@ function getMotionRuntime() {
|
|
|
18
19
|
return readFileSync(join(__dirname, '../dist/motion-runtime.min.js'), 'utf-8');
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export { build, generateComponentCSS, generateTailwindCSS, extractTailwindClasses, cleanComponentHTML, bundleIsland, bundleComponentIslands, deduplicateCSS, markdownToHtml, processMarkdownProps, getMotionRuntime, TemplateProcessor, templateProcessor, defaultDesignTokens, generateTailwindV4Theme, generateTailwindConfig, generateCSSFromTokens };
|
|
22
|
+
export { build, generateComponentCSS, generateTailwindCSS, extractTailwindClasses, cleanComponentHTML, bundleIsland, bundleComponentIslands, deduplicateCSS, markdownToHtml, processMarkdownProps, SafeHtml, getMotionRuntime, TemplateProcessor, templateProcessor, defaultDesignTokens, generateTailwindV4Theme, generateTailwindConfig, generateCSSFromTokens };
|
package/src/markdown.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { marked } from 'marked';
|
|
7
7
|
import DOMPurify from 'dompurify';
|
|
8
8
|
import { JSDOM } from 'jsdom';
|
|
9
|
+
import { SafeHtml } from './safe-html.js';
|
|
9
10
|
|
|
10
11
|
// DOMPurify für Server-Side Rendering konfigurieren
|
|
11
12
|
const window = new JSDOM('').window;
|
|
@@ -160,8 +161,9 @@ export function processMarkdownProps(props, propSchema = null, componentMetadata
|
|
|
160
161
|
options.headingStartLevel = headingStartLevel;
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
// Convert markdown to HTML
|
|
164
|
-
|
|
164
|
+
// Convert markdown to HTML and wrap in SafeHtml
|
|
165
|
+
// (already sanitized by DOMPurify — evaluator will not escape)
|
|
166
|
+
processed[key] = new SafeHtml(markdownToHtml(value, options));
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
package/src/safe-html.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SafeHtml — Marker for pre-sanitized HTML content.
|
|
3
|
+
*
|
|
4
|
+
* When processMarkdownProps() converts markdown to HTML, the result is
|
|
5
|
+
* already sanitized by DOMPurify. Wrapping it in SafeHtml tells the
|
|
6
|
+
* template evaluator to output it without escaping.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const safe = new SafeHtml('<h2>Hello</h2>');
|
|
10
|
+
* safe.toString() // → '<h2>Hello</h2>'
|
|
11
|
+
* safe.__safeHtml // → true (marker for evaluator)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
class SafeHtml {
|
|
15
|
+
constructor(html) {
|
|
16
|
+
this.html = String(html ?? '');
|
|
17
|
+
this.__safeHtml = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
toString() {
|
|
21
|
+
return this.html;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
valueOf() {
|
|
25
|
+
return this.html;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { SafeHtml };
|
|
30
|
+
export default SafeHtml;
|
|
@@ -72,7 +72,8 @@ class TemplateEvaluator {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* {expression} — evaluate and escape for safe HTML output
|
|
75
|
+
* {expression} — evaluate and escape for safe HTML output.
|
|
76
|
+
* SafeHtml instances (from processMarkdownProps) are output without escaping.
|
|
76
77
|
*/
|
|
77
78
|
evalExpression(node, ctx) {
|
|
78
79
|
const value = this.expr.evaluate(node.expression, ctx);
|
|
@@ -80,6 +81,11 @@ class TemplateEvaluator {
|
|
|
80
81
|
if (value === undefined || value === null) return '';
|
|
81
82
|
if (typeof value === 'boolean') return '';
|
|
82
83
|
|
|
84
|
+
// SafeHtml marker — already sanitized by DOMPurify, don't escape
|
|
85
|
+
if (value && value.__safeHtml) {
|
|
86
|
+
return value.toString();
|
|
87
|
+
}
|
|
88
|
+
|
|
83
89
|
// Handle special objects
|
|
84
90
|
const resolved = this.resolveValue(value);
|
|
85
91
|
return this.escapeHtml(String(resolved));
|
|
@@ -360,12 +366,16 @@ class TemplateEvaluator {
|
|
|
360
366
|
// HTML escaping
|
|
361
367
|
// ========================================
|
|
362
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Escape HTML for text content.
|
|
371
|
+
* Only escapes < and > (XSS protection).
|
|
372
|
+
* Does NOT escape & — CMS users legitimately use & in content,
|
|
373
|
+
* and escaping it would turn "Hallo & Hi" into "Hallo & Hi".
|
|
374
|
+
*/
|
|
363
375
|
escapeHtml(str) {
|
|
364
376
|
return str
|
|
365
|
-
.replace(/&/g, '&')
|
|
366
377
|
.replace(/</g, '<')
|
|
367
|
-
.replace(/>/g, '>')
|
|
368
|
-
.replace(/"/g, '"');
|
|
378
|
+
.replace(/>/g, '>');
|
|
369
379
|
}
|
|
370
380
|
|
|
371
381
|
escapeAttr(str) {
|