mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(lint): match @font-face when a comment inside the block holds a brace (#1538)
font_family_without_font_face (and system_font_will_alias) collect
@font-face blocks with `@font-face\s*\{[^}]*\}`, which stops at the first
`}`. A CSS comment inside the block that contains a brace —
`@font-face { /* 400 } regular */ font-family: 'X'; ... }` — truncates the
match before the font-family, so the family is never recorded as declared
and a later `font-family: 'X'` usage is wrongly flagged as used without an
@font-face. Strip CSS comments before scanning so a brace inside one cannot
split a block.
Refs #1534
This commit is contained in:
@@ -215,5 +215,16 @@ describe("font rules", () => {
|
||||
const findings = await findByCode(html, "font_family_without_font_face");
|
||||
expect(findings).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("matches @font-face even when a CSS comment inside the block contains a brace (#1534)", async () => {
|
||||
const html = `<div data-composition-id="test" data-width="1920" data-height="1080">
|
||||
<style>
|
||||
@font-face { /* weight 400 } regular */ font-family: 'Noto Sans SC'; src: url('../fonts/noto-400.woff2'); }
|
||||
.title { font-family: 'Noto Sans SC'; }
|
||||
</style>
|
||||
</div>`;
|
||||
const findings = await findByCode(html, "font_family_without_font_face");
|
||||
expect(findings).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,13 +22,25 @@ const GENERIC_FAMILIES = new Set([
|
||||
"revert",
|
||||
]);
|
||||
|
||||
// A CSS comment can contain a `}` (e.g. `@font-face { /* 400 } regular */
|
||||
// font-family: 'X'; ... }`), which truncates the naive `@font-face\s*\{[^}]*\}`
|
||||
// block match at the comment's brace — so the rule never sees the real
|
||||
// `font-family` and reports a false-positive font_family_without_font_face.
|
||||
// Large/"framework" stylesheets hit this far more often than minimal ones,
|
||||
// which is why a simple <style> passes while a complex one fails. Strip
|
||||
// comments before scanning so a brace inside one cannot split a block. See #1534.
|
||||
function stripCssComments(css: string): string {
|
||||
return css.replace(/\/\*[\s\S]*?\*\//g, " ");
|
||||
}
|
||||
|
||||
function extractFontFaceFamilies(styles: Array<{ content: string }>): Set<string> {
|
||||
const families = new Set<string>();
|
||||
const fontFaceRe = /@font-face\s*\{[^}]*\}/gi;
|
||||
const familyRe = /font-family\s*:\s*(['"]?)([^;'"]+)\1/i;
|
||||
for (const style of styles) {
|
||||
const content = stripCssComments(style.content);
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = fontFaceRe.exec(style.content)) !== null) {
|
||||
while ((match = fontFaceRe.exec(content)) !== null) {
|
||||
const familyMatch = match[0].match(familyRe);
|
||||
if (familyMatch?.[2]) {
|
||||
families.add(familyMatch[2].trim().toLowerCase());
|
||||
@@ -43,7 +55,7 @@ function extractUsedFontFamilies(styles: Array<{ content: string }>): string[] {
|
||||
const seen = new Set<string>();
|
||||
const propRe = /font-family\s*:\s*([^;}{]+)/gi;
|
||||
for (const style of styles) {
|
||||
const withoutFontFace = style.content.replace(/@font-face\s*\{[^}]*\}/gi, "");
|
||||
const withoutFontFace = stripCssComments(style.content).replace(/@font-face\s*\{[^}]*\}/gi, "");
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = propRe.exec(withoutFontFace)) !== null) {
|
||||
const stack = match[1]!;
|
||||
|
||||
Reference in New Issue
Block a user