stack/docs/scripts/generate-platform-navigation.js
Madison cf7a540e1a
fix links and redirect issues on components index page (#796)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

Fixes broken links on /docs/{platform}/components pagse to now work, and
fixes redirect when switching off of react-like platforms when on
components or sdk pages.
<!-- ELLIPSIS_HIDDEN -->


----

> [!IMPORTANT]
> Fixes broken links and redirect issues on components index page by
updating paths and improving URL handling logic.
> 
>   - **Behavior**:
> - Fixes broken links on `/docs/{platform}/components` pages by
updating relative paths in `index.mdx`.
> - Corrects redirect logic when switching platforms on components or
SDK pages in `docs-layout-router.tsx`.
>   - **Functions**:
> - Adds `joinUrlPath()` in `generate-platform-navigation.js` to safely
join URL path segments.
> - Updates `pageExistsForPlatform()` in
`generate-platform-navigation.js` to handle `/index.mdx` paths.
>   - **Misc**:
> - Adds type safety checks for platform handling in
`docs-layout-router.tsx`.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 0a5337de8c. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>


<!-- ELLIPSIS_HIDDEN -->

---------

Co-authored-by: Konsti Wohlwend <[email protected]>
2025-07-23 19:13:30 -07:00

144 lines
4.8 KiB
JavaScript

#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { parse } from 'yaml';
/**
* Generate platform-navigation.ts from docs-platform.yml
* This ensures we have a single source of truth for platform pages
*/
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const DOCS_PLATFORM_YML_PATH = path.join(__dirname, '..', 'docs-platform.yml');
const OUTPUT_PATH = path.join(__dirname, '..', 'src', 'lib', 'platform-navigation.ts');
function generatePlatformNavigation() {
try {
// Read and parse docs-platform.yml
const yamlContent = fs.readFileSync(DOCS_PLATFORM_YML_PATH, 'utf8');
const config = parse(yamlContent);
if (!config.pages || !Array.isArray(config.pages)) {
throw new Error('Invalid docs-platform.yml format: missing pages array');
}
// Validate all platform values
const validPlatforms = new Set(['next', 'react', 'js', 'python']); // Actual platforms used in docs
config.pages.forEach((page, idx) => {
if (!Array.isArray(page.platforms)) {
throw new Error(`Invalid platforms array at page index ${idx}`);
}
page.platforms.forEach(platform => {
if (!validPlatforms.has(platform)) {
throw new Error(`Invalid platform "${platform}" at page index ${idx}`);
}
});
});
// Generate TypeScript content
const tsContent = `// THIS FILE IS AUTO-GENERATED
// Do not edit manually - update docs-platform.yml instead
// Generated on: ${new Date().toISOString()}
import { type Platform } from './platform-utils';
// Platform pages data auto-generated from docs-platform.yml
const PLATFORM_PAGES: { path: string, platforms: Platform[] }[] = [
${config.pages.map(page => {
const platformsStr = JSON.stringify(page.platforms);
return ` { path: ${JSON.stringify(page.path)}, platforms: ${platformsStr} },`;
}).join('\n')}
];
/**
* Safely join URL path segments
*/
function joinUrlPath(...segments: string[]): string {
return segments
.filter(segment => segment && segment.length > 0)
.join('/')
.replace(/\\/+/g, '/'); // Remove duplicate slashes
}
/**
* Check if a specific page exists for a given platform
*/
export function pageExistsForPlatform(path: string, platform: Platform): boolean {
// Normalize path - remove leading slash and ensure .mdx extension
const normalizedPath = path.replace(/^\\//, '');
const pathWithExt = normalizedPath.endsWith('.mdx') ? normalizedPath : \`\${normalizedPath}.mdx\`;
// First try to find exact match
let page = PLATFORM_PAGES.find(p => p.path === pathWithExt);
// If not found and path doesn't end with index, try appending /index.mdx
if (!page && !pathWithExt.includes('/index.mdx')) {
const indexPath = normalizedPath.endsWith('.mdx')
? normalizedPath.replace('.mdx', '/index.mdx')
: \`\${normalizedPath}/index.mdx\`;
page = PLATFORM_PAGES.find(p => p.path === indexPath);
}
return page?.platforms.includes(platform) ?? false;
}
/**
* Get smart redirect URL for platform switching
* If the current page doesn't exist for the target platform, redirect to overview
*/
export function getSmartPlatformRedirect(currentPath: string, targetPlatform: Platform): string {
// Remove /docs/{platform}/ prefix and normalize
const pathWithoutPlatform = currentPath.replace(/^\\/docs\\/[^\\/]+\\//, '');
// If the exact same page exists for target platform, use it
if (pageExistsForPlatform(pathWithoutPlatform, targetPlatform)) {
const cleanPath = pathWithoutPlatform.replace(/\\.mdx$/, '');
return joinUrlPath('/docs', targetPlatform, cleanPath);
}
// Otherwise, redirect to overview
return joinUrlPath('/docs', targetPlatform, 'overview');
}
/**
* Get all pages available for a specific platform
*/
export function getPagesForPlatform(platform: Platform): string[] {
return PLATFORM_PAGES
.filter(page => page.platforms.includes(platform))
.map(page => page.path.replace(/\\.mdx$/, ''));
}
`;
// Write the generated file
fs.writeFileSync(OUTPUT_PATH, tsContent, 'utf8');
console.log('✅ Generated platform-navigation.ts from docs-platform.yml');
console.log(`📄 ${config.pages.length} pages configured for platforms`);
// Show platform distribution
const platformCounts = {};
config.pages.forEach(page => {
page.platforms.forEach(platform => {
platformCounts[platform] = (platformCounts[platform] || 0) + 1;
});
});
console.log('📊 Platform distribution:');
Object.entries(platformCounts).forEach(([platform, count]) => {
console.log(` ${platform}: ${count} pages`);
});
} catch (error) {
console.error('❌ Error generating platform-navigation.ts:', error.message);
process.exit(1);
}
}
// Run the generator
generatePlatformNavigation();