specra 0.2.57 → 0.2.58
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/dist/mdx.js +104 -4
- package/package.json +1 -1
package/dist/mdx.js
CHANGED
|
@@ -100,6 +100,100 @@ const PROP_NAME_MAP = {
|
|
|
100
100
|
* processed by JSX/component preprocessing functions.
|
|
101
101
|
* Matches 3+ backticks or tildes as fence markers.
|
|
102
102
|
*/
|
|
103
|
+
/**
|
|
104
|
+
* Private Use Area character used to mask `|` inside inline code spans
|
|
105
|
+
* so that remark-gfm's table tokenizer doesn't treat them as cell dividers.
|
|
106
|
+
*
|
|
107
|
+
* Why: GFM splits table cells on `|` BEFORE inline parsing recognizes
|
|
108
|
+
* backtick-delimited code spans. So a row like
|
|
109
|
+
* | `{{ name | upper }}` |
|
|
110
|
+
* gets split on the inner pipe, breaking the row. Escaping with `\|` is
|
|
111
|
+
* the GFM-spec workaround, but inside an inline code span the `\` renders
|
|
112
|
+
* literally — useless for users. We swap `|` for U+E000 before parsing
|
|
113
|
+
* and swap it back in the output; PUA chars pass through remark/rehype
|
|
114
|
+
* unchanged and are never HTML-escaped.
|
|
115
|
+
*/
|
|
116
|
+
const PIPE_MARKER = '';
|
|
117
|
+
/**
|
|
118
|
+
* Mask `|` characters inside single-line markdown inline code spans by
|
|
119
|
+
* replacing them with `PIPE_MARKER`. Operates only on non-fenced segments.
|
|
120
|
+
* Idempotent: if no inline code contains `|`, output equals input.
|
|
121
|
+
*/
|
|
122
|
+
function maskInlineCodePipes(markdown) {
|
|
123
|
+
return splitByCodeFences(markdown).map(({ text, isCode }) => {
|
|
124
|
+
if (isCode)
|
|
125
|
+
return text;
|
|
126
|
+
let result = '';
|
|
127
|
+
let i = 0;
|
|
128
|
+
while (i < text.length) {
|
|
129
|
+
if (text[i] !== '`') {
|
|
130
|
+
result += text[i];
|
|
131
|
+
i++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// Count opening backtick run
|
|
135
|
+
let openLen = 0;
|
|
136
|
+
while (i + openLen < text.length && text[i + openLen] === '`')
|
|
137
|
+
openLen++;
|
|
138
|
+
// Find matching closing run of the same length on the same line.
|
|
139
|
+
// (Inline code spans inside table cells are always single-line.)
|
|
140
|
+
let j = i + openLen;
|
|
141
|
+
let closeIdx = -1;
|
|
142
|
+
while (j < text.length && text[j] !== '\n') {
|
|
143
|
+
if (text[j] === '`') {
|
|
144
|
+
let closeLen = 0;
|
|
145
|
+
while (j + closeLen < text.length && text[j + closeLen] === '`')
|
|
146
|
+
closeLen++;
|
|
147
|
+
if (closeLen === openLen) {
|
|
148
|
+
closeIdx = j;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
j += closeLen;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
j++;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (closeIdx === -1) {
|
|
158
|
+
// Unmatched run — emit verbatim
|
|
159
|
+
result += text.slice(i, i + openLen);
|
|
160
|
+
i += openLen;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
const content = text.slice(i + openLen, closeIdx).replace(/\|/g, PIPE_MARKER);
|
|
164
|
+
result += text.slice(i, i + openLen) + content + text.slice(closeIdx, closeIdx + openLen);
|
|
165
|
+
i = closeIdx + openLen;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}).join('');
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Restore `PIPE_MARKER` back to `|` in a string. No-op if the marker
|
|
173
|
+
* isn't present (the common case), so cheap to call blanket.
|
|
174
|
+
*/
|
|
175
|
+
function restorePipeMarkers(s) {
|
|
176
|
+
return s.indexOf(PIPE_MARKER) === -1 ? s : s.split(PIPE_MARKER).join('|');
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Walk an MdxNode tree and restore `PIPE_MARKER` to `|` in every string
|
|
180
|
+
* field that could carry the marker (html content, string props, children).
|
|
181
|
+
*/
|
|
182
|
+
function restorePipeMarkersInNodes(nodes) {
|
|
183
|
+
for (const node of nodes) {
|
|
184
|
+
if (node.content)
|
|
185
|
+
node.content = restorePipeMarkers(node.content);
|
|
186
|
+
if (node.props) {
|
|
187
|
+
for (const key of Object.keys(node.props)) {
|
|
188
|
+
const v = node.props[key];
|
|
189
|
+
if (typeof v === 'string')
|
|
190
|
+
node.props[key] = restorePipeMarkers(v);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (node.children && node.children.length > 0)
|
|
194
|
+
restorePipeMarkersInNodes(node.children);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
103
197
|
function splitByCodeFences(markdown) {
|
|
104
198
|
const fencedCodeRegex = /(^|\n)((`{3,}|~{3,}).*\n[\s\S]*?\n\3\s*(?:\n|$))/g;
|
|
105
199
|
const segments = [];
|
|
@@ -419,6 +513,7 @@ function resolveDeploymentBasePath() {
|
|
|
419
513
|
}
|
|
420
514
|
async function processMarkdownToHtml(markdown) {
|
|
421
515
|
const basePath = resolveDeploymentBasePath();
|
|
516
|
+
const masked = maskInlineCodePipes(markdown);
|
|
422
517
|
const processor = unified()
|
|
423
518
|
.use(remarkParse)
|
|
424
519
|
.use(remarkGfm)
|
|
@@ -432,8 +527,8 @@ async function processMarkdownToHtml(markdown) {
|
|
|
432
527
|
}
|
|
433
528
|
const result = await processor
|
|
434
529
|
.use(rehypeStringify)
|
|
435
|
-
.process(
|
|
436
|
-
return String(result);
|
|
530
|
+
.process(masked);
|
|
531
|
+
return restorePipeMarkers(String(result));
|
|
437
532
|
}
|
|
438
533
|
/**
|
|
439
534
|
* Convert hast properties to component props with correct casing.
|
|
@@ -1065,8 +1160,11 @@ function ensureComponentBlockIntegrity(markdown) {
|
|
|
1065
1160
|
return markdown;
|
|
1066
1161
|
}
|
|
1067
1162
|
async function processMarkdownToMdxNodes(markdown) {
|
|
1163
|
+
// Mask pipes inside inline code spans so GFM tables containing
|
|
1164
|
+
// `{{ x | filter }}`-style code don't get their rows broken.
|
|
1165
|
+
const masked = maskInlineCodePipes(markdown);
|
|
1068
1166
|
// Pre-process JSX expression attributes into HTML-safe string attributes
|
|
1069
|
-
const preprocessed = preprocessJsxExpressions(
|
|
1167
|
+
const preprocessed = preprocessJsxExpressions(masked);
|
|
1070
1168
|
// Dedent content inside component tags so that indented children
|
|
1071
1169
|
// (e.g. <Tabs> inside <Step>) don't get treated as code blocks
|
|
1072
1170
|
// by remark-parse (4+ spaces = indented code in CommonMark).
|
|
@@ -1089,7 +1187,9 @@ async function processMarkdownToMdxNodes(markdown) {
|
|
|
1089
1187
|
const hast = await processor.run(mdast);
|
|
1090
1188
|
// The hast root has children - process them into MdxNodes
|
|
1091
1189
|
const children = hast.children || [];
|
|
1092
|
-
|
|
1190
|
+
const nodes = await hastChildrenToMdxNodes(children);
|
|
1191
|
+
restorePipeMarkersInNodes(nodes);
|
|
1192
|
+
return nodes;
|
|
1093
1193
|
}
|
|
1094
1194
|
/**
|
|
1095
1195
|
* Calculate reading time based on word count
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specra",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.58",
|
|
4
4
|
"description": "A modern documentation library for SvelteKit with built-in versioning, API reference generation, full-text search, and MDX support",
|
|
5
5
|
"svelte": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|