radiant-docs 0.1.56 → 0.1.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.
Files changed (33) hide show
  1. package/dist/index.js +3 -76
  2. package/package.json +2 -4
  3. package/template/astro.config.mjs +16 -26
  4. package/template/package-lock.json +18 -0
  5. package/template/package.json +1 -1
  6. package/template/src/components/Footer.astro +13 -4
  7. package/template/src/components/Header.astro +26 -6
  8. package/template/src/components/SidebarLink.astro +3 -2
  9. package/template/src/components/SidebarSubgroup.astro +14 -13
  10. package/template/src/components/sidebar/SidebarEndpointLink.astro +13 -3
  11. package/template/src/components/sidebar/SidebarOpenApi.astro +2 -0
  12. package/template/src/components/sidebar/SidebarOpenApiPageLink.astro +1 -0
  13. package/template/src/components/user/Accordion.astro +0 -13
  14. package/template/src/components/user/Callout.astro +0 -29
  15. package/template/src/components/user/Card.astro +31 -204
  16. package/template/src/components/user/CardGradient.astro +8 -1
  17. package/template/src/components/user/Column.astro +0 -17
  18. package/template/src/components/user/Columns.astro +4 -153
  19. package/template/src/components/user/Image.astro +17 -30
  20. package/template/src/components/user/Step.astro +0 -10
  21. package/template/src/components/user/Tab.astro +0 -12
  22. package/template/src/components/user/Tabs.astro +2 -9
  23. package/template/src/content.config.ts +1 -1
  24. package/template/src/lib/code/code-block.ts +1 -1
  25. package/template/src/lib/mdx/remark-code-block-component.ts +1 -20
  26. package/template/src/lib/mdx/remark-resolve-internal-links.ts +150 -204
  27. package/template/src/lib/routes.ts +150 -29
  28. package/template/src/lib/utils.ts +127 -12
  29. package/template/src/lib/validation.ts +5 -2826
  30. package/template/src/pages/[...slug].astro +16 -0
  31. package/template/src/lib/code/shiki-theme-config.ts +0 -16
  32. package/template/src/lib/component-error.ts +0 -202
  33. package/template/src/lib/frontmatter-schema.ts +0 -10
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var log = {
36
36
  ),
37
37
  blank: () => console.log("")
38
38
  };
39
- var SUPPORTED_COMMANDS = /* @__PURE__ */ new Set(["dev", "check", "prepare"]);
39
+ var SUPPORTED_COMMANDS = /* @__PURE__ */ new Set(["dev"]);
40
40
  function getUserDocsDir() {
41
41
  return process.cwd();
42
42
  }
@@ -187,66 +187,6 @@ async function runAstroSync(cacheDir) {
187
187
  } catch {
188
188
  }
189
189
  }
