Skip to content

Commit 48c7a13

Browse files
committed
working sdk docs setup
1 parent 830e86d commit 48c7a13

16 files changed

Lines changed: 2817 additions & 2 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,7 @@ logs
5656
*.tsbuildinfo
5757

5858
# Optional REPL history
59-
.node_repl_history
59+
.node_repl_history
60+
61+
# Docs
62+
docs/

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
"test:e2e": "vitest run tests/e2e",
1717
"test:watch": "vitest",
1818
"test:coverage": "vitest run --coverage",
19-
"prepublishOnly": "npm run build"
19+
"docs": "typedoc",
20+
"prepublishOnly": "npm run build",
21+
"create-docs": "npm run create-docs:generate && npm run create-docs:process",
22+
"push-docs": "node scripts/mintlify-post-processing/push-to-docs-repo.js",
23+
"create-docs:generate": "typedoc",
24+
"create-docs:process": "node scripts/mintlify-post-processing/file-processing/file-processing.js"
2025
},
2126
"dependencies": {
2227
"axios": "^1.6.2",
@@ -30,6 +35,8 @@
3035
"dotenv": "^16.3.1",
3136
"eslint": "^8.54.0",
3237
"nock": "^13.4.0",
38+
"typedoc": "^0.28.14",
39+
"typedoc-plugin-markdown": "^4.9.0",
3340
"typescript": "^5.3.2",
3441
"vitest": "^1.0.0"
3542
},
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"functions": "Main Methods",
3+
"interfaces": "Modules"
4+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"$schema": "https://mintlify.com/docs.json",
3+
"theme": "mint",
4+
"name": "Base44 Support Documentation",
5+
"integrations": {
6+
"mixpanel": {
7+
"projectToken": "cc6e9e106e4b833fc3a3819c11b74138"
8+
}
9+
},
10+
"colors": {
11+
"primary": "#FF5500",
12+
"light": "#EEE2C0",
13+
"dark": "#FF5500"
14+
},
15+
"navigation": {
16+
"tabs": [
17+
{
18+
"tab": "SDK Reference",
19+
"groups": [
20+
{
21+
"group": "Main Methods",
22+
"pages": [
23+
"content/functions/createClient",
24+
"content/functions/createClientFromRequest",
25+
"content/functions/getAccessToken",
26+
"content/functions/saveAccessToken",
27+
"content/functions/removeAccessToken",
28+
"content/functions/getLoginUrl"
29+
]
30+
},
31+
{
32+
"group": "Modules",
33+
"pages": ["content/interfaces/Auth"]
34+
}
35+
]
36+
}
37+
]
38+
},
39+
"navbar": {
40+
"links": [
41+
{
42+
"label": "Support",
43+
"href": "https://app.base44.com/support/conversations"
44+
}
45+
],
46+
"primary": {
47+
"type": "button",
48+
"label": "Base44",
49+
"href": "https://base44.com/?utm_source=Mintlify&utm_medium=Main&utm_content=menu"
50+
}
51+
},
52+
"footer": {
53+
"socials": {
54+
"twitter": "https://x.com/base_44",
55+
"discord": "https://discord.com/invite/ThpYPZpVts",
56+
"linkedin": "https://www.linkedin.com/company/base44"
57+
}
58+
},
59+
"custom": {
60+
"stylesheets": ["/styling.css"]
61+
}
62+
}
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Post-processing script for TypeDoc-generated MDX files
5+
*
6+
* TypeDoc now emits .mdx files directly, so this script:
7+
* 1. Processes links to make them Mintlify-compatible
8+
* 2. Removes files for linked types that should be suppressed
9+
* 3. Cleans up the temporary linked types tracking file
10+
* 4. Generates docs.json with navigation structure
11+
* 5. Copies styling.css to docs directory
12+
*/
13+
14+
import fs from 'fs';
15+
import path from 'path';
16+
import { fileURLToPath } from 'url';
17+
18+
const __filename = fileURLToPath(import.meta.url);
19+
const __dirname = path.dirname(__filename);
20+
21+
const DOCS_DIR = path.join(__dirname, '..', '..', '..', 'docs');
22+
const CONTENT_DIR = path.join(DOCS_DIR, 'content');
23+
const LINKED_TYPES_FILE = path.join(CONTENT_DIR, '.linked-types.json');
24+
const TEMPLATE_PATH = path.join(__dirname, 'docs-json-template.json');
25+
const STYLING_CSS_PATH = path.join(__dirname, 'styling.css');
26+
const CATEGORY_MAP_PATH = path.join(__dirname, '../category-map.json');
27+
const TYPES_TO_EXPOSE_PATH = path.join(__dirname, '..', 'types-to-expose.json');
28+
29+
/**
30+
* Get list of linked type names that should be suppressed
31+
*/
32+
function getLinkedTypeNames() {
33+
try {
34+
if (fs.existsSync(LINKED_TYPES_FILE)) {
35+
const content = fs.readFileSync(LINKED_TYPES_FILE, 'utf-8');
36+
return new Set(JSON.parse(content));
37+
}
38+
} catch (e) {
39+
// If file doesn't exist or can't be read, return empty set
40+
}
41+
return new Set();
42+
}
43+
44+
/**
45+
* Load allow-listed type names that should remain in the docs output
46+
*/
47+
function getTypesToExpose() {
48+
try {
49+
const content = fs.readFileSync(TYPES_TO_EXPOSE_PATH, 'utf-8');
50+
const parsed = JSON.parse(content);
51+
if (!Array.isArray(parsed)) {
52+
throw new Error('types-to-expose.json must be an array of strings');
53+
}
54+
return new Set(parsed);
55+
} catch (e) {
56+
console.error(`Error: Unable to read types-to-expose file: ${TYPES_TO_EXPOSE_PATH}`);
57+
console.error(e.message);
58+
process.exit(1);
59+
}
60+
}
61+
62+
/**
63+
* Process links in a file to make them Mintlify-compatible
64+
*/
65+
function processLinksInFile(filePath) {
66+
let content = fs.readFileSync(filePath, 'utf-8');
67+
let modified = false;
68+
69+
// Remove .md and .mdx extensions from markdown links
70+
// This handles both relative and absolute paths
71+
const linkRegex = /\[([^\]]+)\]\(([^)]+)(\.mdx?)\)/g;
72+
const newContent = content.replace(linkRegex, (match, linkText, linkPath, ext) => {
73+
modified = true;
74+
return `[${linkText}](${linkPath})`;
75+
});
76+
77+
if (modified) {
78+
fs.writeFileSync(filePath, newContent, 'utf-8');
79+
return true;
80+
}
81+
82+
return false;
83+
}
84+
85+
/**
86+
* Scan docs content directory and build navigation structure
87+
*/
88+
function scanDocsContent() {
89+
const result = {
90+
functions: [],
91+
interfaces: [],
92+
classes: [],
93+
typeAliases: [],
94+
};
95+
96+
const sections = ['functions', 'interfaces', 'classes', 'type-aliases'];
97+
98+
for (const section of sections) {
99+
const sectionDir = path.join(CONTENT_DIR, section);
100+
if (!fs.existsSync(sectionDir)) continue;
101+
102+
const files = fs.readdirSync(sectionDir);
103+
const mdxFiles = files
104+
.filter((file) => file.endsWith('.mdx'))
105+
.map((file) => path.basename(file, '.mdx'))
106+
.sort()
107+
.map((fileName) => `content/${section}/${fileName}`);
108+
109+
const key = section === 'type-aliases' ? 'typeAliases' : section;
110+
result[key] = mdxFiles;
111+
}
112+
113+
return result;
114+
}
115+
116+
117+
/**
118+
* Get group name for a section, using category map or default
119+
*/
120+
function getGroupName(section, categoryMap) {
121+
if (categoryMap[section]) {
122+
return categoryMap[section];
123+
}
124+
125+
return section;
126+
}
127+
128+
/**
129+
* Generate docs.json from template and scanned content
130+
*/
131+
function generateDocsJson(docsContent) {
132+
const template = JSON.parse(fs.readFileSync(TEMPLATE_PATH, 'utf-8'));
133+
let categoryMap = {};
134+
try {
135+
categoryMap = JSON.parse(fs.readFileSync(CATEGORY_MAP_PATH, 'utf-8'));
136+
} catch (e) {
137+
// If file doesn't exist or can't be read, return empty object
138+
console.error(`Error: Category map file not found: ${CATEGORY_MAP_PATH}`);
139+
}
140+
141+
const groups = [];
142+
143+
if (docsContent.functions.length > 0 && categoryMap.functions) {
144+
groups.push({
145+
group: getGroupName('functions', categoryMap),
146+
pages: docsContent.functions,
147+
});
148+
}
149+
150+
if (docsContent.interfaces.length > 0 && categoryMap.interfaces) {
151+
groups.push({
152+
group: getGroupName('interfaces', categoryMap),
153+
pages: docsContent.interfaces,
154+
});
155+
}
156+
157+
if (docsContent.classes.length > 0 && categoryMap.classes) {
158+
groups.push({
159+
group: getGroupName('classes', categoryMap),
160+
pages: docsContent.classes,
161+
});
162+
}
163+
164+
if (docsContent.typeAliases.length > 0 && categoryMap['type-aliases']) {
165+
groups.push({
166+
group: getGroupName('typeAliases', categoryMap),
167+
pages: docsContent.typeAliases,
168+
});
169+
}
170+
171+
// Find or create SDK Reference tab
172+
let sdkTab = template.navigation.tabs.find(tab => tab.tab === 'SDK Reference');
173+
if (!sdkTab) {
174+
sdkTab = { tab: 'SDK Reference', groups: [] };
175+
template.navigation.tabs.push(sdkTab);
176+
}
177+
178+
sdkTab.groups = groups;
179+
180+
const docsJsonPath = path.join(DOCS_DIR, 'docs.json');
181+
fs.writeFileSync(docsJsonPath, JSON.stringify(template, null, 2) + '\n', 'utf-8');
182+
console.log(`Generated docs.json`);
183+
}
184+
185+
/**
186+
* Copy styling.css to docs directory
187+
*/
188+
function copyStylingCss() {
189+
const targetPath = path.join(DOCS_DIR, 'styling.css');
190+
fs.copyFileSync(STYLING_CSS_PATH, targetPath);
191+
console.log(`Copied styling.css`);
192+
}
193+
194+
/**
195+
* Recursively process all MDX files
196+
*/
197+
function isTypeDocPath(relativePath) {
198+
const normalized = relativePath.split(path.sep).join('/');
199+
return normalized.startsWith('content/interfaces/') ||
200+
normalized.startsWith('content/type-aliases/') ||
201+
normalized.startsWith('content/classes/');
202+
}
203+
204+
/**
205+
* Recursively process all MDX files
206+
*/
207+
function processAllFiles(dir, linkedTypeNames, exposedTypeNames) {
208+
const entries = fs.readdirSync(dir, { withFileTypes: true });
209+
210+
for (const entry of entries) {
211+
const entryPath = path.join(dir, entry.name);
212+
213+
if (entry.isDirectory()) {
214+
processAllFiles(entryPath, linkedTypeNames, exposedTypeNames);
215+
} else if (entry.isFile() && (entry.name.endsWith('.mdx') || entry.name.endsWith('.md'))) {
216+
// Extract the type name from the file path
217+
// e.g., "docs/interfaces/LoginViaEmailPasswordResponse.mdx" -> "LoginViaEmailPasswordResponse"
218+
const fileName = path.basename(entryPath, path.extname(entryPath));
219+
const relativePath = path.relative(DOCS_DIR, entryPath);
220+
const isTypeDoc = isTypeDocPath(relativePath);
221+
const isExposedType = !isTypeDoc || exposedTypeNames.has(fileName);
222+
223+
// Remove any type doc files that are not explicitly exposed
224+
if (isTypeDoc && !isExposedType) {
225+
fs.unlinkSync(entryPath);
226+
console.log(`Removed (not exposed): ${relativePath}`);
227+
continue;
228+
}
229+
230+
// Remove suppressed linked type files (legacy behavior) as long as they aren't exposed
231+
if (linkedTypeNames.has(fileName) && !exposedTypeNames.has(fileName)) {
232+
fs.unlinkSync(entryPath);
233+
console.log(`Removed (suppressed): ${relativePath}`);
234+
} else {
235+
// Process links in the file
236+
if (processLinksInFile(entryPath)) {
237+
console.log(`Processed links: ${relativePath}`);
238+
}
239+
}
240+
}
241+
}
242+
}
243+
244+
/**
245+
* Main function
246+
*/
247+
function main() {
248+
console.log('Processing TypeDoc MDX files for Mintlify...\n');
249+
250+
if (!fs.existsSync(DOCS_DIR)) {
251+
console.error(`Error: Documentation directory not found: ${DOCS_DIR}`);
252+
console.error('Please run "npm run docs:generate" first.');
253+
process.exit(1);
254+
}
255+
256+
// Get list of linked types to suppress
257+
const linkedTypeNames = getLinkedTypeNames();
258+
const exposedTypeNames = getTypesToExpose();
259+
260+
// Process all files (remove suppressed ones and fix links)
261+
// Process content directory specifically
262+
if (fs.existsSync(CONTENT_DIR)) {
263+
processAllFiles(CONTENT_DIR, linkedTypeNames, exposedTypeNames);
264+
} else {
265+
// Fallback to processing entire docs directory
266+
processAllFiles(DOCS_DIR, linkedTypeNames, exposedTypeNames);
267+
}
268+
269+
// Clean up the linked types file
270+
try {
271+
if (fs.existsSync(LINKED_TYPES_FILE)) {
272+
fs.unlinkSync(LINKED_TYPES_FILE);
273+
}
274+
} catch (e) {
275+
// Ignore errors
276+
}
277+
278+
// Scan content and generate docs.json
279+
const docsContent = scanDocsContent();
280+
generateDocsJson(docsContent);
281+
282+
// Copy styling.css
283+
copyStylingCss();
284+
285+
console.log(`\n✓ Post-processing complete!`);
286+
console.log(` Documentation directory: ${DOCS_DIR}`);
287+
}
288+
289+
main();

0 commit comments

Comments
 (0)