7
Helper Berkas dan Filter Blacklist
src/utils/index.ts adalah modul kecil berisi empat export: resolve, getLanguage, getFiles, dan isBlacklisted. Modul itu juga mengimpor src/data/blacklist.json di baris ketiga, sehingga daftar kata terlarang ikut termuat setiap kali modul ini dipakai. src/utils/index.ts:1-31 Pemakainya hanya dua skrip: src/build.ts mengimpor keempat helper, sedangkan src/check.ts hanya mengambil getFiles dan isBlacklisted. src/build.ts:52 src/check.ts:43
Empat helper itu berbeda tugas: resolve dan getLanguage murni menghitung string, getFiles satu-satunya yang membaca direktori data/, dan isBlacklisted satu-satunya yang menyentuh daftar kata terlarang.
| Export | Signature | Hasil | Pemanggil |
|---|---|---|---|
resolve | (...fileName: string[]) => string | path absolut di bawah data/ | src/build.ts src/utils/index.ts:5-74 |
getLanguage | (filePath: string) => string | basename tanpa ekstensi | src/build.ts src/utils/index.ts:9-115 |
getFiles | (dirPath = resolve(), fileList = []) => Promise<string[]> | daftar path berkas .json | src/build.ts, src/check.ts src/utils/index.ts:13-336 |
isBlacklisted | (html: string) => boolean | true bila body memuat kata terlarang | src/build.ts, src/check.ts src/utils/index.ts:35-377 |
Peta ketergantungan helper
graph TD
Build["src/build.ts"]
Check["src/check.ts"]
Resolve["resolve()"]
GetFiles["getFiles()"]
GetLanguage["getLanguage()"]
IsBlacklisted["isBlacklisted()"]
Blacklist["src/data/blacklist.json"]
Data["data/**/*.json"]
Build --> GetFiles
Build --> GetLanguage
Build --> IsBlacklisted
Build --> Resolve
Check --> GetFiles
Check --> IsBlacklisted
GetFiles -->|"dirPath default"| Resolve
GetFiles -->|"rekursi direktori"| GetFiles
GetFiles -->|"readdir + stat"| Data
Resolve -->|"path.resolve"| Data
IsBlacklisted -->|"blacklist.some"| Blacklist
style Data fill:#336791,color:#fff
style Blacklist fill:#336791,color:#fff
style GetFiles fill:#8b5cf6,color:#fff
style GetLanguage fill:#8b5cf6,color:#fff
style Resolve fill:#8b5cf6,color:#fff
style IsBlacklisted fill:#f59e0b,color:#000
style Build fill:#2d7d46,color:#fff
style Check fill:#2d7d46,color:#fff
%% fig: Ketergantungan helper ke data dan blacklist
%% desc: Dua skrip pemanggil memakai empat helper di src/utils/index.ts; hanya getFiles yang membaca direktori data dan hanya isBlacklisted yang membaca blacklist.json.resolve dan letak direktori data/
resolve menggabungkan import.meta.dir milik modul (src/utils) dengan '..', '..', 'data', lalu menyambung sisa argumen. src/utils/index.ts:5-74 Karena itu resolve() tanpa argumen menunjuk ke direktori data/ di akar repository, dan resolve('books', 'id.json') menunjuk ke berkas kategori di dalamnya.
src/build.ts memakai helper yang sama untuk tujuan berbeda. Ia menyusun nama berkas dari ['README', language === 'id' ? null : language, 'md'] yang nilai null-nya dibuang oleh filter, lalu meneruskan import.meta.dir sebagai argumen pertama ke resolve. src/build.ts:87-918 Karena path.resolve memulai ulang perhitungan begitu menemukan path absolut, hasil akhirnya berada di akar repository (README.md atau README.en.md), bukan di dalam data/. src/build.ts:52
getFiles menelusuri data/ secara rekursif
getFiles menerima dirPath (default resolve()) dan fileList (default []), membaca isi direktori dengan fsPromises.readdir, lalu memeriksa setiap entri memakai fsPromises.stat. src/utils/index.ts:13-336 Entri yang berupa berkas dan berakhiran .json didorong ke fileList; entri yang berupa direktori ditelusuri dengan panggilan getFiles rekursif yang membawa fileList yang sama. src/utils/index.ts:24-289
Pengisian fileList terjadi di dalam callback Promise.all, sehingga urutannya mengikuti selesainya masing-masing stat, bukan urutan readdir. src/utils/index.ts:19-3010 Kedua skrip pemanggil cukup menulis await getFiles() tanpa argumen dan menerima seluruh berkas JSON kategori. src/build.ts:1311 src/check.ts:912
getLanguage sebagai kunci pengelompokan
getLanguage memanggil path.basename(filePath, path.extname(filePath)), jadi ekstensi dibuang dan .../books/id.json menjadi id. src/utils/index.ts:9-115 Hasilnya dipakai src/build.ts sebagai kunci peta data per bahasa. src/build.ts:1413 src/build.ts:79-8314 Pemetaan itu yang kemudian menentukan pasangan kategori dan berkas README; pembahasannya ada di Dukungan Multi-Bahasa dan Pipeline Build Markdown.
isBlacklisted dan isi blacklist
isBlacklisted mengembalikan blacklist.some((item) => html.includes(item)). src/utils/index.ts:35-377 Artinya pencocokan berupa substring peka huruf besar-kecil atas seluruh body halaman, bukan pencocokan kata utuh. src/data/blacklist.json berisi tiga entri: "nfsw", "porn", dan "hentai". src/data/blacklist.json:115
⚠️ Entri pertama ditulis
nfsw, bukannsfw. Karena pencocokan memakaiincludes, ejaan itu hanya cocok bila body memuat harfiahnfsw; pesan galat dicheck.tssendiri tetap menyebut "NSFW". src/data/blacklist.json:115 src/check.ts:52-5516
⚠️ Karena pencocokan substring, satu kata terlarang yang muncul di sembarang tempat dalam body sudah cukup untuk menandai halaman. Kata
porndi dalam kata lain yang lebih panjang juga terhitung. src/utils/index.ts:35-377
Dua pemanggil, dua akibat berbeda
src/check.ts memeriksa setiap URL yang belum ada di array crawled. Bila isBlacklisted(body) bernilai true, skrip mencetak Sorry, the NSFW API is not allowed here :) lalu memanggil process.exit(1), sehingga seluruh pemeriksaan berhenti pada URL pertama yang terdeteksi. src/check.ts:52-5516
src/build.ts memakai fungsi yang sama dengan akibat yang lebih ringan: URL hanya didorong ke array crawledx, dan API yang URL-nya ada di crawledx dibuang dari json.apis lewat splice. src/build.ts:52-5417 src/build.ts:64-7318
graph LR
Body["response.text()"]
IsB["isBlacklisted(body)"]
Exit["process.exit(1)"]
Crawledx["crawledx.push(url)"]
Splice["json.apis.splice(i, 1)"]
Keep["status = response.status !== 404"]
Body --> IsB
IsB -->|"true di check.ts"| Exit
IsB -->|"true di build.ts"| Crawledx
Crawledx --> Splice
IsB -->|"false"| Keep
style Body fill:#8b5cf6,color:#fff
style IsB fill:#f59e0b,color:#000
style Exit fill:#dc2626,color:#fff
style Splice fill:#dc2626,color:#fff
style Crawledx fill:#f59e0b,color:#000
style Keep fill:#2d7d46,color:#fff
%% fig: Akibat isBlacklisted di dua pemanggil
%% desc: Body halaman yang sama dinilai isBlacklisted; check.ts langsung menghentikan proses, sedangkan build.ts menandai URL untuk dibuang dari daftar API.| Berkas pemanggil | Baris | Kapan berjalan | Akibat bila isBlacklisted bernilai true |
|---|---|---|---|
src/build.ts | 52-54 | hanya di dalam cabang --rewrite | URL masuk crawledx, lalu API dibuang dari json.apis src/build.ts:64-7318 |
src/check.ts | 52-55 | setiap URL yang belum ada di crawled | cetak Sorry, the NSFW API is not allowed here :), lalu process.exit(1) src/check.ts:52-5516 |
⚠️ Pemeriksaan di
build.tshanya berjalan ketika argumen--rewritediberikan. Tanpa flag ituisBlacklistedtidak pernah dipanggil saat build, sehingga body halaman tidak diperiksa sama sekali. src/build.ts:2019 Rincian cabang tersebut ada di Mode Rewrite dan Status API.
Jalur ke CI
Pemeriksaan check.ts dijalankan sebagai skrip check yang memanggil bun run src/check.ts. package.json:39-4020 Workflow NSFW Check menjalankan bun check pada pull request yang dibuka atau dibuka ulang, sehingga process.exit(1) di isBlacklisted itulah yang menggagalkan job. .github/workflows/nsfw-check.yml:25-2721 Pemicu workflow dan pemeriksaan data lain dibahas di Otomasi GitHub Actions dan Validator Data lewat check.ts.