Stated here rather than discovered later.
Every heading on this page is a link target used elsewhere: by the issue forms, and by the
charcheck-upstream skill, which walks an agent through deciding whether a miss is a bug
worth filing. Renaming one breaks those links, so reword a heading only when the behaviour
it names has changed too.
markup covers .vue only. Svelte is not reachable yet, and is additive behind the same
interface, so it is the most wanted contribution. Plain HTML has its own scope, html.
html covers .html and .htm. A template language on top of it is not understood: a
Jinja, Handlebars or ERB expression is scanned as the prose it looks like, so
{{ user_name }} is text a rule can match inside.
By decision. A scope is not something a config can register, so the set of surfaces is fixed by the release you have installed. A surface that is missing is a feature request rather than a configuration problem.
Not attempted, either.
Not under markup and not under html, so text in a CSS content property is not
checked.
Blocks such as <i18n>, though <i18n> does hold rendered text and is the first candidate
for a follow-up.
markdown covers .md and .markdown. .mdx needs the JSX reader and would inherit its
TypeScript 7 limitation, so it is a separate surface.
By markdown, attributes and text alike. Text in one does render, so this is a miss rather
than a safe answer, and it is the cheap direction to be wrong in. A rule needing that text
can read the file with raw. The html scope does not help here: a rule carries one scope,
so markdown has no way to hand the block over.
Which includes the title="..." that some site generators render as a caption above a code
block.
TypeScript 5, 6 and 7 are all supported, but 7 ships no in-process parser, so on that
version the strings, comments and markup scopes read a token scanner instead of a
syntax tree. Three consequences.
Under strings and comments alike, with an error naming the file. A scanner cannot be
told it is inside a JSX element, and the apostrophe in <p>don't stop</p> would open a
string literal running to the next quote in the file. Every token boundary after that one is
wrong, and a comment is a gap between two token boundaries. See Scopes.
The refused file is named on stderr and the run exits 2: it was not looked at, which is not the same answer as finding nothing in it. Every other file is still scanned and reported.
typescript/unstable/ast, which upstream marks unstable. A rename there is caught and
reported rather than silently matching nothing, but it would still need a release here to
fix.
A scanner has no parser context, so where / divides and where it opens a pattern is
decided by the token walk rather than known. The decision is right on everything the suite
covers, which is the TypeScript compiler's own nine megabytes of source plus a set of cases
written against each ambiguous position. One position is genuinely undecidable from tokens
alone and is read the other way: a labelled block, label: { … }, is taken for an object
literal, so a regular expression opening the statement after its } is read as a division.
Nothing else known diverges.
None of this applies on TypeScript 5 or 6, which are read through the syntax tree.
Needs an exclude glob or a suppression comment under raw, which reads a document as
plain text. The markdown scope skips fences, so this is a reason to prefer it for a docs
tree. (Suppression markers inside fences are ignored under either scope, which is a
separate mechanism.)
Needs the same.
A rule's include is matched against the directory tree, so a file git would not carry is
still scanned: build output, a scratch note left by a working session, a file named in
.gitignore or in .git/info/exclude. A broad pattern such as **/*.md reaches all of
them. node_modules and .git are skipped without being asked for, and so is every other
dotted directory, which is the same rule that makes .github/ need a pattern naming it.
Name the file or the directory in the rule's exclude, or in the top-level ignore to keep
it out of every rule.
A scratch directory that already carries a dot is therefore covered, and adding it to
ignore changes nothing. That is worth knowing in the other direction as well: renaming
.notes/ to notes/ starts scanning it, with no config change to point at afterwards.
This is a surprise the first time and is the intended behaviour, for two reasons. charcheck runs in directories that are not repositories at all, and a file that is about to be committed should already be clean, so checking it before it is tracked is the useful ordering rather than the wrong one. Reading git instead would also mean a rule silently checking fewer files than its globs name, which is the failure this whole page is written against.
--staged is the run that answers the other question. It reads the index, so what a commit
actually carries is checked by something that never opens an untracked file.
Under --staged, that is.
If charcheck reports success on a file you know contains a banned character, the cause is usually one of these two rather than anything above.
A warning on stderr says so:
charcheck: rule "no-em-dash-in-markup" matched no files: site/**/*.vue. Check the
globs; a dotted directory is only entered when a pattern names it.
That last clause is the usual cause. site/**/*.vue does not reach
site/.vitepress/theme/Card.vue, because no pattern names .vitepress, and nobody expects
docs/** to walk into .github either. Name the directory: site/.vitepress/**/*.vue.
The warning does not fire under --staged for a rule that simply had nothing staged. It
describes the globs, not the commit.
A rule with scope: 'strings' cannot see a comment, comments cannot see anything else,
markdown skips fenced code and inline spans, and neither markup nor html reads
<style>. The scope is the first thing
to check once you know the file was opened. See Scopes.
A file the scope cannot read at all is the sharper version of this: it is extracted as empty,
so it reports exactly as a clean one. Where a rule's scope can read none of the files it
matched, a warning on stderr says so. Where it can read only some of them, nothing is said
during the run, and charcheck --report-issue prints the count per rule.