From f956982f965e5ae5a19b5229509e1bde589e796d Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Thu, 18 Jun 2026 16:35:13 +0300 Subject: [PATCH 1/3] feat(llms): convert ApiLink components to markdown links in generated .md files --- docs/angular/astro.config.ts | 2 ++ docs/xplat/astro.config.ts | 2 ++ src/integration.ts | 68 ++++++++++++++++++++++++++++++++---- src/lib/platform-context.ts | 56 ++++++++++++++++++----------- 4 files changed, 100 insertions(+), 28 deletions(-) diff --git a/docs/angular/astro.config.ts b/docs/angular/astro.config.ts index f97a661c80..b02e253fef 100644 --- a/docs/angular/astro.config.ts +++ b/docs/angular/astro.config.ts @@ -3,6 +3,7 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { createDocsSite, type DocsMode } from 'docs-template/integration'; import { IGDOCS_PLATFORMS, type NavLang } from 'docs-template/platform'; +import { getPlatformContext } from 'docs-template/lib/platform-context'; import { generateGridTopics } from './src/scripts/generate-grids.mjs'; import mdx from '@astrojs/mdx'; @@ -77,6 +78,7 @@ export default createDocsSite({ href: mode === 'production' ? `${PROD_HOST}${b}/` : `${STAGING_HOST}${b}/`, })), selectedPackage: 'angular', + platformContext: getPlatformContext('Angular'), source: { tocPath: `${componentsDocsDir}/toc.json`, docsDir: componentsDocsDir, diff --git a/docs/xplat/astro.config.ts b/docs/xplat/astro.config.ts index 0f2866b008..d5946a770b 100644 --- a/docs/xplat/astro.config.ts +++ b/docs/xplat/astro.config.ts @@ -3,6 +3,7 @@ import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { createDocsSite, type DocsMode } from 'docs-template/integration'; import { IGDOCS_PLATFORMS, type NavLang } from 'docs-template/platform'; +import { getPlatformContext } from 'docs-template/lib/platform-context'; import mdx from '@astrojs/mdx'; // --------------------------------------------------------------------------- @@ -339,6 +340,7 @@ export default createDocsSite({ platform: p.key, navLang: lang, mode, + platformContext: getPlatformContext(), build: { format: 'file' }, diff --git a/src/integration.ts b/src/integration.ts index 9df2e34b26..5a24ee6ee3 100644 --- a/src/integration.ts +++ b/src/integration.ts @@ -67,6 +67,8 @@ import { rehypeHeadingAnchors } from 'igniteui-astro-components/plugins/rehype-h import { rehypePagefindIgnore } from 'igniteui-astro-components/plugins/rehype-pagefind-ignore'; import { rehypeStripEmptyParagraphs } from './plugins/rehype-strip-empty-paragraphs'; import { rehypeApiReferencesGrid } from './plugins/rehype-api-references-grid'; +import type { PlatformContext } from 'igniteui-astro-components/lib/types'; +import { resolveApiLink, type ApiLinkProps } from 'igniteui-astro-components/components/mdx/ApiLink/api-link-index'; /** Build / deployment mode. Drives env-var `DOCS_BUILD_MODE`. */ export type DocsMode = 'development' | 'staging' | 'production'; @@ -117,6 +119,42 @@ export interface SiteMetaOptions { * ignored when no package options are available. */ selectedPackage?: string; + /** + * Platform context used to resolve `` components in generated `.md` files. + * When provided, ApiLinks are rendered as markdown links pointing to the correct API docs URL. + * When omitted, ApiLinks fall back to inline code (`` `TypeName.member` ``). + */ + platformContext?: PlatformContext | null; +} + +/** Parse a JSX/MDX attribute string into a plain key→value map. */ +function parseApiLinkAttrs(attrs: string): Record { + const props: Record = {}; + for (const m of attrs.matchAll(/(\w+)="([^"]*)"/g)) props[m[1]] = m[2]; + for (const m of attrs.matchAll(/(\w+)=\{(true|false)\}/g)) props[m[1]] = m[2] === 'true'; + return props; +} + +/** + * Convert an `` JSX element to Markdown. + * Delegates all resolution + label logic to `resolveApiLink` in igniteui-astro-components + * so both the Astro component and this markdown generator stay in sync automatically. + */ +function apiLinkToMd(attrs: string, ctx: PlatformContext | null | undefined): string { + const props = parseApiLinkAttrs(attrs); + + if (!ctx) { + // No platform context — best-effort label from raw props, no URL. + const type = String(props.type ?? ''); + const member = props.member ? String(props.member) : undefined; + const lbl = String(props.label ?? props.module ?? (member ? `${type}.${member}` : type)); + return lbl ? `\`${lbl}\`` : ''; + } + + const result = resolveApiLink(props as ApiLinkProps, ctx); + return result.renderLink + ? `[\`${result.label}\`](${result.url})` + : `\`${result.label}\``; } /** @@ -125,19 +163,26 @@ export interface SiteMetaOptions { * * Removes: * - `import … from '…'` lines at the top of the file - * - Self-closing JSX components: , + * - Self-closing JSX components: , , * - Inline blocks + * + * Converts: + * - `` → markdown link (when ctx resolves the symbol) or inline code */ -function stripMdxForLlms(raw: string): string { +function stripMdxForLlms(raw: string, ctx?: PlatformContext | null): string { return raw // Remove all import lines .replace(/^import\s+.+from\s+['"][^'"]+['"];?\r?\n/gm, '') // Remove blocks (multiline) .replace(/ blocks - * - * Converts: - * - `` → markdown link (when ctx resolves the symbol) or inline code - * - Root-relative links `[label](/path)` → absolute URLs using siteUrl - */ -function stripMdxForLlms(raw: string, ctx?: PlatformContext | null, siteUrl?: string): string { - const site = siteUrl?.replace(/\/$/, '') ?? ''; - return raw - // Remove all import lines - .replace(/^import\s+.+from\s+['"][^'"]+['"];?\r?\n/gm, '') - // Remove blocks (multiline) - .replace(/