radiant-docs 0.1.10 → 0.1.14
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/template/astro.config.mjs +2 -1
- package/template/package-lock.json +3 -0
- package/template/package.json +3 -0
- package/template/src/components/user/ComponentPreviewBlock.astro +24 -3
- package/template/src/lib/mdx/remark-code-block-component.ts +74 -7
- package/template/src/lib/validation.ts +2 -1
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import path from "path";
|
|
|
9
9
|
import { getConfig, validateMdxContent } from "./src/lib/validation";
|
|
10
10
|
import remarkCodeBlockComponent from "./src/lib/mdx/remark-code-block-component";
|
|
11
11
|
import remarkDemoteH1 from "./src/lib/mdx/remark-demote-h1";
|
|
12
|
+
import remarkGfm from "remark-gfm";
|
|
12
13
|
import rehypeSlug from "rehype-slug";
|
|
13
14
|
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
|
14
15
|
|
|
@@ -266,7 +267,7 @@ export default defineConfig({
|
|
|
266
267
|
},
|
|
267
268
|
integrations: [
|
|
268
269
|
mdx({
|
|
269
|
-
remarkPlugins: [remarkDemoteH1, remarkCodeBlockComponent],
|
|
270
|
+
remarkPlugins: [remarkGfm, remarkDemoteH1, remarkCodeBlockComponent],
|
|
270
271
|
rehypePlugins: [
|
|
271
272
|
rehypeSlug,
|
|
272
273
|
[
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
"@fontsource/google-sans-flex": "^5.2.2",
|
|
18
18
|
"@iconify-json/lucide": "^1.2.79",
|
|
19
19
|
"@iconify-json/simple-icons": "^1.2.69",
|
|
20
|
+
"mdast-util-gfm": "^3.1.0",
|
|
21
|
+
"micromark-extension-gfm": "^3.0.0",
|
|
20
22
|
"@readme/oas-to-snippet": "^29.3.0",
|
|
21
23
|
"@resvg/resvg-js": "^2.6.2",
|
|
22
24
|
"@stoplight/spectral-core": "^1.20.0",
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
"prismjs": "^1.30.0",
|
|
35
37
|
"rehype-autolink-headings": "^7.1.0",
|
|
36
38
|
"rehype-slug": "^6.0.0",
|
|
39
|
+
"remark-gfm": "^4.0.1",
|
|
37
40
|
"simple-git": "^3.30.0",
|
|
38
41
|
"tailwindcss": "^4.1.17",
|
|
39
42
|
"yaml": "^2.8.2",
|
package/template/package.json
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"@fontsource/google-sans-flex": "^5.2.2",
|
|
22
22
|
"@iconify-json/lucide": "^1.2.79",
|
|
23
23
|
"@iconify-json/simple-icons": "^1.2.69",
|
|
24
|
+
"mdast-util-gfm": "^3.1.0",
|
|
25
|
+
"micromark-extension-gfm": "^3.0.0",
|
|
24
26
|
"@readme/oas-to-snippet": "^29.3.0",
|
|
25
27
|
"@resvg/resvg-js": "^2.6.2",
|
|
26
28
|
"@stoplight/spectral-core": "^1.20.0",
|
|
@@ -38,6 +40,7 @@
|
|
|
38
40
|
"prismjs": "^1.30.0",
|
|
39
41
|
"rehype-autolink-headings": "^7.1.0",
|
|
40
42
|
"rehype-slug": "^6.0.0",
|
|
43
|
+
"remark-gfm": "^4.0.1",
|
|
41
44
|
"simple-git": "^3.30.0",
|
|
42
45
|
"tailwindcss": "^4.1.17",
|
|
43
46
|
"yaml": "^2.8.2",
|
|
@@ -11,6 +11,7 @@ interface Props {
|
|
|
11
11
|
highlightedLines?: string;
|
|
12
12
|
collapsedLines?: string;
|
|
13
13
|
previewVisibleLines?: number | string;
|
|
14
|
+
showAllCode?: boolean | string;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
const {
|
|
@@ -23,8 +24,19 @@ const {
|
|
|
23
24
|
highlightedLines = "",
|
|
24
25
|
collapsedLines = "",
|
|
25
26
|
previewVisibleLines = 5,
|
|
27
|
+
showAllCode = false,
|
|
26
28
|
} = Astro.props as Props;
|
|
27
29
|
|
|
30
|
+
function toBoolean(value: boolean | string | undefined, defaultValue = false) {
|
|
31
|
+
if (typeof value === "boolean") return value;
|
|
32
|
+
if (typeof value === "string") {
|
|
33
|
+
const normalized = value.trim().toLowerCase();
|
|
34
|
+
if (normalized === "true") return true;
|
|
35
|
+
if (normalized === "false") return false;
|
|
36
|
+
}
|
|
37
|
+
return defaultValue;
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
function parsePositiveInteger(
|
|
29
41
|
value: number | string | undefined,
|
|
30
42
|
fallback: number,
|
|
@@ -43,6 +55,8 @@ function parsePositiveInteger(
|
|
|
43
55
|
|
|
44
56
|
const visibleLines = parsePositiveInteger(previewVisibleLines, 5);
|
|
45
57
|
const totalLineCount = Math.max(1, raw.split("\n").length);
|
|
58
|
+
const shouldShowAllCode = toBoolean(showAllCode, false);
|
|
59
|
+
const isInitiallyExpanded = shouldShowAllCode || totalLineCount <= visibleLines;
|
|
46
60
|
---
|
|
47
61
|
|
|
48
62
|
<div
|
|
@@ -56,7 +70,7 @@ const totalLineCount = Math.max(1, raw.split("\n").length);
|
|
|
56
70
|
</div>
|
|
57
71
|
<div
|
|
58
72
|
class="rd-component-preview__code not-prose relative"
|
|
59
|
-
data-rd-preview-expanded="false"
|
|
73
|
+
data-rd-preview-expanded={isInitiallyExpanded ? "true" : "false"}
|
|
60
74
|
style={{ "--rd-preview-visible-lines": String(visibleLines) }}
|
|
61
75
|
>
|
|
62
76
|
<CodeBlock
|
|
@@ -123,7 +137,14 @@ const totalLineCount = Math.max(1, raw.split("\n").length);
|
|
|
123
137
|
}
|
|
124
138
|
</style>
|
|
125
139
|
|
|
126
|
-
<script
|
|
140
|
+
<script
|
|
141
|
+
is:inline
|
|
142
|
+
define:vars={{
|
|
143
|
+
visibleLines,
|
|
144
|
+
totalLineCount,
|
|
145
|
+
shouldShowAllCode,
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
127
148
|
(() => {
|
|
128
149
|
const script = document.currentScript;
|
|
129
150
|
if (!(script instanceof HTMLScriptElement)) return;
|
|
@@ -161,7 +182,7 @@ const totalLineCount = Math.max(1, raw.split("\n").length);
|
|
|
161
182
|
|
|
162
183
|
syncExpandedHeight();
|
|
163
184
|
|
|
164
|
-
if (totalLineCount <= visibleLines) {
|
|
185
|
+
if (shouldShowAllCode || totalLineCount <= visibleLines) {
|
|
165
186
|
codeWrapper.dataset.rdPreviewExpanded = "true";
|
|
166
187
|
window.addEventListener("resize", syncExpandedHeight, { passive: true });
|
|
167
188
|
return;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Root } from "mdast";
|
|
2
2
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
3
|
+
import { gfmFromMarkdown } from "mdast-util-gfm";
|
|
3
4
|
import { mdxFromMarkdown } from "mdast-util-mdx";
|
|
5
|
+
import { gfm } from "micromark-extension-gfm";
|
|
4
6
|
import { mdxjs } from "micromark-extension-mdxjs";
|
|
5
7
|
import type { Plugin } from "unified";
|
|
6
8
|
import { visitParents } from "unist-util-visit-parents";
|
|
@@ -24,10 +26,15 @@ type ParsedCodeMeta = {
|
|
|
24
26
|
collapsedLines: string;
|
|
25
27
|
};
|
|
26
28
|
|
|
29
|
+
type MdxJsxAttributeValueExpressionNode = {
|
|
30
|
+
type: "mdxJsxAttributeValueExpression";
|
|
31
|
+
value?: string | null;
|
|
32
|
+
};
|
|
33
|
+
|
|
27
34
|
type MdxJsxAttributeNode = {
|
|
28
35
|
type: "mdxJsxAttribute";
|
|
29
36
|
name: string;
|
|
30
|
-
value: string;
|
|
37
|
+
value: string | null | MdxJsxAttributeValueExpressionNode;
|
|
31
38
|
};
|
|
32
39
|
|
|
33
40
|
type MdxJsxFlowElementNode = {
|
|
@@ -146,10 +153,49 @@ function createAttribute(name: string, value: string): MdxJsxAttributeNode {
|
|
|
146
153
|
};
|
|
147
154
|
}
|
|
148
155
|
|
|
156
|
+
function parseBooleanAttributeValue(
|
|
157
|
+
value: MdxJsxAttributeNode["value"],
|
|
158
|
+
): boolean {
|
|
159
|
+
if (value === null) return true;
|
|
160
|
+
|
|
161
|
+
if (typeof value === "string") {
|
|
162
|
+
const normalized = value.trim().toLowerCase();
|
|
163
|
+
if (normalized === "" || normalized === "true") return true;
|
|
164
|
+
if (normalized === "false") return false;
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (
|
|
169
|
+
value?.type === "mdxJsxAttributeValueExpression" &&
|
|
170
|
+
typeof value.value === "string"
|
|
171
|
+
) {
|
|
172
|
+
const normalized = value.value.trim().toLowerCase();
|
|
173
|
+
if (normalized === "true") return true;
|
|
174
|
+
if (normalized === "false") return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function readBooleanAttribute(
|
|
181
|
+
attributes: MdxJsxAttributeNode[] | undefined,
|
|
182
|
+
attributeName: string,
|
|
183
|
+
): boolean {
|
|
184
|
+
if (!Array.isArray(attributes)) return false;
|
|
185
|
+
|
|
186
|
+
const attribute = attributes.find(
|
|
187
|
+
(candidate) =>
|
|
188
|
+
candidate.type === "mdxJsxAttribute" && candidate.name === attributeName,
|
|
189
|
+
);
|
|
190
|
+
if (!attribute) return false;
|
|
191
|
+
|
|
192
|
+
return parseBooleanAttributeValue(attribute.value);
|
|
193
|
+
}
|
|
194
|
+
|
|
149
195
|
function parseComponentPreviewChildren(rawCode: string): unknown[] {
|
|
150
196
|
const parsedTree = fromMarkdown(rawCode, {
|
|
151
|
-
extensions: [mdxjs()],
|
|
152
|
-
mdastExtensions: [mdxFromMarkdown()],
|
|
197
|
+
extensions: [gfm(), mdxjs()],
|
|
198
|
+
mdastExtensions: [gfmFromMarkdown(), mdxFromMarkdown()],
|
|
153
199
|
}) as Root;
|
|
154
200
|
|
|
155
201
|
const children = Array.isArray(parsedTree.children) ? parsedTree.children : [];
|
|
@@ -190,10 +236,7 @@ function getNearestMdxJsxFlowElementName(
|
|
|
190
236
|
ancestors: unknown[],
|
|
191
237
|
): string | null {
|
|
192
238
|
for (let index = ancestors.length - 1; index >= 0; index -= 1) {
|
|
193
|
-
const ancestor = ancestors[index] as
|
|
194
|
-
type?: string;
|
|
195
|
-
name?: string | null;
|
|
196
|
-
};
|
|
239
|
+
const ancestor = ancestors[index] as MdxJsxFlowElementNode;
|
|
197
240
|
if (ancestor?.type !== "mdxJsxFlowElement") continue;
|
|
198
241
|
return typeof ancestor.name === "string" ? ancestor.name : null;
|
|
199
242
|
}
|
|
@@ -201,6 +244,20 @@ function getNearestMdxJsxFlowElementName(
|
|
|
201
244
|
return null;
|
|
202
245
|
}
|
|
203
246
|
|
|
247
|
+
function getNearestMdxJsxFlowElement(
|
|
248
|
+
ancestors: unknown[],
|
|
249
|
+
elementName: string,
|
|
250
|
+
): MdxJsxFlowElementNode | null {
|
|
251
|
+
for (let index = ancestors.length - 1; index >= 0; index -= 1) {
|
|
252
|
+
const ancestor = ancestors[index] as MdxJsxFlowElementNode;
|
|
253
|
+
if (ancestor?.type !== "mdxJsxFlowElement") continue;
|
|
254
|
+
if (ancestor.name !== elementName) continue;
|
|
255
|
+
return ancestor;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
204
261
|
function isInsideMdxJsxTextElement(ancestors: unknown[]): boolean {
|
|
205
262
|
return ancestors.some((ancestor) => {
|
|
206
263
|
const node = ancestor as { type?: string };
|
|
@@ -234,6 +291,9 @@ export const remarkCodeBlockComponent: Plugin<[], Root> = () => {
|
|
|
234
291
|
const isInsideCodeGroup = nearestMdxFlowElementName === "CodeGroup";
|
|
235
292
|
const isInsideComponentPreview =
|
|
236
293
|
nearestMdxFlowElementName === COMPONENT_PREVIEW_NAME;
|
|
294
|
+
const componentPreviewNode = isInsideComponentPreview
|
|
295
|
+
? getNearestMdxJsxFlowElement(ancestors, COMPONENT_PREVIEW_NAME)
|
|
296
|
+
: null;
|
|
237
297
|
const isInsideInlineMdx = isInsideMdxJsxTextElement(ancestors);
|
|
238
298
|
if (isInsideInlineMdx) return;
|
|
239
299
|
|
|
@@ -317,6 +377,13 @@ export const remarkCodeBlockComponent: Plugin<[], Root> = () => {
|
|
|
317
377
|
const previewAttributes = attributes.filter(
|
|
318
378
|
(attribute) => attribute.name !== "inCodeGroup",
|
|
319
379
|
);
|
|
380
|
+
const showAllCode = readBooleanAttribute(
|
|
381
|
+
componentPreviewNode?.attributes,
|
|
382
|
+
"showAllCode",
|
|
383
|
+
);
|
|
384
|
+
if (showAllCode) {
|
|
385
|
+
previewAttributes.push(createAttribute("showAllCode", "true"));
|
|
386
|
+
}
|
|
320
387
|
const previewNode: MdxJsxFlowElementNode = {
|
|
321
388
|
type: "mdxJsxFlowElement",
|
|
322
389
|
name: COMPONENT_PREVIEW_BLOCK_NAME,
|
|
@@ -1438,7 +1438,8 @@ function validateComponentUsage(content: string): void {
|
|
|
1438
1438
|
.replace(/````[\s\S]*?````/g, "") // 4-backtick code blocks (check first, they're longer)
|
|
1439
1439
|
.replace(/```[\s\S]*?```/g, "") // fenced code blocks
|
|
1440
1440
|
.replace(/`[^`]+`/g, "") // inline code
|
|
1441
|
-
|
|
1441
|
+
// JSX string expressions like {'<Component title="Example" />'} or {"<Component />"}
|
|
1442
|
+
.replace(/\{\s*(['"`])(?:\\.|(?!\1)[\s\S])*?\1\s*\}/g, "");
|
|
1442
1443
|
|
|
1443
1444
|
// Find all JSX component tags (PascalCase tags)
|
|
1444
1445
|
// Matches: <ComponentName, <ComponentName>, <ComponentName />, etc.
|