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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "radiant-docs",
3
- "version": "0.1.10",
3
+ "version": "0.1.14",
4
4
  "description": "CLI tool for previewing Radiant documentation locally",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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",
@@ -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 is:inline define:vars={{ visibleLines, totalLineCount }}>
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
- .replace(/\{['"][^'"]*['"]\}/g, ""); // JSX string expressions like {'<Component />'}
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.