|
| 1 | +import { readFileSync, writeFileSync } from 'node:fs' |
| 2 | +import { join } from 'node:path' |
| 3 | + |
| 4 | +import { globSync } from 'glob' |
| 5 | + |
| 6 | +import { tabTitleFixes, tabTitleOrder } from './lib/code-sample-tab-order.js' |
| 7 | + |
| 8 | +const tabsBlockPattern = /\{%\s*tabs\s*%\}[\s\S]*?\{%\s*endtabs\s*%\}/g |
| 9 | + |
| 10 | +const tabPattern = |
| 11 | + /\{%\s*tab\s+title="([^"]+)"\s*%\}([\s\S]*?)\{%\s*endtab\s*%\}/g |
| 12 | + |
| 13 | +function canonicalIndex(title: string): number { |
| 14 | + const normalized = tabTitleFixes[title] ?? title |
| 15 | + return tabTitleOrder.get(normalized) ?? tabTitleOrder.size + 1 |
| 16 | +} |
| 17 | + |
| 18 | +function formatTabsBlock(block: string): { result: string; changed: boolean } { |
| 19 | + const tabs: Array<{ title: string; content: string }> = [] |
| 20 | + for (const match of block.matchAll(tabPattern)) { |
| 21 | + tabs.push({ title: match[1] ?? '', content: match[2] ?? '' }) |
| 22 | + } |
| 23 | + |
| 24 | + if (tabs.length < 2) return { result: block, changed: false } |
| 25 | + |
| 26 | + const originalTitles = tabs.map((t) => t.title) |
| 27 | + const sorted = [...tabs].sort( |
| 28 | + (a, b) => canonicalIndex(a.title) - canonicalIndex(b.title), |
| 29 | + ) |
| 30 | + const sortedTitles = sorted.map((t) => t.title) |
| 31 | + const needsRename = originalTitles.some((t) => t in tabTitleFixes) |
| 32 | + const needsReorder = originalTitles.some((t, i) => t !== sortedTitles[i]) |
| 33 | + |
| 34 | + if (!needsReorder && !needsRename) return { result: block, changed: false } |
| 35 | + |
| 36 | + const lines = ['{% tabs %}'] |
| 37 | + for (const tab of sorted) { |
| 38 | + const title = tabTitleFixes[tab.title] ?? tab.title |
| 39 | + lines.push(`{% tab title="${title}" %}`) |
| 40 | + lines.push(tab.content.trimEnd()) |
| 41 | + lines.push('{% endtab %}') |
| 42 | + lines.push('') |
| 43 | + } |
| 44 | + while (lines.at(-1) === '') lines.pop() |
| 45 | + lines.push('{% endtabs %}') |
| 46 | + |
| 47 | + return { result: lines.join('\n'), changed: true } |
| 48 | +} |
| 49 | + |
| 50 | +const dirs = ['docs/guides', 'docs/brand-guides'] |
| 51 | +const files = dirs.flatMap((dir) => globSync(join(dir, '**/*.md'))) |
| 52 | + |
| 53 | +let totalChanged = 0 |
| 54 | + |
| 55 | +for (const file of files) { |
| 56 | + const content = readFileSync(file, 'utf-8') |
| 57 | + let changed = false |
| 58 | + |
| 59 | + const updated = content.replace(tabsBlockPattern, (block) => { |
| 60 | + const { result, changed: blockChanged } = formatTabsBlock(block) |
| 61 | + if (blockChanged) changed = true |
| 62 | + return result |
| 63 | + }) |
| 64 | + |
| 65 | + if (changed) { |
| 66 | + writeFileSync(file, updated) |
| 67 | + totalChanged++ |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +if (totalChanged > 0) { |
| 72 | + // eslint-disable-next-line no-console |
| 73 | + console.log(`Formatted tabs in ${String(totalChanged)} file(s).`) |
| 74 | +} |
0 commit comments