Skip to content
This repository has been archived by the owner on Nov 29, 2020. It is now read-only.

benwoodward/elixir_svelte_render

Repository files navigation

ARCHIVED

This code works, however, due to the way rendered HTML is passed from node CLI back to Elixir (through stdout), it is very difficult to debug errors. This could probably be fixed but not without a lot of work which I don't have bandwidth for right now. Another big issue is that it doesn't work with es module builds, i.e. if you specify format: 'esm' in your rollup config. This package is tightly coupled with https://github.com/revelrylabs/elixir-nodejs, to get this working with es modules would require a lot of work on both packages.

SvelteRender

Hex.pm License: MIT

WARNING: This is beta. Don't use in production unless you know what you're doing!

Renders Svelte components as HTML

Documentation

The docs can be found at https://hexdocs.pm/svelte_render.

Installation

def deps do
  [
    {:svelte_render, "~> 0.1.0"}
  ]
end

Getting Started with Phoenix

  • Add svelte_render to your dependencies in package.json

    "svelte_render": "file:../deps/svelte_render"
  • Run npm install

    npm install
  • Create a file named render_server.js in your assets folder and add the following

    module.exports = require('svelte_render/priv/server')
  • Add SvelteRender to your Supervisor as a child. We're using the absolute path to ensure we are specifying the correct working directory that contains the render_server.js file we created earlier.

      children = [
        %{
          id: SvelteRender,
          start:
            {SvelteRender, :start_link, [[render_service_path: render_service_path, pool_size: 4]]},
          type: :supervisor
        }
      ]
  • Create a svelte component like:

    <script>
    export let params;
    </script>
    
    <p>Hello from {name}</p>
    
    <style>
    p {
      font-weight: normal;
    }
    </style>
  • Call SvelteRender.render/2 inside the action of your controller

    def index(conn, _params) do
      component_path = "#{File.cwd!()}/assets/src/components/HelloWorld.svelte"
      props = %{params: %{name: "Phoenix"}}
    
      {:safe, helloWorld} = SvelteRender.render(component_path, props)
    
      render(conn, "index.html", helloWorldComponent: helloWorld)
    end

    component_path can either be an absolute path or one relative to the render service. The stipulation is that components must be in the same path or a sub directory of the render service. This is so that the babel compiler will be able to compile it. The service will make sure that any changes you make are picked up. It does this by removing the component_path from node's require cache. If do not want this to happen, make sure to add NODE_ENV to your environment variables with the value production.

  • Render the component in the template

    <%= raw @helloWorldComponent %>
  • To add routing for your components, create a base component, e.g. assets/src/App.svelte and use page.js

Taken from: https://jackwhiting.co.uk/posts/setting-up-routing-in-svelte-with-pagejs/

<script>
  import router from 'page';
  import Home from './routes/Home.svelte';

  let page;
  let params = {name: "svelte"};

  router('/', () => page = Home)

  router.start();
</script>

<svelte:component this={page} params={params} />
  • To hydrate server-created components in the client, add the following to your app.js

    import App from './components/App.svelte';
    window.app = new App({
      target: document.querySelector('#svelte'),
      hydrate: true
    });
  • Example rollup config:

The postcss configs allow you to import a global.scss file into your App.svelte like so:

<style global type="scss" >
  @import "../styles/global.scss";
</style>
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';

const production = !process.env.ROLLUP_WATCH;
const preprocess = sveltePreprocess({
  scss: {
    includePaths: ['src'],
  },
  postcss: {
    plugins: [require('postcss-import')({path: ['src/styles']})],
  },
});

export default {
  input: 'src/app.js',
  output: {
    sourcemap: true,
    format: 'iife',
    name: 'app',
    file: '../priv/static/js/app.js'
  },
  plugins: [
    svelte({
      hydratable: true,

      // enable run-time checks when not in production
      dev: !production,
      preprocess,

      css: css => {
        css.write('../priv/static/css/app.css');
      }
    }),

    resolve({
      browser: true,
      // a dependency in node_modules may have svelte inside it's node_modules folder
      // dedupe option prevents bundling those duplicates
      dedupe: ['svelte']

    }),
    commonjs(),

    // If we're building for production (npm run build
    // instead of npm run dev), minify
    production && terser()
  ],
  watch: {
    clearScreen: false
  }
};
  • Add rollup to your list of watchers in config/dev.exs

    config :your_app, YourApp.Endpoint,
      http: [port: 4000],
      debug_errors: true,
      code_reloader: true,
      check_origin: false,
      watchers: [
        rollup: [
          "--config",
          "--watch",
          cd: Path.expand("../assets", __DIR__)
        ]
      ]

You should now be able to run mix phx.server and your Svelte components with update with live reloading

Development

For debugging you can use priv/cli.js:

node priv/cli --component "/Full/path/to/Component.svelte" --props "{name: 'Svelte'}"
// output:
//   { error: null,
//      markup: '<p>Hello from Svelte</p>',
//      component: '/Users/you/Dev/project_folder/assets/src/Component.svelte' }

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published