Fix Blade files in directory detection (#509)

* Fix Blade directory detection

AI assistance: Codex reproduced the issue, implemented the fix, and ran the validation described in the pull request.

* Fix compound scan suffix matching

AI assistance: Codex addressed review findings and ran the validation described in the pull request.
This commit is contained in:
Paul Bakaus
2026-08-04 14:08:49 -07:00
committed by GitHub
parent 2f609915eb
commit 80e4dd0d58
3 changed files with 37 additions and 4 deletions
+1
View File
@@ -35,6 +35,7 @@ export { detectUrl, createBrowserDetector } from './engines/browser/detect-url.m
export { detectText, extractStyleBlocks, extractCSSinJS } from './engines/regex/detect-text.mjs';
export {
walkDir,
hasScannableExtension,
SCANNABLE_EXTENSIONS,
SKIP_DIRS,
buildImportGraph,
+12 -2
View File
@@ -26,11 +26,20 @@ const HIDDEN_SOURCE_DIRS = new Set(['.vitepress', '.vuepress', '.storybook']);
const SCANNABLE_EXTENSIONS = new Set([
'.html', '.htm', '.css', '.scss', '.sass', '.less',
'.jsx', '.tsx', '.js', '.ts',
'.vue', '.svelte', '.astro',
'.vue', '.svelte', '.astro', '.blade.php',
]);
const HTML_EXTENSIONS = new Set(['.html', '.htm']);
function hasScannableExtension(filename) {
const lower = filename.toLowerCase();
if (SCANNABLE_EXTENSIONS.has(path.extname(lower))) return true;
for (const ext of SCANNABLE_EXTENSIONS) {
if (ext.indexOf('.', 1) !== -1 && lower.endsWith(ext)) return true;
}
return false;
}
const IMPORT_SPECIFIER_PATTERNS = [
/import\s+(?:[\s\S]*?from\s+)?['"]([^'"]+)['"]/g,
/@import\s+(?:url\(\s*)?['"]?([^'");\s]+)['"]?\s*\)?/g,
@@ -46,7 +55,7 @@ function walkDir(dir) {
if (entry.isDirectory() && entry.name.startsWith('.') && !HIDDEN_SOURCE_DIRS.has(entry.name)) continue;
const full = path.join(dir, entry.name);
if (entry.isDirectory()) files.push(...walkDir(full));
else if (SCANNABLE_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) files.push(full);
else if (hasScannableExtension(entry.name)) files.push(full);
}
return files;
}
@@ -194,6 +203,7 @@ export {
SKIP_DIRS,
SCANNABLE_EXTENSIONS,
HTML_EXTENSIONS,
hasScannableExtension,
walkDir,
resolveImport,
buildImportGraph,
+24 -2
View File
@@ -6,7 +6,7 @@ import { spawnSync } from 'child_process';
import {
ANTIPATTERNS, checkElementBorders, checkElementMotion, checkElementGlow, isNeutralColor, isFullPage,
detectText, detectHtml, extractStyleBlocks, extractCSSinJS,
walkDir, SCANNABLE_EXTENSIONS,
walkDir, hasScannableExtension, SCANNABLE_EXTENSIONS,
buildImportGraph, resolveImport,
detectFrameworkConfig, isPortListening, FRAMEWORK_CONFIGS,
} from '../cli/engine/detect-antipatterns.mjs';
@@ -2071,10 +2071,32 @@ describe('walkDir', () => {
expect(SCANNABLE_EXTENSIONS.has('.sass')).toBe(true);
});
test('finds Blade templates during directory scans', () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'impeccable-walk-'));
try {
const blade = path.join(tmp, 'resources', 'views', 'card.blade.php');
const upperBlade = path.join(tmp, 'resources', 'views', 'hero.BLADE.PHP');
fs.mkdirSync(path.dirname(blade), { recursive: true });
fs.writeFileSync(blade, '<div class="bg-orange-900 text-gray-500">Card</div>');
fs.writeFileSync(upperBlade, '<div>Hero</div>');
expect(walkDir(tmp)).toEqual(expect.arrayContaining([blade, upperBlade]));
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
});
test('compound suffix matching does not broaden scans to module extensions', () => {
expect(hasScannableExtension('card.blade.php')).toBe(true);
expect(hasScannableExtension('card.BLADE.PHP')).toBe(true);
for (const file of ['next.config.mjs', 'vite.config.cjs', 'route.mts', 'route.cts']) {
expect(hasScannableExtension(file)).toBe(false);
}
});
test('finds scannable files', () => {
const files = walkDir(FIXTURES);
expect(files.length).toBeGreaterThanOrEqual(3);
expect(files.every(f => SCANNABLE_EXTENSIONS.has(path.extname(f)))).toBe(true);
expect(files.every(hasScannableExtension)).toBe(true);
});
test('returns empty for nonexistent dir', () => {