Skip to content

Instantly share code, notes, and snippets.

@sortofsleepy
Last active May 31, 2024 11:42
Show Gist options
  • Save sortofsleepy/17f43573c1a897cf77cebee5fb6efb70 to your computer and use it in GitHub Desktop.
Save sortofsleepy/17f43573c1a897cf77cebee5fb6efb70 to your computer and use it in GitHub Desktop.

Rollup / SSR / Phoenix LiveView notes

(IN PROGRESS)

Rollup can be configured to build the necessary parts to do SSR with JS frameworks. I've only tested w/ Solid but with some modifications, you can also adapt the following for things like Svelte and perhaps other frameworks.

This is actually based largely off of LiveSvelte but I wanted to use something more common these days like Rollup - this is however just gonna be Rollup since we don't need the other Vite parts for this.

You'll need

  • a build script
  • the Elixir Node package
  • Optionally a Rollup config file.

NPM packages needed

(adjust as needed / desired - these were used to keep things simple-ish)

  • @babel/core
  • @rollup/plugin-babel
  • @rollup/plugin-commonjs
  • @rollup/plugin-node-resolve
  • babel-preset-solid
  • chokidar or some other watcher package.

Build script

  • See the docs for an example.
  • Refer to the Phoenix docs for how to add a new watcher for your build script
  • You can use the config loader functionality or just do everything in the build script itself.
    • Note that when loading a config, the format property of the second argument is the output format you want; the loader seemingly ignores current settings inside the config. For LiveView stuff, you'll want umd as other options will attempt to overwrite the top variable of the browser for some reason. Alternatively you can compile each Phoenix component separately and add script tags for each.

Other Notes

  • Elixir-Node requires you to have an export of functions using the old cjs format, ie
// in <module name>.js
module.exports.<function name> = function (){}

When called, it will end up looking something like


NodeJS.call!({"<module name>", "<function>"}, [<arguments])
  • To trigger rebuilds of the JS on change, you may still need something like chokidar.

  • You can trigger the browser to reload when you change a file by modifying the live_reload field in config/dev.exs

  • Solid JS SSR Docs

Example library module for how to call things in your Solid code.

defmodule LiveSolid do

  def render() do
    # server = file/module name
	# :render = refers to the function within the module to call
	# [] is for any arguments you need to pass to the Nodejs side. 
    try do
      NodeJS.call!({"server", :render}, [], binary: true)
    catch
      :exit, {:noproc, message} ->
	IO.inspect(message)
      end 
    
  end

  # defines where to look for the module.
  # call this for the path parameter when you add the NodeJS supervisor (provided by elixir-nodejs)
  def server_path() do
    {:ok, path} = :application.get_application()
    Application.app_dir(path,"priv/server")
  end 
  

end 

@sortofsleepy
Copy link
Author

sortofsleepy commented May 30, 2024

Ok! Turns out it seems like the Babel path has issues when trying to build tsx for some reason. All the solutions I could find would work with jsx but not tsx for some strange reason so switching to Vite eventually even though you won't need like 90% of what it provides.
Will update file once I work out everything.

Turns out Rollup is fine after all. Confusingly enough, even there is a Babel preset package for Typescript, you still have to "turn on" the reading of Typescript files in the Babel plugin itself; I was expecting the preset to automatically do that.

Here is a sample build function

let input = {

	input: './ssr/ssr.tsx',

	treeshake:false,
	plugins: [

	 
	    nodeResolve({preferBuiltins: true, exportConditions: ["solid", "node"]}),
	    babel({
		babelHelpers: "bundled",
		extensions:[".js",".ts",".tsx"],
		presets: [
		    ["solid", {generate: "ssr", hydratable: true}],
		]
	    }),
	    common()
	],
	preserveEntrySignatures: false
    };

    let bundle
    try {
	bundle = await rollup(input)
	await bundle.write(
	    {
		dir: "../priv/ssr",
		format: "umd"
	    }
	)
    }catch(e){
	console.log("error ", e)	
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment