Skip to content

Commit e2fc1d7

Browse files
committed
feat: improve component loading strategy in useModuleSlots
- Refined production loading to always use manifest-based loading from the module's own build to prevent 404 errors. - Enhanced development mode to prioritize build-time glob loading with HMR support and fallback to runtime dynamic loading. - Standardized error handling with detailed logging for failed component loads in both production and development modes.
1 parent e85557c commit e2fc1d7

1 file changed

Lines changed: 35 additions & 8 deletions

File tree

src/resources/js/composables/useModuleSlots.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,42 @@ async function loadDynamicComponent(
150150

151151
/**
152152
* Resolve a component path to an async component.
153-
* First tries the build-time glob, then falls back to runtime loading.
154-
* CSS injection only happens for runtime-loaded modules (static modules use Vite's built-in CSS handling).
153+
*
154+
* In production, always uses manifest-based loading from the module's own build.
155+
* The build-time glob creates chunks in the main app's output with different content
156+
* hashes than the module's independent build, causing 404s when builds are out of sync.
157+
*
158+
* In development, tries the build-time glob first (HMR support), then falls back
159+
* to runtime loading.
155160
*/
156161
function resolveComponent(module: string, componentPath: string): Component | null {
157-
// Build the glob path: ../../../modules/{module}/resources/js/{componentPath}
158-
const globPath = `../../../modules/${module}/resources/js/${componentPath}`;
162+
// In production, always use manifest-based loading from the module's own build.
163+
if (!import.meta.env.DEV) {
164+
return defineAsyncComponent({
165+
loader: async () => {
166+
const result = await loadDynamicComponent(module, componentPath);
167+
if (!result) {
168+
throw new Error(`Component not found: ${module}/${componentPath}`);
169+
}
170+
void injectModuleStyles(module);
171+
return result;
172+
},
173+
delay: 0,
174+
timeout: 15000,
175+
onError: (error, _retry, fail, attempts) => {
176+
console.error(
177+
`[ModuleSlots] Failed to load component (attempt ${attempts}):`,
178+
`\n Module: ${module}`,
179+
`\n Component: ${componentPath}`,
180+
`\n Error: ${error instanceof Error ? error.message : error}`,
181+
);
182+
fail();
183+
},
184+
});
185+
}
159186

160-
// Try build-time glob first (fastest, for modules present at build time)
187+
// Development: try build-time glob first (fastest, HMR support)
188+
const globPath = `../../../modules/${module}/resources/js/${componentPath}`;
161189
const staticLoader = moduleComponents[globPath];
162190
if (staticLoader) {
163191
return defineAsyncComponent({
@@ -177,19 +205,18 @@ function resolveComponent(module: string, componentPath: string): Component | nu
177205
});
178206
}
179207

180-
// Fallback: try runtime dynamic loading for modules installed after build
208+
// Development fallback: runtime dynamic loading
181209
return defineAsyncComponent({
182210
loader: async () => {
183211
const result = await loadDynamicComponent(module, componentPath);
184212
if (!result) {
185213
throw new Error(`Component not found: ${module}/${componentPath}`);
186214
}
187-
// Inject CSS only for runtime-loaded modules (manifest already cached by loadDynamicComponent)
188215
void injectModuleStyles(module);
189216
return result;
190217
},
191218
delay: 0,
192-
timeout: 15000, // Slightly longer timeout for network requests
219+
timeout: 15000,
193220
onError: (error, _retry, fail, attempts) => {
194221
console.error(
195222
`[ModuleSlots] Failed to load dynamic component (attempt ${attempts}):`,

0 commit comments

Comments
 (0)