DevOps Frontend Backend 10 min read

10x Faster CI: Migrating to Vitest, Oxlint, Rolldown & tsgo

Hoang Dang Tan Phat (Kane)

Hoang Dang Tan Phat (Kane)

Jan 19, 2026

Our CI pipeline was taking over 2 minutes for basic checks. After migrating to modern Rust-based tools, we cut that to under 15 seconds. Here’s the real data and how to do it yourself.

The Problem

Traditional JavaScript tooling is slow:

  • ESLint: Written in JavaScript, single-threaded, plugins add overhead
  • Jest: Heavy startup, complex configuration, slow transforms
  • webpack: Complex bundling, slow rebuilds, memory-intensive
  • tsc: Written in JavaScript, no parallelization

For a monorepo with 13+ packages, these tools were killing our developer experience.

Real CI Benchmarks

Data from optimex-pmm GitHub Actions:

Before Migration (Jan 16, 2026)

StepDuration
Lint (ESLint)100s
Typecheck (tsc)19s
Test (Jest)46s
Build (webpack)~30s
Total~195s

After Migration (Jan 18, 2026)

StepDurationImprovement
Lint (oxlint)1s100x faster
Typecheck (tsgo)2s9x faster
Test (Vitest)12s4x faster
Build (Rolldown)1s30x faster
Total~16s12x faster

1. Replacing ESLint with Oxlint

Oxlint is a Rust-based linter that’s 50-100x faster than ESLint.

Installation

# Remove ESLint
yarn remove eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser

# Add oxlint
yarn add -D oxlint

Configuration

Create oxlint.json:

{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["import", "typescript"],
  "rules": {
    "import/no-duplicates": "error",
    "@typescript-eslint/no-explicit-any": "error",
    "no-unused-vars": [
      "error",
      {
        "vars": "all",
        "args": "after-used",
        "argsIgnorePattern": "^_",
        "ignoreRestSiblings": true,
        "caughtErrors": "all",
        "caughtErrorsIgnorePattern": "^_"
      }
    ]
  },
  "ignorePatterns": ["**/dist/**", "**/node_modules/**", "**/*.spec.ts", "**/*.test.ts", "**/*.js"]
}

Scripts

{
  "scripts": {
    "lint": "oxlint --config oxlint.json apps libs --fix",
    "lint:ci": "oxlint --config oxlint.json apps libs --deny-warnings"
  }
}

Pre-commit Hook

Update lint-staged in package.json:

{
  "lint-staged": {
    "*.{js,ts,tsx}": ["oxlint --fix", "prettier --write"]
  }
}

Result: Lint time dropped from 68s to <1s.

2. Replacing Jest with Vitest

Vitest is a Vite-native test runner with first-class TypeScript support.

Installation

# Remove Jest
yarn remove jest @types/jest ts-jest

# Add Vitest
yarn add -D vitest @vitest/coverage-v8

Configuration

Create vitest.config.ts:

import { resolve } from 'path'
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    include: ['libs/**/*.{spec,test}.ts', 'apps/**/*.{spec,test}.ts'],
    exclude: ['node_modules', 'dist'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov', 'html'],
      reportsDirectory: './coverage',
      include: ['libs/**/src/**/*.ts', 'apps/**/src/**/*.ts'],
      exclude: ['**/*.spec.ts', '**/*.test.ts', '**/index.ts'],
    },
    passWithNoTests: true,
    testTimeout: 30000,
    hookTimeout: 30000,
    setupFiles: ['reflect-metadata'], // For NestJS decorators
  },
  resolve: {
    alias: {
      // Add your monorepo aliases
      '@myapp/shared': resolve(__dirname, 'libs/shared/src/index.ts'),
      '@myapp/database': resolve(__dirname, 'libs/database/src/index.ts'),
    },
  },
})

Scripts

{
  "scripts": {
    "test": "vitest run",
    "test:ci": "vitest run --coverage",
    "test:watch": "vitest"
  }
}

Key Benefits

  • Native ESM: No babel/ts-jest transforms needed
  • Watch mode: Instant re-runs on file changes
  • v8 coverage: Built-in, fast coverage reporting
  • Jest compatible: Most Jest APIs work out of the box

Result: Test time dropped from 46s to 12s.

3. Replacing webpack with Rolldown

Rolldown is a Rust-based bundler designed as a drop-in replacement for Rollup, with webpack-level features and blazing fast performance.

Why Rolldown?

  • Written in Rust: Native performance, parallel processing
  • Rollup-compatible: Same plugin ecosystem, familiar API
  • Built for monorepos: Efficient handling of multiple packages
  • Instant rebuilds: Sub-second builds even for large codebases

