| 
 | 1 | +import fs from 'node:fs/promises';  | 
 | 2 | +import path from 'node:path';  | 
 | 3 | + | 
 | 4 | +const TARGET_CLASSES = ['VPDocAside'];  | 
 | 5 | +const ALLOWED_EXTENSIONS = new Set(['.css', '.js', '.vue']);  | 
 | 6 | +const generateHash = () => Math.random().toString(36).slice(2, 8);  | 
 | 7 | + | 
 | 8 | +const listFilesRecursive = async (directory) => {  | 
 | 9 | +	const entries = await fs.readdir(directory, { withFileTypes: true });  | 
 | 10 | +	const paths = await Promise.all(  | 
 | 11 | +		entries.map(async (entry) => {  | 
 | 12 | +			const resolved = path.resolve(directory, entry.name);  | 
 | 13 | +			return entry.isDirectory() ? listFilesRecursive(resolved) : resolved;  | 
 | 14 | +		}),  | 
 | 15 | +	);  | 
 | 16 | +	return paths.flat().filter(file => ALLOWED_EXTENSIONS.has(path.extname(file)));  | 
 | 17 | +};  | 
 | 18 | + | 
 | 19 | +const rewriteFiles = async (filePaths, classNames) => {  | 
 | 20 | +	const classMap = new Map();  | 
 | 21 | +	classNames.sort((a, b) => b.length - a.length).forEach((name) => {  | 
 | 22 | +		classMap.set(name, `${name}-${generateHash()}`);  | 
 | 23 | +	});  | 
 | 24 | +	await Promise.all(  | 
 | 25 | +		filePaths.map(async (filePath) => {  | 
 | 26 | +			let content = await fs.readFile(filePath, 'utf8');  | 
 | 27 | +			const original = content;  | 
 | 28 | +			classMap.forEach((replacement, originalName) => {  | 
 | 29 | +				const selectorPattern = new RegExp(`(?<![-_\\w])\\.${originalName}(?![-_\\w])`, 'g');  | 
 | 30 | +				content = content.replace(selectorPattern, `.${replacement}`);  | 
 | 31 | +			});  | 
 | 32 | +			const classAttributePattern = /(class(?:Name)?\s*=\s*)(["'])(.*?)\2/g;  | 
 | 33 | +			content = content.replaceAll(classAttributePattern, (_whole, attribute, quote, value) => {  | 
 | 34 | +				const updated = String(value).split(/\s+/).map(cls => classMap.get(cls) || cls).join(' ');  | 
 | 35 | +				return `${attribute}${quote}${updated}${quote}`;  | 
 | 36 | +			});  | 
 | 37 | +			if (content !== original) { await fs.writeFile(filePath, content, 'utf8'); }  | 
 | 38 | +		}),  | 
 | 39 | +	);  | 
 | 40 | +};  | 
 | 41 | + | 
 | 42 | +const input = process.argv[2];  | 
 | 43 | +if (!input) { throw new Error('Input directory is required.'); }  | 
 | 44 | +const directory = path.resolve(input);  | 
 | 45 | +const stats = await fs.stat(directory).catch(() => null);  | 
 | 46 | +if (!stats?.isDirectory()) { throw new Error('Input must be a valid directory.'); }  | 
 | 47 | +const filePaths = await listFilesRecursive(directory);  | 
 | 48 | +await rewriteFiles(filePaths, TARGET_CLASSES);  | 
0 commit comments