Skip to content

Palet Kosong dari JSON yang Valid

Adityo Guni Waluyo

Kunci backgroundColor dan pembaca yang mencari background-color, dua-duanya legal tapi tidak pernah bertemu. Palet kosong tanpa satu pun error.

Ringkasan

Bug-nya sepele tapi siluman: capture nulis key JSON gaya camelCase, sedangkan consumer bacanya pakai kebab-case, jadi lookup-nya gagal total. Nilainya nggak error, cuma string kosong, makanya palette sama font tampil kosong padahal build sukses dan screenshot jelas berwarna. Solusinya satu arah: capture nulis kebab-case plus script validasi sebelum file ditulis.

Sore itu saya membuka index.md hasil generate design system untuk project KotaPortal. Semua computed style komponen dikumpulkan lewat MockForge, workshop capture yang menulis hasilnya ke JSON fragment per komponen. Bagian palette kosong. Font tercatat sebagai array kosong. Saya gulir sampai bawah, lalu buka log build: bersih. Semua langkah selesai tanpa satu pun error.

Padahal halaman yang di-capture jelas punya warna dan font. Screenshot-nya menampilkan header oranye dan body text dengan font yang berbeda dari heading.

Tebakan pertama saya: data capture-nya yang rusak. Atau halaman yang di-capture kebetulan tidak punya style sama sekali. Saya buka salah satu JSON fragment. Di dalamnya ada tiga key camelCase: backgroundColor, borderRadius, fontFamily, semuanya terisi nilai yang masuk akal. Tidak ada null, tidak ada string kosong. Tebakan pertama salah.

Yang salah justru ada di nama key-nya, dan prosesnya gagal dengan sangat diam.

Step yang menurunkan palette dari JSON membaca key seperti background-color secara persis. Script capture menulisnya sebagai backgroundColor. Producer dan consumer memakai nama key berbeda, dan tidak ada satu pihak pun yang memprotes.

Dari sisi capture, tidak ada yang aneh. getComputedStyle mengembalikan objek live read-only berisi resolved value semua CSS property [3]. Objek CSSStyleDeclaration menyediakan accessor bernama camelCase di samping bentuk dashed-nya [2]. Property yang mengandung tanda hubung memang tidak bisa diakses lewat notasi titik, jadi backgroundColor adalah cara paling natural untuk membacanya dari JavaScript [2]. Capture menulis apa yang paling mudah ia baca. Wajar dari sudutnya.

Dari sisi file, juga tidak ada yang aneh. JSON hanya mengenal objek sebagai pasangan name/value, dan RFC 8259 bahkan hanya menyebut bahwa name sebaiknya unik; JSON tidak punya konvensi penamaan key sama sekali [4]. File berisi key camelCase tetap JSON valid. Validator mana pun akan diam.

Kegagalan baru muncul di pembaca, dan itu pun bukan berupa error. Lookup background-color tidak menemukan apa pun. getPropertyValue mengembalikan string kosong untuk property yang tidak terisi [1]. Hasilnya diteruskan begitu saja: palet kosong, fonts: [], nol error di log.

String kosong itu persis sama dengan hasil ketika sebuah komponen memang tidak punya background. Dua kondisi berbeda, satu representasi, nol exception. Saya sempat menuduh capture padahal capture-nya baik-baik saja; yang bermasalah adalah perjanjian nama key di antara keduanya.

Nama key disepakati, bukan ditafsirkan

Pelajarannya bukan "jangan pakai camelCase". Kedua bentuk itu legal. Masalahnya, kontrak data antara producer dan consumer hanya bertahan kalau nama key-nya sepakat sampai byte, dan kegagalan yang muncul ketika tidak sepakat bisa berupa string kosong alih-alih exception. Kalau kegagalannya berupa exception, saya sudah tahu sejak build pertama. Kalau berupa string kosong, bug-nya lolos sampai file hasil generate.

Prinsip yang saya pegang sekarang: producer yang menyesuaikan nama key ke bentuk yang dipakai consumer, bukan sebaliknya. Di kasus ini consumer membaca lewat getPropertyValue, yang memang mengharuskan nama property dalam bentuk hyphen case [1], jadi kebab-case di producer adalah pilihan yang paling sedikit menimbulkan salah tafsir.

Perbaikannya satu arah: ubah capture agar menulis key kebab-case, lalu tambahkan validasi di producer supaya kesalahan nama key tertangkap sebelum file ditulis.

// check-keys.mjs (pastikan semua key fragment sudah kebab-case
import { readFileSync, readdirSync } from "node:fs";

// jalankan dari root project
const dir = "./capture/fragments";
for (const f of readdirSync(dir).filter((n) => n.endsWith(".json"))) {
  const data = JSON.parse(readFileSync(`${dir}/${f}`, "utf8"));
  for (const key of Object.keys(data.styles ?? {})) {
    if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(key)) {
      console.error(`${f}: key tidak kebab-case -> ${key}`);
    }
  }
}

Jalankan sebelum step derivasi. Kalau tidak ada output, semua key aman. Kalau muncul backgroundColor seperti di atas, producer masih menulis camelCase.

Gejala yang perlu diingat: kalau hasil generate menampilkan palet kosong padahal screenshot halaman jelas berwarna, periksa nama key di JSON fragment dulu sebelum menuduh datanya rusak.

Keputusan yang saya ambil setelah ini: semua kontrak key di repo ditulis dalam bentuk yang dipakai pembacanya, dan producer wajib lolos check script di atas sebelum menulis file apa pun. Kesepakatan nama key bukan soal selera gaya penulisan. Ini soal apakah producer dan consumer benar-benar membaca kontrak yang sama.

Sumber

  1. MDN, CSSStyleDeclaration: getPropertyValue()
  2. MDN, CSSStyleDeclaration
  3. MDN, Window: getComputedStyle()
  4. RFC Editor, RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format

Artikel terkait