Languages and extensions
| Language id | Extensions |
|---|---|
typescript | .ts .mts .cts .tsx .jsx .js .mjs .cjs |
vue | .vue |
python | .py .pyw |
java | .java |
php | .php .phtml |
go | .go |
Never scanned, whatever the extension: .d.ts, .d.mts, .d.cts, .min.js, .min.mjs, .min.cjs.
The language id is what --lang, --over LANG=N and a [section] in the config take, so
typescript covers every JavaScript and TypeScript file however it is parsed. An id that is not
one of these is an error, not a silent no-op.
.js is parsed with the TypeScript grammar, which accepts a superset of JavaScript. Flow
annotations are the one thing this misparses.
A declaration file and a minified one are both skipped outright, for opposite reasons: the first
holds only signatures so every unit scores zero, and the second is generated output whose whole
body rolls up into one unit nobody will refactor. The skipped names are whole suffixes, so
app.mini.js, jasmine.js and a file simply called min.js are all still scanned.
A .vue file scores its <script> and <script setup> blocks, with lang="ts" choosing the
TypeScript grammar and anything else (including no lang) choosing the JSX-capable one, which
is the grammar .js already uses. Each block is parsed on its own, and their file-level code is
added together into the one <toplevel> a component reports. Reported lines are lines in the
.vue file, so editing a template moves a finding without changing its baseline key.
| Construct | Scored |
|---|---|
<script> and <script setup>, any lang |
yes, as vue |
a render() function written in a script block |
yes, as vue |
JSX inside <script lang="tsx"> |
yes, as vue |
<template>: v-if, v-for, {{ }}, @click="a && b()" |
no |
<style>, and custom blocks such as <docs> or <i18n> |
no |
<script src="./logic.ts"> |
no, but logic.ts is scanned on its own, as typescript |
Two consequences are worth knowing:
- The template is deliberately not scored. A
v-ifchain is real branching, but the specification was not written against templates and counting them would make a component incomparable with the same logic written in TypeScript. Arender()function is scored, because it is ordinary script code, so moving logic out of a template and intorender()makes it visible, and a component that never had a template was never hidden. - A
src=block belongs to the file it points at. That file is scored astypescript, so a[vue]threshold does not reach it.
A Python method is keyed by its class, OrderService::process. A property’s getter, setter and
deleter redefine one name on purpose, so the accessor a decorator declares joins the key:
Cart::total, Cart::total.setter and Cart::total.deleter are baselined apart. A def whose
body is only ..., such as an @overload stub or a protocol method, declares a signature and is
not a unit, and .pyi stubs are not scanned at all.
A .py file is skipped as generated by the same header rule as Java below: the comments before
its first line of code say both “generated” and “do not edit”. That covers protobuf, gRPC and
Thrift output. A Django migration says only “Generated by Django”, because it is meant to be
edited, so it stays scored.
Java overloads freely, so a Java method or constructor is keyed by its parameter types as well as
its name: OrderService::process(Order, User). Each type is its simple name, without type
arguments or annotations, so two overloads are baselined apart and switching between an import
and a qualified name never re-keys one.
Java has no single generated-code convention, so a .java file is read first and then skipped
when the comments before its first line of code say both “generated” and “do not edit”. That
covers protobuf, Thrift, Avro and JavaCC output, on disk and through --stdin alike. Most
generated sources sit in a gitignored target/ or build/ directory and are never scanned in
the first place.
Go marks generated code in the file rather than its name, so a .go file is read first and then
turned away if a // Code generated … DO NOT EDIT. line comes before its package clause, which
is the convention the Go toolchain itself follows. Such a file is neither scored nor counted, on
disk and through --stdin alike. vendor/ and testdata/ get no special treatment: if a
repository commits them, exclude = ["vendor/**", "**/testdata/**"] keeps them out.
A Go method is keyed by its receiver type, so func (s *Stack[T]) Push() reports as
Stack::Push and keeps its baseline entry when the receiver switches between pointer and value.