Skip to content

Commit 625583c

Browse files
louis-preclaude
andcommitted
ci: Add format-code-sample-tabs to CI format workflow
Adds a codegen script that auto-fixes code sample tab order and titles in guide and brand-guide markdown files. Runs as part of the Format CI workflow, which auto-commits fixes on non-main branches. The script: - Reorders tabs to match API reference canonical order (JavaScript → cURL → Python → Ruby → PHP → C# → Java → Go → Seam CLI) - Normalizes tab titles ("cURL (bash)" → "cURL", "Javascript" → "JavaScript") - Ensures GitBook tab syncing works across all code blocks on a page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dc0adaf commit 625583c

3 files changed

Lines changed: 96 additions & 1 deletion

File tree

.github/workflows/format.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ jobs:
2929
passphrase: ${{ secrets.GPG_PASSPHRASE }}
3030
- name: Setup
3131
uses: ./.github/actions/setup
32+
- name: Format code sample tabs
33+
run: npm run format-code-sample-tabs
3234
- name: Format
3335
run: npm run format
3436
- name: Commit

codegen/format-code-sample-tabs.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { readFileSync, writeFileSync } from 'node:fs'
2+
import { join } from 'node:path'
3+
4+
import { globSync } from 'glob'
5+
6+
const CANONICAL_ORDER = [
7+
'JavaScript',
8+
'cURL',
9+
'Python',
10+
'Ruby',
11+
'PHP',
12+
'C#',
13+
'Java',
14+
'Go',
15+
'Seam CLI',
16+
]
17+
18+
const TITLE_FIXES: Record<string, string> = {
19+
'cURL (bash)': 'cURL',
20+
Bash: 'cURL',
21+
Javascript: 'JavaScript',
22+
javascript: 'JavaScript',
23+
}
24+
25+
const tabsBlockPattern = /\{%\s*tabs\s*%\}[\s\S]*?\{%\s*endtabs\s*%\}/g
26+
27+
const tabPattern =
28+
/\{%\s*tab\s+title="([^"]+)"\s*%\}([\s\S]*?)\{%\s*endtab\s*%\}/g
29+
30+
function canonicalIndex(title: string): number {
31+
const normalized = TITLE_FIXES[title] ?? title
32+
const idx = CANONICAL_ORDER.indexOf(normalized)
33+
return idx === -1 ? CANONICAL_ORDER.length + 1 : idx
34+
}
35+
36+
function formatTabsBlock(block: string): { result: string; changed: boolean } {
37+
const tabs: Array<{ title: string; content: string }> = []
38+
for (const match of block.matchAll(tabPattern)) {
39+
tabs.push({ title: match[1], content: match[2] })
40+
}
41+
42+
if (tabs.length < 2) return { result: block, changed: false }
43+
44+
const originalTitles = tabs.map((t) => t.title)
45+
const sorted = [...tabs].sort(
46+
(a, b) => canonicalIndex(a.title) - canonicalIndex(b.title),
47+
)
48+
const sortedTitles = sorted.map((t) => t.title)
49+
const needsRename = originalTitles.some((t) => t in TITLE_FIXES)
50+
const needsReorder = originalTitles.some((t, i) => t !== sortedTitles[i])
51+
52+
if (!needsReorder && !needsRename) return { result: block, changed: false }
53+
54+
const lines = ['{% tabs %}']
55+
for (const tab of sorted) {
56+
const title = TITLE_FIXES[tab.title] ?? tab.title
57+
lines.push(`{% tab title="${title}" %}`)
58+
lines.push(tab.content.trimEnd())
59+
lines.push('{% endtab %}')
60+
lines.push('')
61+
}
62+
while (lines.at(-1) === '') lines.pop()
63+
lines.push('{% endtabs %}')
64+
65+
return { result: lines.join('\n'), changed: true }
66+
}
67+
68+
const dirs = ['docs/guides', 'docs/brand-guides']
69+
const files = dirs.flatMap((dir) => globSync(join(dir, '**/*.md')))
70+
71+
let totalChanged = 0
72+
73+
for (const file of files) {
74+
const content = readFileSync(file, 'utf-8')
75+
let changed = false
76+
77+
const updated = content.replace(tabsBlockPattern, (block) => {
78+
const { result, changed: blockChanged } = formatTabsBlock(block)
79+
if (blockChanged) changed = true
80+
return result
81+
})
82+
83+
if (changed) {
84+
writeFileSync(file, updated)
85+
totalChanged++
86+
}
87+
}
88+
89+
if (totalChanged > 0) {
90+
// eslint-disable-next-line no-console
91+
console.log(`Formatted tabs in ${String(totalChanged)} file(s).`)
92+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"lint": "eslint .",
2525
"postlint": "prettier --check --ignore-path .prettierignore .",
2626
"format": "prettier --write --ignore-path .prettierignore .",
27-
"preformat": "eslint --fix ."
27+
"preformat": "eslint --fix .",
28+
"format-code-sample-tabs": "tsx codegen/format-code-sample-tabs.ts"
2829
},
2930
"engines": {
3031
"node": ">=22.11.0",

0 commit comments

Comments
 (0)