because I got bored of customising my CV for every job
at main 116 lines 3.3 kB view raw
1import type { ComponentType } from "react"; 2 3/** 4 * Type guard to check if a value is a React component 5 */ 6const isReactComponent = (value: unknown): value is ComponentType => { 7 return typeof value === "function"; 8}; 9 10/** 11 * Type guard to check if a value is an object with a default property 12 */ 13const hasDefaultProperty = (value: unknown): value is { default: unknown } => { 14 return ( 15 value !== null && 16 typeof value === "object" && 17 "default" in value && 18 value.default !== undefined 19 ); 20}; 21 22/** 23 * Extract MDX component from a module (for both .md and .mdx files) 24 * Handles both direct component exports and default exports 25 */ 26const extractMDXComponent = (module: unknown): ComponentType | null => { 27 if (isReactComponent(module)) { 28 return module; 29 } 30 31 if (hasDefaultProperty(module)) { 32 const defaultExport = module.default; 33 if (isReactComponent(defaultExport)) { 34 return defaultExport; 35 } 36 } 37 38 return null; 39}; 40 41/** 42 * Auto-discovered markdown and MDX files as MDX components 43 * Both .md and .mdx files are processed by the MDX plugin 44 * Lazy loading enabled for better performance 45 */ 46const mdxModules = import.meta.glob("../../content/**/*.{md,mdx}", { 47 eager: false, 48}); 49 50/** 51 * Get the MDX component for a given documentation slug 52 * 53 * Normalizes the slug and searches for matching files in the content directory. 54 * Handles README files as index files (e.g., "api/readme" matches slug "api"). 55 * Returns a promise that resolves to the component for lazy loading. 56 * 57 * @param slug - The documentation slug (e.g., "docs/architecture", "components/button") 58 * @returns Promise that resolves to the React component for the documentation page, or null if not found 59 */ 60export const getDocComponent = async ( 61 slug: string, 62): Promise<ComponentType | null> => { 63 const normalizedSlug = slug.toLowerCase().replace(/^\/+|\/+$/g, ""); 64 65 for (const [path, moduleLoader] of Object.entries(mdxModules)) { 66 const normalizedPath = path 67 .replace(/^\.\.\/\.\.\/content\//, "") 68 .replace(/\.(md|mdx)$/, "") 69 .toLowerCase(); 70 71 let matches = normalizedPath === normalizedSlug; 72 73 if (!matches && normalizedPath.endsWith("/readme")) { 74 const dirPath = normalizedPath.replace(/\/readme$/, ""); 75 matches = 76 dirPath === normalizedSlug || dirPath === `${normalizedSlug}/readme`; 77 } 78 79 if (!matches && normalizedPath === `${normalizedSlug}/readme`) { 80 matches = true; 81 } 82 83 if (matches) { 84 const module = await moduleLoader(); 85 const component = extractMDXComponent(module); 86 87 if (component) { 88 return component; 89 } 90 } 91 } 92 93 return null; 94}; 95 96/** 97 * Get a list of all available documentation slugs 98 * 99 * Extracts slugs from all discovered markdown and MDX files, 100 * excluding README files and empty slugs. 101 * 102 * @returns Array of normalized documentation slugs 103 */ 104export const getAvailableDocs = (): string[] => { 105 return Object.keys(mdxModules) 106 .map((path) => { 107 const normalizedPath = path 108 .replace(/^\.\.\/\.\.\/content\//, "") 109 .replace(/\.(md|mdx)$/, ""); 110 return normalizedPath.toLowerCase(); 111 }) 112 .filter((slug) => { 113 const filename = slug.split("/").pop() ?? ""; 114 return filename !== "readme" && slug.length > 0; 115 }); 116};