mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
design-lint: eleventh review cycle fixes
A nested rule under a parent longer than the selector cap falls back to the parent's first part cut to 256 characters, the :is() wrapper is built only on the expanding path, and every stored selector is cut at MAX_SELECTOR; the limited stripTags counts visibility with the whitespace class its callers collapse on and never cuts a surrogate pair; glWords slides its window by innerHeight. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
6d01b1f3cd
commit
7c9dab30ef
3 files changed
+8
-6
No files matched your search
@@ -86,15 +86,16 @@ export function selectorNames(selectors: string[]): { tags: Set<string>; classes
|
||||
return { tags, classes };
|
||||
}
|
||||
|
||||
/** A selector longer than this is a nesting blow-up (comma lists nested in comma lists), not a stylesheet selector; past it the nested selector stands without its parent, so a sheet costs its length and not parts^depth. */
|
||||
/** A selector longer than this is a nesting blow-up (comma lists nested in comma lists, a parent repeated per '&'), not a stylesheet selector; a nested selector that would pass it stands without its parent, and every stored selector is cut there, so a sheet costs its length and not parts^depth. */
|
||||
const MAX_SELECTOR = 8192;
|
||||
|
||||
/** Expand a nested selector against its parent: `&` is replaced, a bare selector is descendant-joined. */
|
||||
function nestSelector(sel: string, parent: string): string {
|
||||
const p = parent.includes(',') ? `:is(${parent})` : parent;
|
||||
const parts = splitTop(sel).map((part) => part.trim());
|
||||
const substitutions = parts.reduce((n, part) => n + Math.max(1, part.split('&').length - 1), 0);
|
||||
if (sel.length + p.length * substitutions > MAX_SELECTOR) return parts.map((part) => part.replace(/&/g, '').trim()).filter(Boolean).join(', ') || p;
|
||||
const wrapped = parent.includes(',') ? parent.length + 5 : parent.length;
|
||||
if (sel.length + wrapped * substitutions > MAX_SELECTOR) return parts.map((part) => part.replace(/&/g, '').trim()).filter(Boolean).join(', ') || splitTop(parent.slice(0, 256))[0].trim();
|
||||
const p = parent.includes(',') ? `:is(${parent})` : parent;
|
||||
return parts.map((part) => part.includes('&') ? part.replace(/&/g, p) : `${p} ${part}`).join(', ');
|
||||
}
|
||||
|
||||
@@ -167,7 +168,8 @@ export function parseCss(source: string, file: string, lineOffset = 0): CssRule[
|
||||
if (depthLeft > 0) walk(blockStart, blockEnd, media, keyframes, null, depthLeft - 1);
|
||||
}
|
||||
} else {
|
||||
const sel = parent && !keyframes ? nestSelector(selector, parent) : selector;
|
||||
const nested = parent && !keyframes ? nestSelector(selector, parent) : selector;
|
||||
const sel = nested.length > MAX_SELECTOR ? nested.slice(0, MAX_SELECTOR) : nested;
|
||||
rules.push(makeRule(sel, blockStart, blockEnd, media, keyframes));
|
||||
if (!keyframes && depthLeft > 0 && src.slice(blockStart, blockEnd).includes('{')) walk(blockStart, blockEnd, media, keyframes, sel, depthLeft - 1);
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ function blankRanges(s: string, ranges: Array<[number, number]>, cut = false): s
|
||||
export function stripTags(s: string, keep = false, limit = Infinity): string {
|
||||
let out = '', i = 0, seen = 0;
|
||||
for (;;) {
|
||||
if (limit < Infinity) for (let k = i; k < s.length && s[k] !== '<'; k++) if (s[k] > ' ' && ++seen > limit) return out + s.slice(i, k + 1);
|
||||
if (limit < Infinity) for (let k = i; k < s.length && s[k] !== '<'; k++) if (!/\s/.test(s[k]) && ++seen > limit) return out + s.slice(i, k + (s.charCodeAt(k) >= 0xd800 && s.charCodeAt(k) < 0xdc00 ? 2 : 1));
|
||||
const lt = s.indexOf('<', i);
|
||||
if (lt < 0) break;
|
||||
const gt = s.indexOf('>', lt + 1);
|
||||
|
||||
@@ -155,7 +155,7 @@ export function glWords(P: Probes, ctx: HeroCtx): ProbeFinding[] {
|
||||
const H = document.documentElement.scrollHeight;
|
||||
let maxWords = 0, at = 0;
|
||||
for (let y = 0; y < H; y += 50) {
|
||||
const w = Math.round(words.reduce((a, x) => { const h = Math.max(1, x.bottom - x.top); const ov = Math.max(0, Math.min(x.bottom, y + 900) - Math.max(x.top, y)); return a + x.c * Math.min(1, ov / h); }, 0));
|
||||
const w = Math.round(words.reduce((a, x) => { const h = Math.max(1, x.bottom - x.top); const ov = Math.max(0, Math.min(x.bottom, y + innerHeight) - Math.max(x.top, y)); return a + x.c * Math.min(1, ov / h); }, 0));
|
||||
if (w > maxWords) { maxWords = w; at = y; }
|
||||
}
|
||||
if (maxWords > 95) out.push({ rule: 'screen-word-budget', grade: 'fail', evidence: maxWords + ' words on one ' + innerHeight + 'px screen (from y=' + at + ')', fix: 'one idea per screen: cut to about 80 words and make the section an object' });
|
||||
|
||||
Reference in new issue
Block a user