Ausgeloest von pruef-modi-wortleck, die seit Tagen rot war: "24
Fundstellen -- der verborgene Zugang waere damit auffindbar." Die
erste Frage war, ob sie recht hat. Gemessen: ja, und schlimmer als
gedacht.
WER DIESE DATEIEN BEKOMMT: Nicht nur Kollegen. `curl` ohne jeden Keks
auf workspace.dogfather-universe.com liefert die Skripte und
Stilvorlagen des Arbeitsplatzes mit HTTP 200 -- fuer jeden im Netz
lesbar.
DARUNTER crew-haus.css, 27 806 Byte, das ganze Farbhaus von Team Dogi
samt seiner Kommentare. In deren Kopf stand seit dem 10.09. woertlich
"Diese Datei wird NUR auf crew.dogfather-universe.com ausgeliefert."
Das war eine Behauptung, keine Tatsache: Die Weiche biegt `haus.css`
auf diesen Namen um, wer den Namen direkt nannte, ging an ihr vorbei.
Ein Kommentar, der eine Absicht beschreibt, erfuellt sie nicht.
Zwei Stellen im Haus haben sich damit widersprochen -- die Datei sagte
"nur auf crew.", pruef-crew-adresse verlangte ausdruecklich das
Gegenteil ("sie ist kein Geheimnis"). Durchgesetzt war die offene
Fassung. Filipe hat am 21.09. entschieden: zusperren. Die alte
Begruendung dagegen war die Sorge, eine Sperre koenne die Pruefung
blind machen -- die ist jetzt beantwortet statt vermieden: Jeder
Eintrag der Tafel wird in BEIDE Richtungen gemessen, und dass die
Umbiegung von `haus.css` auf crew. weiterhin greift, ebenfalls.
DIE ADRESSE WAR DAS EIGENTLICHE GEHEIMNIS, nicht das Wort. Die
Rollennamen stehen auf der Zugangswand von Team Dogi ohnehin offen --
so gewollt. WO sie liegt, stand dagegen im Klartext in gate.css,
gate.js und crew-haus.css, alle drei oeffentlich abrufbar. Jetzt steht
sie in keiner ausgelieferten Datei mehr.
DIE PRUEFUNG SELBST HATTE DREI MAENGEL:
1. Sie kannte die Mehrzahl nicht. `\bmodi\b` fand "ein Modi", nicht
"die Modis" -- 23 weitere Stellen, darunter der Satz "Fuer die
rechte Hand und die Modis gibt es dort den stillen Zugang". Also
genau die Auskunft, die sie verhindern soll, in der Form, die sie
nicht sah.
2. Ihre Ausnahmeliste war abgeschrieben. Sie nannte eine Datei; die
zweite, die der Server auf crew. begrenzt, haette sie nie
erfahren. Ausgenommen ist jetzt genau das, was der Server auch
durchsetzt (GEHOERT_ZU_ADRESSE) -- kein Muster, sondern die
Durchsetzung selbst.
3. Sie mahnte 45 Kommentare an. Filipes Entscheidung: in Kommentaren
ja, in Code und sichtbaren Texten nein. Rot, das immer da ist,
wird ueberlesen -- und dann faengt sie auch den echten Fall nicht.
Dafuer neu: helfer-ohne-kommentar.mjs, ein Zerleger, der Kommentare
zeichengenau durch Leerzeichen ersetzt, ohne Zeichenketten,
Vorlagen oder Suchmuster zu beschaedigen. KEIN Suchausdruck -- genau
daran ist am 20.09. die Portumstellung gescheitert, mit gueltigem
JavaScript ohne das Feld `port`. Dreizehn Proben in beide Richtungen
beweisen, dass er weder zu viel noch zu wenig wegnimmt; ein Zerleger,
der zu viel nimmt, macht die ganze Pruefung STILL gruen.
Von 45 blieben damit drei echte, alle behoben:
- daten.modis -> daten.leute (Feldname in der Auskunft, also Code)
- zwei Saetze, die Menschen lesen, auf "die Moderation" umgestellt --
fuer jemanden, der neu im Treff ist, sogar klarer
Nebenbei, beim selben Durchgang gefunden und geschlossen:
- teamlage.js fiel auf einen Rollennamen zurueck. Nachgemessen: Die
Stilvorlage kennt zu dieser Karte gar keine solche Regel -- der
Rueckfall hat nie etwas bewirkt und nur das Wort ausgeliefert.
- werdegang.js fuehrte die Saetze ueber den Rollengruppen selbst,
mit den Rollennamen als Schluessel. Der Kommentar daneben
behauptete sogar, sie seien "abgeleitet, nicht abgeschrieben".
Sie kommen jetzt vom Server, und pruef-werdegang misst, dass
jede Gruppe ihren eigenen behaelt.
pruef-modi-wortleck 8/0 (war 5, davon 1 rot)
pruef-crew-adresse 144/0 (war 135)
pruef-werdegang 98 statt 95 Pruefungen, dieselben 3 alten Fehler
pruef-team-ampel 32/0, pruef-team-stufen 28/0, pruef-hilfe 84/0
Co-Authored-By: Claude Opus 5 <[email protected]>
250 lines
8.9 KiB
JavaScript
250 lines
8.9 KiB
JavaScript
/* =====================================================================
|
|
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 <script>/<style>
|
|
* bekommen ihre eigene Behandlung -- sonst bliebe ein Kommentar
|
|
* mitten im Skript stehen. */
|
|
export function htmlOhneKommentar(quelle) {
|
|
const t = String(quelle);
|
|
let aus = "";
|
|
let i = 0;
|
|
while (i < t.length) {
|
|
if (t.startsWith("<!--", i)) {
|
|
let j = t.indexOf("-->", i + 4);
|
|
j = j === -1 ? t.length : j + 3;
|
|
aus += t.slice(i, j).replace(/[^\n]/g, " ");
|
|
i = j;
|
|
continue;
|
|
}
|
|
const skript = /^<script\b[^>]*>/i.exec(t.slice(i, i + 200));
|
|
if (skript) {
|
|
const start = i + skript[0].length;
|
|
const ende = t.toLowerCase().indexOf("</script", start);
|
|
const bis = ende === -1 ? t.length : ende;
|
|
aus += skript[0] + jsOhneKommentar(t.slice(start, bis));
|
|
i = bis;
|
|
continue;
|
|
}
|
|
const stil = /^<style\b[^>]*>/i.exec(t.slice(i, i + 200));
|
|
if (stil) {
|
|
const start = i + stil[0].length;
|
|
const ende = t.toLowerCase().indexOf("</style", start);
|
|
const bis = ende === -1 ? t.length : ende;
|
|
aus += stil[0] + cssOhneKommentar(t.slice(start, bis));
|
|
i = bis;
|
|
continue;
|
|
}
|
|
aus += t[i];
|
|
i++;
|
|
}
|
|
return aus;
|
|
}
|
|
|
|
/** Waehlt anhand der Endung. Unbekannte Endungen kommen UNVERAENDERT
|
|
* zurueck -- lieber eine Fundstelle zu viel melden als eine zu
|
|
* wenig. Wer hier stillschweigend alles durchliesse, haette eine
|
|
* Pruefung gebaut, die bei jeder neuen Dateiart blind wird. */
|
|
export function ohneKommentar(text, endung) {
|
|
const e = String(endung || "").toLowerCase();
|
|
if (e === ".js" || e === ".mjs") return jsOhneKommentar(text);
|
|
if (e === ".css") return cssOhneKommentar(text);
|
|
if (e === ".html" || e === ".htm") return htmlOhneKommentar(text);
|
|
return String(text);
|
|
}
|