190
- function toCommandOutput(value) {
191
- if (!value) return "";
192
- if (Buffer.isBuffer(value)) return value.toString("utf-8");
193
- return String(value);
194
- }
195
- function getCommandOutput(error) {
196
- return [
197
- toCommandOutput(error?.stdout),
198
- toCommandOutput(error?.stderr),
199
- toCommandOutput(error?.message)
200
- ].filter(Boolean).join("\n");
201
- }
202
- function extractUserErrors(output) {
203
- const errors = [];
204
- for (const line of output.split("\n")) {
205
- if (!line.includes("[USER_ERROR]")) continue;
206
- const message = line.split("[USER_ERROR]:")[1]?.trim() || line.trim();
207
- if (message && !errors.includes(message)) {
208
- errors.push(message);
209
- }
210
- }
211
- return errors;
212
- }
213
- function getOutputTail(output, maxLines = 40) {
214
- return output.split("\n").map((line) => line.trim()).filter(Boolean).slice(-maxLines).join("\n");
215
- }
216
- function runFrameworkCheck(cacheDir) {
217
- log.info("Checking documentation...");
218
- try {
219
- execSync("npm run check", {
220
- cwd: cacheDir,
221
- stdio: "pipe",
222
- env: {
223
- ...process.env,
224
- RADIANT_CHECK: "1"
225
- },
226
- encoding: "utf-8"
227
- });
228
- log.success("Documentation check passed.");
229
- } catch (error) {
230
- const output = getCommandOutput(error);
231
- const userErrors = extractUserErrors(output);
232
- log.blank();
233
- log.error("Documentation check failed.");
234
- if (userErrors.length > 0) {
235
- log.blank();
236
- for (const userError of userErrors) {
237
- log.error(userError);
238
- }
239
- } else {
240
- const outputTail = getOutputTail(output);
241
- if (outputTail) {
242
- log.blank();
243
- console.log(outputTail);
244
- }
245
- }
246
- log.blank();
247
- process.exit(typeof error?.status === "number" ? error.status : 1);
248
- }
249
- }
250
190
  function startDevServer(cacheDir) {
251
191
  const child = spawn("npm", ["run", "dev"], {
252
192
  cwd: cacheDir,
@@ -352,14 +292,12 @@ ${colors.bright}${colors.cyan}\u25C6 Radiant${colors.reset} v${VERSION}
352
292
  ${colors.bright}Usage:${colors.reset}
353
293
  radiant Start the dev server
354
294
  radiant dev Start the dev server
355
- radiant check Validate and build the docs once
356
- radiant prepare Prepare cached framework dependencies
357
295
  radiant --help Show this help message
358
296
  radiant --version Show version
359
297
 
360
298
  ${colors.bright}Description:${colors.reset}
361
299
  Run this command from your documentation folder (containing docs.json)
362
- to preview your docs locally with hot reload, or run a one-time check.
300
+ to preview your docs locally with hot reload.
363
301
  `);
364
302
  }
365
303
  async function run() {
@@ -386,24 +324,13 @@ async function run() {
386
324
  const docsDir = getUserDocsDir();
387
325
  const cacheDir = getCacheDir(docsDir);
388
326
  const contentDir = getContentDir(cacheDir);
389
- if (command !== "prepare") {
390
- await validateUserDocs(docsDir);
391
- }
327
+ await validateUserDocs(docsDir);
392
328
  const needsInstall = await setupCache(cacheDir);
393
329
  if (needsInstall) {
394
330
  await installDependencies(cacheDir);
395
331
  }
396
- if (command === "prepare") {
397
- log.success("Radiant framework cache is ready.");
398
- return;
399
- }
400
332
  log.info("Syncing your documentation...");
401
333
  await syncContent(docsDir, contentDir);
402
- if (command === "check") {
403
- log.success("Content synced.");
404
- runFrameworkCheck(cacheDir);
405
- return;
406
- }
407
334
  await runAstroSync(cacheDir);
408
335
  log.success("Content synced.");
409
336
  log.info("Starting dev server...");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "radiant-docs",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "description": "CLI tool for previewing Radiant documentation locally",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,9 +11,7 @@
11
11
  "build": "tsup src/index.ts --format esm --clean && npm run bundle-template",
12
12
  "dev": "tsup src/index.ts --format esm --watch",
13
13
  "prepublishOnly": "npm run build",
14
- "release": "npm version patch && npm publish",
15
- "sync-docs-agent-version": "node scripts/sync-docs-agent-version.js",
16
- "version": "npm run sync-docs-agent-version && git add ../workers/docs-agent/container/Dockerfile"
14
+ "release": "npm version patch && npm publish"
17
15
  },
18
16
  "dependencies": {
19
17
  "chokidar": "^4.0.3",
@@ -8,7 +8,11 @@ import preact from "@astrojs/preact";
8
8
  import icon from "astro-icon";
9
9
  import fs from "fs";
10
10
  import path from "path";
11
- import { getConfig, validateMdxContent } from "./src/lib/validation";
11
+ import {
12
+ getConfig,
13
+ isPublishableStaticAssetPath,
14
+ validateMdxContent,
15
+ } from "./src/lib/validation";
12
16
  import remarkCodeBlockComponent from "./src/lib/mdx/remark-code-block-component";
13
17
  import remarkDemoteH1 from "./src/lib/mdx/remark-demote-h1";
14
18
  import remarkResolveInternalLinks from "./src/lib/mdx/remark-resolve-internal-links";
@@ -69,12 +73,10 @@ const headingAnchorContent = {
69
73
  };
70
74
 
71
75
  const shouldValidateDocs =
72
- process.env.npm_lifecycle_event === "build" ||
73
- process.env.npm_lifecycle_event === "check" ||
74
- process.env.RADIANT_CHECK === "1";
76
+ process.env.npm_lifecycle_event === "build";
75
77
 
76
78
  if (shouldValidateDocs) {
77
- // Run validation before Astro's internal validation for build/check commands.
79
+ // Run validation before Astro's internal validation for builds.
78
80
  try {
79
81
  await getConfig();
80
82
  await validateMdxContent();
@@ -91,24 +93,12 @@ function copyContentAssets() {
91
93
  const DOCS_DIR = path.join(CWD, "src/content/docs");
92
94
  const PUBLIC_DIR = path.join(CWD, "public");
93
95
 
94
- const imageExtensions = [
95
- ".svg",
96
- ".png",
97
- ".jpg",
98
- ".jpeg",
99
- ".webp",
100
- ".gif",
101
- ".avif",
102
- ".ico",
103
- ];
104
-
105
96
  function shouldCopyFile(filePath) {
106
- const ext = path.extname(filePath).toLowerCase();
107
- return imageExtensions.includes(ext);
97
+ return isPublishableStaticAssetPath(filePath);
108
98
  }
109
99
 
110
- // Build a set of all image file paths in src/content/docs
111
- function collectImagePaths(dir, baseDir, pathSet) {
100
+ // Build a set of all publishable asset paths in src/content/docs
101
+ function collectAssetPaths(dir, baseDir, pathSet) {
112
102
  if (!fs.existsSync(dir)) return;
113
103
 
114
104
  const entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -120,7 +110,7 @@ function copyContentAssets() {
120
110
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
121
111
 
122
112
  if (entry.isDirectory()) {
123
- collectImagePaths(entryPath, baseDir, pathSet);
113
+ collectAssetPaths(entryPath, baseDir, pathSet);
124
114
  } else if (entry.isFile() && shouldCopyFile(entryPath)) {
125
115
  // Store relative path from baseDir
126
116
  const relativePath = path.relative(baseDir, entryPath);
@@ -213,15 +203,15 @@ function copyContentAssets() {
213
203
  function syncAssets() {
214
204
  if (!fs.existsSync(DOCS_DIR)) return;
215
205
 
216
- // Step 1: Collect all valid image paths from source
217
- const validImagePaths = new Set();
218
- collectImagePaths(DOCS_DIR, DOCS_DIR, validImagePaths);
206
+ // Step 1: Collect all valid asset paths from source
207
+ const validAssetPaths = new Set();
208
+ collectAssetPaths(DOCS_DIR, DOCS_DIR, validAssetPaths);
219
209
 
220
- // Step 2: Copy all images from source to public
210
+ // Step 2: Copy all publishable assets from source to public
221
211
  copyDirectory(DOCS_DIR, PUBLIC_DIR);
222
212
 
223
213
  // Step 3: Delete orphaned files in public
224
- deleteOrphanedFiles(PUBLIC_DIR, PUBLIC_DIR, validImagePaths);
214
+ deleteOrphanedFiles(PUBLIC_DIR, PUBLIC_DIR, validAssetPaths);
225
215
 
226
216
  // Step 4: Clean up empty directories
227
217
  cleanupEmptyDirs(PUBLIC_DIR, PUBLIC_DIR);
@@ -43,6 +43,7 @@
43
43
  "preact": "^10.29.0",
44
44
  "prism-themes": "^1.9.0",
45
45
  "prismjs": "^1.30.0",
46
+ "radiant-docs-validator": "^0.1.17",
46
47
  "rehype-autolink-headings": "^7.1.0",
47
48
  "rehype-slug": "^6.0.0",
48
49
  "remark-gfm": "^4.0.1",
@@ -11631,6 +11632,23 @@
11631
11632
  ],
11632
11633
  "license": "MIT"
11633
11634
  },
11635
+ "node_modules/radiant-docs-validator": {
11636
+ "version": "0.1.17",
11637
+ "resolved": "https://registry.npmjs.org/radiant-docs-validator/-/radiant-docs-validator-0.1.17.tgz",
11638
+ "integrity": "sha512-r7+1M//tK7Iuk0J56BgQca+a+NNHWAywl+m7OkTu/oFYPHawZ4MEoCdoq3pbKnTVaZ6Fgxiz5PmrqgwL4rb1Rw==",
11639
+ "license": "MIT",
11640
+ "dependencies": {
11641
+ "@iconify-json/fluent": "^1.2.47",
11642
+ "@iconify-json/lucide": "^1.2.79",
11643
+ "@iconify-json/simple-icons": "^1.2.69",
11644
+ "@mdx-js/mdx": "^3.1.1",
11645
+ "@stoplight/spectral-core": "^1.20.0",
11646
+ "@stoplight/spectral-rulesets": "^1.22.0",
11647
+ "shiki": "^3.20.0",
11648
+ "yaml": "^2.8.2",
11649
+ "zod": "^3.25.76"
11650
+ }
11651
+ },
11634
11652
  "node_modules/radix3": {
11635
11653
  "version": "1.1.2",
11636
11654
  "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
@@ -5,7 +5,6 @@
5
5
  "scripts": {
6
6
  "dev": "astro dev",
7
7
  "start": "tsx runner.ts",
8
- "check": "astro build",
9
8
  "prebuild": "rm -rf public/pagefind",
10
9
  "build": "astro build && node scripts/remove-assistant-for-non-pro.mjs && node scripts/generate-og-metadata.mjs && node scripts/generate-og-images.mjs && node scripts/stamp-og-image-versions.mjs && node scripts/stamp-image-versions.mjs && pagefind --site dist && node scripts/stamp-pagefind-runtime-version.mjs && node scripts/generate-proxy-allowed-origins.mjs && node scripts/generate-robots-txt.mjs",
11
10
  "preview": "astro preview",
@@ -48,6 +47,7 @@
48
47
  "preact": "^10.29.0",
49
48
  "prism-themes": "^1.9.0",
50
49
  "prismjs": "^1.30.0",
50
+ "radiant-docs-validator": "^0.1.17",
51
51
  "rehype-autolink-headings": "^7.1.0",
52
52
  "rehype-slug": "^6.0.0",
53
53
  "remark-gfm": "^4.0.1",
@@ -2,7 +2,7 @@
2
2
  import Icon from "./ui/Icon.astro";
3
3
  import { getConfig } from "../lib/validation";
4
4
  import LogoLink from "./LogoLink.astro";
5
- import { withBasePath } from "../lib/base-path";
5
+ import { resolveConfiguredHref } from "../lib/routes";
6
6
 
7
7
  interface Props {
8
8
  askAiEnabled?: boolean;
@@ -33,6 +33,15 @@ const socialIcons: Record<string, string> = {
33
33
  reddit: "simple-icons:reddit",
34
34
  podcast: "lucide:podcast",
35
35
  };
36
+
37
+ const footerLinks = footer?.links
38
+ ? await Promise.all(
39
+ footer.links.map(async (link) => ({
40
+ ...link,
41
+ href: await resolveConfiguredHref(link.href, config.hiddenPageRoutes),
42
+ })),
43
+ )
44
+ : [];
36
45
  ---
37
46
 
38
47
  <footer class="border-t border-border-light pt-16" data-pagefind-ignore>
@@ -63,11 +72,11 @@ const socialIcons: Record<string, string> = {
63
72
  )
64
73
  }
65
74
  {
66
- footer.links && (
75
+ footerLinks.length > 0 && (
67
76
  <div class="flex flex-wrap justify-center gap-x-8 gap-y-3">
68
- {footer.links.map((link) => (
77
+ {footerLinks.map((link) => (
69
78
  <a
70
- href={withBasePath(link.href)}
79
+ href={link.href}
71
80
  class="text-sm text-neutral-500 hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-neutral-100 transition-colors duration-300"
72
81
  >
73
82
  {link.text}
@@ -3,7 +3,7 @@ import Icon from "./ui/Icon.astro";
3
3
  import { getConfig, type ThemeColorByMode } from "../lib/validation";
4
4
  import Search from "./Search.astro";
5
5
  import LogoLink from "./LogoLink.astro";
6
- import { withBasePath } from "../lib/base-path";
6
+ import { resolveConfiguredHref } from "../lib/routes";
7
7
  import { getAssistantPanelRuntimeConfig } from "../lib/assistant-panel-config";
8
8
  import {
9
9
  getDocsBaseColorShade,
@@ -108,6 +108,26 @@ const navbarPrimaryButtonStyle = navbarPrimaryThemeColors
108
108
  )}`,
109
109
  ].join("; ")
110
110
  : "";
111
+ const navbarLinks = config.navbar?.links
112
+ ? await Promise.all(
113
+ config.navbar.links.map(async (link) => ({
114
+ ...link,
115
+ href: await resolveConfiguredHref(link.href, config.hiddenPageRoutes),
116
+ })),
117
+ )
118
+ : [];
119
+ const navbarSecondaryHref = config.navbar?.secondary
120
+ ? await resolveConfiguredHref(
121
+ config.navbar.secondary.href,
122
+ config.hiddenPageRoutes,
123
+ )
124
+ : "";
125
+ const navbarPrimaryHref = config.navbar?.primary
126
+ ? await resolveConfiguredHref(
127
+ config.navbar.primary.href,
128
+ config.hiddenPageRoutes,
129
+ )
130
+ : "";
111
131
  ---
112
132
 
113
133
  <header
@@ -176,7 +196,7 @@ const navbarPrimaryButtonStyle = navbarPrimaryThemeColors
176
196
  {
177
197
  config.navbar && (
178
198
  <nav class="hidden xs:flex items-center gap-3">
179
- {config.navbar.links && (
199
+ {navbarLinks.length > 0 && (
180
200
  <div
181
201
  class:list={[
182
202
  "space-x-1",
@@ -185,7 +205,7 @@ const navbarPrimaryButtonStyle = navbarPrimaryThemeColors
185
205
  : "hidden lg:flex",
186
206
  ]}
187
207
  >
188
- {config.navbar.links.map((l, i, a) => (
208
+ {navbarLinks.map((l, i, a) => (
189
209
  <a
190
210
  class:list={[
191
211
  "items-center gap-1 text-[13px] font-[450] dark:font-normal text-neutral-600/85 hover:text-neutral-600 dark:text-neutral-200/90 dark:hover:text-neutral-200 duration-200 px-1.5 py-[5px] whitespace-nowrap",
@@ -193,7 +213,7 @@ const navbarPrimaryButtonStyle = navbarPrimaryThemeColors
193
213
  ? "hidden 2xl:flex"
194
214
  : "flex",
195
215
  ]}
196
- href={withBasePath(l.href)}
216
+ href={l.href}
197
217
  >
198
218
  {l.icon && (
199
219
  <Icon
@@ -214,7 +234,7 @@ const navbarPrimaryButtonStyle = navbarPrimaryThemeColors
214
234
  "h-[33px] items-center gap-1.5 px-[11px] text-[13px] bg-linear-to-br from-neutral-100/70 to-neutral-100/90 dark:bg-none dark:bg-neutral-800/80 text-neutral-900/85 hover:text-neutral-600 dark:text-neutral-200/95 dark:hover:text-neutral-200 rounded-lg [corner-shape:superellipse(1.2)] border-[0.5px] border-neutral-900/10 dark:border-white/5 transition-all whitespace-nowrap",
215
235
  config.navbar.primary ? "hidden lg:flex" : "flex",
216
236
  ]}
217
- href={withBasePath(config.navbar.secondary.href)}
237
+ href={navbarSecondaryHref}
218
238
  >
219
239
  {config.navbar.secondary.icon && (
220
240
  <Icon
@@ -233,7 +253,7 @@ const navbarPrimaryButtonStyle = navbarPrimaryThemeColors
233
253
  "navbar-primary-trigger font-[350] dark:font-[450] h-8 flex items-center gap-2 px-3 text-[13px] rounded-lg [corner-shape:superellipse(1.2)] duration-200 transition-all whitespace-nowrap hover:opacity-95",
234
254
  ]}
235
255
  style={navbarPrimaryButtonStyle}
236
- href={withBasePath(config.navbar.primary.href)}
256
+ href={navbarPrimaryHref}
237
257
  >
238
258
  {config.navbar.primary.icon && (
239
259
  <Icon
@@ -1,5 +1,6 @@
1
1
  ---
2
- import { buildMdxPageHref, deriveTitleFromEntryId } from "../lib/utils";
2
+ import { deriveTitleFromEntryId } from "../lib/utils";
3
+ import { getMdxRouteHref } from "../lib/routes";
3
4
  import Icon from "./ui/Icon.astro";
4
5
  import Tag from "./ui/Tag.astro";
5
6
  import { getCollection } from "astro:content";
@@ -37,7 +38,7 @@ if (!entry) {
37
38
  // Use title from docs.json config if provided, otherwise derive from entry id
38
39
  const text = configTitle || deriveTitleFromEntryId(entry.id);
39
40
 
40
- const href = buildMdxPageHref({
41
+ const href = await getMdxRouteHref({
41
42
  filePath,
42
43
  groupSlug,
43
44
  homePath: config.home,
@@ -2,12 +2,8 @@
2
2
  import { getConfig, type NavGroup } from "../lib/validation";
3
3
  import SidebarLink from "./SidebarLink.astro";
4
4
  import SidebarOpenApiPageLink from "./sidebar/SidebarOpenApiPageLink.astro";
5
- import {
6
- buildMdxPageHref,
7
- buildOpenApiEndpointHref,
8
- parseOpenApiEndpoint,
9
- slugify,
10
- } from "../lib/utils";
5
+ import { parseOpenApiEndpoint, slugify } from "../lib/utils";
6
+ import { getMdxRouteHref, getOpenApiEndpointRouteHref } from "../lib/routes";
11
7
  import Tag from "./ui/Tag.astro";
12
8
  import Icon from "./ui/Icon.astro";
13
9
 
@@ -26,17 +22,18 @@ const groupId = `group-${currentPrefix}`;
26
22
  const listId = `list-${groupId}`;
27
23
 
28
24
  // Check if any child page is active (shallow check, no recursion needed)
29
- const containsActivePage = item.pages.some((child) => {
25
+ let containsActivePage = false;
26
+ for (const child of item.pages) {
30
27
  let href: string | null = null;
31
28
 
32
29
  if (typeof child === "string") {
33
- href = buildMdxPageHref({
30
+ href = await getMdxRouteHref({
34
31
  filePath: child,
35
32
  groupSlug: currentPrefix,
36
33
  homePath: config.home,
37
34
  });
38
35
  } else if ("page" in child) {
39
- href = buildMdxPageHref({
36
+ href = await getMdxRouteHref({
40
37
  filePath: child.page,
41
38
  groupSlug: currentPrefix,
42
39
  homePath: config.home,
@@ -44,7 +41,8 @@ const containsActivePage = item.pages.some((child) => {
44
41
  } else if ("openapi" in child) {
45
42
  const parsedEndpoint = parseOpenApiEndpoint(child.openapi.endpoint);
46
43
  if (parsedEndpoint) {
47
- href = buildOpenApiEndpointHref({
44
+ href = await getOpenApiEndpointRouteHref({
45
+ source: child.openapi.source,
48
46
  path: parsedEndpoint.path,
49
47
  method: parsedEndpoint.method,
50
48
  groupSlug: currentPrefix,
@@ -52,13 +50,16 @@ const containsActivePage = item.pages.some((child) => {
52
50
  }
53
51
  }
54
52
 
55
- if (!href) return false;
53
+ if (!href) continue;
56
54
 
57
55
  // Normalize paths for comparison (remove trailing slashes)
58
56
  const normalizedCurrent = Astro.url.pathname.replace(/\/$/, "");
59
57
  const normalizedTarget = href.replace(/\/$/, "");
60
- return normalizedCurrent === normalizedTarget;
61
- });
58
+ if (normalizedCurrent === normalizedTarget) {
59
+ containsActivePage = true;
60
+ break;
61
+ }
62
+ }
62
63
  ---
63
64
 
64
65
  <div
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  import Tag from "../ui/Tag.astro";
3
- import { buildOpenApiEndpointHref } from "../../lib/utils";
4
3
  import { methodColors } from "../../lib/utils";
4
+ import { getOpenApiEndpointRouteHref } from "../../lib/routes";
5
5
  import type { NavTag } from "../../lib/validation";
6
6
 
7
7
  interface Props {
8
+ source: string;
8
9
  method: string;
9
10
  path: string;
10
11
  summary?: string;
@@ -13,10 +14,19 @@ interface Props {
13
14
  parentSlug?: string;
14
15
  }
15
16
 
16
- const { method, path: pathStr, summary, title, tag, parentSlug = "" } = Astro.props;
17
+ const {
18
+ source,
19
+ method,
20
+ path: pathStr,
21
+ summary,
22
+ title,
23
+ tag,
24
+ parentSlug = "",
25
+ } = Astro.props;
17
26
 
18
27
  const normalizedMethod = method.toLowerCase();
19
- const href = buildOpenApiEndpointHref({
28
+ const href = await getOpenApiEndpointRouteHref({
29
+ source,
20
30
  path: pathStr,
21
31
  method: normalizedMethod,
22
32
  groupSlug: parentSlug,
@@ -182,6 +182,7 @@ const sortedTagGroups: TagGroup[] = Array.from(tagGroups.entries())
182
182
  {group.endpoints.map((endpoint) => (
183
183
  <li>
184
184
  <SidebarEndpointLink
185
+ source={openApiPath}
185
186
  method={endpoint.method}
186
187
  path={endpoint.path}
187
188
  summary={endpoint.summary}
@@ -196,6 +197,7 @@ const sortedTagGroups: TagGroup[] = Array.from(tagGroups.entries())
196
197
  untaggedEndpoints.map((endpoint, index) => (
197
198
  <li>
198
199
  <SidebarEndpointLink
200
+ source={openApiPath}
199
201
  method={endpoint.method}
200
202
  path={endpoint.path}
201
203
  summary={endpoint.summary}
@@ -47,6 +47,7 @@ if (!matchedPath) {
47
47
  ---
48
48
 
49
49
  <SidebarEndpointLink
50
+ source={item.openapi.source}
50
51
  method={endpointMethod}
51
52
  path={matchedPath}
52
53
  summary={endpointSummary}
@@ -1,6 +1,5 @@
1
1
  ---
2
2
  import Icon from "../ui/Icon.astro";
3
- import { validateProps } from "../../lib/component-error";
4
3
 
5
4
  interface Props {
6
5
  title: string;
@@ -19,18 +18,6 @@ const TITLE_SIZE_CLASS = {
19
18
  xl: "text-xl",
20
19
  "2xl": "text-2xl",
21
20
  } as const;
22
-
23
- validateProps(
24
- "Accordion",
25
- { title, icon, defaultOpen, titleSize },
26
- {
27
- title: { required: true, type: "string" },
28
- icon: { type: "string" },
29
- defaultOpen: { type: "boolean" },
30
- titleSize: { enum: ["xs", "sm", "md", "lg", "xl", "2xl"] },
31
- },
32
- Astro.url.pathname,
33
- );
34
21
  ---
35
22
 
36
23
  <div
@@ -1,6 +1,5 @@
1
1
  ---
2
2
  import Icon from "../ui/Icon.astro";
3
- import { validateProps } from "../../lib/component-error";
4
3
 
5
4
  type CalloutType = "warning" | "info" | "tip" | "danger" | "success";
6
5
 
@@ -43,36 +42,8 @@ const typeDefaults: Record<
43
42
  },
44
43
  };
45
44
 
46
- const rawProps = Astro.props as { icon?: unknown; title?: unknown };
47
- const rawIcon = rawProps.icon;
48
- const rawTitle = rawProps.title;
49
45
  const { type = "info", title, icon, accent = true, color } = Astro.props;
50
46
 
51
- validateProps(
52
- "Callout",
53
- { type, title, icon, accent, color },
54
- {
55
- type: { enum: ["warning", "info", "tip", "danger", "success"] },
56
- title: { type: ["string", "boolean"] },
57
- icon: { type: ["string", "boolean"] },
58
- accent: { type: "boolean" },
59
- color: { type: "string" },
60
- },
61
- Astro.url.pathname,
62
- );
63
-
64
- if (rawIcon === true) {
65
- throw new Error(
66
- `[USER_ERROR]: <Callout>: Invalid prop "icon": expected string or false, got true (in ${Astro.url.pathname})`,
67
- );
68
- }
69
-
70
- if (rawTitle === true) {
71
- throw new Error(
72
- `[USER_ERROR]: <Callout>: Invalid prop "title": expected string or false, got true (in ${Astro.url.pathname})`,
73
- );
74
- }
75
-
76
47
  const defaults = typeDefaults[type];
77
48
  const resolvedTitle = title === false ? null : (title ?? defaults.title);
78
49
  const resolvedIcon = icon === false ? null : (icon ?? defaults.icon);