Setup Phoenix with Svelte+Rollup

Joshua Nussbaum - Jan 29 '20 - - Dev Community

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
Enter fullscreen mode Exit fullscreen mode

Hop into the project and let's setup git:

cd my_app
git init
git add .
git commit --message "Initial commit 🐣"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Create a place to put global css:

touch assets/css/global.css
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Install those packages:

(cd assets && yarn)
Enter fullscreen mode Exit fullscreen mode

Make sure to exclude all node_modules from version control:

echo /assets/node_modules >> .gitignore
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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__)]]
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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'
  }
})
Enter fullscreen mode Exit fullscreen mode

Start the server

mix phx.server
Enter fullscreen mode Exit fullscreen mode

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

. . . . . . . . . . . . . . . . . . .