JavaScript

JavaScript Code Review with LGTM

AI code review for JavaScript: LGTM catches == vs === bugs, missing await on Promises, unhandled rejections, and event-listener cleanup leaks.

How LGTM reviews JavaScript PRs

Tree-sitter parses .js / .jsx / .mjs / .cjs files. JavaScript's dynamic nature means symbol resolution is best-effort: the agents see what's exported and imported, but full type info (which would require TS-style annotation) isn't available.

This is why TypeScript projects get richer reviews — more type info means fewer 'could be anything' false positives. JavaScript projects are still substantially reviewed; the agents just lean on naming conventions, JSDoc comments (when present), and runtime patterns more.

Per-PR: PageRank ranks the symbols related to the diff, agents see context, output structured findings. The same 6-agent pipeline applies — but the Bugs agent has JS-specific heuristics for the failure modes that plain JS produces.

Common JavaScript bugs LGTM catches

Equality bugs. `if (x == null)` is a common idiom (covers both null and undefined); `if (x == 0)` is almost always wrong (also matches '' and false). The agent distinguishes by context.

Missing await on Promise-returning calls. `function save() { db.write() }` — db.write() returns a Promise that nobody awaits. Same pattern as TypeScript; the agent catches it in both.

Unhandled rejections. .then() without .catch() at top level. process.on('unhandledRejection') as a safety net is fine; missing it on Node servers can crash the process silently in modern Node.

Prototype pollution. `Object.assign(target, JSON.parse(userInput))` where userInput could contain __proto__. Flagged by Security agent.

Event listener cleanup. `element.addEventListener('click', handler)` without a corresponding removeEventListener — memory leak in long-lived browser pages and React components without effect cleanup.

for-in on arrays. `for (let i in arr)` instead of `for (let i of arr)` or `arr.forEach` — iterates over inherited properties too. Subtle bug, easy to spot in review.

Tree-sitter coverage for JavaScript

Excellent grammar. ES6 modules, CommonJS, JSX, optional chaining, nullish coalescing, top-level await, dynamic import — all parse cleanly.

Mixed JS+TS monorepos work fine. Each file uses the appropriate tree-sitter grammar based on extension.

Build outputs (dist/, build/, .next/, .nuxt/) are auto-excluded by path heuristics. Source maps (.map) are also excluded — they're not source.

Module resolution follows package.json's exports/main/module fields where present. Workspaces (npm/yarn/pnpm) resolve sub-package imports correctly.

Setup notes for JavaScript projects

Install the LGTM GitHub App. Index time: 20-60 seconds for typical Node apps, longer for monorepos with many packages.

Node + React + Vue + Express + Next + Nuxt + Astro — all supported with the JS grammar. Framework-specific patterns (React hooks, Vue composables) are partly recognised by naming + import heuristics.

JSDoc comments are extracted. Repos that document with JSDoc get richer agent context, similar to TypeScript's type info but lower fidelity.

node_modules is excluded (universal). Build artifacts excluded by common heuristics.

Example bugs LGTM catches

Subtle equality bug
// ❌ Bug: matches 0, '', false, null, undefined
function isEmpty(value) {
    return value == false;
}

isEmpty(0);     // true   <-- probably not what you want
isEmpty('');    // true
isEmpty(false); // true
isEmpty(null);  // true

// LGTM finding:
// "Loose equality with false matches many falsy values. If
//  the intent is 'no value', use 'value == null' (matches null
//  + undefined only). For empty-string check, use 'value === ""'."
Event listener never removed
// ❌ Bug: handler accumulates on every render
function Component() {
  const [data, setData] = useState();
  useEffect(() => {
    window.addEventListener('resize', () => setData(window.innerWidth));
    // <-- no cleanup return!
  }); // <-- no dep array either
}

// LGTM finding:
// "useEffect adds listener every render with no cleanup.
//  Return () => window.removeEventListener(...) from the effect.
//  Empty dep array if registration should run once."

See LGTM reviewing JavaScript

Node + React + Vue + serverless · npm/yarn/pnpm workspaces

Go to the product page

JavaScript review FAQs

JavaScript vs TypeScript review quality difference?

TypeScript review is richer because the type info helps the agents narrow down 'is this actually a bug or is it fine?' For JS projects with good JSDoc, the gap narrows. For untyped JS with no comments, the agents lean more on naming + structure heuristics.

Node + Express + Fastify all supported?

Yes. The Security agent has light awareness of common patterns — SQL building from req.body, missing helmet, exposed routes without auth middleware. Not a substitute for an AppSec scan but catches common smells.

React Native + Electron + serverless?

All parse the same way. The Bugs agent's heuristics carry across — useEffect cleanup, async without await, etc. work everywhere JS runs.

Mixed JS+TS monorepo?

Fine. Each file picked up by the appropriate grammar. Cross-language imports (TS file imports a JS module) resolve via package.json + tsconfig paths.

Cost per JS review?

$0.04-$0.12 for a 300-line PR on GPT-4o via BYOK. JS code is typically less verbose than Java, more verbose than Python — costs land in the middle.

Related across LGTM

Other languages