js-ts + tools

This commit is contained in:
Adam Skotarczak 2025-06-03 23:34:49 +02:00
parent 6567b4e966
commit beb1c8c9ed
13 changed files with 1225 additions and 21 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.vscode/
node_modules/
dist/

View File

@ -13,7 +13,10 @@ Kleine Sammlung von Gedächnissstützen für den privaten und persönlichen Gebr
- [Markdown](#markdown)
- [Mermaid](#mermaid)
- [Server](#server)
- [Plesk](#plesk)
- [Webserver](#webserver)
- [Programmierung](#programmierung)
- [ts - js - node](#ts---js---node)
- [Neue Dokumente](#neue-dokumente)
---
@ -28,14 +31,14 @@ Kleine Sammlung von Gedächnissstützen für den privaten und persönlichen Gebr
### AsciiDoc
- [Asciidoctor PDF: Kapitel bleibt „Chapter“ Fehleranalyse & Workaround](dokus/asciidoc/asciidoctor-theme-bug-workaround.md)
- [Asciidoctor PDF: Kapitel bleibt „Chapter“ Fehleranalyse & Workaround](dokus/asciidoc/asciidoctor-theme-bug-workaround.md)
---
### Git
- [Artikel: git](dokus/git/git.md)
- [SSH-Zugriff auf Git-Repository in WSL einrichten](dokus/git/git-ssh-remote.md)
- [SSH-Zugriff auf Git-Repository in WSL einrichten](dokus/git/git-ssh-remote.md)
- [Git Remote-Branches: Häufige Aufgaben und Lösungen](dokus/git/git-remote-branch.md)
### Markdown
@ -46,14 +49,24 @@ Kleine Sammlung von Gedächnissstützen für den privaten und persönlichen Gebr
### Server
#### Plesk
- [Plesk ausgesperrt bei Rechnerwechsel](dokus/plesk/plesk-benutzer-schon-vorhanden.md)
#### Webserver
- [Let's Encrypt: Fehlerbehebung bei Challenge-Timeout unter Plesk](dokus/apache-plesk/lets-encrypt-plesk.md)
- [Let's Encrypt: Fehlerbehebung bei Challenge-Timeout unter Plesk](dokus/apache-plesk/lets-encrypt-plesk.md)
---
### Programmierung
#### ts - js - node
- [Einstieg in JavaScript-Dialekte und TypeScript-Konfiguration](dokus/js-ts/js-ts-dialekte.md)
## Neue Dokumente
> führe `scan.cmd` oder [`link_collector.py`](./tools/collector/link_collector.py) aus um nach neuen Dateien zu suchen!
> führe `scan.cmd` oder [`link_collector.py`](./tools/collector/link_collector.py) oder `npm run scan` aus um nach neuen Dateien zu suchen!
**NEUE EINTRÄGE AUS './tools/collector/link_collector.py'->**
**NEUE EINTRÄGE AUS 'link_collector'->**

View File

@ -0,0 +1,345 @@
# Einstieg in JavaScript-Dialekte und TypeScript-Konfiguration
## ✨ Ziel dieses Dokuments
Diese Anleitung richtet sich an Anfänger, die lernen wollen, wann sie welchen JavaScript-Dialekt (CommonJS vs. ESM) verwenden sollten, wie sie TypeScript korrekt konfigurieren und worauf sie bei modernen Projekten achten müssen.
## Inhalt
- [Einstieg in JavaScript-Dialekte und TypeScript-Konfiguration](#einstieg-in-javascript-dialekte-und-typescript-konfiguration)
- [✨ Ziel dieses Dokuments](#-ziel-dieses-dokuments)
- [Inhalt](#inhalt)
- [🔢 Begriffe erklärt](#-begriffe-erklärt)
- [JavaScript-Dialekte (Modulsysteme)](#javascript-dialekte-modulsysteme)
- [Was ist TypeScript?](#was-ist-typescript)
- [🔍 Wann verwende ich was?](#-wann-verwende-ich-was)
- [🔧 TypeScript richtig konfigurieren](#-typescript-richtig-konfigurieren)
- [📦 Was ist ESM?](#-was-ist-esm)
- [✅ Wann kann/sollte/muss man **ESM** verwenden?](#-wann-kannsolltemuss-man-esm-verwenden)
- [🟢 **Du kannst ESM verwenden**, wenn](#-du-kannst-esm-verwenden-wenn)
- [🟡 **Du solltest ESM verwenden**, wenn](#-du-solltest-esm-verwenden-wenn)
- [🔴 **Du musst ESM verwenden**, wenn](#-du-musst-esm-verwenden-wenn)
- [⚙️ TypeScript-Konfiguration für ESM](#-typescript-konfiguration-für-esm)
- [📌 Merkmale von ESM](#-merkmale-von-esm)
- [⚠️ Besonderheiten und Stolperfallen](#-besonderheiten-und-stolperfallen)
- [✅ Fazit ESM](#-fazit-esm)
- [🌐 CommonJS-Konfiguration CJS (Alternative)](#-commonjs-konfiguration-cjs-alternative)
- [📦 Was ist CommonJS?](#-was-ist-commonjs)
- [✅ Wann kann/sollte/muss man **CommonJS** verwenden?](#-wann-kannsolltemuss-man-commonjs-verwenden)
- [🟢 **Du kannst CJS verwenden**, wenn](#-du-kannst-cjs-verwenden-wenn)
- [🟡 **Du solltest CJS verwenden**, wenn](#-du-solltest-cjs-verwenden-wenn)
- [🔴 **Du musst CJS verwenden**, wenn](#-du-musst-cjs-verwenden-wenn)
- [⚙️ TypeScript-Konfiguration für CJS](#-typescript-konfiguration-für-cjs)
- [📌 Merkmale von CommonJS](#-merkmale-von-commonjs)
- [✅ Fazit CommonJS](#-fazit-commonjs)
- [📄 Fazit: Empfehlungen](#-fazit-empfehlungen)
- [📁 Dateiendungen in Node.js](#-dateiendungen-in-nodejs)
- [⚠️ Typische Fehler vermeiden](#-typische-fehler-vermeiden)
- [🚀 CLI-Build-Tipps (tsc)](#-cli-build-tipps-tsc)
- [📍 Weiterführende Themen](#-weiterführende-themen)
---
## 🔢 Begriffe erklärt
### JavaScript-Dialekte (Modulsysteme)
| Name | Kürzel | Typ | Verwendung heute |
| ------------------ | ------ | ----------- | ----------------------- |
| CommonJS | CJS | `require()` | Ältere Node.js-Projekte |
| ECMAScript Modules | ESM | `import` | Browser, moderne Tools |
- **CommonJS:** Nutzt `require()` und `module.exports`. Standard bis Node 12.
- **ESM:** Nutzt `import`/`export`. Standard im Browser und in Node ab Version 14+.
### Was ist TypeScript?
TypeScript ist eine statisch typisierte Obermenge von JavaScript, die zu normalem JS kompiliert wird.
---
## 🔍 Wann verwende ich was?
| Ziel | Modulformat | Empfehlung | Grund |
| -------------------------- | ------------ | -------------- | ------------------------------------------ |
| 📅 Moderne Webanwendung | ESM | Ja | Browser erwartet ESM |
| 📄 Node.js CLI-Tool | ESM oder CJS | Beides möglich | ESM zukunftssicher, CJS maximal kompatibel |
| 🔐 Legacy-Node-Integration | CJS | Ja | Viele NPM-Pakete sind CJS-only |
| 🔌 Electron Renderer | ESM | Ja (Pflicht) | Chromium-only, kein CJS |
| 🔁 Vite/Webpack-Projekt | ESM | Ja | Toolchain basiert auf ESM |
---
## 🔧 TypeScript richtig konfigurieren
Eine vollständige Zusammenfassung zu **ESM (ECMAScript Modules)** wann du es **verwenden kannst**, **solltest** oder **musst**, sowie alle wesentlichen technischen Eigenschaften. Stil und Umfang entsprechen exakt der vorherigen CommonJS-Zusammenfassung.
---
### 📦 Was ist ESM?
**ESM (ECMAScript Modules)** ist das moderne Modulsystem von JavaScript:
- verwendet `import`/`export`-Syntax
- wird nativ von allen modernen Browsern und Node.js (ab v14+) unterstützt
- ist Standard in allen modernen JS-Toolchains (Vite, Webpack, esbuild, etc.)
```js
// ESM-Stil
import fs from 'node:fs/promises';
export function read(path) {
return fs.readFile(path, 'utf-8');
}
```
---
### ✅ Wann kann/sollte/muss man **ESM** verwenden?
#### 🟢 **Du kannst ESM verwenden**, wenn
- du mit **modernen Node.js-Versionen (v16+)** arbeitest
- du ein **Frontend-Tool** oder ein **Browser-kompatibles Projekt** baust
- du Tree-Shaking oder Dynamic `import()` nutzen willst
- du Typkonsistenz mit dem Browser willst (gleiche Syntax wie dort)
#### 🟡 **Du solltest ESM verwenden**, wenn
| Situation | Grund |
| ------------------------------------------------------------------- | ---------------------------- |
| 🌐 Du entwickelst Webanwendungen mit Vite, Nuxt, Webpack o. ä. | Diese Tools basieren auf ESM |
| 💡 Du möchtest moderne Syntax und Tooling nutzen | ESM ist Standard |
| 🔄 Du willst `import()` dynamisch verwenden | Nur in ESM verfügbar |
| 📚 Du schreibst eine wiederverwendbare Bibliothek für neue Projekte | Zukunftssicherheit |
| 🧩 Du verwendest Deno, Bun oder moderne Plattformen | ESM only |
#### 🔴 **Du musst ESM verwenden**, wenn
- du im **Browser** arbeitest (ohne Bundler) → dort ist `import` Pflicht
- du in **Electron-Renderer-Prozessen** arbeitest
- du ein Projekt hast mit `"type": "module"` und `.js`-Dateien → dann interpretiert Node sie nur als ESM
- du ESM-only-Dependencies nutzt (z.B. `chalk@5`, `node-fetch@3`)
---
### ⚙️ TypeScript-Konfiguration für ESM
```jsonc
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}
```
Und in der `package.json`:
```json
{
"type": "module"
}
```
**Wichtig:** Ohne `"type": "module"` behandelt Node deine `.js`-Dateien **nicht als ESM**, sondern als CommonJS.
---
### 📌 Merkmale von ESM
| Merkmal | ESM |
| ------------------------- | -------------------------------------------- |
| Import-Syntax | `import`, `export` |
| Export-Syntax | `export default`, `export {...}` |
| Dateiendung `.js` | **.js** nur mit `"type": "module"` |
| `.mjs` | explizit ESM |
| `__dirname`, `__filename` | ❌ nicht vorhanden, nur via `import.meta.url` |
| Dynamische Imports | ✅ `await import('...')` |
| Tree Shaking | ✅ möglich |
| Browser-kompatibel | ✅ nativ |
| CommonJS-Interop | ❗ mit Einschränkungen (`createRequire`) |
---
### ⚠️ Besonderheiten und Stolperfallen
| Problem | Lösung |
| --------------------------------------------- | ------------------------------------------- |
| ❌ `__dirname`/`__filename` fehlen | nutze `import.meta.url` + `fileURLToPath()` |
| ❌ `require()` nicht verfügbar | verwende `import`, oder `createRequire()` |
| ❌ `.js` muss beim Import angegeben werden | z.B. `import './utils.js'` |
| ❌ viele alte Pakete sind nicht ESM-kompatibel | ggf. CommonJS verwenden |
---
### ✅ Fazit ESM
> **ESM ist der offizielle Standard für moderne JavaScript-Entwicklung** sowohl im Browser als auch in modernen Node.js-Umgebungen.
Du solltest ESM verwenden, wenn du:
- neue Projekte startest,
- moderne Toolchains nutzt,
- oder langfristige Kompatibilität willst.
In Zweifelsfällen gilt: **Wenn du mit aktuellen Tools und Node-Versionen arbeitest, nimm ESM.**
Nur bei Problemen mit alten Paketen oder Tooling solltest du temporär auf CommonJS zurückfallen.
---
## 🌐 CommonJS-Konfiguration CJS (Alternative)
### 📦 Was ist CommonJS?
**CommonJS (CJS)** ist das ursprüngliche Modulsystem von Node.js:
- verwendet `require()` zum Importieren
- verwendet `module.exports` oder `exports` zum Exportieren
- keine native Unterstützung für `import`/`export`
- funktioniert nur in Node.js (nicht im Browser ohne Bundler)
```js
// CommonJS-Stil
const fs = require('fs');
module.exports = { read: fs.readFileSync };
```
---
### ✅ Wann kann/sollte/muss man **CommonJS** verwenden?
#### 🟢 **Du kannst CJS verwenden**, wenn
- du ein CLI-Tool oder Node.js-Skript erstellst
- du maximale **Kompatibilität** ohne ESM-Komplikationen willst
- du keine modernen ESM-Features brauchst
- du auf Build-Tools verzichtest und direkt `.js`-Dateien startest
#### 🟡 **Du solltest CJS verwenden**, wenn
| Situation | Grund |
| ------------------------------------------------------------------- | ------------------------------------------------------ |
| 🔄 Du arbeitest mit älteren Node.js-Versionen (z.B. v12 oder v10) | ESM ist dort nicht stabil |
| 📦 Du nutzt NPM-Pakete, die **kein ESM unterstützen** | `require()` wird erwartet |
| 🧪 Du verwendest alte Tools, Tests oder Konfigurationen | z.B. alte `mocha`, `gulpfile.js`, `webpack.config.js` |
| 🧱 Du brauchst direkten Zugriff auf `__dirname`, `__filename` | Diese fehlen in ESM |
| 📜 Du möchtest `.js`-Dateien ohne `.mjs` oder `type: module` nutzen | CommonJS macht keine Vorgaben für Dateiendungen |
#### 🔴 **Du musst CJS verwenden**, wenn
- das Node.js-Modul, das du importieren willst, **nur `module.exports` exportiert** (kein ESM-Support)
- dein Projekt oder Tool **nicht mit ESM kompatibel ist** (ältere Tooling oder NPM-Module)
- du `require()` oder dynamisches Laden (`require(dynamicPath)`) verwendest, was in ESM nicht geht
---
### ⚙️ TypeScript-Konfiguration für CJS
```jsonc
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true
}
}
```
Und in der `package.json`:
```json
{
"type": "commonjs"
}
```
**Hinweis:** Das `"type"`-Feld ist für Node wichtig, nicht für TypeScript.
---
### 📌 Merkmale von CommonJS
| Merkmal | CommonJS |
| ------------------------- | -------------------------- |
| Import-Syntax | `require()` |
| Export-Syntax | `module.exports = …` |
| Dateiendung | `.js` (keine `.mjs` nötig) |
| Direkt ausführbar | ✅ Ohne zusätzliche Flags |
| `__dirname`, `__filename` | ✅ verfügbar |
| Browserkompatibilität | ❌ Nur mit Bundler möglich |
| Dynamische Importe | ✅ `require(dynamicPath)` |
| Tree Shaking | ❌ nicht möglich |
---
### ✅ Fazit CommonJS
> **CommonJS ist stabil, weit verbreitet und maximal kompatibel.**
> Wenn du **einfach nur ein Node-Tool oder CLI-Skript baust** und keine besonderen Build-Prozesse oder Browser-Kompatibilität brauchst: **Nutze CommonJS.**
Für moderne ESM-Projekte im Web oder wenn du zukunftssicher entwickeln willst, solltest du auf ESM setzen aber CJS bleibt in vielen Fällen die einfachste und robusteste Wahl.
---
## 📄 Fazit: Empfehlungen
- **Neue Projekte:** Nutze ESM mit `"type": "module"`
- **Bestehende Tools nutzen `require()`?** Dann CJS
- **Browser + Node gemeinsam nutzen?** ESM-only + klare Trennung von `shared/`, `client/`, `server/`
- **Tooling wie Vite, Nuxt, Webpack?** Immer ESM
---
## 📁 Dateiendungen in Node.js
| Dateiendung | Verhalten |
| ----------- | ------------------- |
| `.js` | Hängt von `type` ab |
| `.mjs` | Immer ESM |
| `.cjs` | Immer CommonJS |
---
## ⚠️ Typische Fehler vermeiden
- **`SyntaxError: Cannot use import statement outside a module`**
`type` fehlt in `package.json`
- **`Cannot find module` bei relativen Imports**
`.js` fehlt am Ende beim ESM-Import
- **`__dirname` ist not defined**
→ Du nutzt ESM, verwende `import.meta.url`
---
## 🚀 CLI-Build-Tipps (tsc)
```bash
npx tsc # Kompiliert Projekt laut tsconfig.json
chmod +x dist/index.js # Macht CLI-Datei ausführbar
```
Header für CLI-Datei:
```ts
#!/usr/bin/env node
```
---
## 📍 Weiterführende Themen
- Dual-Exports für NPM (ESM + CJS gleichzeitig)
- "exports"-Feld in `package.json`
- Tools wie `tsup`, `vite`, `esbuild` als Alternativen zu `tsc`
- Einstieg in CLI-Tools: `commander`, `yargs`, `chalk`, `ora`

499
package-lock.json generated
View File

@ -1,28 +1,502 @@
{
"name": "adminslog",
"name": "tools",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tools",
"version": "1.0.0",
"dependencies": {
"fs": "^0.0.1-security",
"path": "^0.12.7",
"process": "^0.11.10"
"process": "^0.11.10",
"typescript": "^5.8.3"
},
"bin": {
"link-collector": "dist/index.js"
},
"devDependencies": {
"@types/node": "^22.15.21"
"@types/node": "^22.15.29",
"esbuild": "^0.25.5"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"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==",
"version": "22.15.29",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz",
"integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/esbuild": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.5",
"@esbuild/android-arm": "0.25.5",
"@esbuild/android-arm64": "0.25.5",
"@esbuild/android-x64": "0.25.5",
"@esbuild/darwin-arm64": "0.25.5",
"@esbuild/darwin-x64": "0.25.5",
"@esbuild/freebsd-arm64": "0.25.5",
"@esbuild/freebsd-x64": "0.25.5",
"@esbuild/linux-arm": "0.25.5",
"@esbuild/linux-arm64": "0.25.5",
"@esbuild/linux-ia32": "0.25.5",
"@esbuild/linux-loong64": "0.25.5",
"@esbuild/linux-mips64el": "0.25.5",
"@esbuild/linux-ppc64": "0.25.5",
"@esbuild/linux-riscv64": "0.25.5",
"@esbuild/linux-s390x": "0.25.5",
"@esbuild/linux-x64": "0.25.5",
"@esbuild/netbsd-arm64": "0.25.5",
"@esbuild/netbsd-x64": "0.25.5",
"@esbuild/openbsd-arm64": "0.25.5",
"@esbuild/openbsd-x64": "0.25.5",
"@esbuild/sunos-x64": "0.25.5",
"@esbuild/win32-arm64": "0.25.5",
"@esbuild/win32-ia32": "0.25.5",
"@esbuild/win32-x64": "0.25.5"
}
},
"node_modules/fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
@ -54,6 +528,19 @@
"node": ">= 0.6.0"
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",

View File

@ -1,10 +1,24 @@
{
"name": "tools",
"version": "1.0.0",
"type": "module",
"bin": {
"link-collector": "./dist/index.js"
},
"scripts": {
"build": "tsc",
"postbuild": "node tools/dist/fscopy.js -s tools/dist/collector/link_collector.js -t tools/collector/link_collector.js && npm run clean",
"scan": "node tools/collector/link_collector.js",
"clean": "node ./tools/dist/fsdel.js ./tools/dist/collector/link_collector.js"
},
"dependencies": {
"fs": "^0.0.1-security",
"path": "^0.12.7",
"process": "^0.11.10"
"process": "^0.11.10",
"typescript": "^5.8.3"
},
"devDependencies": {
"@types/node": "^22.15.21"
"@types/node": "^22.15.29",
"esbuild": "^0.25.5"
}
}

View File

@ -1,8 +1,8 @@
{
// Verzeichnis relativ zum Script, das durchsucht wird
"root_dirs": [
"dokus"
],
"root_dirs": [
"dokus"
],
// Nur Dateien mit dieser Endung (wird erweitert)
"extensions": [".md"],
@ -10,6 +10,7 @@
// Zielpfad für die Linkausgabe (Markdown-Datei)
"output_file": "README.md",
// Optional: Log für bereits verarbeitete Dateien
// Optional: Log für bereits verarbeitete Dateien. Dateien die dort
// enthalten sind, werden nicht nochmals verabeitet.
"processed_log": "processed.log"
}

View File

@ -0,0 +1,188 @@
#!/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();

View File

@ -3,3 +3,5 @@ dokus/asciidoc/asciidoctor-theme-bug-workaround.md
dokus/git/git-remote-branch.md
dokus/git/git-ssh-remote.md
dokus/git/git.md
dokus/plesk/plesk-benutzer-schon-vorhanden.md
dokus/js-ts/js-ts-dialekte.md

View File

@ -0,0 +1,16 @@
{
// Verzeichnis relativ zum Script, das durchsucht wird
"root_dirs": [
"dokus"
],
// Nur Dateien mit dieser Endung (wird erweitert)
"extensions": [".md"],
// Zielpfad für die Linkausgabe (Markdown-Datei)
"output_file": "README.md",
// Optional: Log für bereits verarbeitete Dateien. Dateien die dort
// enthalten sind, werden nicht nochmals verabeitet.
"processed_log": "processed.log"
}

View File

@ -1,8 +1,13 @@
#!/usr/bin/env ts-node
#!/usr/bin/env node
import * as fs from 'fs';
import * as path from 'path';
import * as process from 'process';
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);
type Config = {
root_dirs?: string[];

80
tools/src/fscopy.ts Normal file
View File

@ -0,0 +1,80 @@
#!/usr/bin/env ts-node
/**
* Robust plattformunabhängiger Dateikopierer
* Verwendung: node fscopy.ts -s <quelle> -t <ziel>
* Optionen: -h | --help
*/
import * as fs from "fs";
import * as path from "path";
function showHelp(): void {
console.log(`
Copy Files with node.js
Verwendung:
node fscopy.ts -s <quelle> -q <ziel>
Parameter:
-s, --source Pfad zur Quelldatei (z.B. static/.htaccess)
-t, --target Pfad zur Zieldatei (z.B. dist/app/.htaccess)
-h, --help Diese Hilfe anzeigen
`);
process.exit(0);
}
function parseArgs(): { source: string; ziel: string } {
const args = process.argv.slice(2);
let source = "";
let ziel = "";
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case "-s":
case "--source":
source = args[++i];
break;
case "-t":
case "--target":
ziel = args[++i];
break;
case "-h":
case "--help":
showHelp();
break;
default:
console.error(`Unbekannter Parameter: ${args[i]}`);
showHelp();
}
}
if (!source || !ziel) {
console.error("❌ Quelle und Ziel müssen angegeben werden.");
showHelp();
}
return { source, ziel };
}
function copyFile(source: string, ziel: string): void {
const zielVerzeichnis = path.dirname(ziel);
fs.mkdir(zielVerzeichnis, { recursive: true }, (mkdirErr) => {
if (mkdirErr) {
console.error(`❌ Zielverzeichnis konnte nicht erstellt werden: ${mkdirErr.message}`);
process.exit(1);
}
fs.copyFile(source, ziel, (copyErr) => {
if (copyErr) {
console.error(`❌ Fehler beim Kopieren: ${copyErr.message}`);
process.exit(1);
}
console.log(`✔ Datei erfolgreich kopiert: ${source}${ziel}`);
});
});
}
// Ausführung
const { source, ziel } = parseArgs();
copyFile(source, ziel);

41
tools/src/fsdel.ts Normal file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env ts-node
import * as fs from 'fs';
import * as path from 'path';
/**
* Löscht eine Datei am angegebenen Pfad.
*
* @param filePath - Pfad zur Datei, die gelöscht werden soll
*/
function deleteFile(filePath: string): void {
const resolvedPath = path.resolve(filePath);
if (!fs.existsSync(resolvedPath)) {
console.error(`❌ Datei nicht gefunden: ${resolvedPath}`);
process.exit(1);
}
try {
fs.unlinkSync(resolvedPath);
console.log(`✔ Datei gelöscht: ${resolvedPath}`);
} catch (err) {
console.error(`❌ Fehler beim Löschen: ${(err as Error).message}`);
process.exit(1);
}
}
// Kommandozeilenargumente
const [, , arg] = process.argv;
if (!arg || arg === '-h' || arg === '--help') {
console.log(`Verwendung:
ts-node delete.ts <dateipfad>
Beispiel:
ts-node delete.ts ./log/output.txt`);
process.exit(0);
}
// Datei löschen
deleteFile(arg);

11
tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"outDir": "./tools/dist",
"rootDir": "./tools/src",
"strict": true
},
"include": ["tools/src"]
}