See it live here
module.exports = {
...
entry: {
svelte: {
import: './node_modules/svelte/src/index-client.js',
library: {
name: 'svelte',
type: 'global'
}
}
...
},
...
};
Add the svelte-loader
module.exports = {
...
module: {
rules: [
{
test: /\.(svelte|svelte\.js)$/,
use: 'svelte-loader'
},
{
// required to prevent errors from Svelte on Webpack 5+, omit on Webpack 4
test: /node_modules\/svelte\/.*\.mjs$/,
resolve: {
fullySpecified: false
}
}
]
},
resolve: {
extensions: ['.json', '.js', '.jsx', '.svelte'],
conditionNames: ['svelte']
}
...
}
In src/Counter.svelte
, let's define the following:
<script>
export let count = 0;
console.log("Counter loaded!");
export function handleClick() {
count += 1;
return count;
}
export const getCount = () => count;
</script>
<button class="counter btn btn-primary" on:click={handleClick}>
clicks: {count}
</button>
And, we'll have the bundler make us a .js
distributable:
module.exports = {
...
entry:{
Counter: {
import: './src/Counter.svelte',
library: {
name: 'Counter',
type: 'global'
}
}
}
...
}
Since we have multiple entry points that will overlap (e.g., svelte internals in this case), we will use a single runtime. The runtime in Webpack takes care of loading for us.
module.exports = {
...
optimization: {
runtimeChunk: 'single'
},
...
};
If you skip this step, you will get an obscure error along the lines of:
Uncaught TypeError: Cannot read properties of undefined (reading 'call')
<script src="dist/runtime.js"></script>
<script src="dist/svelte.js"></script>
<script src="dist/Counter.js"></script>
After the scripts above, add:
<script>
const container = document.getElementById('counter-container')
const counter = svelte.mount(Counter,
{
target: container,
props: {
count: 1
}
}
);
</script>
Notice, we initialized the count to 1 by providing it as a property.
The steps outlined here are usable for other bundlers too. The setup difficulty is less burdensome the technical debt and maintenance headache of glue logic.