Skip to content

Commit 87f6f3c

Browse files
committed
Added python and javascript clients to search
1 parent 5fa7383 commit 87f6f3c

File tree

1 file changed

+102
-10
lines changed

1 file changed

+102
-10
lines changed

src/markdoc/search.mjs

Lines changed: 102 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as path from 'path'
66
import { createLoader } from 'simple-functional-loader'
77
import * as url from 'url'
88
import javascript from "../../schema/javascript.json" assert { type: "json" };
9+
import python from '../../schema/python.json' assert { type: "json" }
910

1011
const __filename = url.fileURLToPath(import.meta.url)
1112
const slugify = slugifyWithCounter()
@@ -42,10 +43,96 @@ function extractSections(node, sections, isRoot = true) {
4243
}
4344
}
4445

46+
/**
47+
* Extract searchable sections from JavaScript and Python JSON documentation files
48+
* @param {Object} jsonData - The JSON documentation object
49+
* @returns {Array} - Array of url and sections for search indexing
50+
*/
51+
function extractJSONSections(jsonData, basePath) {
52+
const result = []
53+
slugify.reset()
54+
const title = `${jsonData.language}`
55+
56+
// Extract from modules
57+
if (jsonData.modules && Array.isArray(jsonData.modules)) {
58+
// For each module
59+
jsonData.modules.forEach(module => {
60+
// For each class in the module
61+
if (module.classes && Array.isArray(module.classes)) {
62+
module.classes.forEach(classObj => {
63+
// Create a section for the class itself
64+
const className = `${title} ${classObj.name}`
65+
const classHash = [classObj.name, ...classObj.memberFunctions.map(p => p.name)].join('').replaceAll(/[^a-zA-Z0-9]/g, '').toLowerCase();
66+
const classContent = [classObj.summary || '']
67+
68+
result.push({
69+
url: `${basePath}`,
70+
sections: [[className, classHash, classContent]]
71+
})
72+
73+
// Process member functions
74+
if (classObj.memberFunctions && Array.isArray(classObj.memberFunctions)) {
75+
classObj.memberFunctions.forEach(func => {
76+
const functionName = `${className}.${func.name}`
77+
78+
// Generate the correct anchor based on function name only
79+
// This ensures clean, predictable anchor links
80+
81+
// Create a clean hash with no special characters
82+
const functionHash = [func.name, ...func.parameters.map(p => p.name)].join('').replaceAll(/[^a-zA-Z0-9]/g, '').toLowerCase();
83+
84+
// Gather content for this function
85+
const functionContent = []
86+
87+
// Add summary
88+
if (func.summary) {
89+
functionContent.push(func.summary)
90+
}
91+
92+
// Add examples
93+
if (func.examples && Array.isArray(func.examples) && func.examples.length > 0) {
94+
functionContent.push('Examples:')
95+
func.examples.forEach(example => {
96+
functionContent.push(example)
97+
})
98+
}
99+
100+
// Add parameters
101+
if (func.parameters && Array.isArray(func.parameters) && func.parameters.length > 0) {
102+
functionContent.push('Parameters:')
103+
func.parameters.forEach(param => {
104+
const paramDesc = `${param.name} (${param.type})${param.summary ? ': ' + param.summary : ''}`
105+
functionContent.push(paramDesc)
106+
})
107+
}
108+
109+
// Add return type
110+
if (func.returns) {
111+
const returnDesc = `Returns: ${func.returns.type}${func.returns.summary ? ' - ' + func.returns.summary : ''}`
112+
functionContent.push(returnDesc)
113+
}
114+
115+
// Create section for this function
116+
const functionTitle = `${functionName}`
117+
result.push({
118+
url: `${basePath}`,
119+
sections: [[functionTitle, functionHash, functionContent]]
120+
})
121+
})
122+
}
123+
})
124+
}
125+
})
126+
}
127+
128+
return result
129+
}
130+
45131
export default function withSearch(nextConfig = {}) {
46132
let cache = new Map()
47133

48-
return Object.assign({}, nextConfig, {
134+
return {
135+
...nextConfig,
49136
webpack(config, options) {
50137
config.module.rules.push({
51138
test: __filename,
@@ -77,12 +164,13 @@ export default function withSearch(nextConfig = {}) {
77164

78165
return { url, sections }
79166
})
80-
81-
// FIXME: Enable search of javascript and python too
82-
// const javascriptSections = javascript.modules.map((module) => {
83-
84-
// return [module.title, null, []]
85-
// })
167+
168+
// Extract sections from JavaScript and Python JSON files
169+
const javascriptSections = extractJSONSections(javascript, '/docs/javascript')
170+
const pythonSections = extractJSONSections(python, '/docs/python')
171+
172+
// Add JSON documentation sections to the search data
173+
data = [...data, ...javascriptSections, ...pythonSections]
86174

87175
// When this file is imported within the application
88176
// the following module is loaded:
@@ -107,8 +195,12 @@ export default function withSearch(nextConfig = {}) {
107195
108196
for (let { url, sections } of data) {
109197
for (let [title, hash, content] of sections) {
198+
// Check if the URL already contains a hash to avoid duplication
199+
const urlAlreadyHasHash = url.includes('#');
200+
const finalUrl = urlAlreadyHasHash ? url : url + (hash ? ('#' + hash) : '');
201+
110202
sectionIndex.add({
111-
url: url + (hash ? ('#' + hash) : ''),
203+
url: finalUrl,
112204
title,
113205
content: [title, ...content].join('\\n'),
114206
pageTitle: hash ? sections[0][0] : undefined,
@@ -140,6 +232,6 @@ export default function withSearch(nextConfig = {}) {
140232
}
141233

142234
return config
143-
},
144-
})
235+
}
236+
}
145237
}

0 commit comments

Comments
 (0)