Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

code-scent

code-scent is a Node CLI dev-tool for finding code smells in JavaScript and TypeScript. It feels like a test runner: point it at source paths, get file-and-line diagnostics, use the exit code in CI, or keep it running in watch mode.

The smell names and design intent follow Martin Fowler’s Refactoring: Improving the Design of Existing Code, Second Edition. Descriptions in this project are original paraphrases; the book remains the authoritative explanation. Fowler also maintains the official Refactoring catalog.

Code smells are prompts for investigation, not proof that code is wrong. Static rules are necessarily heuristic, so thresholds are configurable and individual rules can be disabled.

Quick start

Requires Node 20 or newer.

npm install --save-dev code-scent

npx code-scent src

With npm 7+, the locally installed binary can also be run through an npm script:

{
  "scripts": {
    "lint:smells": "code-scent src --fail-on error"
  }
}
npm run lint:smells

While developing this repository, the same command can be run directly:

node ./bin/code-scent.js src

Common forms:

code-scent                         # checks ./src
code-scent src packages/core      # checks multiple source trees
code-scent src --watch             # reruns after edits
code-scent . --format json         # machine-readable diagnostics
code-scent src --fail-on error     # warnings do not fail the process
code-scent src --rule loops=off    # one-run rule override
code-scent --list-smells           # all 24 catalog entries

Supported extensions are .js, .cjs, .mjs, .jsx, .ts, .cts, .mts, and .tsx. Common generated and dependency directories are skipped.

Configuration

The CLI automatically loads the first configuration file it finds in the working directory:

  • code-scent.config.js
  • code-scent.config.mjs
  • code-scent.config.cjs
  • code-scent.config.json

For example:

import { defineConfig } from 'code-scent/config';

export default defineConfig({
  failOn: 'warning', // warning | error | never
  format: 'pretty',  // pretty | json
  exclude: ['node_modules', 'dist', 'coverage', '.git', 'generated'],
  rules: {
    'long-function': { maxLines: 60, maxStatements: 35 },
    'long-parameter-list': { maxParameters: 5 },
    'mysterious-name': { enabled: false }
  }
});

See code-scent.config.example.js and src/config.js for every default threshold.

Detection coverage

The complete second-edition catalog is represented. The CLI separates smells that have useful static evidence from smells that need history or human design judgment.

Smell Detection Evidence used
Mysterious Name Heuristic Repeated one-character or low-information declarations
Duplicated Code Heuristic Exact normalized function bodies above size thresholds
Long Function Heuristic Lines and statement count
Long Parameter List Heuristic Declared parameter count
Global Data Heuristic Module-scope let and var declarations
Mutable Data Heuristic Assignment/update density inside a function
Divergent Change Manual review Requires knowledge of responsibilities and reasons to change
Shotgun Surgery Change history Requires observing one change spread across modules
Feature Envy Heuristic Concentrated access through another object
Data Clumps Heuristic Repeated parameter groups across functions
Primitive Obsession Heuristic Large groups of primitive TypeScript parameters
Repeated Switches Heuristic Repeated case-label shapes across the project
Loops Heuristic Long imperative loops that may hide a transformation
Lazy Element Manual review Usage and design intent are needed
Speculative Generality Manual review Current and anticipated use must be understood
Temporary Field Heuristic Empty-by-default fields used by only one method
Message Chains Heuristic Member-access chain depth
Middle Man Heuristic Ratio of methods that only delegate
Insider Trading Heuristic Repeated access to private-looking foreign members
Large Class Heuristic Class lines and method count
Alternative Classes with Different Interfaces Manual review Semantic roles cannot be reliably inferred from syntax
Data Class Heuristic Fields and accessors without detected behavior
Refused Bequest Heuristic Subclass methods that reject inherited operations
Comments Heuristic Comments that resemble disabled source code

The conservative gaps are intentional. For example, import fan-out alone does not prove Shotgun Surgery, and an unused abstraction alone does not prove Speculative Generality.

Exit codes

  • 0: no finding at or above the configured failure threshold
  • 1: findings crossed failOn or --max-warnings
  • 2: the CLI itself could not run, such as an invalid option or missing path

Syntax errors are reported as error findings so one bad file does not prevent other files from being analyzed.

Programmatic API

import { analyze, defaultConfig } from 'code-scent';

const result = await analyze(['src/index.ts'], {
  cwd: process.cwd(),
  config: defaultConfig
});

for (const finding of result.findings) {
  console.log(finding.ruleId, finding.file, finding.line, finding.message);
}

Development

npm install
npm test
npm run check

The tests use Node’s built-in test runner. The only runtime dependency is Babel’s parser.

Publishing

After signing in to npm and choosing a new version, publish from a clean working tree:

npm test
npm run check
npm version patch
npm publish

npm publish is configured for public access and runs the same test and check commands through prepublishOnly. Verify the exact release contents first with npm pack --dry-run.

This project is not affiliated with or endorsed by Martin Fowler or Pearson.

About

A Node CLI tool for finding code smells in JavaScript and TypeScript

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages