Masalah Hard Reload pada Arsitektur Inertia.js
Inertia.js bekerja dengan memotong navigasi browser tradisional dan menggantinya dengan permintaan XHR (X-Inertia). Saat pengembang secara tidak sengaja menggunakan tag native HTML <a href="..."> atau <form action="...">, browser akan melakukan hard reload secara penuh (full page reload). Hal ini merusak state aplikasi di memory, memicu unmount pada persistent layout, mengunduh ulang aset CSS/JS, dan merusak performa Single Page Application (SPA).
Solusi deterministik untuk mencegah regresi ini adalah dengan menambahkan aturan linting statis berbasis Abstract Syntax Tree (AST) melalui ESLint, yang dijalankan otomatis pada siklus git pre-commit dan CI pipeline.
Konfigurasi ESLint: no-restricted-syntax
ESLint menyediakan rule bawaan no-restricted-syntax yang memanfaatkan selector AST (esquery). Aturan ini dapat memblokir tag native JSX <a> dan <form>, sambil tetap mengecualikan tautan keluar atau file download.
Implementasi pada eslint.config.js (Flat Config)
import js from "@eslint/js";
export default [
js.configs.recommended,
{
files: ["resources/js/**/*.{jsx,tsx}"],
rules: {
"no-restricted-syntax": [
"error",
{
selector: "JSXOpeningElement[name.name='a']:not(:has(JSXAttribute[name.name='target'][value.value='_blank'])):not(:has(JSXAttribute[name.name='download'])):not(:has(JSXAttribute[name.name='rel'][value.value='external']))",
message: "Gunakan komponen <Link> dari @inertiajs alih-alih <a> native untuk navigasi internal SPA."
},
{
selector: "JSXOpeningElement[name.name='form'][attributes.length>0]:has(JSXAttribute[name.name='action'])",
message: "Hindari <form action='...'>. Gunakan hook useForm() atau router.visit() untuk mutasi data Inertia."
}
]
}
}
];
Penjelasan AST Selector
JSXOpeningElement[name.name='a']: Menargetkan elemen JSX pembuka bernamaa.:not(:has(...)): Selector negasi. Jika elemen memiliki atributtarget="_blank",download, ataurel="external", rule tidak akan menandai elemen tersebut sebagai error.JSXAttribute[name.name='action']: Memblokir form native yang mengandalkan submit default HTML yang memicu perpindahan halaman non-AJAX.
Catatan untuk Pengguna Vue: Jika menggunakan Vue SFC, gunakan rulevue/no-restricted-syntaxdari packageeslint-plugin-vuedengan selector setara berbasis template AST:VElement[name='a']:not([target='_blank']).
Menangani Kasus Edge Case: Eksternal URL & File Download
Validasi AST statis rentan menimbulkan false positive bila tidak memperhitungkan rute non-SPA. Tiga skenario di mana tag native tetap valid:
- External Links: Menuju domain pihak ketiga. Selalu wajibkan atribut
target="_blank"ataurel="external"agar linter mengizinkan penggunaan tag<a>. - File Download / Direct Assets: Unduhan PDF atau export spreadsheet dari controller Laravel (bukan respons Inertia). Gunakan atribut HTML5
downloadpada tag<a>. - Anchor / In-page Hash: Navigasi internal ID seperti
href="#section-faq". Jika diperlukan, tambahkan pengecualian AST untuk atributhrefyang diawali karakter#.
// Contoh penggunaan yang lolos linting
<!-- Navigasi Internal (Benar) -->
<Link href="/dashboard">Dashboard</Link>
<!-- Eksternal URL (Lolos selector: memiliki target="_blank") -->
<a href="https://docs.example.com" target="_blank" rel="noopener noreferrer">Docs</a>
<!-- File Download (Lolos selector: memiliki atribut download) -->
<a href="/reports/export-csv" download>Unduh Laporan</a>
Otomasi Pre-Commit: Husky & lint-staged
Linting harus dijalankan pada fase sedini mungkin. Menggunakan Husky bersama lint-staged memastikan pengembang tidak dapat melakukan commit kode yang melanggar standar.
1. Instalasi Dependency
npm install --save-dev husky lint-staged
npx husky init
2. Konfigurasi package.json
{
"lint-staged": {
"resources/js/**/*.{js,jsx,ts,tsx,vue}": [
"eslint --fix --max-warnings=0"
]
}
}
3. Konfigurasi Hook .husky/pre-commit
npx lint-staged
Integrasi pada GitHub Actions CI Pipeline
Pre-commit lokal dapat dilewati pengembang menggunakan flag --no-verify. Continuous Integration (CI) berfungsi sebagai enforcement mutlak sebelum kode di-merge ke branch utama.
name: Frontend Code Quality
on:
pull_request:
branches: [main, master]
push:
branches: [main, master]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: npx eslint resources/js/ --max-warnings=0
Flag --max-warnings=0 memastikan eksekusi CI langsung gagal (exit code non-zero) jika ditemukan satupun peringatan, memaksa tim mematuhi navigasi berbasis SPA secara konsisten.
Trade-offs dan Strategi Debugging
- Dynamic Hrefs: ESLint statis tidak dapat mengevaluasi apakah variabel URL
<a href={dynamicUrl}>mengarah ke rute internal atau eksternal pada runtime. Tetapkan konvensi tim: jika link dinamis berpotensi eksternal, bungkus dalam utilitas custom wrapper link. - Escape Hatch Terkontrol: Jika terdapat kebutuhan khusus menggunakan tag
<a>internal (misalnya me-reset total memory leak aplikasi), gunakan comment ignore spesifik:// eslint-disable-next-line no-restricted-syntaxdan sertakan alasan di atas komentar tersebut.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!