← Back to Blog

6 npm Packages Every TypeScript Project Should Run in CI

By Nark Team

Every TypeScript project needs these six tools running in CI: the TypeScript compiler for type checking, ESLint for linting, Prettier for formatting, Vitest for testing, a security scanner, and Nark for dependency error handling. Most projects run the first four. The last two are where production incidents and security vulnerabilities slip through code review.

Quick Answer: TypeScript + ESLint + Prettier + Vitest covers types, style, and logic. Add npm audit (or Semgrep) for security and nark for dependency error handling. Here is a ready-to-copy GitHub Actions workflow that runs all six.


1. TypeScript (typescript)

npx tsc --noEmit

TypeScript's compiler is the foundation. It catches type mismatches, missing properties, structural incompatibilities, and null dereferences (with strictNullChecks).

CI step:

- name: TypeScript type check
  run: npx tsc --noEmit

Key tsconfig settings for maximum type safety:

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true
  }
}

strict enables strictNullChecks, noImplicitAny, and other important rules. noUncheckedIndexedAccess catches array[i] accesses that may be undefined. exactOptionalPropertyTypes prevents accidentally assigning undefined to optional properties.


2. ESLint (eslint + @typescript-eslint)

npx eslint src --ext .ts,.tsx --max-warnings 0

ESLint enforces code quality rules beyond what TypeScript checks. The @typescript-eslint plugin adds TypeScript-specific rules that are particularly valuable.

Install:

npm install --save-dev eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser

Minimal .eslintrc.json for TypeScript:

{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": "./tsconfig.json"
  },
  "rules": {
    "@typescript-eslint/no-floating-promises": "error",
    "@typescript-eslint/no-explicit-any": "warn",
    "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
  }
}

@typescript-eslint/no-floating-promises is critical — it catches async function calls with no await and no .catch(). Without this rule, unhandled promise rejections can crash Node.js processes silently.

CI step:

- name: ESLint
  run: npx eslint src --ext .ts,.tsx --max-warnings 0

3. Prettier (prettier)

npx prettier --check src

Prettier enforces consistent code formatting. Running it in CI (check mode) prevents formatting drift and eliminates style-based code review comments.

Install:

npm install --save-dev prettier

Minimal .prettierrc:

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 100
}

CI step:

- name: Prettier
  run: npx prettier --check src

4. Vitest (vitest)

npx vitest run

Vitest runs your unit and integration tests. It's faster than Jest for TypeScript projects (native ESM support, TypeScript without Babel), and shares configuration with Vite if you use it.

Install:

npm install --save-dev vitest @vitest/coverage-v8

vitest.config.ts:

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov'],
      thresholds: {
        lines: 80,
      },
    },
  },
});

CI step:

- name: Vitest
  run: npx vitest run --coverage

If your team prefers Jest, the CI step is the same structure: npx jest --coverage.


5. Security Scanner (npm audit or semgrep)

npm audit --audit-level=high

npm audit checks your dependency tree against the npm security advisory database. It finds packages with known CVEs before they reach production.

CI step:

- name: Security audit
  run: npm audit --audit-level=high

For deeper static analysis — SQL injection, hardcoded secrets, insecure crypto — add Semgrep:

- name: Semgrep — security patterns
  uses: returntocorp/semgrep-action@v1
  with:
    config: p/typescript

p/typescript is Semgrep's curated TypeScript security ruleset. It catches eval(), prototype pollution, SQL injection, and other patterns.

npm audit and Semgrep are complementary: audit finds vulnerable packages, Semgrep finds vulnerable code patterns.


6. Nark — Dependency Error Handling

npx nark --tsconfig tsconfig.json

Nark is the quality gate that the first five tools miss. It checks whether your code correctly handles the runtime errors that your npm dependencies can throw.

What Nark catches that the other five tools miss:

// Passes tsc ✅, ESLint ✅, Prettier ✅, Vitest ✅ (no test for this), npm audit ✅
// nark: FAILS — axios network error case not handled
async function getUser(id: string): Promise<User> {
  try {
    const response = await axios.get<User>(`/api/users/${id}`);
    return response.data;
  } catch (error) {
    // error.response is undefined on network errors
    // accessing it crashes with TypeError
    throw new Error(`API error: ${error.response.status}`);
  }
}

Nark knows that axios.get() throws AxiosError with three distinct shapes, that error.response can be undefined on network failures, and that the above code will crash at error.response.status when there's a timeout or DNS failure.

Install: No install required. npx nark runs directly.

CI step:

- name: nark — dependency error handling
  run: npx nark --tsconfig tsconfig.json

Packages covered: 160+ npm packages, including axios, @prisma/client, stripe, ioredis, pg, openai, @anthropic-ai/sdk, nodemailer, jsonwebtoken, and more.


The Complete Workflow

# .github/workflows/ci.yml
name: CI

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  ci:
    name: CI
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: TypeScript type check
        run: npx tsc --noEmit

      - name: ESLint
        run: npx eslint src --ext .ts,.tsx --max-warnings 0

      - name: Prettier
        run: npx prettier --check src

      - name: Vitest
        run: npx vitest run --coverage

      - name: npm audit
        run: npm audit --audit-level=high

      - name: nark — dependency error handling
        run: npx nark --tsconfig tsconfig.json

What Each Tool Catches (No Overlap)

CategoryToolWhat It Catches
Type safetyTypeScriptWrong types, missing properties, null dereferences
Code qualityESLintLanguage anti-patterns, unhandled promises, unused vars
FormattingPrettierInconsistent whitespace, quotes, trailing commas
Logic regressionsVitestTest failures, coverage drops
Vulnerable packagesnpm auditCVEs in dependencies
Runtime error handlingNarkIncorrect/missing npm package error handling

These six tools cover six distinct categories with zero overlap. Missing any one leaves a gap that the other five cannot fill.


Rollout Strategy for Existing Projects

If you're adding these to an existing project that has accumulated issues, use a phased rollout to avoid a wall of failing CI:

Week 1: TypeScript (fix all type errors), Prettier (format everything)

Week 2: ESLint (add rules incrementally, fix violations)

Week 3: Vitest (write tests for critical paths, set initial coverage threshold)

Week 4: npm audit (resolve or document accepted vulnerabilities)

Week 5: Nark with --min-severity error (address errors, note warnings for later)

Week 6+: Nark without min-severity filter, fix all warnings

Each week adds one gate. The team adapts before the next gate goes live.


Frequently Asked Questions

Should I run all six on every commit?

PR-based CI (on pull_request) is the right default. Pre-push hooks are optional for fast tools (TypeScript, ESLint, Prettier). Vitest and Nark are better suited to CI than pre-commit hooks to avoid slowing down local development.

Are there lighter alternatives to Vitest?

Vitest is already fast. If your test suite is slow, the issue is usually database setup/teardown in integration tests, not Vitest itself. Consider --reporter=verbose to identify slow tests.

How do I handle Nark violations on packages we don't fully control?

Use exclusion patterns in .nark/config.yaml for vendor code, generated files, or test mocks. The Nark scan should cover your application code, not third-party code.

Can I add these incrementally without breaking CI?

Yes. Add continue-on-error: true to any step while you fix violations, then remove it once clean. Don't use --max-warnings 0 on ESLint or --min-severity error on Nark until you're ready to fail on everything.


Try It Now

Run Nark locally to see what it finds:

npx nark --tsconfig ./tsconfig.json

Then add to CI:

- name: nark — dependency error handling
  run: npx nark --tsconfig tsconfig.json

Nark is the sixth quality gate that completes your TypeScript CI setup.