Akar Masalah: False Positive no-undef pada Nuxt 3

Nuxt 3 mengandalkan sistem auto-imports untuk composables bawaan (seperti ref, computed, useFetch, navigateTo), komponen, dan utility functions. Fitur ini meniadakan kebutuhan deklarasi import eksplisit di setiap file.

Masalah muncul ketika ESLint standar dijalankan pada repositori Nuxt 3. Parser ESLint menganalisis Abstract Syntax Tree (AST) secara statis tanpa konteks kompilasi Nuxt. Akibatnya, ESLint memunculkan ratusan error palsu no-undef untuk setiap fungsi bawaan yang digunakan.

Pendekatan manual masa lalu, seperti mendaftarkan identifier satu per satu ke dalam blok globals di konfigurasi ESLint, rapuh dan sulit dikelola. Setiap modul baru atau fungsi kustom di direktori composables/ memerlukan pembaruan konfigurasi manual.

Solusi Native: @nuxt/eslint dan Flat Config

Modul resmi @nuxt/eslint mengintegrasikan sistem tipe dan metadata Nuxt langsung ke ESLint Flat Config (ESLint 9+). Modul ini bekerja dengan cara mengompilasi daftar identitas yang di-auto-import ke dalam konfigurasi flat perantara di .nuxt/eslint.config.mjs.

Install dependensi yang dibutuhkan:

pnpm add -D eslint @nuxt/eslint typescript

Aktifkan modul pada nuxt.config.ts:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/eslint'
  ],
  eslint: {
    config: {
      standalone: false // Mengintegrasikan langsung dengan file eslint.config.mjs proyek
    }
  }
})

Kemudian buat atau perbarui eslint.config.mjs di root proyek menggunakan helper withNuxt yang diekspor otomatis dari direktori build Nuxt:

// eslint.config.mjs
import withNuxt from './.nuxt/eslint.config.mjs'

export default withNuxt(
  // Masukkan aturan kustom tambahan di sini
  {
    rules: {
      'no-console': ['warn', { allow: ['warn', 'error'] }]
    }
  }
)

Helper withNuxt() menyuntikkan rules, parser Vue/TypeScript, dan globals yang dihasilkan langsung oleh instance Nuxt. Hasilnya, no-undef hilang tanpa mengorbankan integritas pengecekan variabel yang benar-benar tidak terdefinisi.

Validasi Cepat Lokal: simple-git-hooks dan lint-staged

Menjalankan linting ke seluruh codebase pada setiap commit memperlambat feedback loop developer. Gunakan kombinasi simple-git-hooks (alternatif minimalis untuk Husky) dan lint-staged agar ESLint hanya memverifikasi file yang masuk ke staging area Git.

Pasang dependensi:

pnpm add -D simple-git-hooks lint-staged

Konfigurasikan hook dan task pada package.json:

{
  "scripts": {
    "postinstall": "simple-git-hooks",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix"
  },
  "simple-git-hooks": {
    "pre-commit": "pnpm exec lint-staged"
  },
  "lint-staged": {
    "*.{js,ts,vue,mjs}": [
      "eslint --fix"
    ]
  }
}

Jalankan inisialisasi hook sekali secara manual:

pnpm exec simple-git-hooks

Setiap kali perintah git commit dieksekusi, Git hook secara otomatis memeriksa file .vue, .ts, dan .js yang dimodifikasi. Jika ada pelanggaran format atau syntax error yang tidak bisa diperbaiki otomatis oleh ESLint, commit akan dibatalkan seketika.

Workflow CI: Mitigasi Type Generation Mismatch

Tantangan utama menjalankan @nuxt/eslint pada Continuous Integration (CI) adalah ketiadaan folder .nuxt/ pada fresh runner instance. Jika CI langsung menjalankan eslint ., build akan gagal dengan error Cannot find module './.nuxt/eslint.config.mjs'.

Solusi yang tidak efisien adalah menjalankan nuxt build sebelum linting. Langkah ini membuang waktu CI karena melakukan optimasi asset, bundling Vite/Webpack, dan kompilasi server engine Nitro yang tidak dibutuhkan oleh ESLint.

Solusi yang benar: jalankan nuxi prepare. Perintah ini mengeksekusi siklus type-generation dan schema-generation Nuxt untuk menghasilkan folder .nuxt/ tanpa melakukan build penuh.

Berikut konfigurasi pipeline GitHub Actions (.github/workflows/lint.yml):

name: Code Quality

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

jobs:
  eslint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v3
        with:
          version: 9

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Generate Nuxt Types and ESLint Context
        run: pnpm exec nuxi prepare

      - name: Run ESLint
        run: pnpm exec eslint .

Troubleshooting dan Performa

Peringatan Cache CI: Jangan menambahkan direktori .nuxt/ ke dalam action cache CI. Artifact yang dihasilkan oleh nuxi prepare harus selalu sinkron dengan versi dependensi dan source code terkini pada commit tersebut guna menghindari konflik type definitions.

1. File Tidak Terdeteksi di Editor Lokal

Jika VS Code atau IDE menampilkan error modul hilang pada eslint.config.mjs, pastikan development server pernah dijalankan minimal sekali (pnpm dev) atau jalankan pnpm exec nuxi prepare agar direktori .nuxt tercipta di lingkungan lokal.

2. Menangani False Positive pada Script Setup Macros

Makro bawaan Vue seperti defineProps, defineEmits, dan defineExpose ditangani otomatis oleh sub-konfigurasi Vue yang diinjeksi oleh withNuxt(). Jika makro tersebut tetap ditandai sebagai unresolved, pastikan file memiliki ekstensi .vue valid dan tidak ada konfigurasi parser custom yang menimpa vue-eslint-parser di eslint.config.mjs.