sdocs 0.0.50 → 0.0.51
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/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.0.51] - 2026-07-05
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **The sdoc grammar colors `[PAGE]` bodies as markdown.** Headings, bold,
|
|
15
|
+
lists, and fences highlight in page prose (the body's block indent is
|
|
16
|
+
accounted for, so indented bodies don't read as code blocks). Applies
|
|
17
|
+
everywhere the grammar is used — the VS Code extension and ` ```sdoc `
|
|
18
|
+
fences on the site.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Page expressions with string literals no longer break the page.** The
|
|
23
|
+
markdown pass HTML-escaped quotes (and `&&`, `<`) inside `{…}` expressions,
|
|
24
|
+
so `{@render colorBox("#ff0000")}` or `{format("x")}` in a `[PAGE]` body
|
|
25
|
+
failed to compile. Balanced `{…}` spans now pass through verbatim; prose
|
|
26
|
+
around them escapes exactly as before, and code fences and inline code stay
|
|
27
|
+
inert.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **`\{` escapes a literal brace in page prose.** Previously a lone brace
|
|
32
|
+
written in prose could break the page; a backslash-escaped brace now renders
|
|
33
|
+
as an inert literal `{`.
|
|
34
|
+
|
|
10
35
|
## [0.0.50] - 2026-07-05
|
|
11
36
|
|
|
12
37
|
### Fixed
|
|
@@ -229,10 +229,11 @@
|
|
|
229
229
|
"patterns": [
|
|
230
230
|
{
|
|
231
231
|
"begin": "(^|\\G)",
|
|
232
|
-
"while": "(^|\\G)(?!\\s*\\[/PAGE\\]\\s*$)",
|
|
232
|
+
"while": "(^|\\G)(?!\\s*\\[/PAGE\\]\\s*$)(?:\\t|[ ]{1,4})?",
|
|
233
|
+
"contentName": "meta.embedded.block.markdown",
|
|
233
234
|
"patterns": [
|
|
234
235
|
{
|
|
235
|
-
"include": "
|
|
236
|
+
"include": "text.html.markdown"
|
|
236
237
|
}
|
|
237
238
|
]
|
|
238
239
|
}
|
|
@@ -27,6 +27,55 @@ function escapeHtml(text) {
|
|
|
27
27
|
.replace(/>/g, '>')
|
|
28
28
|
.replace(/"/g, '"');
|
|
29
29
|
}
|
|
30
|
+
/** marked-equivalent prose escaping (entities already present survive). */
|
|
31
|
+
function escapeProse(text) {
|
|
32
|
+
return text
|
|
33
|
+
.replace(/&(?!(?:#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g, '&')
|
|
34
|
+
.replace(/</g, '<')
|
|
35
|
+
.replace(/>/g, '>')
|
|
36
|
+
.replace(/"/g, '"')
|
|
37
|
+
.replace(/'/g, ''');
|
|
38
|
+
}
|
|
39
|
+
/** Index of the `}` closing the `{` at `open`, or -1. Skips quoted strings. */
|
|
40
|
+
function findExpressionEnd(text, open) {
|
|
41
|
+
let depth = 0;
|
|
42
|
+
for (let i = open; i < text.length; i++) {
|
|
43
|
+
const ch = text[i];
|
|
44
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
45
|
+
for (i++; i < text.length && text[i] !== ch; i++) {
|
|
46
|
+
if (text[i] === '\\')
|
|
47
|
+
i++;
|
|
48
|
+
}
|
|
49
|
+
if (i >= text.length)
|
|
50
|
+
return -1;
|
|
51
|
+
}
|
|
52
|
+
else if (ch === '{')
|
|
53
|
+
depth++;
|
|
54
|
+
else if (ch === '}' && --depth === 0)
|
|
55
|
+
return i;
|
|
56
|
+
}
|
|
57
|
+
return -1;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Escape prose while passing balanced `{…}` spans through verbatim, so Svelte
|
|
61
|
+
* expressions survive the markdown pass — string literals, `&&`, comparisons.
|
|
62
|
+
* An unmatched `{` falls back to prose escaping, exactly as before.
|
|
63
|
+
*/
|
|
64
|
+
function escapeTextKeepingExpressions(text) {
|
|
65
|
+
let out = '';
|
|
66
|
+
let from = 0;
|
|
67
|
+
for (let i = 0; i < text.length; i++) {
|
|
68
|
+
if (text[i] !== '{')
|
|
69
|
+
continue;
|
|
70
|
+
const end = findExpressionEnd(text, i);
|
|
71
|
+
if (end === -1)
|
|
72
|
+
break;
|
|
73
|
+
out += escapeProse(text.slice(from, i)) + text.slice(i, end + 1);
|
|
74
|
+
from = end + 1;
|
|
75
|
+
i = end;
|
|
76
|
+
}
|
|
77
|
+
return out + escapeProse(text.slice(from));
|
|
78
|
+
}
|
|
30
79
|
function plainText(tokens) {
|
|
31
80
|
let out = '';
|
|
32
81
|
for (const token of tokens) {
|
|
@@ -97,6 +146,18 @@ export async function renderPageMarkdown(source) {
|
|
|
97
146
|
// tags, {expressions} stay expressions.
|
|
98
147
|
html(token) {
|
|
99
148
|
return token.text;
|
|
149
|
+
},
|
|
150
|
+
// Prose escapes for HTML, but balanced {…} spans stay verbatim so
|
|
151
|
+
// expressions keep their quotes and operators. A backslash-escaped
|
|
152
|
+
// brace (`\{`) becomes an inert literal brace.
|
|
153
|
+
text(token) {
|
|
154
|
+
if (token.type === 'escape')
|
|
155
|
+
return escapeBraces(escapeProse(token.text));
|
|
156
|
+
if ('tokens' in token && token.tokens)
|
|
157
|
+
return this.parser.parseInline(token.tokens);
|
|
158
|
+
if ('escaped' in token && token.escaped)
|
|
159
|
+
return token.text;
|
|
160
|
+
return escapeTextKeepingExpressions(token.text);
|
|
100
161
|
}
|
|
101
162
|
}
|
|
102
163
|
});
|