specra 0.2.66 → 0.2.67
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 +113 -15
- package/package.json +2 -1
package/dist/mdx.js
CHANGED
|
@@ -10,6 +10,7 @@ import remarkGfm from "remark-gfm";
|
|
|
10
10
|
import remarkMath from "remark-math";
|
|
11
11
|
import remarkRehype from "remark-rehype";
|
|
12
12
|
import rehypeSlug from "rehype-slug";
|
|
13
|
+
import { slug as githubSlug } from "github-slugger";
|
|
13
14
|
import rehypeRaw from "rehype-raw";
|
|
14
15
|
import rehypeKatex from "rehype-katex";
|
|
15
16
|
import rehypeStringify from "rehype-stringify";
|
|
@@ -170,11 +171,92 @@ function maskInlineCodePipes(markdown) {
|
|
|
170
171
|
}).join('');
|
|
171
172
|
}
|
|
172
173
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
174
|
+
* Private Use Area characters used to mask `<` and `>` written inside code
|
|
175
|
+
* (inline spans or fenced blocks) that sits within a component block.
|
|
176
|
+
*
|
|
177
|
+
* Why: `ensureComponentBlockIntegrity` deliberately collapses blank lines so
|
|
178
|
+
* that an entire `<Callout>…</Callout>` stays one CommonMark HTML block. But
|
|
179
|
+
* inside an HTML block, markdown no longer applies — code spans and fences stop
|
|
180
|
+
* being code — so a documented tag such as
|
|
181
|
+
* An inline `<script src="/x.js">` tag.
|
|
182
|
+
* is handed to rehype-raw as *real HTML*. parse5 opens a raw-text `<script>`
|
|
183
|
+
* element that is never closed and swallows every following sibling, so the
|
|
184
|
+
* next heading silently disappears from the page and its ToC link 404s.
|
|
185
|
+
*
|
|
186
|
+
* Masking the angle brackets before remark sees them keeps the block inert for
|
|
187
|
+
* parse5, and `restoreCodeMarkers` puts them back after the component's
|
|
188
|
+
* children have been re-parsed as markdown. PUA chars pass through
|
|
189
|
+
* remark/rehype unchanged and are never HTML-escaped — same trick as
|
|
190
|
+
* `PIPE_MARKER` above.
|
|
191
|
+
*/
|
|
192
|
+
const LT_MARKER = '';
|
|
193
|
+
const GT_MARKER = '';
|
|
194
|
+
/**
|
|
195
|
+
* Mask `<` and `>` inside fenced code blocks and single-line inline code spans.
|
|
196
|
+
* Applied only to the interior of component blocks, where markdown code markers
|
|
197
|
+
* lose their meaning. Component tags themselves live outside code and are left
|
|
198
|
+
* alone, so `<Callout>` still parses as a component.
|
|
199
|
+
*/
|
|
200
|
+
function maskCodeAngleBrackets(markdown) {
|
|
201
|
+
const maskAngles = (s) => s.replace(/</g, LT_MARKER).replace(/>/g, GT_MARKER);
|
|
202
|
+
return splitByCodeFences(markdown).map(({ text, isCode }) => {
|
|
203
|
+
if (isCode)
|
|
204
|
+
return maskAngles(text);
|
|
205
|
+
let result = '';
|
|
206
|
+
let i = 0;
|
|
207
|
+
while (i < text.length) {
|
|
208
|
+
if (text[i] !== '`') {
|
|
209
|
+
result += text[i];
|
|
210
|
+
i++;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
let openLen = 0;
|
|
214
|
+
while (i + openLen < text.length && text[i + openLen] === '`')
|
|
215
|
+
openLen++;
|
|
216
|
+
let j = i + openLen;
|
|
217
|
+
let closeIdx = -1;
|
|
218
|
+
while (j < text.length && text[j] !== '\n') {
|
|
219
|
+
if (text[j] === '`') {
|
|
220
|
+
let closeLen = 0;
|
|
221
|
+
while (j + closeLen < text.length && text[j + closeLen] === '`')
|
|
222
|
+
closeLen++;
|
|
223
|
+
if (closeLen === openLen) {
|
|
224
|
+
closeIdx = j;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
j += closeLen;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
j++;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (closeIdx === -1) {
|
|
234
|
+
result += text.slice(i, i + openLen);
|
|
235
|
+
i += openLen;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const content = maskAngles(text.slice(i + openLen, closeIdx));
|
|
239
|
+
result += text.slice(i, i + openLen) + content + text.slice(closeIdx, closeIdx + openLen);
|
|
240
|
+
i = closeIdx + openLen;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return result;
|
|
244
|
+
}).join('');
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Restore `PIPE_MARKER` back to `|`, and `LT_MARKER`/`GT_MARKER` back to
|
|
248
|
+
* `<`/`>`, in a string. No-op if no marker is present (the common case), so
|
|
249
|
+
* cheap to call blanket.
|
|
175
250
|
*/
|
|
176
251
|
function restorePipeMarkers(s) {
|
|
177
|
-
|
|
252
|
+
let out = s;
|
|
253
|
+
if (out.indexOf(PIPE_MARKER) !== -1)
|
|
254
|
+
out = out.split(PIPE_MARKER).join('|');
|
|
255
|
+
if (out.indexOf(LT_MARKER) !== -1)
|
|
256
|
+
out = out.split(LT_MARKER).join('<');
|
|
257
|
+
if (out.indexOf(GT_MARKER) !== -1)
|
|
258
|
+
out = out.split(GT_MARKER).join('>');
|
|
259
|
+
return out;
|
|
178
260
|
}
|
|
179
261
|
/**
|
|
180
262
|
* Walk an MdxNode tree and restore `PIPE_MARKER` to `|` in every string
|
|
@@ -1168,7 +1250,14 @@ function ensureComponentBlockIntegrity(markdown) {
|
|
|
1168
1250
|
// code fences. The code fence content is raw text inside the HTML block and
|
|
1169
1251
|
// will be re-processed by processComponentChildren through the markdown
|
|
1170
1252
|
// pipeline, which restores proper code formatting.
|
|
1171
|
-
|
|
1253
|
+
// Mask `<`/`>` written inside code (inline spans and fences) BEFORE the
|
|
1254
|
+
// blank-line collapse turns this whole block into one raw HTML block.
|
|
1255
|
+
// Once it is raw HTML, markdown code markers no longer protect their
|
|
1256
|
+
// contents, and a documented tag like `<script src="...">` reaches parse5
|
|
1257
|
+
// as real HTML — a raw-text element that never closes and swallows every
|
|
1258
|
+
// sibling after the component, heading and all. Restored by
|
|
1259
|
+
// `restorePipeMarkers` once the children are re-parsed as markdown.
|
|
1260
|
+
const collapsed = maskCodeAngleBrackets(block).replace(/^\s*$/gm, '<!-- -->');
|
|
1172
1261
|
result += markdown.slice(lastIndex, blockStart) + collapsed;
|
|
1173
1262
|
lastIndex = blockEnd;
|
|
1174
1263
|
openTagRegex.lastIndex = blockEnd;
|
|
@@ -1623,17 +1712,26 @@ export function getAdjacentDocs(currentSlug, allDocs) {
|
|
|
1623
1712
|
export function extractTableOfContents(content) {
|
|
1624
1713
|
const headingRegex = /^(#{2,3})\s+(.+)$/gm;
|
|
1625
1714
|
const toc = [];
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1715
|
+
// Headings inside fenced code blocks are code, not headings. They get no
|
|
1716
|
+
// `id` in the rendered page, so a ToC entry for them can only ever 404.
|
|
1717
|
+
for (const { text: segment, isCode } of splitByCodeFences(content)) {
|
|
1718
|
+
if (isCode)
|
|
1719
|
+
continue;
|
|
1720
|
+
let match;
|
|
1721
|
+
headingRegex.lastIndex = 0;
|
|
1722
|
+
while ((match = headingRegex.exec(segment)) !== null) {
|
|
1723
|
+
const level = match[1].length;
|
|
1724
|
+
const text = match[2];
|
|
1725
|
+
// Use the SAME slugger rehype-slug uses, rather than approximating it.
|
|
1726
|
+
// The old hand-rolled version stripped `_` and trimmed a trailing `-`,
|
|
1727
|
+
// while github-slugger keeps both — so every heading naming a snake_case
|
|
1728
|
+
// identifier (`list_display`, `on_upload`, `cache_page`) produced a ToC
|
|
1729
|
+
// link that pointed at nothing. Backticks are stripped first because
|
|
1730
|
+
// rehype-slug slugs the RENDERED heading text, where `code` markup is
|
|
1731
|
+
// already gone.
|
|
1732
|
+
const id = githubSlug(text.replace(/`/g, ""));
|
|
1733
|
+
toc.push({ id, title: text, level });
|
|
1734
|
+
}
|
|
1637
1735
|
}
|
|
1638
1736
|
return toc;
|
|
1639
1737
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specra",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.67",
|
|
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",
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"clsx": "^2.1.1",
|
|
82
82
|
"date-fns": "^4.1.0",
|
|
83
83
|
"embla-carousel-svelte": "^8.5.1",
|
|
84
|
+
"github-slugger": "^2.0.0",
|
|
84
85
|
"gray-matter": "^4.0.3",
|
|
85
86
|
"hast-util-to-html": "^9.0.0",
|
|
86
87
|
"js-yaml": "^4.1.1",
|