ts-version
- ts version vom link_collector hinzugefügt
This commit is contained in:
parent
42afb2769a
commit
d8afdfabc3
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
74
package-lock.json
generated
Normal file
74
package-lock.json
generated
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"name": "adminslog",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"process": "^0.11.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.15.21"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.15.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
||||||
|
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs": {
|
||||||
|
"version": "0.0.1-security",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
|
||||||
|
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/path": {
|
||||||
|
"version": "0.12.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
|
||||||
|
"integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"process": "^0.11.1",
|
||||||
|
"util": "^0.10.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/util": {
|
||||||
|
"version": "0.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||||
|
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "2.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
package.json
Normal file
10
package.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"process": "^0.11.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.15.21"
|
||||||
|
}
|
||||||
|
}
|
220
tools/collector/link_collector.ts
Normal file
220
tools/collector/link_collector.ts
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
#!/usr/bin/env ts-node
|
||||||
|
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as process from 'process';
|
||||||
|
|
||||||
|
type Config = {
|
||||||
|
root_dirs?: string[];
|
||||||
|
output_file?: string;
|
||||||
|
processed_log?: string;
|
||||||
|
extensions?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Args = {
|
||||||
|
scan: string[] | null;
|
||||||
|
ignore: string[];
|
||||||
|
reset: boolean;
|
||||||
|
hilfe: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptInfo {
|
||||||
|
static dir(): string {
|
||||||
|
return path.dirname(__filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
static cwd(): string {
|
||||||
|
return process.cwd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigLoader {
|
||||||
|
static load(filename = 'config.jsonc'): Config {
|
||||||
|
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(): Args {
|
||||||
|
const argv = process.argv.slice(2);
|
||||||
|
const result: Args = { 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(): void {
|
||||||
|
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: string[], ignoreDirs: string[], extensions: string[]): Generator<string> {
|
||||||
|
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: string): string {
|
||||||
|
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 {
|
||||||
|
private readonly logPath: string;
|
||||||
|
private entries: Set<string>;
|
||||||
|
|
||||||
|
constructor(filename: string) {
|
||||||
|
this.logPath = path.join(ScriptInfo.dir(), filename);
|
||||||
|
this.entries = new Set<string>();
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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: string): boolean {
|
||||||
|
return this.entries.has(posixPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(newPaths: string[]): void {
|
||||||
|
const content = newPaths.map(p => p.replace(/\\/g, '/')).join('\n') + '\n';
|
||||||
|
fs.appendFileSync(this.logPath, content, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
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: string, links: string[]): void {
|
||||||
|
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: string[] = [];
|
||||||
|
const newProcessed: string[] = [];
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user