| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 1 | /** |
| 2 | * Build-time script to auto-generate demo index page |
| 3 | */ |
| 4 | |
| 5 | import * as fs from "fs"; |
| 6 | import * as path from "path"; |
| 7 | |
| 8 | interface DemoInfo { |
| 9 | name: string; |
| 10 | title: string; |
| 11 | description?: string; |
| 12 | fileName: string; |
| 13 | } |
| 14 | |
| 15 | async function generateIndex() { |
| 16 | const demoDir = path.join(__dirname); |
| 17 | const files = await fs.promises.readdir(demoDir); |
| 18 | |
| 19 | // Find all .demo.ts files |
| 20 | const demoFiles = files.filter((file) => file.endsWith(".demo.ts")); |
| 21 | |
| 22 | const demos: DemoInfo[] = []; |
| 23 | |
| 24 | for (const file of demoFiles) { |
| 25 | const componentName = file.replace(".demo.ts", ""); |
| 26 | const filePath = path.join(demoDir, file); |
| 27 | |
| 28 | try { |
| 29 | // Read the file content to extract title and description |
| 30 | const content = await fs.promises.readFile(filePath, "utf-8"); |
| 31 | |
| 32 | // Extract title from the demo module |
| 33 | const titleMatch = content.match(/title:\s*['"]([^'"]+)['"]/); |
| 34 | const descriptionMatch = content.match(/description:\s*['"]([^'"]+)['"]/); |
| 35 | |
| 36 | demos.push({ |
| 37 | name: componentName, |
| 38 | title: titleMatch ? titleMatch[1] : formatComponentName(componentName), |
| 39 | description: descriptionMatch ? descriptionMatch[1] : undefined, |
| 40 | fileName: file, |
| 41 | }); |
| 42 | } catch (error) { |
| 43 | console.warn(`Failed to process demo file ${file}:`, error); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | // Sort demos alphabetically |
| 48 | demos.sort((a, b) => a.title.localeCompare(b.title)); |
| 49 | |
| 50 | // Generate HTML index |
| 51 | const html = generateIndexHTML(demos); |
| 52 | |
| 53 | // Write the generated index |
| 54 | const indexPath = path.join(demoDir, "index-generated.html"); |
| 55 | await fs.promises.writeFile(indexPath, html, "utf-8"); |
| 56 | |
| 57 | console.log(`Generated demo index with ${demos.length} components`); |
| 58 | console.log("Available demos:", demos.map((d) => d.name).join(", ")); |
| 59 | } |
| 60 | |
| 61 | function formatComponentName(name: string): string { |
| 62 | return name |
| 63 | .replace(/^sketch-/, "") |
| 64 | .replace(/-/g, " ") |
| 65 | .replace(/\b\w/g, (l) => l.toUpperCase()); |
| 66 | } |
| 67 | |
| 68 | function generateIndexHTML(demos: DemoInfo[]): string { |
| 69 | const demoLinks = demos |
| 70 | .map((demo) => { |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 71 | const href = `demo.html#${demo.name}`; |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 72 | const description = demo.description ? ` - ${demo.description}` : ""; |
| 73 | |
| 74 | return ` <li> |
| 75 | <a href="${href}"> |
| 76 | <strong>${demo.title}</strong>${description} |
| 77 | </a> |
| 78 | </li>`; |
| 79 | }) |
| 80 | .join("\n"); |
| 81 | |
| 82 | return `<!DOCTYPE html> |
| 83 | <html lang="en"> |
| 84 | <head> |
| 85 | <meta charset="UTF-8" /> |
| 86 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 87 | <title>Sketch Web Components - Demo Index</title> |
| 88 | <link rel="stylesheet" href="demo.css" /> |
| 89 | <style> |
| 90 | body { |
| 91 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
| 92 | max-width: 800px; |
| 93 | margin: 40px auto; |
| 94 | padding: 20px; |
| 95 | line-height: 1.6; |
| 96 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 97 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 98 | h1 { |
| 99 | color: #24292f; |
| 100 | border-bottom: 1px solid #d1d9e0; |
| 101 | padding-bottom: 10px; |
| 102 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 103 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 104 | .demo-list { |
| 105 | list-style: none; |
| 106 | padding: 0; |
| 107 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 108 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 109 | .demo-list li { |
| 110 | margin: 15px 0; |
| 111 | padding: 15px; |
| 112 | border: 1px solid #d1d9e0; |
| 113 | border-radius: 6px; |
| 114 | background: #f6f8fa; |
| 115 | transition: background-color 0.2s; |
| 116 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 117 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 118 | .demo-list li:hover { |
| 119 | background: #ffffff; |
| 120 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 121 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 122 | .demo-list a { |
| 123 | text-decoration: none; |
| 124 | color: #0969da; |
| 125 | display: block; |
| 126 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 127 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 128 | .demo-list a:hover { |
| 129 | text-decoration: underline; |
| 130 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 131 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 132 | .demo-list strong { |
| 133 | font-size: 16px; |
| 134 | display: block; |
| 135 | margin-bottom: 5px; |
| 136 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 137 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 138 | .stats { |
| 139 | background: #fff8dc; |
| 140 | padding: 15px; |
| 141 | border-radius: 6px; |
| 142 | margin: 20px 0; |
| 143 | border-left: 4px solid #f9c23c; |
| 144 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 145 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 146 | .runner-link { |
| 147 | display: inline-block; |
| 148 | padding: 10px 20px; |
| 149 | background: #0969da; |
| 150 | color: white; |
| 151 | text-decoration: none; |
| 152 | border-radius: 6px; |
| 153 | margin-top: 20px; |
| 154 | } |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 155 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 156 | .runner-link:hover { |
| 157 | background: #0860ca; |
| 158 | } |
| 159 | </style> |
| 160 | </head> |
| 161 | <body> |
| 162 | <h1>Sketch Web Components Demo Index</h1> |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 163 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 164 | <div class="stats"> |
| 165 | <strong>Auto-generated index</strong><br> |
| 166 | Found ${demos.length} demo component${demos.length === 1 ? "" : "s"} • Last updated: ${new Date().toLocaleString()} |
| 167 | </div> |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 168 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 169 | <p> |
| 170 | This page provides an overview of all available component demos. |
| 171 | Click on any component below to view its interactive demo. |
| 172 | </p> |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 173 | |
| 174 | <a href="demo.html" class="runner-link">🚀 Launch Demo Runner</a> |
| 175 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 176 | <h2>Available Component Demos</h2> |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 177 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 178 | <ul class="demo-list"> |
| 179 | ${demoLinks} |
| 180 | </ul> |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 181 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 182 | <hr style="margin: 40px 0; border: none; border-top: 1px solid #d1d9e0;"> |
| banksean | 581bd79 | 2025-07-20 18:30:12 -0700 | [diff] [blame] | 183 | |
| Sean McCullough | 618bfb2 | 2025-06-25 20:52:30 +0000 | [diff] [blame] | 184 | <p> |
| 185 | <em>This index is automatically generated from available <code>*.demo.ts</code> files.</em><br> |
| 186 | To add a new demo, create a <code>component-name.demo.ts</code> file in this directory. |
| 187 | </p> |
| 188 | </body> |
| 189 | </html> |
| 190 | `; |
| 191 | } |
| 192 | |
| 193 | // Run the generator if this script is executed directly |
| 194 | if (require.main === module) { |
| 195 | generateIndex().catch(console.error); |
| 196 | } |
| 197 | |
| 198 | export { generateIndex }; |