Frontend Assets
Frontend Vue milik Panel dipublish ke dalam application, bukan di-import langsung dari package. vendor:publish menyalin source ke resources/js, lalu Vite milik application membangunnya bersama entrypoint frontend Anda sendiri.
Sebuah Panel juga dapat mendeklarasikan entrypoint tambahan yang hanya dimuat pada Page milik Panel tersebut dan tidak dimuat di tempat lain.
Gunakan dokumentasi ini ketika:
- menyiapkan frontend PandaBear;
- melakukan upgrade package;
- atau ketika sebuah Panel membutuhkan stylesheet/entrypoint sendiri.
Mempublish Frontend
php artisan panel:installCommand tersebut:
- mempublish config dan frontend;
- membuat scaffold Panel pertama;
- meregistrasikan Panel;
- memeriksa frontend toolchain;
- menawarkan pembuatan account pertama.
Jika ingin menjalankan langkah-langkahnya secara manual:
php artisan vendor:publish --tag=panda-panel-config
php artisan vendor:publish --tag=panda-panel-assets
php artisan vendor:publish --tag=panda-panel-migrations
php artisan vendor:publish --tag=panda-panel-stubs
php artisan vendor:publish --tag=panda-panel # config + migrations + assets
npm install && npm run build2
3
4
5
6
7
| Tag | Yang Dipublish |
|---|---|
panda-panel-config | config/panda-panel.php |
panda-panel-assets | frontend Vue dan resources/css/panda-panel.css |
panda-panel-migrations | table notifications dan column Email Code two-factor |
panda-panel-stubs | generator stubs ke stubs/panel |
panda-panel | config, migrations, dan assets sekaligus |
File Dipublish ke Mana
PandaPanel\Support\Installer\PublishedAssets::map() menjadi satu-satunya source of truth untuk publish map. Mapping yang sama dibaca oleh vendor:publish dan panel:assets.
| Source Package | Tujuan di Application |
|---|---|
resources/js/panel | FrontendPaths::panel() — default resources/js/panel |
resources/js/components | resources/js/components |
resources/js/composables | resources/js/composables |
resources/js/lib | resources/js/lib |
resources/js/pages | resources/js/pages |
resources/js/types | resources/js/types |
resources/css/panda-panel.css | resources/css/panda-panel.css |
use PandaPanel\Support\Installer\PublishedAssets;
PublishedAssets::map(); // array<string, string> source => destination
PublishedAssets::files(); // array<string, string> destination => source, per file2
3
4
Dua lokasi frontend dapat dikonfigurasi:
// config/panda-panel.php
'frontend' => [
'panel_path' => 'js/panel',
'pages_path' => 'js/pages/Panels',
],2
3
4
5
use PandaPanel\Support\FrontendPaths;
FrontendPaths::panel(); // /app/resources/js/panel
FrontendPaths::panel('layouts'); // /app/resources/js/panel/layouts
FrontendPaths::pages(); // /app/resources/js/pages/Panels
FrontendPaths::pages('Admin/Widgets'); // /app/resources/js/pages/Panels/Admin/Widgets2
3
4
5
6
| Method | Signature | Config Key | Default |
|---|---|---|---|
panel | static panel(string $path = ''): string | panda-panel.frontend.panel_path | js/panel |
pages | static pages(string $path = ''): string | panda-panel.frontend.pages_path | js/pages/Panels |
Keduanya di-resolve di bawah resource_path().
pages() juga menjadi root untuk pattern import.meta.glob milik component registry. Karena itu jika Anda memindahkan path ini, glob registry juga harus disesuaikan. Lihat Component Registries.
Mengapa Dipublish, Bukan Di-import dari Package
Seluruh component registry frontend menggunakan import.meta.glob sebagai allowlist atas tree milik application.
Component yang tidak pernah dilihat build milik application tidak dapat di-resolve pada runtime. Karena itu source component harus berada di dalam tree yang dibangun application.
Konsekuensinya, file yang dipublish menjadi milik application:
- masuk repository application;
- ikut build application;
- dapat dibaca;
- dapat di-debug;
- dapat diedit.
Trade-off dari desain ini adalah update package tidak dapat secara diam-diam memperbarui file yang sekarang dimiliki project.
panel:assets dibuat untuk menangani masalah upgrade tersebut secara aman.
Tiga Lokasi Frontend
Pemisahan ini memang diperlukan karena @inertiajs/vite hanya melakukan glob terhadap resources/js/pages/**.
| Lokasi | Fungsi | Dapat Di-resolve Inertia |
|---|---|---|
resources/js/panel/** | layout, component, renderer, composable, registry, type | tidak |
resources/js/pages/panel/** | generic Page bawaan framework | ya |
resources/js/pages/Panels/{Panel}/** | application-specific Page dan custom component | ya |
Setiap Page PandaBear yang dipublish menentukan layout-nya sendiri:
defineOptions({ layout: PanelLayout }); // PanelBlankLayout for auth pagesKarena itu Anda tidak perlu mengatur layout PandaBear di resources/js/app.ts.
Kesalahan yang dapat dilakukan host application adalah mengoverride layout tersebut secara paksa:
page.default.layout = AppLayout; // replaces the panel shell
page.default.layout ??= AppLayout; // correct2
Assignment tanpa kondisi akan memasukkan seluruh screen Panel ke dalam shell application utama. Hasilnya bisa terlihat sebagai HTTP 200 tanpa error, tetapi sidebar host tampil dan navigation PandaBear hilang.
panel:install membaca app.ts dan melaporkan file serta line jika menemukan pattern tersebut.
Entrypoint per Panel
Sebuah Panel dapat mendeklarasikan Vite entrypoint yang hanya dimuat pada Page Panel itu:
$panel->assets('resources/css/panels/admin.css');| Method | Signature | Catatan |
|---|---|---|
assets | assets(string ...$entrypoints): self | Akumulatif; duplicate hanya ditambahkan sekali |
getAssets | getAssets(): list<string> |
Entrypoint tersebut perlu dikeluarkan oleh root Inertia view milik application, resources/views/app.blade.php.
Package tidak mempublish atau mengedit file ini karena root view merupakan milik application:
@vite([
'resources/css/app.css',
'resources/js/app.ts',
"resources/js/pages/{$page['component']}.vue",
...(panel()?->getAssets() ?? []),
])2
3
4
5
6
Di luar request Panel:
panel()menghasilkan null, sehingga spread tidak menambahkan apa pun pada Page starter kit maupun Panel lain.
Panel tanpa asset tambahan juga tidak menambahkan apa pun. Karena itu baris tersebut aman ditambahkan bahkan sebelum Panel pertama membutuhkan custom entrypoint.
Entrypoint juga harus masuk ke Vite input.
Ada dua perubahan yang memang harus dilakukan.
Path yang sama harus dicantumkan pada vite.config.ts:
export default defineConfig({
plugins: [laravel({
input: [
'resources/css/app.css',
'resources/js/app.ts',
'resources/css/panels/admin.css',
],
})],
});2
3
4
5
6
7
8
9
Jika tidak, Vite tidak memiliki entry tersebut dan Page akan gagal dengan manifest error.
Failure ini memang benar: asset yang dideklarasikan tetapi tidak dibuild adalah kesalahan konfigurasi, bukan kondisi yang perlu disembunyikan.
Daftar asset sendiri tidak dikirim ke frontend melalui Inertia. toSharedArray() tidak memiliki key assets.
Browser hanya menerima tag hasil Vite, bukan metadata tentang bagaimana tag tersebut dihasilkan.
Menjaga Published File Tetap Up-to-date
Gunakan:
php artisan panel:assets # report only
php artisan panel:assets --update # write the safe ones
php artisan panel:assets --force # also overwrite files you edited2
3
Menggunakan vendor:publish saja saat upgrade tidak cukup aman.
Tanpa --force, semua file yang sudah ada dilewati sehingga tidak ada update.
Dengan --force, seluruh file ditimpa, termasuk file yang sengaja Anda modifikasi.
Kedua mode tersebut tidak dapat membedakan:
file stale karena package berubahdengan:
file memang diedit applicationPandaPanel\Support\Installer\AssetManifest menyimpan keadaan setiap file ketika pertama dipublish di .panel-assets.json pada root application.
Nilai ketiga tersebut menjadi common ancestor untuk three-way comparison.
| Status | Arti | Ditulis oleh --update |
|---|---|---|
new | Application belum pernah mempublish file tersebut | ya |
stale | Pernah dipublish, tidak diedit local, package berubah | ya |
current | Local tidak berubah dan package juga tidak berubah | tidak |
modified | File pernah dipublish lalu diedit application | hanya dengan --force |
conflict | File diedit application dan berubah di upstream | hanya dengan --force |
deleted | File pernah dipublish lalu sengaja dihapus application | tidak pernah |
removed-upstream | File pernah dipublish tetapi package sudah tidak mengirimkannya | tidak pernah |
use PandaPanel\Support\Installer\AssetManifest;
AssetManifest::path(); // /app/.panel-assets.json
AssetManifest::exists(); // bool
AssetManifest::read(); // array<string, string> destination => hash
AssetManifest::compare(?array $files = null); // array<string, array{status, destination, source}>
AssetManifest::write(array $existing = []); // record the current state2
3
4
5
6
7
.panel-assets.json sebaiknya masuk repository.
Fungsinya mirip composer.lock: file tersebut merekam keputusan dan baseline project.
Jika disimpan di bootstrap/cache, file akan mudah diregenerate dan kehilangan fungsi historisnya. Jika disimpan di storage, file biasanya ikut gitignore dan dapat hilang pada deployment pertama.
Conflict tidak dianggap kegagalan command. panel:assets berhasil menemukan kondisi yang memerlukan keputusan manusia, sehingga command tetap exit 0.
Mengembalikan non-zero hanya karena file sengaja diedit developer justru dapat menggagalkan deployment yang valid.
Setelah file benar-benar ditulis, rebuild frontend:
npm run buildBuild Milik Package Sendiri
vite.config.ts pada repository package tidak menghasilkan artifact yang dikirim ke user.
Tujuannya adalah memastikan seluruh file frontend package benar-benar dapat di-resolve dan dikompilasi bersama.
Entrypoint build package dibuat dengan melakukan glob terhadap seluruh tree. Jika hanya beberapa component ditulis manual sebagai entry, build hanya akan menguji sebagian kecil source.
Isi build/ pada package bukan deliverable framework.
Catatan
- CSS PandaBear menggunakan Tailwind v4. Class yang dikonfigurasi dari PHP melalui
cssHooks()berupa arbitrary string yang tidak muncul pada source file yang discan Tailwind. Gunakan class yang memang muncul di application atau tambahkan Provider ke content/source scan Tailwind. - Warna Panel dikirim sebagai CSS custom property pada attribute
style, bukan sebagai class. Karena itucolors()menerima value, sedangkancssHooks()menerima class name. - Jangan membangun Tailwind class melalui string interpolation. Column span, badge color, grid column, dan content width semuanya dipetakan melalui literal record di frontend agar Tailwind dapat melihat class saat build.
panel:installbersifat idempotent kecuali saat menggunakan--force. Command melakukan publish, check, lalu melaporkan seluruh pekerjaan yang masih perlu dilakukan pada akhir proses agar warning tidak tenggelam di antara success message.- Entrypoint sebuah Panel hanya dimuat pada Page Panel tersebut. Entrypoint tidak ikut dimuat pada Panel lain ataupun Page milik starter kit.
PanelAssetTestmelakukan assertion untuk ketiga kondisi tersebut.