Installation

# Remove webpack
yarn remove webpack webpack-cli webpack-node-externals ts-loader

# Add rolldown
yarn add -D rolldown

Configuration

Create rolldown.config.mjs:

import { defineConfig } from 'rolldown'

export default defineConfig({
  input: 'src/main.ts',
  output: {
    dir: '../../dist/apps/api-server',
    format: 'cjs',
    sourcemap: true,
    entryFileNames: '[name].js',
  },
  // External: npm packages, scoped packages, node built-ins
  external: [/^@/, /^[a-z]/, /node:/],
  tsconfig: '../../tsconfig.json',
})

Key Configuration Points

  1. External dependencies: Use regex patterns to externalize all npm packages
  2. TypeScript support: Native TS support via tsconfig option
  3. Output format: CommonJS for Node.js backends, ESM for libraries

Scripts

{
  "scripts": {
    "build": "rolldown -c"
  }
}

Turbo Integration

For monorepos, configure Turbo to orchestrate builds:

{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["../../dist/**"],
      "outputLogs": "errors-only"
    }
  }
}

webpack vs Rolldown Comparison

FeaturewebpackRolldown
LanguageJavaScriptRust
Cold start5-10s<1s
Incremental2-5s<100ms
Memory usageHighLow
Config complexityHighSimple

Result: Build time dropped from ~30s to ~1s.

4. Faster Typechecking with tsgo

tsgo is Microsoft’s official native port of TypeScript, written in Go. It’s designed to be a drop-in replacement for tsc with significantly faster performance.

Installation

# Install the preview build
npm install @typescript/native-preview

# Use tsgo instead of tsc
npx tsgo --noEmit

VS Code Integration

A preview VS Code extension is available. Enable it in settings:

{
  "typescript.experimental.useTsgo": true
}

Current Status

tsgo is still in development. Feature status:

  • Complete: Parsing, type checking, JSX, build mode, incremental builds
  • In Progress: JSDoc, declaration emit
  • Prototype: Watch mode
  • Not Ready: Language service API

Turbo Configuration

For monorepos, configure Turbo for caching:

{
  "tasks": {
    "typecheck": {
      "dependsOn": ["^build"],
      "outputs": []
    }
  }
}

Scripts

{
  "scripts": {
    "typecheck": "tsgo --noEmit"
  }
}

Result: Typecheck time dropped from 19s to 9s with tsc+cache, and to 2s with tsgo.

5. GitHub Actions CI

Complete workflow:

name: CI

on:
  push:
    branches: [main, staging, develop]
  pull_request:
    branches: [main, staging, develop]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: Cache node_modules
        uses: actions/cache@v4
        with:
          path: |
            node_modules
            **/node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}

      - run: yarn install --frozen-lockfile

      - name: Lint
        run: yarn lint:ci

      - name: Typecheck
        run: yarn typecheck

      - name: Test
        run: yarn test:ci

      - name: Build
        run: yarn build

Performance Summary

ToolBeforeAfterSpeedup
LintingESLint (100s)oxlint (1s)100x
TestingJest (46s)Vitest (12s)4x
Buildingwebpack (30s)Rolldown (1s)30x
Typechecktsc (19s)tsc+cache (2s)9x
Total CI~195s~16s12x

Migration Tips

  1. Migrate incrementally: Start with linting (biggest win, lowest risk)
  2. Keep ESLint rules parity: oxlint supports most common rules
  3. Test locally first: Run both old and new tools, compare output
  4. Update pre-commit hooks: Lint-staged with oxlint is blazing fast
  5. Use Turbo: Orchestration and caching multiply the benefits
  6. Rolldown for backends: Perfect for Node.js APIs with external dependencies

Migration Order

Recommended sequence for minimal risk:

  1. Oxlint - Drop-in replacement, immediate 100x speedup
  2. Vitest - Most Jest tests work unchanged
  3. Rolldown - Simple config for Node.js backends
  4. tsgo - Wait for stable release or use Turbo caching

Conclusion

Rust-based tools are production-ready and deliver massive performance improvements. The migration took about a day total and reduced our CI time by 12x. Developer experience improved dramatically with instant lint feedback, fast test runs, and sub-second builds.

The JavaScript ecosystem is being rewritten in Rust, and now is the time to adopt these tools.

Resources

vitest oxlint rolldown typescript ci-cd performance
Hoang Dang Tan Phat (Kane)

Hoang Dang Tan Phat (Kane)

Full-stack developer with 8+ years experience. Building scalable systems with Go, TypeScript, and React.