Embedding SVGs into Lustre apps

A demo of how to use embeds/files and html_lustre_converter together.

It contains a very simple lustre demo app, showing our favorite Starfish!

The build script is located in test/build.gleam, as suggested on the Discord.

The icons/lucy.svg was taken from the Gleam website, with the width and height attributes removed for demo purposes.

You can check out the full example project here.

Running

Since html_lustre_converter depends on javascript_dom_parser, only the Deno runtime is supported right now.

gleam run -m build
gleam run -m lustre/dev start

build.gleam source code

NOTE: In a real project, you would add a seperate build path dev dependency. This avoids issues when regenerating files that your project already depends on.

import embeds/files
import embeds/generate
import gleam/io
import gleam/option
import gleam/string
import html_lustre_converter
import javascript_dom_parser/deno_polyfill

pub fn main() {
  // required by html_lustre_converter
  deno_polyfill.install_polyfill()

  let result =
    files.generate(files.Options(
      // where to get files from?
      sources: [
        files.Source(
          // take all files from ./icons (relative to the project root)...
          src: "./icons",
          // generate a module called "icons.gleam"...
          module: "icons",
          // put all found icons in the same module...
          flatten: files.FlattenAll,
          // regardless of how deep the directory tree is...
          max_depth: option.None,
          // and we are only interested in *.svg files...
          filter: fn(_is_dir, path) {
            // we are only interested in svg files.
            string.ends_with(path, ".svg")
          },
        ),
      ],
      // how to convert a file into Gleam source code?
      print: fn(path, data) {
        // if we get a svg text file, convert it using html_lustre_converter.
        // NOTE: we technically already filtered in sources,
        // so this check is reduntant currently!
        case string.ends_with(path, ".svg"), data {
          True, generate.Text(svg) ->
            generate.FunctionBody(html_lustre_converter.convert(svg))
          _, _ -> generate.Skip
        }
      },
      // add the required imports
      header: string.join(
        [
          "import lustre/element/svg",
          // html_lustre_converter generates attribute calls without namespaces
          "import lustre/attribute.{attribute}",
        ],
        "\n",
      ),
      // only regenerate the module if our icons have changed.
      force: False,
    ))

  case result {
    Ok(Nil) -> Nil
    Error(errs) -> io.println_error(generate.describe_errors(errs))
  }
}
Search Document