Skip to content

Commit 0857ffd

Browse files
committed
build: create llms.txt and markdown files
1 parent e66ad32 commit 0857ffd

File tree

4 files changed

+911
-4
lines changed

4 files changed

+911
-4
lines changed

packages/__docs__/buildScripts/build-docs.mts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@ import { theme as canvasTheme } from '@instructure/canvas-theme'
3131
import { theme as canvasHighContrastTheme } from '@instructure/canvas-high-contrast-theme'
3232
import type {
3333
LibraryOptions,
34-
MainDocsData, ProcessedFile
34+
MainDocsData,
35+
ProcessedFile
3536
} from './DataTypes.mjs'
3637
import { getFrontMatter } from './utils/getFrontMatter.mjs'
37-
import { createRequire } from "module"
38+
import { createRequire } from 'module'
3839
import { fileURLToPath, pathToFileURL } from 'url'
40+
import { generateIndividualMarkdownFiles } from './generate-markdowns.mjs'
41+
import { generateLLMS } from './generate-llms.mjs'
3942

4043
const __filename = fileURLToPath(import.meta.url)
4144
const __dirname = path.dirname(__filename)
@@ -101,7 +104,7 @@ const pathsToIgnore = [
101104
// deprecated packages and modules:
102105
'**/InputModeListener.ts',
103106
// regression testing app:
104-
'**/regression-test/**',
107+
'**/regression-test/**'
105108
]
106109

107110
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
@@ -146,9 +149,24 @@ function buildDocs() {
146149
markdownsAndSources
147150
)
148151

152+
generateIndividualMarkdownFiles(
153+
buildDir + 'docs/',
154+
buildDir + 'markdowns/'
155+
)
156+
const parentOfDocs = path.dirname(buildDir + 'docs/')
157+
generateLLMS(
158+
buildDir + 'markdown-and-sources-data.json',
159+
path.join(parentOfDocs, 'llms.txt'),
160+
'https://instructure.design/pr-preview/pr-2109/markdowns/',
161+
path.join(__dirname, '../buildScripts/summaries.json')
162+
)
163+
149164
// eslint-disable-next-line no-console
150165
console.log('Copying icons data...')
151-
fs.copyFileSync(projectRoot + '/packages/ui-icons/__build__/icons-data.json', buildDir + 'icons-data.json')
166+
fs.copyFileSync(
167+
projectRoot + '/packages/ui-icons/__build__/icons-data.json',
168+
buildDir + 'icons-data.json'
169+
)
152170

153171
// eslint-disable-next-line no-console
154172
console.log('Finished building documentation data')
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { writeFileSync, readFileSync } from 'fs'
2+
3+
interface SectionData {
4+
docs?: string[]
5+
sections?: string[]
6+
title?: string
7+
}
8+
9+
function generateDocsMarkdown(
10+
filePath: string,
11+
summariesFilePath?: string,
12+
baseUrl?: string
13+
14+
): string {
15+
const fileContent = readFileSync(filePath, 'utf-8')
16+
const jsonData = JSON.parse(fileContent)
17+
const { sections } = jsonData
18+
19+
let summaries: Record<string, string> = {}
20+
// Generate summaries object form the summaries file for later lookup
21+
if (summariesFilePath) {
22+
try {
23+
const fileContent = readFileSync(summariesFilePath, 'utf-8')
24+
const data = JSON.parse(fileContent)
25+
26+
data.forEach((item: { title: string; summary?: string }) => {
27+
summaries[item.title] = item.summary || ''
28+
})
29+
} catch (error) {
30+
console.warn('Could not load summaries file:', error)
31+
}
32+
}
33+
34+
let markdownContent = `# Instructure UI (InstUI) - React Component Library\n\n - version 10.24.1 \n\n `
35+
markdownContent += `- Instructure UI (InstUI) is a comprehensive React component library.\n\n`
36+
37+
// Main Documentation section
38+
markdownContent += `## Documentation\n\n`
39+
40+
// Add User Guides wrapper
41+
markdownContent += `### User Guides\n`
42+
43+
Object.entries(sections as Record<string, SectionData>).forEach(
44+
([sectionKey, sectionData]) => {
45+
const sectionTitle = sectionData.title || sectionKey
46+
47+
// Only process these specific sections
48+
if (!['Getting Started', 'Guides', 'Patterns'].includes(sectionTitle)) {
49+
return
50+
}
51+
52+
markdownContent += `#### ${sectionTitle}\n\n`
53+
54+
if (sectionData.docs && sectionData.docs.length > 0) {
55+
const uniqueDocs = [...new Set(sectionData.docs)]
56+
uniqueDocs.forEach((doc) => {
57+
// Skip unnecessary documents
58+
if (
59+
doc === 'CODE_OF_CONDUCT' ||
60+
doc === 'LICENSE' ||
61+
doc.includes('upgrade-guide')
62+
) {
63+
return
64+
}
65+
const displayName = doc
66+
.replace(/([A-Z])/g, ' $1') // Add space before each capital letter first
67+
.replace(/-/g, ' ') // Then replace hyphens with spaces
68+
.trim() // Remove any leading/trailing spaces
69+
.toLowerCase() // Convert everything to lowercase
70+
.replace(/^\w/, (char) => char.toUpperCase()) // Capitalize only the first letter
71+
72+
const summary = summaries[doc]
73+
markdownContent += `- [${displayName}](${baseUrl}${doc}.md)${
74+
summary ? `: ${summary}` : ''
75+
}\n`
76+
})
77+
}
78+
markdownContent += '\n'
79+
}
80+
)
81+
82+
// Add Components section (sibling of User Guides)
83+
markdownContent += `### Components\n\n`
84+
85+
const componentsSection = sections['components'] as SectionData | undefined
86+
if (componentsSection) {
87+
const allComponents = [...new Set(componentsSection.docs || [])]
88+
allComponents.forEach((component) => {
89+
const summary = summaries[component]
90+
markdownContent += `- [${component}](${baseUrl}${component}.md)${
91+
summary ? `: ${summary}` : ''
92+
}\n`
93+
})
94+
}
95+
96+
// Process component subsections like AI Components and utilities, skip deprecated
97+
if (componentsSection?.sections && componentsSection.sections.length > 0) {
98+
const subsections = componentsSection.sections
99+
subsections.forEach((subsectionPath: string) => {
100+
if (subsectionPath.toLowerCase().includes('deprecated')) return
101+
102+
const subsection = sections[subsectionPath] as SectionData | undefined
103+
if (subsection && subsection.docs && subsection.docs.length > 0) {
104+
const subsectionTitle = subsection.title
105+
markdownContent += `\n#### ${subsectionTitle}\n\n`
106+
// Avoid duplicates
107+
const uniqueSubDocs = [...new Set(subsection.docs)]
108+
uniqueSubDocs.forEach((doc) => {
109+
const summary = summaries[doc]
110+
markdownContent += `- [${doc}](${baseUrl}${doc}.md)${
111+
summary ? `: ${summary}` : ''
112+
}\n`
113+
})
114+
}
115+
})
116+
}
117+
118+
return markdownContent
119+
}
120+
121+
function generateLLMS(
122+
inputFilePath: string,
123+
outputFilePath: string,
124+
baseUrl: string,
125+
summariesFilePath?: string
126+
127+
) {
128+
const markdownContent = generateDocsMarkdown(inputFilePath, summariesFilePath, baseUrl)
129+
writeFileSync(outputFilePath, markdownContent)
130+
}
131+
132+
export { generateLLMS }

0 commit comments

Comments
 (0)