189 lines
6.5 KiB
JavaScript
189 lines
6.5 KiB
JavaScript
#!/usr/bin/env node
|
||
import * as fs from 'node:fs';
|
||
import * as path from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
import { dirname } from 'node:path';
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = dirname(__filename);
|
||
class ScriptInfo {
|
||
static dir() {
|
||
return path.dirname(__filename);
|
||
}
|
||
static cwd() {
|
||
return process.cwd();
|
||
}
|
||
}
|
||
class ConfigLoader {
|
||
static load(filename = 'config.jsonc') {
|
||
const configPath = path.join(ScriptInfo.dir(), filename);
|
||
if (!fs.existsSync(configPath)) {
|
||
console.error(` ⚠ Konfigurationsdatei nicht gefunden: ${configPath}`);
|
||
process.exit(1);
|
||
}
|
||
const raw = fs.readFileSync(configPath, 'utf8');
|
||
const clean = raw
|
||
.split('\n')
|
||
.filter(line => !line.trim().startsWith('//'))
|
||
.join('');
|
||
return JSON.parse(clean);
|
||
}
|
||
}
|
||
class ArgParser {
|
||
static parse() {
|
||
const argv = process.argv.slice(2);
|
||
const result = { scan: null, ignore: [], reset: false, hilfe: false };
|
||
while (argv.length) {
|
||
const arg = argv.shift();
|
||
if (!arg)
|
||
break;
|
||
if (arg === '-h' || arg === '--hilfe') {
|
||
result.hilfe = true;
|
||
}
|
||
else if (arg === '--reset') {
|
||
result.reset = true;
|
||
}
|
||
else if ((arg === '-s' || arg === '--scan') && argv.length) {
|
||
result.scan = argv.shift().split(',').map(x => x.trim()).filter(Boolean);
|
||
}
|
||
else if ((arg === '-x' || arg === '--ignore') && argv.length) {
|
||
result.ignore = argv.shift().split(',').map(x => x.trim()).filter(Boolean);
|
||
}
|
||
else {
|
||
console.warn(` ⚠ Unbekannter Parameter: ${arg}`);
|
||
result.hilfe = true;
|
||
break;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
static showHelp() {
|
||
console.log(`
|
||
(C) 2025 - Adam Skotarczak (ionivation.com)
|
||
|
||
🛈 Tool das Verzeichnisse nach Markdown-Dateien durchsucht und diese an eine definierte Liste anhängt als Markdown-Link.
|
||
Verwendung: ts-node link_collector.ts [OPTIONEN]
|
||
|
||
-s, --scan Kommagetrennte Liste von Verzeichnissen zum Durchsuchen (relativ zum Aufrufpfad)
|
||
-x, --ignore Kommagetrennte Liste von Verzeichnissen, die ignoriert werden sollen
|
||
--reset Löscht das Logfile 'processed.log' und beendet sich
|
||
-h, --hilfe Zeigt diese Hilfe
|
||
|
||
Beispiel:
|
||
ts-node link_collector.ts -s docs,notes -x "docs/alt"
|
||
`);
|
||
}
|
||
}
|
||
class FileScanner {
|
||
static *find(rootDirs, ignoreDirs, extensions) {
|
||
for (const root of rootDirs) {
|
||
const absRoot = path.resolve(root);
|
||
const stack = [absRoot];
|
||
while (stack.length) {
|
||
const current = stack.pop();
|
||
const rel = path.relative(ScriptInfo.cwd(), current).replace(/\\/g, '/');
|
||
if (ignoreDirs.some(ignored => rel.startsWith(ignored)))
|
||
continue;
|
||
const entries = fs.readdirSync(current, { withFileTypes: true });
|
||
for (const entry of entries) {
|
||
const fullPath = path.join(current, entry.name);
|
||
if (entry.isDirectory()) {
|
||
stack.push(fullPath);
|
||
}
|
||
else if (extensions.some(ext => entry.name.endsWith(ext))) {
|
||
yield fullPath;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
class TitleExtractor {
|
||
static extract(filepath) {
|
||
try {
|
||
const content = fs.readFileSync(filepath, 'utf8');
|
||
for (const line of content.split('\n')) {
|
||
if (line.trim().startsWith('#')) {
|
||
return line.replace(/^#+/, '').trim();
|
||
}
|
||
}
|
||
}
|
||
catch (e) {
|
||
console.error(`⚠ Fehler beim Lesen von ${filepath}: ${e}`);
|
||
}
|
||
return path.basename(filepath, path.extname(filepath));
|
||
}
|
||
}
|
||
class ProcessedLog {
|
||
constructor(filename) {
|
||
this.logPath = path.join(ScriptInfo.dir(), filename);
|
||
this.entries = new Set();
|
||
this.load();
|
||
}
|
||
load() {
|
||
if (!fs.existsSync(this.logPath))
|
||
return;
|
||
const lines = fs.readFileSync(this.logPath, 'utf8').split('\n').map(x => x.trim()).filter(Boolean);
|
||
this.entries = new Set(lines.map(p => p.replace(/\\/g, '/')));
|
||
}
|
||
has(posixPath) {
|
||
return this.entries.has(posixPath);
|
||
}
|
||
update(newPaths) {
|
||
const content = newPaths.map(p => p.replace(/\\/g, '/')).join('\n') + '\n';
|
||
fs.appendFileSync(this.logPath, content, 'utf8');
|
||
}
|
||
reset() {
|
||
if (fs.existsSync(this.logPath)) {
|
||
fs.unlinkSync(this.logPath);
|
||
console.log('🧹 Logfile gelöscht:', this.logPath);
|
||
}
|
||
else {
|
||
console.log('ℹ Logfile existierte nicht:', this.logPath);
|
||
}
|
||
}
|
||
}
|
||
class MarkdownAppender {
|
||
static append(outputFile, links) {
|
||
const content = links.join('\n') + '\n';
|
||
fs.appendFileSync(outputFile, content, 'utf8');
|
||
}
|
||
}
|
||
// Einstiegspunkt
|
||
function main() {
|
||
const config = ConfigLoader.load();
|
||
const args = ArgParser.parse();
|
||
if (args.hilfe) {
|
||
ArgParser.showHelp();
|
||
return;
|
||
}
|
||
const log = new ProcessedLog(config.processed_log || 'processed.log');
|
||
if (args.reset) {
|
||
log.reset();
|
||
return;
|
||
}
|
||
const cwd = ScriptInfo.cwd();
|
||
const outputFile = path.resolve(cwd, config.output_file || 'output.md');
|
||
const rootDirs = (args.scan || config.root_dirs || []).map(d => path.resolve(d));
|
||
const ignoreDirs = (args.ignore || []).map(d => path.relative(cwd, path.resolve(d)).replace(/\\/g, '/'));
|
||
const extensions = config.extensions || ['.md'];
|
||
const newLinks = [];
|
||
const newProcessed = [];
|
||
for (const absPath of FileScanner.find(rootDirs, ignoreDirs, extensions)) {
|
||
const relPath = path.relative(cwd, absPath).replace(/\\/g, '/');
|
||
if (path.resolve(absPath) === outputFile || log.has(relPath))
|
||
continue;
|
||
const title = TitleExtractor.extract(absPath);
|
||
newLinks.push(`- [${title}](${relPath})`);
|
||
newProcessed.push(relPath);
|
||
}
|
||
if (newLinks.length > 0) {
|
||
MarkdownAppender.append(outputFile, newLinks);
|
||
log.update(newProcessed);
|
||
console.log(`✔ ${newLinks.length} neue Links hinzugefügt.`);
|
||
}
|
||
else {
|
||
console.log(' ℹ Keine neuen Dateien gefunden.');
|
||
}
|
||
}
|
||
main();
|