schmelczer-dev/scripts/check-no-em-dashes.mjs
2026-05-26 08:28:37 +01:00

95 lines
2 KiB
JavaScript

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.');