You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/scripts/rtl-check.ts

82 lines
3.1 KiB

/**
* rtl-check — the mechanical guard of the build contract's RTL row
* (docs/guides/component-guide.md). Until now that row's guard was
* "rule (LIVE)", i.e. human review, and it is how the Slider defect of
* `013ceac57` reached production: three vertical rules paired a logical
* `inset-inline-start` with a physical `translateX`, and nothing in CI looked
* at CSS declarations — `eidos-lint` classifies selectors.
*
* RTL-1 a physical inline-axis displacement (`transform: translateX/
* translate/translate3d`, or the independent `translate` property) in
* a block that also anchors on `inset-inline*` / `margin-inline*`.
* The anchor flips with the writing direction, the displacement does
* not: correct in LTR, a full element-width off-axis in RTL.
*
* RTL-2 inside a `:dir()` block, one logical face turned off and the other
* repainted. The property had ALREADY mirrored, so relocating it
* cancels the mirror and parks it opposite the siblings left alone.
* This is the shape that survived the `[dir='rtl']` → `:dir(rtl)`
* migration untouched, because that pass rewrote selectors and never
* read a body — a mechanical migration inherits what it translates.
*
* Both rules, their deliberate exclusions and their escape hatches
* (`rtl-physical:` / `rtl-mirror:`) live in `src/uix/eidos/rtl-lint.ts`.
*/
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, relative } from 'node:path';
import { lintRtlGeometry, lintRtlMirror } from '../src/uix/eidos/rtl-lint';
const REPO = process.cwd();
const EIDOS_DIR = join(REPO, 'src', 'uix', 'eidos');
function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir)) {
const path = join(dir, entry);
if (statSync(path).isDirectory()) walk(path, out);
else if (entry.endsWith('.css')) out.push(path);
}
return out;
}
function main(): void {
const files = walk(EIDOS_DIR);
let errors = 0;
for (const file of files) {
const rel = relative(REPO, file).replace(/\\/g, '/');
const css = readFileSync(file, 'utf8');
for (const f of lintRtlGeometry(css)) {
errors++;
console.error(
`ERROR [RTL-1] ${rel}:${f.line} — physical "${f.translate}" against logical ` +
`${f.anchors.map((a) => `"${a}"`).join(' + ')} in ${f.selector}`
);
}
for (const f of lintRtlMirror(css)) {
errors++;
console.error(
`ERROR [RTL-2] ${rel}:${f.line} — double flip: "${f.neutral}" turns a face off and ` +
`"${f.payload}" repaints the other, but ${f.family} had already mirrored ` +
`in ${f.selector}`
);
}
}
if (errors > 0) {
console.error(
`\nrtl-check: ${errors} error(s) across ${files.length} files\n` +
'RTL-1: express the offset logically (margin-inline-start: calc(…)), go fully\n' +
'physical (left/right), or mark the line /* rtl-physical: <reason> */ if meant.\n' +
'RTL-2: DELETE the rule and check the base declaration — it had already mirrored.'
);
process.exit(1);
}
console.log(`rtl-check: 0 error(s) across ${files.length} files`);
}
main();

Powered by TurnKey Linux.