diff --git a/package.json b/package.json index 3a6501f..cfcbe53 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "prepublishOnly": "npm run build", "create-docs": "npm run create-docs:generate && npm run create-docs:process", "create-docs-local": "npm run create-docs && npm run copy-docs-local", - "push-docs": "node scripts/mintlify-post-processing/push-to-docs-repo.js", "copy-docs-local": "node scripts/mintlify-post-processing/copy-to-local-docs.js", "create-docs:generate": "typedoc", "create-docs:process": "node scripts/mintlify-post-processing/file-processing/file-processing.js" diff --git a/scripts/mintlify-post-processing/copy-to-local-docs.js b/scripts/mintlify-post-processing/copy-to-local-docs.js index ec29357..e7195f4 100644 --- a/scripts/mintlify-post-processing/copy-to-local-docs.js +++ b/scripts/mintlify-post-processing/copy-to-local-docs.js @@ -71,6 +71,9 @@ Examples: return { target }; } +// Target location within mintlify-docs for SDK reference docs +const SDK_DOCS_TARGET_PATH = "developers/references/sdk/docs"; + function scanSdkDocs(sdkDocsDir) { const result = {}; @@ -104,7 +107,8 @@ function updateDocsJson(repoDir, sdkFiles) { const docsContent = fs.readFileSync(docsJsonPath, "utf8"); const docs = JSON.parse(docsContent); - // Build the new SDK Reference groups + // Build the new SDK Reference groups using the new path structure + const basePath = SDK_DOCS_TARGET_PATH; const groupMap = new Map(); // group name -> pages array const addToGroup = (groupName, pages) => { @@ -118,28 +122,28 @@ function updateDocsJson(repoDir, sdkFiles) { if (sdkFiles.functions?.length > 0 && categoryMap.functions) { addToGroup( categoryMap.functions, - sdkFiles.functions.map((file) => `sdk-docs/functions/${file}`) + sdkFiles.functions.map((file) => `${basePath}/functions/${file}`) ); } if (sdkFiles.interfaces?.length > 0 && categoryMap.interfaces) { addToGroup( categoryMap.interfaces, - sdkFiles.interfaces.map((file) => `sdk-docs/interfaces/${file}`) + sdkFiles.interfaces.map((file) => `${basePath}/interfaces/${file}`) ); } if (sdkFiles.classes?.length > 0 && categoryMap.classes) { addToGroup( categoryMap.classes, - sdkFiles.classes.map((file) => `sdk-docs/classes/${file}`) + sdkFiles.classes.map((file) => `${basePath}/classes/${file}`) ); } if (sdkFiles["type-aliases"]?.length > 0 && categoryMap["type-aliases"]) { addToGroup( categoryMap["type-aliases"], - sdkFiles["type-aliases"].map((file) => `sdk-docs/type-aliases/${file}`) + sdkFiles["type-aliases"].map((file) => `${basePath}/type-aliases/${file}`) ); } @@ -155,7 +159,7 @@ function updateDocsJson(repoDir, sdkFiles) { `SDK Reference pages: ${JSON.stringify(sdkReferencePages, null, 2)}` ); - // Navigate to: Developers tab -> SDK group -> SDK Reference group + // Navigate to: Developers tab -> anchors -> References anchor -> groups -> JavaScript SDK -> SDK Reference const developersTab = docs.navigation.tabs.find( (tab) => tab.tab === "Developers" ); @@ -165,40 +169,45 @@ function updateDocsJson(repoDir, sdkFiles) { process.exit(1); } - // Find the SDK group (it's a top-level group in the Developers tab) - const sdkGroup = developersTab.groups.find((g) => g.group === "SDK"); + // Find the References anchor (new structure uses anchors instead of groups at tab level) + const referencesAnchor = developersTab.anchors?.find( + (anchor) => anchor.anchor === "References" + ); - if (!sdkGroup) { - console.error("Could not find 'SDK' group in Developers tab"); + if (!referencesAnchor) { + console.error("Could not find 'References' anchor in Developers tab"); process.exit(1); } - // Find SDK Reference within SDK's pages (it's a nested group object) - const sdkRefIndex = sdkGroup.pages.findIndex( + // Find the JavaScript SDK group within the References anchor + const jsSdkGroup = referencesAnchor.groups?.find( + (g) => g.group === "JavaScript SDK" + ); + + if (!jsSdkGroup) { + console.error( + "Could not find 'JavaScript SDK' group in References anchor" + ); + process.exit(1); + } + + // Find SDK Reference within JavaScript SDK's pages (it's a nested group object) + const sdkRefIndex = jsSdkGroup.pages.findIndex( (page) => typeof page === "object" && page.group === "SDK Reference" ); if (sdkRefIndex === -1) { - console.error("Could not find 'SDK Reference' group in SDK"); + console.error("Could not find 'SDK Reference' group in JavaScript SDK"); process.exit(1); } // Update the SDK Reference pages with our generated groups - sdkGroup.pages[sdkRefIndex] = { + jsSdkGroup.pages[sdkRefIndex] = { group: "SDK Reference", icon: "brackets-curly", pages: sdkReferencePages, }; - // Remove the old standalone "SDK Reference" tab if it exists - const oldSdkTabIndex = docs.navigation.tabs.findIndex( - (tab) => tab.tab === "SDK Reference" - ); - if (oldSdkTabIndex !== -1) { - console.log("Removing old standalone 'SDK Reference' tab..."); - docs.navigation.tabs.splice(oldSdkTabIndex, 1); - } - // Write updated docs.json console.log(`Writing updated docs.json to ${docsJsonPath}...`); fs.writeFileSync(docsJsonPath, JSON.stringify(docs, null, 2) + "\n", "utf8"); @@ -237,13 +246,16 @@ function main() { } try { - // Remove the existing sdk-docs directory - const sdkDocsTarget = path.join(target, "sdk-docs"); + // Remove the existing SDK docs directory at the new location + const sdkDocsTarget = path.join(target, SDK_DOCS_TARGET_PATH); if (fs.existsSync(sdkDocsTarget)) { - console.log(`Removing existing sdk-docs directory...`); + console.log(`Removing existing SDK docs directory at ${SDK_DOCS_TARGET_PATH}...`); fs.rmSync(sdkDocsTarget, { recursive: true, force: true }); } + // Ensure parent directories exist + fs.mkdirSync(sdkDocsTarget, { recursive: true }); + // Copy the docs directory to the target console.log(`Copying docs to ${sdkDocsTarget}...`); fs.cpSync(DOCS_SOURCE_PATH, sdkDocsTarget, { recursive: true }); @@ -261,7 +273,15 @@ function main() { // Update the docs.json file updateDocsJson(target, sdkFiles); + // Also remove the old sdk-docs location if it exists (migration cleanup) + const oldSdkDocsLocation = path.join(target, "sdk-docs"); + if (fs.existsSync(oldSdkDocsLocation)) { + console.log(`Removing old sdk-docs directory at root level...`); + fs.rmSync(oldSdkDocsLocation, { recursive: true, force: true }); + } + console.log("\n✅ Successfully copied SDK docs to local mintlify-docs repo"); + console.log(` Target: ${SDK_DOCS_TARGET_PATH}`); console.log(`\nTo preview the docs, run 'mintlify dev' in ${target}`); } catch (e) { console.error(`Error: Failed to copy docs: ${e}`); diff --git a/scripts/mintlify-post-processing/file-processing/file-processing.js b/scripts/mintlify-post-processing/file-processing/file-processing.js index 06de994..fd799b9 100755 --- a/scripts/mintlify-post-processing/file-processing/file-processing.js +++ b/scripts/mintlify-post-processing/file-processing/file-processing.js @@ -33,25 +33,56 @@ const APPENDED_ARTICLES_PATH = path.join( // Controlled via env var so we can re-enable Panel injection when needed. const PANELS_ENABLED = process.env.MINTLIFY_INCLUDE_PANELS === "true"; -const MODULE_RENAMES = { - AgentsModule: "agents", - AnalyticsModule: "analytics", - AppLogsModule: "app-logs", - AuthModule: "auth", - ConnectorsModule: "connectors", - EntitiesModule: "entities", - FunctionsModule: "functions", - IntegrationsModule: "integrations", - SsoModule: "sso", -}; - -const REVERSE_MODULE_RENAMES = Object.entries(MODULE_RENAMES).reduce( - (acc, [k, v]) => { - acc[v] = k; - return acc; - }, - {} -); +/** + * Converts a PascalCase module name to kebab-case. + * E.g., "AgentsModule" -> "agents", "AppLogsModule" -> "app-logs" + * + * @param {string} name - The PascalCase name (e.g., "AgentsModule") + * @returns {string | null} - The kebab-case name, or null if not a module name + */ +function deriveModuleRename(name) { + if (!name.endsWith("Module")) { + return null; + } + + // Remove "Module" suffix + const withoutModule = name.slice(0, -6); + + // Convert PascalCase to kebab-case + // Insert hyphen before each capital letter (except the first), then lowercase + const kebabCase = withoutModule + .replace(/([a-z])([A-Z])/g, "$1-$2") + .toLowerCase(); + + return kebabCase; +} + +/** + * Checks if a name is a module name (ends with "Module") and returns its renamed version. + * Uses the derived rename algorithm. + * + * @param {string} name - The name to check + * @returns {string | null} - The renamed version, or null if not a module + */ +function getModuleRename(name) { + return deriveModuleRename(name); +} + +/** + * Checks if a name is a renamed module (kebab-case) and returns its original name. + * + * @param {string} name - The kebab-case name to check + * @returns {string | null} - The original PascalCase module name, or null if not found + */ +function getReverseModuleRename(name) { + // Convert kebab-case back to PascalCase and add "Module" suffix + const pascalCase = name + .split("-") + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(""); + + return `${pascalCase}Module`; +} /** * Get list of linked type names that should be suppressed @@ -170,8 +201,9 @@ Access to additional integration packages. // If filename has extension, strip it for checking map const nameWithoutExt = filename.replace(/\.mdx?$/, ""); - if (MODULE_RENAMES[nameWithoutExt]) { - pathParts[pathParts.length - 1] = MODULE_RENAMES[nameWithoutExt]; + const moduleRename = getModuleRename(nameWithoutExt); + if (moduleRename) { + pathParts[pathParts.length - 1] = moduleRename; linkPath = pathParts.join("/"); } @@ -223,15 +255,22 @@ function performModuleRenames(dir) { let targetName = nameWithoutExt; let needsRename = false; - if (MODULE_RENAMES[nameWithoutExt]) { - targetName = MODULE_RENAMES[nameWithoutExt]; + const moduleRename = getModuleRename(nameWithoutExt); + if (moduleRename) { + targetName = moduleRename; needsRename = true; - } else if (REVERSE_MODULE_RENAMES[nameWithoutExt]) { - // It's already renamed (e.g. "entities"), but we should ensure title is correct - targetName = nameWithoutExt; + } else if (nameWithoutExt.match(/^[a-z]+(-[a-z]+)*$/)) { + // It's already in kebab-case (e.g. "entities", "app-logs"), might be a renamed module + // Check if we can derive an original module name from it + const possibleOriginal = getReverseModuleRename(nameWithoutExt); + if (possibleOriginal) { + targetName = nameWithoutExt; + } } - if (needsRename || REVERSE_MODULE_RENAMES[targetName]) { + // Process if it needs renaming OR if it looks like a module file (for title updates) + const isModuleFile = needsRename || nameWithoutExt.match(/^[a-z]+(-[a-z]+)*$/); + if (isModuleFile) { const newPath = path.join(dir, `${targetName}.mdx`); // Always use .mdx let content = fs.readFileSync(entryPath, "utf-8"); @@ -420,12 +459,16 @@ function processAllFiles(dir, linkedTypeNames, exposedTypeNames) { const relativePath = path.relative(DOCS_DIR, entryPath); const isTypeDoc = isTypeDocPath(relativePath); - // Check if exposed. Handle renamed modules by checking reverse map. + // Check if exposed. Handle renamed modules by deriving the original name. // Use both the raw filename and any potential original name - const originalName = REVERSE_MODULE_RENAMES[fileName] || fileName; + // If the filename is kebab-case, it might be a renamed module + const possibleOriginalModule = fileName.match(/^[a-z]+(-[a-z]+)*$/) + ? getReverseModuleRename(fileName) + : null; + const originalName = possibleOriginalModule || fileName; // If it's a renamed module (e.g. "entities"), treat it as exposed if "EntitiesModule" is exposed - const isRenamedModule = !!REVERSE_MODULE_RENAMES[fileName]; + const isRenamedModule = !!possibleOriginalModule; const isExposedType = !isTypeDoc || @@ -627,12 +670,13 @@ function applyAppendedArticles(appendedArticles) { continue; } - // Check if host was renamed + // Check if host was renamed (derives rename automatically for *Module names) let effectiveHostKey = hostKey; const pathParts = hostKey.split("/"); const hostName = pathParts[pathParts.length - 1]; - if (MODULE_RENAMES[hostName]) { - pathParts[pathParts.length - 1] = MODULE_RENAMES[hostName]; + const hostModuleRename = getModuleRename(hostName); + if (hostModuleRename) { + pathParts[pathParts.length - 1] = hostModuleRename; effectiveHostKey = pathParts.join("/"); } @@ -650,12 +694,13 @@ function applyAppendedArticles(appendedArticles) { const collectedHeadings = PANELS_ENABLED ? [] : null; for (const appendKey of appendList) { - // Check if appended file was renamed (unlikely for EntityHandler but good for consistency) + // Check if appended file was renamed (derives rename automatically for *Module names) let effectiveAppendKey = appendKey; const appendParts = appendKey.split("/"); const appendName = appendParts[appendParts.length - 1]; - if (MODULE_RENAMES[appendName]) { - appendParts[appendParts.length - 1] = MODULE_RENAMES[appendName]; + const appendModuleRename = getModuleRename(appendName); + if (appendModuleRename) { + appendParts[appendParts.length - 1] = appendModuleRename; effectiveAppendKey = appendParts.join("/"); } diff --git a/scripts/mintlify-post-processing/push-to-docs-repo.js b/scripts/mintlify-post-processing/push-to-docs-repo.js deleted file mode 100644 index c1143c6..0000000 --- a/scripts/mintlify-post-processing/push-to-docs-repo.js +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env node - -import fs from "fs"; -import path from "path"; -import os from "os"; -import { execSync } from "child_process"; - -console.debug = () => {}; // Disable debug logging. Comment this out to enable debug logging. - -const DOCS_SOURCE_PATH = path.join(import.meta.dirname, "../../docs/content"); -const TARGET_DOCS_REPO_URL = "git@github.com:base44-dev/mintlify-docs.git"; -const CATEGORY_MAP_PATH = path.join(import.meta.dirname, "./category-map.json"); - -function parseArgs() { - const args = process.argv.slice(2); - let branch = null; - - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - - if (arg === "--branch" && i + 1 < args.length) { - branch = args[++i]; - } - } - return { branch }; -} - -function scanSdkDocs(sdkDocsDir) { - const result = {}; - - // Get a list of all the subdirectories in the sdkDocsDir - const subdirectories = fs - .readdirSync(sdkDocsDir) - .filter((file) => fs.statSync(path.join(sdkDocsDir, file)).isDirectory()); - console.log(`Subdirectories: ${subdirectories}`); - - for (const subdirectory of subdirectories) { - const subdirectoryPath = path.join(sdkDocsDir, subdirectory); - const files = fs - .readdirSync(subdirectoryPath) - .filter((file) => file.endsWith(".mdx")); - result[subdirectory] = files.map((file) => path.basename(file, ".mdx")); - } - return result; -} - -function updateDocsJson(repoDir, sdkFiles) { - const docsJsonPath = path.join(repoDir, "docs.json"); - let categoryMap = {}; - try { - categoryMap = JSON.parse(fs.readFileSync(CATEGORY_MAP_PATH, "utf8")); - } catch (e) { - console.error(`Error: Category map file not found: ${CATEGORY_MAP_PATH}`); - process.exit(1); - } - - console.log(`Reading docs.json from ${docsJsonPath}...`); - const docsContent = fs.readFileSync(docsJsonPath, "utf8"); - const docs = JSON.parse(docsContent); - - // Build the new SDK Reference groups - const groupMap = new Map(); // group name -> pages array - - const addToGroup = (groupName, pages) => { - if (!groupName || pages.length === 0) return; - if (!groupMap.has(groupName)) { - groupMap.set(groupName, []); - } - groupMap.get(groupName).push(...pages); - }; - - if (sdkFiles.functions?.length > 0 && categoryMap.functions) { - addToGroup( - categoryMap.functions, - sdkFiles.functions.map((file) => `sdk-docs/functions/${file}`) - ); - } - - if (sdkFiles.interfaces?.length > 0 && categoryMap.interfaces) { - addToGroup( - categoryMap.interfaces, - sdkFiles.interfaces.map((file) => `sdk-docs/interfaces/${file}`) - ); - } - - if (sdkFiles.classes?.length > 0 && categoryMap.classes) { - addToGroup( - categoryMap.classes, - sdkFiles.classes.map((file) => `sdk-docs/classes/${file}`) - ); - } - - if (sdkFiles["type-aliases"]?.length > 0 && categoryMap["type-aliases"]) { - addToGroup( - categoryMap["type-aliases"], - sdkFiles["type-aliases"].map((file) => `sdk-docs/type-aliases/${file}`) - ); - } - - // Convert map to array of nested groups for SDK Reference - const sdkReferencePages = Array.from(groupMap.entries()).map( - ([groupName, pages]) => ({ - group: groupName, - pages: pages.sort(), // Sort pages alphabetically within each group - }) - ); - - console.debug( - `SDK Reference pages: ${JSON.stringify(sdkReferencePages, null, 2)}` - ); - - // Navigate to: Developers tab -> SDK group -> SDK Reference group - const developersTab = docs.navigation.tabs.find( - (tab) => tab.tab === "Developers" - ); - - if (!developersTab) { - console.error("Could not find 'Developers' tab in docs.json"); - process.exit(1); - } - - // Find the SDK group (it's a top-level group in the Developers tab) - const sdkGroup = developersTab.groups.find((g) => g.group === "SDK"); - - if (!sdkGroup) { - console.error("Could not find 'SDK' group in Developers tab"); - process.exit(1); - } - - // Find SDK Reference within SDK's pages (it's a nested group object) - const sdkRefIndex = sdkGroup.pages.findIndex( - (page) => typeof page === "object" && page.group === "SDK Reference" - ); - - if (sdkRefIndex === -1) { - console.error("Could not find 'SDK Reference' group in SDK"); - process.exit(1); - } - - // Update the SDK Reference pages with our generated groups - sdkGroup.pages[sdkRefIndex] = { - group: "SDK Reference", - icon: "brackets-curly", - pages: sdkReferencePages, - }; - - // Remove the old standalone "SDK Reference" tab if it exists - const oldSdkTabIndex = docs.navigation.tabs.findIndex( - (tab) => tab.tab === "SDK Reference" - ); - if (oldSdkTabIndex !== -1) { - console.log("Removing old standalone 'SDK Reference' tab..."); - docs.navigation.tabs.splice(oldSdkTabIndex, 1); - } - - // Write updated docs.json - console.log(`Writing updated docs.json to ${docsJsonPath}...`); - fs.writeFileSync(docsJsonPath, JSON.stringify(docs, null, 2) + "\n", "utf8"); - - console.log("Successfully updated docs.json"); -} - -function main() { - const { branch } = parseArgs(); - if (!branch) { - console.error("Error: --branch is required"); - process.exit(1); - } - - if (!/^[a-zA-Z0-9\-_\/]+$/.test(branch)) { - console.error( - "Error: Invalid branch name. Branch name must contain only letters, numbers, hyphens, underscores, and forward slashes." - ); - process.exit(1); - } - - console.log(`Branch: ${branch}`); - - if ( - !fs.existsSync(DOCS_SOURCE_PATH) || - !fs.statSync(DOCS_SOURCE_PATH).isDirectory() - ) { - console.error(`Error: docs directory does not exist: ${DOCS_SOURCE_PATH}`); - process.exit(1); - } - - let tempRepoDir; - try { - // Create temporary directory - tempRepoDir = fs.mkdtempSync(path.join(os.tmpdir(), "mintlify-docs-")); - // Clone the repository - console.log(`Cloning repository to ${tempRepoDir}...`); - execSync(`git clone ${TARGET_DOCS_REPO_URL} ${tempRepoDir}`); - - // Check if the specified branch already exists remotely - const branchExists = - execSync(`git ls-remote --heads origin ${branch}`, { - cwd: tempRepoDir, - encoding: "utf8", - }).trim().length > 0; - - if (branchExists) { - console.log(`Branch ${branch} already exists. Checking it out...`); - execSync(`git checkout -b ${branch} origin/${branch}`, { - cwd: tempRepoDir, - }); - } else { - console.log(`Branch ${branch} does not exist. Creating it...`); - execSync(`git checkout -b ${branch}`, { cwd: tempRepoDir }); - } - - // Remove the existing sdk-docs directory - fs.rmSync(path.join(tempRepoDir, "sdk-docs"), { - recursive: true, - force: true, - }); - - // Copy the docs directory to the temporary repository - fs.cpSync(DOCS_SOURCE_PATH, path.join(tempRepoDir, "sdk-docs"), { - recursive: true, - }); - - // Remove README.mdx - it's not used in the docs navigation - fs.rmSync(path.join(tempRepoDir, "sdk-docs", "README.mdx"), { - force: true, - }); - - // Scan the sdk-docs directory - const sdkDocsDir = path.join(tempRepoDir, "sdk-docs"); - const sdkFiles = scanSdkDocs(sdkDocsDir); - - console.debug(`SDK files: ${JSON.stringify(sdkFiles, null, 2)}`); - - // Update the docs.json file - updateDocsJson(tempRepoDir, sdkFiles); - - // Commit the changes - execSync(`git add docs.json`, { cwd: tempRepoDir }); - execSync(`git add sdk-docs`, { cwd: tempRepoDir }); - - const stagedOutput = execSync(`git diff --cached --name-only`, { - cwd: tempRepoDir, - encoding: "utf8", - }); - - const stagedChanges = stagedOutput.trim(); - - if (!stagedChanges.length) { - console.log( - "No staged changes detected (docs.json / sdk-docs). Skipping commit and push." - ); - return; - } - - console.log(`Changes staged for commit:\n${stagedChanges}`); - - execSync(`git commit -m "Auto-updates to SDK Reference Docs"`, { - cwd: tempRepoDir, - }); - execSync(`git push --set-upstream origin ${branch}`, { cwd: tempRepoDir }); - - console.log("Successfully committed and pushed the changes"); - } catch (e) { - console.error(`Error: Failed to commit and push changes: ${e}`); - process.exit(1); - } finally { - // Remove the temporary directory - fs.rmSync(tempRepoDir, { recursive: true, force: true }); - } -} - -main();