Host Modules
Ada sembilan belas module @/… yang di-import oleh published components milik panel tetapi tidak dikirim oleh package. Module tersebut merupakan milik application Anda: sebagian dihasilkan Wayfinder dari route table application, sedangkan sisanya biasanya disediakan oleh Laravel Vue starter kit. Gunakan halaman ini ketika npm run build gagal pada module specifier, atau ketika memasang panel ke application yang bukan berasal dari starter kit.
Package tidak dapat mengirim module tersebut, dan walaupun secara teknis bisa, melakukannya justru akan salah. Bagian berikut menjelaskan alasannya.
Contoh minimal yang berfungsi
Minta installer menunjukkan apa yang masih kurang:
php artisan panel:installInstaller melaporkan nama module secara langsung:
The published components import these modules, which belong to your
application and are not there yet:
@/routes/two-factor
@/components/UserMenuContent
@/composables/useTwoFactorAuth
`@/routes/*` and `@/actions/*` are generated — run `php artisan wayfinder:generate`.
The rest come with a Laravel Vue starter kit.2
3
4
5
6
7
8
9
Dua command berikut menyelesaikan kasus umum:
php artisan wayfinder:generate # menulis @/routes/* dan @/actions/*
npm run build2
Daftar module
Daftar HOST_MODULES berada di PandaPanel\Support\Installer\FrontendRequirements dan diproses sesuai urutan deklarasinya. Constant tersebut private; gunakan missingHostModules() untuk membacanya dari luar class.
| Module | Jenis | Sumber |
|---|---|---|
@/routes | generated | Wayfinder |
@/routes/login | generated | Wayfinder |
@/routes/register | generated | Wayfinder |
@/routes/password | generated | Wayfinder |
@/routes/two-factor | generated | Wayfinder |
@/routes/verification | generated | Wayfinder |
@/actions/App/Http/Controllers/Settings/ProfileController | generated | Wayfinder |
@/actions/App/Http/Controllers/Settings/SecurityController | generated | Wayfinder |
@/actions/Laravel/Passkeys/Http/Controllers/PasskeyRegistrationController | generated | Wayfinder |
@/components/Heading | component | starter kit |
@/components/UserInfo | component | starter kit |
@/components/UserMenuContent | component | starter kit |
@/components/PasskeyItem | component | starter kit |
@/components/PasskeyRegister | component | starter kit |
@/components/TwoFactorRecoveryCodes | component | starter kit |
@/components/TwoFactorSetupModal | component | starter kit |
@/composables/useTwoFactorAuth | composable | starter kit |
@/types | types | starter kit |
@/types/ui | types | starter kit |
Module mana yang hilang langsung menunjukkan tindakan yang perlu dilakukan. Jika seluruh @/routes/* dan @/actions/* hilang, Wayfinder belum dijalankan. Jika hanya beberapa component yang hilang, application kemungkinan bukan Laravel Vue starter kit dan Anda perlu menyediakan module tersebut sendiri.
Mengapa module ini tidak dikirim package
Dua jenis module secara prinsip memang tidak dapat dipaketkan dengan benar, sedangkan sisanya akan salah jika dipaksakan.
routes/* dan actions/* adalah generated code. Wayfinder menulisnya berdasarkan route table dan controller milik application Anda. Menyimpan copy di dalam package hanya akan menjadi snapshot route application lain yang langsung salah ketika route atau controller di-rename.
Components adalah bagian dari design application Anda. UserMenuContent.vue, misalnya, adalah tempat project menaruh account links miliknya sendiri. Mengirim versi bawaan dari package berarti berpotensi overwrite file yang sudah dimiliki starter kit dan hampir pasti sudah diedit project.
Alasan yang sama membuat userMenuItems() pada panel diserialisasi, bukan langsung dirender. Entries dikirim melalui panel.shell.userMenuItems, lalu component menu milik application yang menentukan cara menggambarnya.
Memeriksa application
FrontendRequirements adalah class yang digunakan installer dan dapat dipanggil dari mana saja:
use PandaPanel\Support\Installer\FrontendRequirements;
FrontendRequirements::missingHostModules();
// ['@/routes/two-factor', '@/components/UserMenuContent']
FrontendRequirements::npmPackages();
// ['@inertiajs/vue3@^3.0.0', '@internationalized/date@^3.12.0', …]
FrontendRequirements::missingNpmPackages();
// pair yang sama, difilter hanya dependency yang belum dideklarasikan application
FrontendRequirements::hasVite();
// bool
FrontendRequirements::missingInertia();
// ['an Inertia root view at resources/views/app.blade.php']
FrontendRequirements::layoutOverrides();
// [['file' => 'resources/js/app.ts', 'line' => 4, 'code' => 'page.default.layout = AppLayout;']]2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| Method | Signature | Return |
|---|---|---|
missingHostModules | static missingHostModules(): array | list<string> — specifier @/… yang tidak ditemukan di disk |
npmPackages | static npmPackages(): array | list<string> — pasangan name@range dari dependencies package |
missingNpmPackages | static missingNpmPackages(): array | daftar yang sama setelah dependency application yang sudah ada dikeluarkan |
hasVite | static hasVite(): bool | apakah vite.config.ts atau vite.config.js tersedia |
missingInertia | static missingInertia(): array | list<string> — deskripsi bagian Inertia yang masih hilang |
layoutOverrides | static layoutOverrides(): array | list<array{file: string, line: int, code: string}> |
Cara module dicari
Pencarian tidak memakai extension di specifier karena Wayfinder menghasilkan .ts, component menggunakan .vue, dan starter kit dapat menulis module sebagai directory dengan index. Setiap nama diuji terhadap:
.ts .vue .d.ts /index.ts /index.vue /index.d.tsTidak ada entry bare '' secara sengaja. File::exists() juga bernilai true untuk directory, sehingga jika bare path ikut diuji, module berbentuk directory akan dianggap tersedia padahal file module sebenarnya belum ada. Contohnya @/types tidak boleh dianggap terpenuhi hanya karena folder resources/js/types/ ada.
.d.ts dimasukkan karena starter kit dapat menyediakan shared types pada resources/js/types/index.d.ts, yang merupakan jawaban valid terhadap import @/types.
NPM dependencies
npmPackages() membaca package.json milik package sendiri, bukan memelihara daftar kedua. Daftar kedua akan langsung berisiko stale ketika component baru meng-import dependency tambahan. missingNpmPackages() membandingkannya dengan package.json application, mencakup dependencies dan devDependencies, karena yang penting adalah dependency dideklarasikan oleh project. Transitive dependency yang kebetulan ada di node_modules hari ini dapat hilang ketika dependency lain di-upgrade.
Installer mencetak hasilnya sebagai command yang dapat langsung dijalankan:
npm install @inertiajs/vue3@^3.0.0 \
@internationalized/date@^3.12.0 \
…
npm run build2
3
4
Pemeriksaan layout override
Ada satu integration seam yang tidak dapat diperbaiki dari dalam package:
page.default.layout = AppLayout; // mengganti shell panelSetiap panel page mendeklarasikan layout sendiri melalui defineOptions({ layout: PanelLayout }). Assignment unconditional di resources/js/app.ts menimpa pilihan tersebut setelah page selesai mendeklarasikannya. Akibatnya panel dirender di shell milik application — sidebar host, bukan panel navigation — tetap HTTP 200 dan tanpa log.
layoutOverrides() membaca resources/js/app.ts, app.js, ssr.ts, dan ssr.js, lalu melaporkan setiap line yang meng-assign .layout tanpa fallback. Tiga bentuk berikut aman:
page.default.layout ??= AppLayout;
page.default.layout ||= AppLayout;
page.default.layout = page.default.layout || AppLayout;2
3
panel:install tidak menyelesaikan proses secara silent jika menemukan assignment bermasalah; installer menyebut file dan line number-nya.
Stand-in pada frontend/host
Repository package menyimpan stand-in minimal untuk seluruh sembilan belas module. File tersebut digunakan hanya ketika type-checking dan membangun package secara mandiri. Tidak ada yang dipublish, diekspor melalui Composer, atau dapat diakses application.
frontend/host/
routes/index.ts login.ts register.ts password.ts two-factor.ts
verification.ts shape.ts
actions/App/Http/Controllers/Settings/ProfileController.ts
App/Http/Controllers/Settings/SecurityController.ts
Laravel/Passkeys/Http/Controllers/PasskeyRegistrationController.ts
components/Heading.vue UserInfo.vue UserMenuContent.vue PasskeyItem.vue
PasskeyRegister.vue TwoFactorRecoveryCodes.vue
TwoFactorSetupModal.vue
composables/useTwoFactorAuth.ts
types/index.ts ui.ts inertia.d.ts2
3
4
5
6
7
8
9
10
11
Resolusi dilakukan dalam dua langkah dan berurutan: @/x berarti cari resources/js/x terlebih dahulu, lalu fallback ke frontend/host/x hanya jika file memang tidak menjadi bagian package.
// tsconfig.json
"paths": {
"@/*": ["./resources/js/*", "./frontend/host/*"]
}2
3
4
vite.config.ts menerapkan urutan resolusi dua tahap yang sama untuk bundler dari daftar yang sama, sehingga build dan type-check tidak pernah memiliki arti @/x yang berbeda. Vite alias biasa tidak dapat merepresentasikan fallback seperti ini — alias hanya satu mapping, dan dua alias akan membuat salah satunya men-shadow yang lain pada semua path.
Fallback inilah yang membuat npm run typecheck dapat berjalan. Tanpanya seluruh published tree akan gagal resolve sembilan belas import, sehingga toolchain tidak sempat memeriksa code lain yang sebenarnya dapat dianalisis.
Menjaga contract tetap jujur
Stand-in yang berbeda dari API starter kit sebenarnya dapat membuat breakage nyata lolos type-check. Tiga mekanisme menjaga contract tetap sinkron:
- Setiap stub mendeklarasikan prop, emit, dan export yang benar-benar dipakai panel — tanpa
anypada surface yang penting — sehingga menghapus prop dari stub akan memecahkan build package. panel:installmemeriksa application nyata untuk seluruh path tersebut, sehingga contract diuji di tempat integration benar-benar terjadi, bukan hanya pada simulation package.FrontendContractTestmemindai setiap published file untuk import@/…yang tidak dipenuhi package dan memastikan semuanya tercantum pada declared list. Module yang mulai di-import tetapi tidak masuk daftar akan membuat installer salah melaporkan application sebagai lengkap, sehingga test harus gagal.
Menulis module sendiri
Jika application bukan starter kit, components dan module tersebut harus Anda sediakan. Stub di frontend/host/ adalah specification mengenai export yang dibutuhkan. Contoh surface yang benar-benar digunakan:
// resources/js/types/ui.ts — flash toast yang ditulis middleware panel
export interface FlashToast {
type: 'success' | 'error' | 'warning' | 'info';
message: string;
url?: string | null;
urlLabel?: string | null;
}2
3
4
5
6
7
// resources/js/composables/useTwoFactorAuth.ts
import type { Ref } from 'vue';
export function useTwoFactorAuth(): {
hasSetupData: Ref<boolean>;
clearTwoFactorAuthData: () => void;
};2
3
4
5
6
7
<!-- resources/js/components/Heading.vue -->
<script setup lang="ts">
defineProps<{
title: string;
description?: string;
variant?: 'default' | 'small';
}>();
</script>2
3
4
5
6
7
8
Ikuti surface/contract-nya, bukan styling-nya — styling tetap sepenuhnya milik application Anda.
Gotchas
- Missing host module membuat
npm run buildgagal pada module specifier. Error-nya benar tetapi muncul pada lapisan yang kurang membantu, sehingga installer memeriksa module terlebih dahulu dan menyebut setiap nama yang hilang. - Wayfinder output pada banyak project tidak di-commit. Fresh clone atau CI job perlu menjalankan
php artisan wayfinder:generatesebelumnpm run build, atau seluruh import@/routes/*gagal. - Directory bukan module. Keberadaan
resources/js/types/tidak cukup untuk memenuhi@/types; harus ada.ts,.d.ts, atau index file yang sesuai. missingNpmPackages()membacapackage.json, bukannode_modules. Dependency yang ter-install tetapi tidak dideklarasikan tetap dilaporkan missing secara sengaja.- Tidak ada file di
frontend/hostyang dipublish. Directory tersebut bukan bagian publish map; import@/…pada application selalu me-resolve keresources/jsmilik application. - Account menu entries adalah responsibility application.
panel.shell.userMenuItemsdikirim melalui wire, tetapiUserMenuContent.vueadalah host component sehingga package tidak menggambarnya.