diff --git a/.agents/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.agents/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.agents/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.agents/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.agents/skills/impeccable/scripts/detector/node/file-system.mjs b/.agents/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.agents/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.agents/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.claude/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.claude/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.claude/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.claude/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.claude/skills/impeccable/scripts/detector/node/file-system.mjs b/.claude/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.claude/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.claude/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.cursor/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.cursor/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.cursor/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.cursor/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.cursor/skills/impeccable/scripts/detector/node/file-system.mjs b/.cursor/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.cursor/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.cursor/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.gemini/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.gemini/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.gemini/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.gemini/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.gemini/skills/impeccable/scripts/detector/node/file-system.mjs b/.gemini/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.gemini/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.gemini/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.github/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.github/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.github/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.github/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.github/skills/impeccable/scripts/detector/node/file-system.mjs b/.github/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.github/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.github/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.grok/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.grok/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.grok/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.grok/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.grok/skills/impeccable/scripts/detector/node/file-system.mjs b/.grok/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.grok/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.grok/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.kiro/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.kiro/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.kiro/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.kiro/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.kiro/skills/impeccable/scripts/detector/node/file-system.mjs b/.kiro/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.kiro/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.kiro/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.opencode/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.opencode/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.opencode/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.opencode/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.opencode/skills/impeccable/scripts/detector/node/file-system.mjs b/.opencode/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.opencode/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.opencode/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.pi/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.pi/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.pi/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.pi/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.pi/skills/impeccable/scripts/detector/node/file-system.mjs b/.pi/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.pi/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.pi/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.qoder/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.qoder/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.qoder/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.qoder/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.qoder/skills/impeccable/scripts/detector/node/file-system.mjs b/.qoder/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.qoder/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.qoder/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.rovodev/skills/impeccable/scripts/detector/node/file-system.mjs b/.rovodev/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.rovodev/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.rovodev/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.trae-cn/skills/impeccable/scripts/detector/node/file-system.mjs b/.trae-cn/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.trae-cn/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.trae-cn/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.trae/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.trae/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.trae/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.trae/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.trae/skills/impeccable/scripts/detector/node/file-system.mjs b/.trae/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.trae/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.trae/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/.vibe/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/.vibe/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/.vibe/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/.vibe/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/.vibe/skills/impeccable/scripts/detector/node/file-system.mjs b/.vibe/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/.vibe/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/.vibe/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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, diff --git a/plugin/skills/impeccable/scripts/detector/detect-antipatterns.mjs b/plugin/skills/impeccable/scripts/detector/detect-antipatterns.mjs index c5bcf064c..e88397e37 100644 --- a/plugin/skills/impeccable/scripts/detector/detect-antipatterns.mjs +++ b/plugin/skills/impeccable/scripts/detector/detect-antipatterns.mjs @@ -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, diff --git a/plugin/skills/impeccable/scripts/detector/node/file-system.mjs b/plugin/skills/impeccable/scripts/detector/node/file-system.mjs index 6a74fa353..964f6712d 100644 --- a/plugin/skills/impeccable/scripts/detector/node/file-system.mjs +++ b/plugin/skills/impeccable/scripts/detector/node/file-system.mjs @@ -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,