T
TyphonJSβ€’6mo ago
geoidesic

Dynamic Imports work on dev server but not in build?

I'm having something weird. I'm doing dynamic async imports for certain dynamic components. It's working just find when run via a dev server (e.g. npm run dev) but once I build it, then the imports break. From the error message it seems that the import is done via URL, rather than via code. The URL exists and is browsable in the dev server, but not once built. Am I doing it wrong? Or are dynamic imports not available to the build? E.g.
const importComponent = async (importPath, componentName) => {
const { default: Component } = await import(
/* @vite-ignore */ `../${importPath}${componentName}.svelte`
);
return Component;
};

let importPath;

<div class="tab-content">
{#each tabs as tab}
{#if tab.id === activeTab}
{#if typeof tab.component === 'object'}
<svelte:component this={tab.component} />
{/if}
{#if typeof tab.component === 'string' && importPath}
{#await importComponent(importPath, tab.component)}
<i class="spinner fas fa-circle-notch fa-spin"></i>
{:then Component}
<svelte:component this={Component} />
{:catch error}
<p>Error loading component: {error.message}</p>
{/await}
{/if}
{/if}
{/each}
</div>
</div>
const importComponent = async (importPath, componentName) => {
const { default: Component } = await import(
/* @vite-ignore */ `../${importPath}${componentName}.svelte`
);
return Component;
};

let importPath;

<div class="tab-content">
{#each tabs as tab}
{#if tab.id === activeTab}
{#if typeof tab.component === 'object'}
<svelte:component this={tab.component} />
{/if}
{#if typeof tab.component === 'string' && importPath}
{#await importComponent(importPath, tab.component)}
<i class="spinner fas fa-circle-notch fa-spin"></i>
{:then Component}
<svelte:component this={Component} />
{:catch error}
<p>Error loading component: {error.message}</p>
{/await}
{/if}
{/if}
{/each}
</div>
</div>
So this works if I run the dev server. It seems to find it via URL:
http://localhost:30001/modules/foundryvtt-my-module/components/organisms/dnd5e/Tabs/Abilities.svelte
http://localhost:30001/modules/foundryvtt-my-module/components/organisms/dnd5e/Tabs/Abilities.svelte
However, once built, the path it looks for changes, leaving out the module id and reporting a 404:
http://localhost:3000/modules/components/organisms/dnd5e/Tabs/Abilities.svelte
http://localhost:3000/modules/components/organisms/dnd5e/Tabs/Abilities.svelte
Even if I correct the ommssion in the URL manually I still see a 404 Any ideas?
18 Replies
Vauxs
Vauxsβ€’6mo ago
Svelte is a compiled language, the issue here most likely lies in the fact that you cannot just dynamically import a Svelte component after the build process is complete. In build, there is no .svelte remaining. It looks like dynamic imports are supported, but I imagine you have to explicitly state the components you are going to import somewhere so the compiler / bundler can pick them up regardless.
Vauxs
Vauxsβ€’6mo ago
@geoidesic You may be interested in this answer to your problem https://stackoverflow.com/a/71350674
Stack Overflow
Svelte/SvelteKit: Dynamic import of components with variable
I want to dynamically import components without importing a specific component. I want to set the component name with a variable, received from the store: <script lang="ts"> // ...
Vauxs
Vauxsβ€’6mo ago
(The Vite ignore also does not aspire much confidence lol)
TyphonJS (Michael)
TyphonJS (Michael)β€’6mo ago
This veers into territory that is beyond TRL and specific to a customized build setup on your side which you'll have to do the hard work to debug and fix yourself. I'd like to help, but I can't spend my time debugging your customized build setup. There likely is a solution and I'd have to research the best solution, but this veers into where I'd need to charge a consulting fee which of course at that point you just need to buck up and figure it out yourself. I generally question why this is required. This appears to be another instance and is not the first time that you've jumped to overcomplicated solutions that otherwise aren't necessary. You may have better luck examining various search results on the topic / try some of them and if that fails ask on the main Svelte Discord server and if you are lucky someone will help you or point to reasonable articles and advice. You could also read the documentation for whatever Vite / Rollup plugins you are attempting to use as there are limitations to dynamic import. While at it read up on Vite and learn how it works better since you are diving into deeper waters. The reason things just work in the dev server is because the dev server is unbundled which is not the case for production builds which impose additional restrictions in configuration / usage for dynamic imports. A Foundry specific consideration you'll also need to test is when a route prefix is configured for the server. I don't want to discourage you, but I'd be glad to help in the future for anything related to TRL.
geoidesic
geoidesicOPβ€’6mo ago
Thank you! This link was very helpful. For reference, I went from this:
export const importComponent = async (importPath, componentName) => {
const { default: Component } = await import(
/* @vite-ignore */ `${importPath}${componentName}.svelte`
);
return Component;
};
export const importComponent = async (importPath, componentName) => {
const { default: Component } = await import(
/* @vite-ignore */ `${importPath}${componentName}.svelte`
);
return Component;
};
with template
<div class="tab-content">
{#each tabs as tab}
{#if tab.id === activeTab}
{#if typeof tab.component === 'object'}
<svelte:component this={tab.component} />
{/if}
{#if typeof tab.component === 'string' && importPath}
{#await importComponent(importPath, tab.component)}
<i class="spinner fas fa-circle-notch fa-spin"></i>
{:then Component}
<svelte:component this={Component} />
{:catch error}
<p>Error loading component: {error.message}</p>
{/await}
{/if}
{/if}
{/each}
</div>
<div class="tab-content">
{#each tabs as tab}
{#if tab.id === activeTab}
{#if typeof tab.component === 'object'}
<svelte:component this={tab.component} />
{/if}
{#if typeof tab.component === 'string' && importPath}
{#await importComponent(importPath, tab.component)}
<i class="spinner fas fa-circle-notch fa-spin"></i>
{:then Component}
<svelte:component this={Component} />
{:catch error}
<p>Error loading component: {error.message}</p>
{/await}
{/if}
{/if}
{/each}
</div>
to this
$: tabComponents = {}

onMount(async () => {
for (const tab of tabs) {
const module = await import(`~/src/components/organisms/dnd5e/Tabs/${tab.component}.svelte`);
tabComponents[tab.component] = module.default;
}
});
$: tabComponents = {}

onMount(async () => {
for (const tab of tabs) {
const module = await import(`~/src/components/organisms/dnd5e/Tabs/${tab.component}.svelte`);
tabComponents[tab.component] = module.default;
}
});
with this template
<div class="tab-content">
{#each tabs as tab}
{#if tab.id === activeTab && tabComponents[tab.component]}
<svelte:component this={tabComponents[tab.component]} {sheet} />
{/if}
{/each}
</div>
<div class="tab-content">
{#each tabs as tab}
{#if tab.id === activeTab && tabComponents[tab.component]}
<svelte:component this={tabComponents[tab.component]} {sheet} />
{/if}
{/each}
</div>
... which works!
TyphonJS (Michael)
TyphonJS (Michael)β€’6mo ago
The struggle bus is real and everyone gets on it from time to time. Good job...
No description
Vauxs
Vauxsβ€’6mo ago
stup? πŸ˜…
TyphonJS (Michael)
TyphonJS (Michael)β€’6mo ago
I asked ChatGPT and it said it was an inside joke that things are going wrong... uh huh... right... ;P
Vauxs
Vauxsβ€’6mo ago
also I like how the bus is on what I presume to be a bicycle track if not just a railline ai cant stop goofing up
TyphonJS (Michael)
TyphonJS (Michael)β€’6mo ago
No description
Vauxs
Vauxsβ€’6mo ago
"likely"
TyphonJS (Michael)
TyphonJS (Michael)β€’6mo ago
Right! ;P It doesn't know what is in the image. I was like.. Huh.. Maybe that is some kind of programmer joke or something... Nope..
geoidesic
geoidesicOPβ€’6mo ago
It works BUT... it also proliferates a whole bunch of unexpected mapping files for every import, which – with the vite config as it stands – just get dumped into the root of the project. Not ideal. It works but it's messy.
No description
TyphonJS (Michael)
TyphonJS (Michael)β€’6mo ago
Yes... If you read the documentation you'd understand why... πŸ˜‰ Should I tell you or perhaps you go and read it yourself...
geoidesic
geoidesicOPβ€’6mo ago
To which documentation are you referring? A link would indeed be welcome.
TyphonJS (Michael)
TyphonJS (Michael)β€’6mo ago
Vite documentation. When you do
await import(`~/src/components/organisms/dnd5e/Tabs/${tab.component}.svelte`);
await import(`~/src/components/organisms/dnd5e/Tabs/${tab.component}.svelte`);
Vite doesn't know what files you might access at runtime so it has to target all of the files in the Tabs directory. It turns the above into a glob `~/src/components/organisms/dnd5e/Tabs/*.svelte. So all Svelte files in that directory must be separately compiled because you can request any of them. To reduce this you can create a more specific directory that only contains the Svelte files that you might reference dynamically. There also are other dynamic import mechanisms that look like you can write negative patterns and create more specific directives for Vite to control which files are targetted for potential dynamic import. This is import.meta.glob. I haven't used any of that personally, so you'll have to go read the documentation.
geoidesic
geoidesicOPβ€’6mo ago
Ok thank you. I'll have a think about how brave I'm feeling πŸ˜…
TyphonJS (Michael)
TyphonJS (Michael)β€’6mo ago
.... crack a book it won't hurt.... ;P
Want results from more Discord servers?
Add your server