import { readdir, readFile, stat } from 'node:fs/promises'; import path from 'node:path'; const forbidden = String.fromCodePoint(0x2014); const root = process.cwd(); const textExtensions = new Set([ '.astro', '.css', '.html', '.js', '.json', '.md', '.mjs', '.ts', '.txt', '.vtt', '.webmanifest', '.xml', ]); const roots = [ 'src', 'public', 'scripts', 'README.md', 'package.json', 'astro.config.mjs', ].map((entry) => path.resolve(root, entry)); async function exists(filePath) { try { await stat(filePath); return true; } catch { return false; } } async function walk(entryPath) { const entryStat = await stat(entryPath); if (entryStat.isFile()) return [entryPath]; const entries = await readdir(entryPath, { withFileTypes: true }); const files = []; for (const entry of entries) { const fullPath = path.join(entryPath, entry.name); if (entry.isDirectory()) { files.push(...(await walk(fullPath))); } else if (entry.isFile()) { files.push(fullPath); } } return files; } function lineAndColumn(text, index) { const before = text.slice(0, index); const lines = before.split('\n'); return { line: lines.length, column: lines.at(-1).length + 1, }; } const files = []; for (const entry of roots) { if (!(await exists(entry))) continue; files.push(...(await walk(entry))); } const textFiles = files.filter((file) => textExtensions.has(path.extname(file))); const failures = []; for (const file of textFiles) { const text = await readFile(file, 'utf8'); let index = text.indexOf(forbidden); while (index !== -1) { const { line, column } = lineAndColumn(text, index); failures.push(`${path.relative(root, file)}:${line}:${column}`); index = text.indexOf(forbidden, index + forbidden.length); } } if (failures.length > 0) { console.error(`Em dashes are not allowed:\n${failures.join('\n')}`); process.exit(1); } console.log('No em dashes found.');