By default, Phoenix uses Webpack for asset bundling. This step-by-step guide shows how to use rollup instead and configure rollup to use Svelte.
Why Rollup?
Rollup is especially great at tree-shaking, which results in the smallest bundle size. It originates from Rich Harris, who is also the creator of Svelte. This makes it an ideal choice for Svelte projects.
Personally, I find it easier to understand than Webpack (but that's just me).
Phoenix setup
Start by creating a new project without Webpack:
mix phx.new my_app --no-webpack
Hop into the project and let's setup git
:
cd my_app
git init
git add .
git commit --message "Initial commit 🐣"
Assets folder setup
Since we told phx.new
to not use webpack
, we need to create the assets
directories ourselves:
mkdir -p assets/js assets/css assets/static/images
Create a place to put global css:
touch assets/css/global.css
Add an assets/package.json
to define all the frontend scripts and dependencies:
{
"name": "assets",
"version": "1.0.0",
"scripts": {
"deploy": "rollup --config",
"watch": "rollup --config --watch"
},
"dependencies": {
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.0.0",
"@rollup/plugin-node-resolve": "^7.0.0",
"rollup": "^1.20.0",
"rollup-plugin-postcss": "^2.0.5",
"rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-terser": "^5.1.2",
"svelte": "^3.0.0",
"svelte-preprocess": "^3.3.1"
}
}
Install those packages:
(cd assets && yarn)
Make sure to exclude all node_modules
from version control:
echo /assets/node_modules >> .gitignore
Rollup config
Add a basic configuration in assets/rollup.config.js
:
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import autoPreprocess from 'svelte-preprocess';
import postcss from 'rollup-plugin-postcss';
import { terser } from 'rollup-plugin-terser';
// it's production mode if MIX_ENV is "prod"
const production = process.env.MIX_ENV == "prod";
export default {
// main entry point
input: 'js/main.js',
// define output path & format and request sourcemaps
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: '../priv/static/js/app.js'
},
// define all the plugins we'd like to use
plugins: [
// the postcss plugin is used to preprocess css
// for more info, see: https://www.npmjs.com/package/rollup-plugin-postcss
postcss(),
// the svelte plugin converts .svelte files to .js equivalent
svelte({
// the preprocessor plugin allows you to use <style type="scss"> or <script lang="typescript"> inside .svelte files
// for more info, see: https://www.npmjs.com/package/svelte-preprocess
preprocess: autoPreprocess(),
// enable run-time checks when not in production
dev: !production,
// take css output and write it to priv/static
css: css => {
css.write('../priv/static/css/app.css');
}
}),
// the resolve plugin resolves modules located in node_modules folder
resolve({
// resolve modules that are designed to run in browser
browser: true,
// a dependency in node_modules may have svelte inside it's node_modules folder
// dedupe option prevents bundling those duplicates
dedupe: ['svelte']
}),
// use commonjs import convention
commonjs(),
// for production builds, use minification
production && terser()
],
// don't clear terminal screen after each re-compilation
watch: {
clearScreen: false
}
}
In dev mode, Phoenix can run yarn watch
for us. Simply add a watcher in config/dev.exs
:
--- watchers: []
+++ watchers: [yarn: ["watch", cd: Path.expand("../assets", __DIR__)]]
Using Svelte
Let's create our first svelte file assets/js/App.svelte
:
<script>
export let name
</script>
<style>
h1 { color: teal; }
</style>
<h1>Hello {name}!</h1>
We need something that will mount this to the DOM, so create an entry point .js
file assets/js/main.js
:
// import phoenix html helpers (optional)
import 'phoenix_html'
// any css we import will be bundled in app.css
import '../css/global.css'
// import our component
import App from './App.svelte'
// instantiate the component
new App({
// mount it to `document.body`
target: document.body,
// pass some props (optional)
props: {
name: 'world'
}
})
Start the server
mix phx.server
Et Voila, you should see "Hello World" ✨
Example Repo
You can find a fully working repo here:
https://github.com/joshnuss/phoenix_svelte_rollup_example
Also, I'm working on a video course for svelte: https://svelte.video
✌