Wiring a JS/TS Project into Sonarqube
Sonarqube's Java docs are deep. JS/TS, less so — here's a working setup.
~/posts/sonarqube-javascript $ cat post.md
Why
Sonarqube’s documentation skews heavily Java. If your team is Java-first, you’ll eventually feel it — the day they ask you to put a JS project on Sonarqube to track Code Smell, the docs and the blog posts you can find aren’t going to be quite enough. This post assumes you already know what Sonarqube is and what it’s for, and walks through the bits you’ll need on top of the basic guides.
Background
- For languages other than Java, Sonarqube doesn’t run tests for you. You run them locally and upload the coverage file alongside the source.
- The base Docker image, while primarily Java-focused, still bundles configs for several languages. The non-Java support is thinner than Java’s, but it’s something.
- Sonarqube only accepts one coverage format —
lcov.info. Not the most common one. More on generating it below. - Uploading the build doesn’t need a password, locally or in CI. All you need is the Sonarqube URL. That’s also a fine reason to keep it on the internal network.
Walkthrough
Once the server’s up, configure the project. The JS setup is slightly
fiddly — using a small wrapper package saves time. Honestly, the
“third-party” packages I found mostly just wrap sonar-scanner, same
as what you’d write yourself. The Java console app pulls things like
the version, declared dependencies, and test results out of maven;
on the JS side those need to be wired up explicitly. Doing it by hand
means maintaining the version in two places, which is silly.
You might think: “I’ll just read the version out of package.json.”
That’s the same work the wrapper does, so save yourself the
reimplementation.
yarn add --dev sonarqube-scanner
Usage:
import * as Sonar from "sonarqube-scanner";
Sonar({
options: {
// ...
},
serverUrl: "https://sonarqube.dev.something.com/",
}, () => done());
What goes into options:
package.json already has things like title, description, and
version, but a few fields need overriding because they don’t match
Java conventions. The project name in particular: JS packages, under
npm’s influence, often go with @org/project, whose special
characters Sonarqube won’t take. Use org-project:
options: {
...other,
"sonar.projectKey": "org-project",
}
Sources and coverage next. Sonarqube doesn’t tell tests apart from
source files and doesn’t run tests for you. You’re uploading the src
tree plus coverage/lcov.info. The lcov file is easy to produce —
whether you’re on nyc, jest, or istanbul, the lcov reporter
exists, no extra deps needed.
options: {
...other,
"sonar.inclusions": "src/**,coverage/lcov.info",
"sonar.javascript.lcov.reportPaths": "coverage/lcov.info",
"sonar.typescript.lcov.reportPaths": "coverage/lcov.info",
}
In theory inclusions is sufficient, but declaring exclusions
alongside it makes the config easier to read (the same reason
tsconfig.json keeps both):
options: {
...other,
"sonar.exclusions": "node_modules/**,dist/**,.cache/**,infrastructure/**",
}
That’s enough to run it.