Skip to content

Commit 770c1e8

Browse files
authored
[docs-infra] Fix markdown generation script to find correct files (#46954)
1 parent 7847e5e commit 770c1e8

File tree

3 files changed

+55
-35
lines changed

3 files changed

+55
-35
lines changed

packages/api-docs-builder-core/materialUi/projectSettings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export const projectSettings: ProjectSettings = {
4646
isGlobalClassName: isGlobalState,
4747
// #host-reference
4848
baseApiUrl: 'https://mui.com',
49+
pagesManifestPath: path.join(process.cwd(), 'docs/data/material/pages.ts'),
4950
nonComponentFolders: [
5051
'material/getting-started',
5152
'material/customization',

packages/api-docs-builder/ProjectSettings.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,11 @@ export interface ProjectSettings {
114114
*/
115115
baseApiUrl?: string;
116116
/**
117-
* Determines the non-component folders to be included in the llms.txt file.
117+
* The path to the `pages.ts` manifest file for public markdown generation.
118+
*/
119+
pagesManifestPath?: string;
120+
/**
121+
* Determines the non-component folders for ordering in the llms.txt file.
118122
* The folders are relative to the `docs/data` directory.
119123
*/
120124
nonComponentFolders?: string[];

scripts/buildLlmsDocs/index.ts

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ import kebabCase from 'lodash/kebabCase';
6363
import { processMarkdownFile, processApiFile } from '@mui/internal-scripts/generate-llms-txt';
6464
import { ComponentInfo, ProjectSettings } from '@mui-internal/api-docs-builder';
6565
import { getHeaders } from '@mui/internal-markdown';
66-
import { fixPathname } from '@mui-internal/api-docs-builder/buildApiUtils';
67-
import replaceUrl from '@mui-internal/api-docs-builder/utils/replaceUrl';
6866
import findComponents from '@mui-internal/api-docs-builder/utils/findComponents';
6967
import findPagesMarkdown from '@mui-internal/api-docs-builder/utils/findPagesMarkdown';
7068

@@ -153,21 +151,26 @@ async function findComponentsToProcess(
153151
continue;
154152
}
155153

156-
// Get the markdown file path from the first demo
157-
const firstDemo = demos[0];
158-
const markdownPath = firstDemo ? firstDemo.filePath : undefined;
159-
160154
// Get API JSON path
161155
const apiJsonPath = path.join(
162156
componentInfo.apiPagesDirectory,
163157
`${path.basename(componentInfo.apiPathname)}.json`,
164158
);
165159

160+
const primaryDemo = demos.find(
161+
(demo) =>
162+
demo.demoPathname.toLowerCase().includes(`/${kebabCase(componentInfo.name)}/`) ||
163+
demo.demoPathname.toLowerCase().includes(`/react-${kebabCase(componentInfo.name)}/`),
164+
);
165+
166+
const demoToUse = primaryDemo || demos[0];
167+
const markdownPathToUse = demoToUse ? demoToUse.filePath : undefined;
168+
166169
components.push({
167170
name: componentInfo.name,
168171
componentInfo,
169-
demos,
170-
markdownPath,
172+
demos: primaryDemo ? [primaryDemo] : demos,
173+
markdownPath: markdownPathToUse,
171174
apiJsonPath: fs.existsSync(apiJsonPath) ? apiJsonPath : undefined,
172175
});
173176
} catch (error) {
@@ -219,38 +222,46 @@ function extractMarkdownInfo(markdownPath: string): { title: string; description
219222
* Find all non-component markdown files from specified folders
220223
*/
221224
function findNonComponentMarkdownFiles(
222-
folders: string[],
225+
projectSettings: ProjectSettings,
223226
grep: RegExp | null,
224227
): Array<{ markdownPath: string; outputPath: string }> {
228+
if (!projectSettings.pagesManifestPath) {
229+
return [];
230+
}
231+
const pagesContent = fs.readFileSync(projectSettings.pagesManifestPath, 'utf-8');
232+
233+
// Extract all pathname strings using regex
234+
const pathnameRegex = /pathname:\s*['"`]([^'"`]+)['"`]/g;
235+
const matches = Array.from(pagesContent.matchAll(pathnameRegex));
236+
225237
// Get all markdown files using the existing findPagesMarkdown utility
226238
const allMarkdownFiles = findPagesMarkdown();
227239

228240
const files: Array<{ markdownPath: string; outputPath: string }> = [];
229241

230-
for (const page of allMarkdownFiles) {
231-
// Check if the page belongs to one of the specified folders
232-
const belongsToFolder = folders.some((folder) => page.pathname.startsWith(`/${folder}`));
233-
if (!belongsToFolder) {
234-
continue;
235-
}
236-
242+
for (const match of matches) {
243+
const pathname = match[1];
244+
const parsedPathname = pathname
245+
.replace('/material-ui/', '/material/')
246+
.replace('/react-', '/components/');
237247
// Apply grep filter if specified
238248
if (grep) {
239-
const fileName = path.basename(page.filename);
240-
if (!grep.test(fileName) && !grep.test(page.pathname)) {
249+
const fileName = path.basename(parsedPathname);
250+
if (!grep.test(fileName) && !grep.test(parsedPathname)) {
241251
continue;
242252
}
243253
}
244-
245-
// Apply fixPathname first, then replaceUrl to get the proper output structure (like components)
246-
const afterFixPathname = fixPathname(page.pathname);
247-
const fixedPathname = replaceUrl(afterFixPathname, '/material-ui/');
248-
const outputPath = `${fixedPathname.replace(/^\//, '').replace(/\/$/, '')}.md`;
249-
250-
files.push({
251-
markdownPath: page.filename,
252-
outputPath,
253-
});
254+
// Filter out external links and special patterns
255+
if (pathname.startsWith('/material-ui/')) {
256+
const page = allMarkdownFiles.find((p) => p.pathname === parsedPathname);
257+
258+
if (page) {
259+
files.push({
260+
markdownPath: page.filename,
261+
outputPath: `${pathname.startsWith('/') ? pathname.slice(1) : pathname}.md`,
262+
});
263+
}
264+
}
254265
}
255266

256267
return files;
@@ -427,15 +438,12 @@ async function buildLlmsDocs(argv: ArgumentsCamelCase<CommandOptions>): Promise<
427438
// Found ${components.length} components to process
428439

429440
// Find non-component markdown files if specified in project settings
430-
let nonComponentFiles: Array<{ markdownPath: string; outputPath: string }> = [];
431441
const nonComponentFolders = (projectSettings as any).nonComponentFolders;
432-
if (nonComponentFolders && nonComponentFolders.length > 0) {
433-
nonComponentFiles = findNonComponentMarkdownFiles(nonComponentFolders, grep);
434-
// Found ${nonComponentFiles.length} non-component markdown files to process
435-
}
442+
const nonComponentFiles = findNonComponentMarkdownFiles(projectSettings, grep);
436443

437444
// Track generated files for llms.txt
438445
const generatedFiles: GeneratedFile[] = [];
446+
const generatedComponentRecord: Record<string, boolean> = {};
439447

440448
// Process each component
441449
let processedCount = 0;
@@ -478,6 +486,7 @@ async function buildLlmsDocs(argv: ArgumentsCamelCase<CommandOptions>): Promise<
478486
originalMarkdownPath: component.markdownPath,
479487
category: 'components',
480488
});
489+
generatedComponentRecord[outputFileName] = true;
481490
}
482491
}
483492
} catch (error) {
@@ -488,6 +497,10 @@ async function buildLlmsDocs(argv: ArgumentsCamelCase<CommandOptions>): Promise<
488497
// Process non-component markdown files
489498
for (const file of nonComponentFiles) {
490499
try {
500+
if (generatedComponentRecord[file.outputPath]) {
501+
// Skip files that have already been generated as component docs
502+
continue;
503+
}
491504
// Processing non-component file: ${path.relative(process.cwd(), file.markdownPath)}
492505

493506
// Process the markdown file with demo replacement
@@ -560,7 +573,9 @@ async function buildLlmsDocs(argv: ArgumentsCamelCase<CommandOptions>): Promise<
560573
projectName = dirName.charAt(0).toUpperCase() + dirName.slice(1);
561574
}
562575

563-
const llmsContent = generateLlmsTxt(files, projectName, dirName, ORIGIN);
576+
const llmsContent = generateLlmsTxt(files, projectName, dirName, ORIGIN)
577+
.replace(/Ui/g, 'UI')
578+
.replace(/Api/g, 'API');
564579
const llmsPath = path.join(outputDir, dirName, 'llms.txt');
565580

566581
// Ensure directory exists

0 commit comments

Comments
 (0)