Skip to content

Languages and extensions

Language idExtensions
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-if chain 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. A render() function is scored, because it is ordinary script code, so moving logic out of a template and into render() 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 as typescript, 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.