/* ===================================================================== helfer-ohne-kommentar.mjs — Kommentare entfernen, ohne den Rest zu beschaedigen (21.09.2026) --------------------------------------------------------------------- WOFUER Filipes Entscheidung vom 21.09.2026 zum Rollennamen der Community: Er darf nicht in CODE und nicht in SICHTBAREN TEXTEN stehen -- in Kommentaren schon. Die Begruendung ist messbar: Auf der Zugangswand von Team Dogi stehen die drei Rollennamen ohnehin offen ("3 rollen. dogfather. rechte hand und modis"), und geschuetzt wird der Zugang durch den Code, nicht durch ein Wort. Eine Pruefung, die 45 Kommentare anmahnt, waere eine Dauer-Rotmeldung -- und Rot, das immer da ist, wird ueberlesen. Damit diese Regel ueberhaupt pruefbar ist, muss jemand sagen koennen, was Kommentar ist und was nicht. Genau das tut diese Datei. --------------------------------------------------------------------- WARUM KEIN EINFACHER SUCHEN-UND-ERSETZEN-AUSDRUCK Ein Muster, das einfach vom naechsten Sternchen-Schraegstrich bis zum uebernaechsten loescht, sieht richtig aus und ist es nicht. Es zerschneidet zum Beispiel eine Adresse, in der dieselbe Zeichenfolge mitten in einer Zeichenkette steht, oder ein Suchmuster, das Schraegstrich und Sternchen als Zeichen enthaelt -- und macht aus gueltigem Programmtext Unsinn. (Beide Beispiele stehen hier bewusst NICHT ausgeschrieben: Ein Kommentar, der die Zeichenfolge enthaelt, mit der ein Kommentar endet, beendet sich selbst. Beim ersten Versuch ist genau das passiert -- in der Datei, die davor warnt.) IM HAUS IST DIESE FALLE SCHON EINMAL ZUGESCHNAPPT: Am 20.09.2026 hat ein Ausdruck beim Umstellen der Portnummern `", port: 4231, path: "` fuer eine Zeichenkette gehalten. Das Ergebnis war gueltiges JavaScript ohne das Feld `port` -- `node --check` sagte bei allen 164 Dateien "in Ordnung", und aufgefallen ist es erst, weil ein Lauf gegen einen GEMESSENEN Vorstand verglichen wurde. Deshalb wird hier Zeichen fuer Zeichen gelesen und mitgefuehrt, wo man gerade steht: in Programmtext, in einer Zeichenkette, in einer Vorlage, in einem Suchmuster oder in einem Kommentar. --------------------------------------------------------------------- DAS SCHWIERIGE STUECK: IST `/` EIN TEILEN ODER EIN SUCHMUSTER `a / b` teilt, `/a/.test(x)` sucht. Unterscheiden kann man das nur am ZEICHEN DAVOR: Nach einem Wert (Zahl, Name, schliessende Klammer) teilt es; nach einem Operator, einem Komma oder einer oeffnenden Klammer beginnt ein Suchmuster. Das ist die Regel, die auch Browser benutzen, und sie ist fuer diesen Zweck genau genug. WAS SIE NICHT KANN: `return /x/` gegen `a++ /x/` unterscheiden, wenn davor ein Schluesselwort steht, das wie ein Name aussieht. Deshalb gelten `return`, `typeof`, `case`, `in`, `of`, `new`, `delete`, `void`, `do` und `else` ausdruecklich als Operator. Die Liste steht unten und ist abgezaehlt, nicht geraten. --------------------------------------------------------------------- WAS ZURUECKKOMMT Derselbe Text, gleich lang, mit Leerzeichen an den Stellen, an denen Kommentar stand. GLEICH LANG ist Absicht: So stimmen Zeilennummern und Spalten weiterhin, und ein Fund laesst sich ohne Umrechnung in der Originaldatei zeigen. Zeilenumbrueche bleiben deshalb erhalten. ===================================================================== */ /* Nach diesen Woertern beginnt ein `/` ein Suchmuster und kein Teilen. Abgezaehlt, nicht geraten -- siehe Kopf. */ const VOR_SUCHMUSTER = new Set([ "return", "typeof", "case", "in", "of", "new", "delete", "void", "do", "else", "yield", "await", "instanceof", ]); /** Ersetzt jeden Kommentar in JavaScript durch Leerzeichen. * * Zeichenketten, Vorlagen und Suchmuster bleiben unangetastet -- * auch wenn `//` oder darin steht. */ export function jsOhneKommentar(quelle) { const t = String(quelle); let aus = ""; let i = 0; /* Das letzte Zeichen, das kein Leerraum war. Daran haengt die Entscheidung Teilen-oder-Suchmuster. */ let letztes = ""; let letztesWort = ""; const schreibe = (s) => { aus += s; }; const leeren = (s) => { aus += s.replace(/[^\n]/g, " "); }; while (i < t.length) { const c = t[i]; const d = t[i + 1]; /* ---- Kommentare ------------------------------------------------ */ if (c === "/" && d === "/") { let j = i; while (j < t.length && t[j] !== "\n") j++; leeren(t.slice(i, j)); i = j; continue; } if (c === "/" && d === "*") { let j = t.indexOf("*/", i + 2); j = j === -1 ? t.length : j + 2; leeren(t.slice(i, j)); i = j; continue; } /* ---- Zeichenketten --------------------------------------------- */ if (c === '"' || c === "'" || c === "`") { let j = i + 1; while (j < t.length) { if (t[j] === "\\") { j += 2; continue; } if (t[j] === c) { j++; break; } /* In einer Vorlage kann `${…}` wieder Programmtext sein. Fuer den Zweck hier -- Kommentare finden -- reicht es, das Stueck als Ganzes stehen zu lassen: Ein Kommentar darin waere ohnehin Programmtext und soll nicht verschwinden. */ j++; } schreibe(t.slice(i, j)); letztes = c; letztesWort = ""; i = j; continue; } /* ---- Suchmuster ------------------------------------------------ */ if (c === "/") { const wertDavor = /[\w$)\]]/.test(letztes) && !VOR_SUCHMUSTER.has(letztesWort); if (!wertDavor) { let j = i + 1; let inKlasse = false; let fertig = false; while (j < t.length) { const z = t[j]; if (z === "\\") { j += 2; continue; } if (z === "\n") break; /* unfertig -- dann war es Teilen */ if (z === "[") inKlasse = true; else if (z === "]") inKlasse = false; else if (z === "/" && !inKlasse) { j++; fertig = true; break; } j++; } if (fertig) { while (j < t.length && /[a-z]/.test(t[j])) j++; /* Flaggen */ schreibe(t.slice(i, j)); letztes = "/"; letztesWort = ""; i = j; continue; } } } /* ---- alles andere ---------------------------------------------- */ schreibe(c); if (!/\s/.test(c)) { letztes = c; if (/[\w$]/.test(c)) letztesWort += c; else letztesWort = ""; } i++; } return aus; } /** CSS kennt nur die Block-Form und Zeichenketten. */ export function cssOhneKommentar(quelle) { const t = String(quelle); let aus = ""; let i = 0; while (i < t.length) { const c = t[i]; if (c === "/" && t[i + 1] === "*") { let j = t.indexOf("*/", i + 2); j = j === -1 ? t.length : j + 2; aus += t.slice(i, j).replace(/[^\n]/g, " "); i = j; continue; } if (c === '"' || c === "'") { let j = i + 1; while (j < t.length) { if (t[j] === "\\") { j += 2; continue; } if (t[j] === c) { j++; break; } j++; } aus += t.slice(i, j); i = j; continue; } aus += c; i++; } return aus; } /** HTML: heraus, und die eingebetteten