Skip to content

Commit e6d1a47

Browse files
committed
docs: obfuscate aside classname
1 parent de2719d commit e6d1a47

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"scripts": {
66
"dev": "vitepress dev --open",
77
"build": "vitepress build",
8+
"prepare": "node ./scripts/hash-class-names.js ./node_modules/vitepress/dist/client/theme-default",
89
"preview": "vitepress preview"
910
},
1011
"devDependencies": {

docs/scripts/hash-class-names.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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

Comments
 (0)