From cc68983e7294c34bf2cba8ca94cee41e94e03bf5 Mon Sep 17 00:00:00 2001 From: Bart Veneman Date: Sun, 26 Oct 2025 19:25:31 +0100 Subject: [PATCH 1/2] fix: ignore js-like sources --- src/lib/filter-entries.test.ts | 20 ++++++++++++++---- src/lib/filter-entries.ts | 34 ++++++++++++++++++++++++------- src/lib/html-parser.test.ts | 18 ++++++++++------ src/lib/test/kitchen-sink.test.ts | 16 +++++++++++++++ 4 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/lib/filter-entries.test.ts b/src/lib/filter-entries.test.ts index 78ffafc..8de45af 100644 --- a/src/lib/filter-entries.test.ts +++ b/src/lib/filter-entries.test.ts @@ -1,5 +1,6 @@ import { test, expect } from '@playwright/test' import { filter_coverage } from './filter-entries.js' +import { Coverage } from './parse-coverage.js' test('filters out JS files', () => { let entries = [ @@ -8,7 +9,7 @@ test('filters out JS files', () => { text: 'console.log("Hello world")', ranges: [{ start: 0, end: 25 }], }, - ] + ] satisfies Coverage[] expect(filter_coverage(entries)).toEqual([]) }) @@ -19,7 +20,7 @@ test('keeps files with CSS extension', () => { text: 'a{color:red}', ranges: [{ start: 0, end: 13 }], }, - ] + ] satisfies Coverage[] expect(filter_coverage(entries)).toEqual(entries) }) @@ -37,7 +38,7 @@ test('keeps extension-less URL with HTML text', () => { text: 'a{color:red;}', ranges: [{ start: 0, end: 13 }], // ranges are remapped }, - ] + ] satisfies Coverage[] expect(filter_coverage(entries)).toEqual(expected) }) @@ -48,6 +49,17 @@ test('keeps extension-less URL with CSS text (running coverage in vite dev mode) text: 'a{color:red;}', ranges: [{ start: 0, end: 13 }], }, - ] + ] satisfies Coverage[] expect(filter_coverage(entries)).toEqual(entries) }) + +test('filters out extension-less JS', () => { + let entries = [ + { + url: 'http://example.com', + text: 'var a = 10; console.log(a);', + ranges: [{ start: 0, end: 29 }], + }, + ] satisfies Coverage[] + expect(filter_coverage(entries)).toEqual([]) +}) diff --git a/src/lib/filter-entries.ts b/src/lib/filter-entries.ts index ca9ea82..2b3d63c 100644 --- a/src/lib/filter-entries.ts +++ b/src/lib/filter-entries.ts @@ -6,6 +6,26 @@ function is_html(text: string): boolean { return /<\/?(html|body|head|div|span|script|style)/i.test(text) } +// Matches: element selectors, class/id selectors, attribute selectors, @rules +const SELECTOR_REGEX = /(@[a-z-]+|\[[^\]]+\]|[a-zA-Z_#.-][a-zA-Z0-9_-]*)\s*\{/ +// Check for CSS properties (property: value pattern) +const DECLARATION_REGEX = /^\s*[a-zA-Z-]+\s*:\s*.+;?\s*$/m + +function is_css_like(text: string): boolean { + return SELECTOR_REGEX.test(text) || DECLARATION_REGEX.test(text) +} + +function is_js_like(text: string): boolean { + try { + // Only parses the input, does not execute it. + // NEVER EXECUTE THIS UNTRUSTED CODE!!! + new Function(text) + return true + } catch { + return false + } +} + export function filter_coverage(coverage: Coverage[]): Coverage[] { let result = [] @@ -29,13 +49,13 @@ export function filter_coverage(coverage: Coverage[]): Coverage[] { continue } - // At this point it can only be CSS - // TODO: that's not true, check if it's css-like of js-like - result.push({ - url: entry.url, - text: entry.text, - ranges: entry.ranges, - }) + if (is_css_like(entry.text) && !is_js_like(entry.text)) { + result.push({ + url: entry.url, + text: entry.text, + ranges: entry.ranges, + }) + } } return result diff --git a/src/lib/html-parser.test.ts b/src/lib/html-parser.test.ts index bd04d9b..297b773 100644 --- a/src/lib/html-parser.test.ts +++ b/src/lib/html-parser.test.ts @@ -36,10 +36,16 @@ test('finds style tags with attributes', () => { expect(parse('')).toEqual([{ textContent: '.css{}' }]) }) -test('ignores style tags without end tag', () => { - expect(parse('