@zero.sc/veil
v0.0.3
Published
Change or remove comments without touching code — a scanner that knows regex literals.
Maintainers
Readme
The problem this solves
Source comments say why the code is the way it is, which is exactly what makes them useful and exactly what makes some of them unpublishable. Internal ticket numbers, code names, rule numbers, paths on someone's machine, or simply a language your users do not read — all reasonable in a repository and none of it something you want inside a published artifact.
So you write a regex and run it over the file, and it eats your code. That is not hypothetical:
a rule that removed empty parentheses from prose, applied file-wide, turned update: () => void
into update: => void. The rule was fine. The scope was wrong.
Scoping to comments needs a scanner, and the naive scanner has a specific failure that is worse
than not working — it works most of the time. It tracks quotes but not regex literals, so a line
like /[&<>"']/g reads as the start of a string, and it loses sync for the rest of the file.
Measured on one codebase: that single line hid sixteen comments in the file containing it, and
a translation pipeline skipped that stretch entirely. Nobody noticed until one of those comments
shipped.
Install
npm install @zero.sc/veilNo dependencies.
Sixty seconds
import { scanComments, scrubComments, stripComments } from '@zero.sc/veil';
// Where are the comments? Not the ones inside strings, templates or regexes.
scanComments(source); // [{ kind: 'line', text, start, end }, …]
// Replace inside comments only — the same text in a string literal is left alone.
const { text, replaced } = scrubComments(source, [
{ pattern: /\bTICKET-\d+\b/g, replace: 'an internal ticket' },
]);
// Or remove them outright.
const cleaned = stripComments(source, (t) => /[^\x00-\x7F]/.test(t));What's inside
| Export | What it does |
|--------|--------------|
| scanComments | Comment spans, aware of strings, templates and regex literals |
| scrubComments | Replace inside comments only, back to front so spans stay valid |
| stripComments | Remove matching comments and fold the blank line they leave |
| codeOnly | Blank the comments, keep the line count — for checks that must tell inside from outside |
codeOnly is the one people do not expect to need until they do. Asking "does this file contain
something shaped like a secret" gets a different answer for an example written in a comment than
for a real value, and a check that cannot separate them either flags your documentation or misses
the value. Blanking preserves offsets, so line numbers in the report still point at the right place.
How division is told from regex
There is no full parser here. / is division when the preceding token ends a value — an
identifier, a number, ), ], } — and a regex otherwise, with an exception list for keywords
that end a value shape but precede a regex (return, typeof, throw, case, yield, await
and the rest).
That heuristic is what real tokenisers use short of parsing, and the important property is the direction it fails in: when it guesses wrong it finds fewer comments. It never mistakes code for a comment, so it never edits code.
Honest limits
- Heuristic, not a parser. See above. Exotic code can hide a comment from it; it cannot make it damage code.
- JavaScript-family syntax. TypeScript, JSX and CSS (with
lineComments: false) work. Python, Ruby and shell do not — their comment and string rules are different. - Template expressions are skipped whole. A comment inside
${…}is not reported. It belongs to an expression, not to the file's narration. - No JSDoc structure. Comments come back as text. Parsing tags is somebody else's job.
- This is 0.0.x.
Around it
| | |
|---|---|
| Docs | kit.zero.sc |
| Small functions | @zero.sc/util |
| Everything | @zero.sc |
License
MIT OR Zero License v1.0 — take whichever you prefer. Choosing MIT is enough; nothing further is required of you.
The Zero name, marks and logos are not covered — build anything you like with this code, just don't present it as a Zero product.
Copyright (c) 2026 Zero. Source Code begins at Zero.
