UNPKG

scanex

Version:

A powerful CLI tool that automatically discovers and bundles related source code into a single markdown file, perfect for sharing with LLMs like ChatGPT, Claude, or Cursor AI.

123 lines (105 loc) 3.77 kB
import { join, dirname } from 'node:path'; import { existsSync } from 'node:fs'; export const name = 'html'; export const exts = ['.html', '.htm']; export function scan(src, { file }) { if (!src || src.trim() === '') return []; const results = []; // HTML file reference patterns const patterns = [ // Script tags /<script[^>]+src=["']([^"']+)["']/gi, // Link tags (CSS, etc.) /<link[^>]+href=["']([^"']+)["']/gi, // Image tags /<img[^>]+src=["']([^"']+)["']/gi, // Audio/Video sources /<(?:audio|video)[^>]+src=["']([^"']+)["']/gi, /<source[^>]+src=["']([^"']+)["']/gi, // Iframe sources /<iframe[^>]+src=["']([^"']+)["']/gi, // Object data /<object[^>]+data=["']([^"']+)["']/gi, // Embed sources /<embed[^>]+src=["']([^"']+)["']/gi, // Form actions /<form[^>]+action=["']([^"']+)["']/gi, // Anchor hrefs (only local files) /<a[^>]+href=["']([^"'#][^"']*\.(?:html|htm|php|jsp|asp))["']/gi, ]; for (const pattern of patterns) { let match; while ((match = pattern.exec(src)) !== null) { const filePath = match[1]; // Skip external URLs, data URLs, and fragments if (filePath && !filePath.startsWith('http://') && !filePath.startsWith('https://') && !filePath.startsWith('data:') && !filePath.startsWith('mailto:') && !filePath.startsWith('tel:') && !filePath.startsWith('//') && !filePath.startsWith('#')) { // Determine the type based on file extension or context let type = 'file_reference'; if (filePath.match(/\.(?:js|mjs|ts)$/i)) { type = 'script'; } else if (filePath.match(/\.css$/i)) { type = 'stylesheet'; } else if (filePath.match(/\.(?:png|jpg|jpeg|gif|svg|webp|ico)$/i)) { type = 'image'; } else if (filePath.match(/\.(?:html|htm)$/i)) { type = 'html_page'; } results.push({ type, value: filePath }); } } } // Remove duplicates const unique = results.filter((item, index, arr) => arr.findIndex(other => other.type === item.type && other.value === item.value) === index ); return unique; } export function resolve(spec, { projectRoot, file }) { const { type, value } = spec; if (['file_reference', 'script', 'stylesheet', 'image', 'html_page'].includes(type)) { const currentDir = dirname(file); // Handle absolute paths (starting with /) if (value.startsWith('/')) { const absolutePath = join(projectRoot, value.slice(1)); if (existsSync(absolutePath)) return absolutePath; // Try common web root directories const webRoots = ['public', 'static', 'assets', 'www', 'dist', 'build']; for (const webRoot of webRoots) { const webPath = join(projectRoot, webRoot, value.slice(1)); if (existsSync(webPath)) return webPath; } } else { // Handle relative paths const relativePath = join(currentDir, value); if (existsSync(relativePath)) return relativePath; // Try relative to project root const projectPath = join(projectRoot, value); if (existsSync(projectPath)) return projectPath; // Try common asset directories const assetDirs = [ 'public', 'static', 'assets', 'src', 'app/assets', 'app/assets/javascripts', 'app/assets/stylesheets', 'app/assets/images', 'dist', 'build' ]; for (const dir of assetDirs) { const assetPath = join(projectRoot, dir, value); if (existsSync(assetPath)) return assetPath; } } } return null; }