diff --git a/package.json b/package.json index e4485d289..8ce293233 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "packages/*" ], "scripts": { - "check": "check && bun run --sequential --workspaces check", + "check": "bun run cspell-order:check && check && bun run --sequential --workspaces check", + "cspell-order:check": "bun run scripts/check-cspell-order.ts", + "cspell-order:format": "bun run scripts/format-cspell-order.ts", "test": "bun run --workspaces test run", "test:coverage": "bun run --workspaces test:coverage", "prepare": "simple-git-hooks", diff --git a/scripts/check-cspell-order.ts b/scripts/check-cspell-order.ts new file mode 100644 index 000000000..5b5cf7697 --- /dev/null +++ b/scripts/check-cspell-order.ts @@ -0,0 +1,46 @@ +const configPath = 'cspell.yml'; +const lines = (await Bun.file(configPath).text()).split('\n'); +const wordsSectionIndex = lines.findIndex((line) => /^words:\s*$/.test(line)); + +if (wordsSectionIndex === -1) { + throw new Error(`Could not find the words section in ${configPath}`); +} + +const words: { line: number; value: string }[] = []; + +for (let index = wordsSectionIndex + 1; index < lines.length; index++) { + const line = lines[index]; + const wordMatch = line.match(/^ {2}- (.*)$/); + + if (wordMatch) { + words.push({ line: index + 1, value: wordMatch[1] }); + continue; + } + + if (line.trim() && !line.startsWith(' ')) break; +} + +const outOfOrder = words.flatMap((word, index) => { + if (index === 0) return []; + + const previous = words[index - 1]; + return previous.value.toLowerCase() > word.value.toLowerCase() + ? [{ previous, word }] + : []; +}); + +if (outOfOrder.length > 0) { + console.error( + `${configPath}: words must be sorted alphabetically (case-insensitive):`, + ); + for (const { previous, word } of outOfOrder) { + console.error( + ` line ${word.line} "${word.value.slice(0, 40)}" is before line ${previous.line} "${previous.value.slice(0, 40)}"`, + ); + } + process.exitCode = 1; +} else { + console.log(`${configPath}: words are sorted alphabetically.`); +} + +export default {}; diff --git a/scripts/format-cspell-order.ts b/scripts/format-cspell-order.ts new file mode 100644 index 000000000..c60775507 --- /dev/null +++ b/scripts/format-cspell-order.ts @@ -0,0 +1,43 @@ +const configPath = 'cspell.yml'; +const originalConfig = await Bun.file(configPath).text(); +const lines = originalConfig.split('\n'); +const wordsSectionIndex = lines.findIndex((line) => /^words:\s*$/.test(line)); + +if (wordsSectionIndex === -1) { + throw new Error(`Could not find the words section in ${configPath}`); +} + +const wordLines: { index: number; line: string; value: string }[] = []; + +for (let index = wordsSectionIndex + 1; index < lines.length; index++) { + const line = lines[index]; + const wordMatch = line.match(/^ {2}- (.*)$/); + + if (wordMatch) { + wordLines.push({ index, line, value: wordMatch[1] }); + continue; + } + + if (line.trim() && !line.startsWith(' ')) break; +} + +const sortedWordLines = [...wordLines].sort((a, b) => { + const first = a.value.toLowerCase(); + const second = b.value.toLowerCase(); + return first < second ? -1 : first > second ? 1 : 0; +}); + +for (const [index, wordLine] of wordLines.entries()) { + lines[wordLine.index] = sortedWordLines[index].line; +} + +const sortedConfig = lines.join('\n'); + +if (sortedConfig !== originalConfig) { + await Bun.write(configPath, sortedConfig); + console.log(`${configPath}: sorted words alphabetically.`); +} else { + console.log(`${configPath}: words are already sorted alphabetically.`); +} + +export default {};