Memperbarui Published Assets
Frontend Vue milik panel disalin ke application Anda melalui vendor:publish lalu dibuild oleh Vite milik application. Artinya setiap file tersebut menjadi milik project Anda: masuk repository, ikut build, dan dapat diedit. Konsekuensinya adalah seluruh isi halaman ini — setelah file menjadi milik application, composer update tidak dapat memperbaruinya secara otomatis. php artisan panel:assets digunakan untuk mengetahui file mana yang tertinggal dari package, file mana yang Anda ubah, dan file mana yang berubah di kedua sisi.
Satu command utama
php artisan panel:assets # hanya report, tidak menulis file
php artisan panel:assets --update # menulis file yang aman untuk diperbarui
npm run build2
3
Menjalankan command tanpa option hanya bertanya pada state saat ini. Command mencetak jumlah per status, menampilkan path setiap conflict, lalu exit 0. --update hanya menulis dua status: file yang belum pernah Anda miliki dan file yang dapat dibuktikan belum pernah Anda ubah. Setelah itu manifest dicatat ulang.
Mengapa vendor:publish tidak dapat menangani upgrade ini
vendor:publish hanya memiliki dua mode, dan keduanya tidak cukup untuk upgrade asset yang sudah menjadi milik application.
Tanpa --force, seluruh file yang sudah ada dilewati sehingga tidak ada update. Dengan --force, seluruh file dioverwrite, termasuk file yang sengaja Anda custom. vendor:publish tidak dapat membedakan keduanya karena “file di disk berbeda dari copy package” sama-sama benar untuk file stale dan file yang memang diedit developer.
Hash ketiga
Informasi yang hilang sama seperti nilai yang diberikan git merge-base: seperti apa file tersebut ketika pertama kali dipublish. PandaPanel\Support\Installer\AssetManifest mencatat state tersebut dalam .panel-assets.json di root application. Tiga hash mengubah comparison dua arah yang ambigu menjadi comparison tiga arah yang dapat menentukan status dengan jelas.
| Di disk | Di package | Status | --update | --force |
|---|---|---|---|---|
| = manifest | = manifest | current | tidak | tidak |
| = manifest | ≠ manifest | stale | menulis | menulis |
| ≠ manifest | = manifest | modified | tidak | menulis |
| ≠ manifest | ≠ manifest | conflict | tidak | menulis |
| tidak ada | ada | deleted | tidak | tidak |
| tidak ada di manifest, berbeda | ada | new | menulis | menulis |
| tidak ada di manifest, identik | ada | current | tidak | tidak |
| ada di manifest | tidak lagi dikirim package | removed-upstream | tidak | tidak |
Secara default hanya dua keadaan yang benar-benar menunjukkan application belum memiliki keputusan terhadap file yang ditulis: file baru yang belum pernah ada, dan file stale yang belum pernah Anda ubah. File yang berubah di kedua sisi dilaporkan beserta path lalu dibiarkan apa adanya. Menebak versi mana yang harus menang adalah cara paling cepat sebuah upgrade menghapus pekerjaan developer.
Constants status bersifat public sehingga application dapat match terhadap constant, bukan hard-code string:
use PandaPanel\Support\Installer\AssetManifest;
AssetManifest::NEW; // 'new'
AssetManifest::CURRENT; // 'current'
AssetManifest::STALE; // 'stale'
AssetManifest::MODIFIED; // 'modified'
AssetManifest::CONFLICT; // 'conflict'
AssetManifest::DELETED; // 'deleted'
AssetManifest::REMOVED_UPSTREAM; // 'removed-upstream'2
3
4
5
6
7
8
9
Membaca report
Setiap status memiliki label sendiri dan hanya status dengan count lebih dari nol yang dicetak:
| Status | Label pada report | Warna |
|---|---|---|
new | new | green |
stale | out of date | yellow |
conflict | CONFLICT | red |
modified | yours | blue |
deleted | deleted by you | gray |
removed-upstream | no longer shipped | gray |
current | current | gray |
out of date ........................................ 12
CONFLICT ............................................ 1
yours ............................................... 4
current ........................................... 3222
3
4
Hanya conflict yang ditampilkan satu per satu. File current biasanya merupakan mayoritas besar, dan mencetak ratusan path current hanya membuat report sulit dibaca.
Count dihitung sebelum file ditulis. Karena itu run dengan --update melaporkan state yang ditemukan saat command mulai, bukan state setelah command selesai.
panel:assets
PandaPanel\Console\Commands\PanelAssetsCommand:
protected $signature = 'panel:assets
{--update : Write the files that are safe to write}
{--force : Also overwrite files this application has edited}';2
3
| Option | Efek |
|---|---|
| (tidak ada) | Hanya report. Tidak menulis file dan tidak membuat/mengubah .panel-assets.json. |
--update | Menulis new dan stale. Manifest ditulis ulang setelahnya jika setidaknya satu file berubah. |
--force | Implies write. Memperluas writable set dari --update menjadi modified dan conflict, tanpa menambah status lain. |
Exit code selalu 0. Conflict bukan kegagalan command; command justru berhasil menemukan sesuatu yang membutuhkan keputusan manusia. Non-zero exit karena conflict akan membuat deploy gagal hanya karena ada file yang memang sengaja dicustom.
Menyelesaikan conflict
Conflict berarti package berubah dan application juga mengubah file yang sama. Keduanya merupakan pekerjaan yang valid, sehingga command mencetak path lalu berhenti menyentuh file tersebut:
1 file(s) changed both here and upstream. Neither copy is safe to throw away, so nothing was
written. Diff each against the package copy under vendor/chocoalano/panel, then re-run with
--force once you have merged:
resources/js/panel/tables/DataTable.vue2
3
4
5
Copy package tersedia pada relative path yang sama di dalam installed package:
diff -u \
resources/js/panel/tables/DataTable.vue \
vendor/chocoalano/panel/resources/js/panel/tables/DataTable.vue2
3
Lakukan merge manual ke copy application, lalu jalankan command kembali ketika conflict dianggap selesai:
php artisan panel:assets --force
npm run build2
Perhatikan behavior --force: option ini menimpa dengan versi package. Jadi jika Anda ingin mempertahankan perubahan custom, merge terlebih dahulu ke file application dan pastikan hasil final Anda aman sebelum menjalankan workflow yang memaksa package copy. Jika Anda memang ingin menerima versi package sepenuhnya, --force adalah jalurnya. Tidak ada option per-file; granularity berlaku untuk satu run penuh.
Yang tidak dilakukan --force
--force hanya memperluas status writable menjadi modified dan conflict. Option ini tetap tidak menyentuh:
deleted— file yang Anda hapus sengaja tetap dihapus.removed-upstream— file yang sudah tidak dikirim package tidak dihidupkan kembali karena application mungkin sudah mengadopsinya sebagai file sendiri.- entry apa pun yang tidak memiliki source package, yang pada praktiknya sama dengan kasus di atas.
Manifest file
use PandaPanel\Support\Installer\AssetManifest;
AssetManifest::path(); // /var/www/app/.panel-assets.json
AssetManifest::exists(); // bool2
3
4
{
"_": "Written by php artisan panel:install / panel:assets. Commit this file: it is the record of which version of the panel frontend this application published, and without it an upgrade cannot tell your edits from a stale copy.",
"files": {
"resources/css/panda-panel.css": "…",
"resources/js/panel/tables/DataTable.vue": "…"
}
}2
3
4
5
6
7
Key adalah application-relative destination dan di-sort. Value adalah content hash xxh128; CRLF dinormalisasi ke LF sehingga checkout Windows atau editor yang menulis CRLF tidak membuat seluruh file terlihat seperti diedit.
Commit file ini. Manifest merekam keputusan project seperti composer.lock. Jika disimpan di bootstrap/cache, file akan dianggap disposable dan diregenerate sehingga kehilangan fungsi historisnya; jika diletakkan di storage, biasanya akan ter-ignore Git dan hilang pada deploy.
Jika manifest belum ada, command memberi warning lalu tetap berjalan:
WARN No .panel-assets.json, so there is no record of what this application published.
Everything already identical to the package reads as current; anything else reads as new.
Run --update to write one.2
3
Manifest dengan JSON invalid diperlakukan seperti absent, bukan fatal. Worst case adalah file dibaca sebagai new, sama seperti application yang memang belum pernah memiliki manifest.
API
Kedua class berikut static dan aman dipanggil dari Tinker, test, atau deploy script.
PandaPanel\Support\Installer\AssetManifest
| Method | Signature | Return |
|---|---|---|
path | static path(): string | base_path('.panel-assets.json') |
exists | static exists(): bool | apakah file manifest ada |
read | static read(): array<string, string> | relative destination => recorded hash, [] jika absent/unparseable |
write | static write(array $existing = []): void | menghitung ulang hash setiap shipped file di disk lalu menulis manifest |
compare | static compare(?array $files = null): array | relative destination => array{status, destination, source} |
use PandaPanel\Support\Installer\AssetManifest;
$report = AssetManifest::compare();
$report['resources/js/panel/tables/DataTable.vue'];
// [
// 'status' => 'stale',
// 'destination' => '/var/www/app/resources/js/panel/tables/DataTable.vue',
// 'source' => '/var/www/app/vendor/chocoalano/panel/resources/js/panel/tables/DataTable.vue',
// ]2
3
4
5
6
7
8
9
10
compare() juga menambahkan setiap entry yang masih tercatat pada manifest tetapi sudah tidak dikirim package, dengan status removed-upstream dan source bernilai null.
Argument $files — destination => source — tersedia agar status dapat diuji menggunakan scratch fixture. Repository package sendiri juga bertindak sebagai test application, sehingga dengan real map, “file di disk” dan “file di package” akan selalu sama dan case penting seperti stale/conflict sulit diuji. Pada application normal, jangan isi argument ini.
write($existing) mempertahankan hash dari $existing untuk file yang tidak sedang ditulis, dan menghitung hash terhadap copy milik application, bukan package. Perbedaan ini merupakan inti manifest: manifest merekam state yang application miliki. Command memanggil AssetManifest::write(AssetManifest::read()) setelah copy selesai ditulis, bukan sebelumnya.
PandaPanel\Support\Installer\PublishedAssets
| Method | Signature | Return |
|---|---|---|
map | static map(): array<string, string> | absolute source => destination, format yang diperlukan publishes() |
files | static files(): array<string, string> | absolute destination => source, satu entry per file |
relative | static relative(string $path): string | destination relatif seperti ditampilkan report |
use PandaPanel\Support\Installer\PublishedAssets;
count(PublishedAssets::map()); // 7 — directories dan stylesheet
count(PublishedAssets::files()); // seluruh file di dalamnya
PublishedAssets::relative('/var/www/app/resources/js/panel/tables/DataTable.vue');
// 'resources/js/panel/tables/DataTable.vue'2
3
4
5
6
7
Dua map sengaja memiliki arah berbeda. map() menggunakan bentuk yang diperlukan vendor:publish, sedangkan files() menggunakan bentuk yang lebih nyaman untuk comparison. Map dibangun setiap kali method dipanggil, bukan disimpan dalam constant, karena dua destination dapat dikonfigurasi. Membaca config pada class-definition time akan membekukan value yang kebetulan aktif saat package discovery.
Isi report dan yang tidak termasuk
Report mencakup persis publish map berikut:
| Sumber package | Destination 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 |
Dua destination bergerak mengikuti config, sehingga PublishedAssets::map() menjadi satu-satunya tempat peta tersebut dideklarasikan:
// config/panda-panel.php
'frontend' => [
'panel_path' => 'js/panel', // PandaPanel\Support\FrontendPaths::panel()
'pages_path' => 'js/pages/Panels', // PandaPanel\Support\FrontendPaths::pages()
],2
3
4
5
use PandaPanel\Support\FrontendPaths;
FrontendPaths::panel(); // …/resources/js/panel
FrontendPaths::panel('icons/registry.ts');
FrontendPaths::pages(); // …/resources/js/pages/Panels
FrontendPaths::pages('Admin/Widgets');2
3
4
5
6
Yang tidak masuk report dan tidak pernah ditulis panel:assets:
- File custom Anda sendiri di dalam published directory. Contoh
resources/js/pages/Panels/Admin/Widgets/*.vueberada di dalam destination yang juga dipublish package, tetapi bukan file yang package kirim. Karena itu tidak muncul difiles()dan tidak pernah dibandingkan. config/panda-panel.php, migrations, generator stubs. Semuanya memiliki tag publish masing-masing:panda-panel-config,panda-panel-migrations,panda-panel-stubs. Re-publish dan lakukan diff manual bila perlu.- Output Wayfinder.
resources/js/routesdanresources/js/actionsdihasilkan dari route table application dan bukan bagian publish map. Lihat Wayfinder routes. resources/js/app.ts,vite.config.ts,resources/views/app.blade.php. File tersebut sepenuhnya milik application. Package tidak pernah rewrite, yang juga berarti upgrade starter kit dapat mengembalikan layout override bermasalah tanpa pernah disebutpanel:assets.
Setelah package upgrade
composer update chocoalano/panel
php artisan panel:assets # baca report terlebih dahulu
php artisan panel:assets --update # tulis hanya yang aman
php artisan panel:icons # icon registry adalah published file
npm run build2
3
4
5
6
7
npm run build tidak optional. Setiap component registry menggunakan import.meta.glob yang dievaluasi saat build, sehingga file yang sudah berubah di disk belum akan masuk bundle sampai build dijalankan kembali.
Untuk sekaligus mengecek integration seam frontend — npm dependencies, host modules, Vite, Inertia, dan layout rule — jalankan pemeriksaan installer tanpa membuat panel atau user:
php artisan panel:install --no-panel --no-user --no-interactionCommand tersebut juga menulis .panel-assets.json secara unconditional dan dapat digunakan untuk membuat manifest pada application yang sudah melakukan publish sebelum manifest feature tersedia.
Gotchas
--updatehanya menulis manifest jika setidaknya satu file ditulis. Application yang seluruh filenya identik dengan package dibaca sebagaicurrent; command tidak menulis file dan manifest tetap tidak muncul walaupun warning menyarankan membuatnya. Gunakanphp artisan panel:install --no-panel --no-useruntuk membuat manifest.panel:assetstanpa option tidak pernah menulis manifest. Mencatat hash sebagai side effect dari operasi report akan membuat jawaban run berikutnya bergantung pada apakah developer pernah menjalankan report sebelumnya.- File yang Anda hapus dapat kembali pada run selanjutnya.
deletedtidak ditulis oleh--update, tetapi manifest write berikutnya dapat menghapus record file tersebut. Pada run setelah itu, file dibaca sebagainewdan dapat ditulis lagi. Jika file bawaan harus benar-benar hilang, hapus dependency/import yang memerlukannya atau siap file tersebut muncul lagi. - Entry
removed-upstreambersifat sticky. Command melakukanwrite(read()), sehingga record untuk file yang tidak lagi dikirim package tetap dipertahankan. Hapus line dari.panel-assets.jsonjika Anda ingin status berhenti muncul. panel:iconsmembuat generated registry menjadi file milik application. Command menulis ulangresources/js/panel/icons/registry.tsberdasarkan icon yang dideklarasikan panel, sehingga file tersebut dibaca sebagaimodified.--forcedapat overwrite dengan package copy. Selalu jalankanphp artisan panel:iconssetelah operasi--forceyang menyentuh asset.- Perbedaan line ending tidak dianggap edit. Hash menormalisasi
\r\nke\n, sehingga checkout Windows tidak membuat seluruh report menjadi conflict. --forceberlaku untuk satu run penuh. Tidak ada option force per-path. Selesaikan file yang memang ingin dipertahankan terlebih dahulu, lalu gunakan force dengan kesadaran penuh.- Repository package sendiri selalu terlihat
current. Published copy pada repository package memang file package yang sama. Itulah alasanAssetManifest::compare()menerima injected map untuk testing.
See also
- Published asset structure
- Wayfinder routes, Host modules
- Icons, Tailwind theme, CSS hooks
- Frontend assets, Component registries
panel:assets,panel:install, publish tags- Frontend requirements, Laravel Vue starter kit setup
- Asset manifest, Resolving asset conflicts, Upgrade guide
- NPM build, Icon registry
- Troubleshooting: asset conflicts