Vize

Elixir bindings for the Vize Vue.js toolchain via Rust NIFs.

Compile, lint, and analyze Vue Single File Components at native speed — including Vapor mode IR for BEAM-native SSR.

Features

Installation

def deps do
  [
    {:vize, "~> 0.6.0"}
  ]
end

Requires a Rust toolchain (rustup recommended). The NIF compiles automatically on mix compile.

Usage

Compile SFC

{:ok, result} = Vize.compile_sfc("""
<template>
  <button @click="count++">{{ count }}</button>
</template>

<script setup>
import { ref } from &#39;vue&#39;
const count = ref(0)
</script>

<style scoped>
button { color: blue; }
</style>
""")

result.code     # Generated JavaScript
result.css      # Compiled CSS
result.errors   # []

Pass a filename for stable scoped CSS data-v-xxxx attributes and content hashes:

{:ok, result} = Vize.compile_sfc(source, filename: "App.vue")

result.template_hash  # "de5ddf78a0f8d31a"
result.style_hash     # "3efafd39ec9747f9"
result.script_hash    # "1a8dae0fef50c189"

Vapor Mode

{:ok, result} = Vize.compile_vapor("<div>{{ msg }}</div>")
result.code       # Vapor JS (no virtual DOM)
result.templates  # Static HTML templates

Vapor IR (for BEAM-native SSR)

{:ok, ir} = Vize.vapor_ir("<div :class=\"cls\">{{ msg }}</div>")

ir.templates             # ["<div> </div>"]
ir.element_template_map  # [{0, 0}]  — element ID → template index
ir.block                 # %{operations: [...], effects: [...], returns: [...]}

The IR exposes every Vue construct as Elixir maps with :kind atoms:

Kind Vue Feature
:set_text{{ expr }}
:set_prop:attr="expr"
:set_htmlv-html
:set_dynamic_propsv-bind="obj"
:set_event@event="handler"
:if_nodev-if / v-else-if / v-else
:for_nodev-for
:create_component<Component />
:directivev-show, v-model, custom

Static expressions are tagged as {:static, "value"} tuples, dynamic expressions are plain strings.

Vapor Split (for Phoenix LiveView)

{:ok, split} = Vize.vapor_split("<div :class=\"cls\"><p>{{ msg }}</p></div>")

split.statics  # ["<div class=\"", "\"><p>", "</p></div>"]
split.slots    # [%{kind: :set_prop, values: ["cls"]}, %{kind: :set_text, values: ["msg"]}]

Produces a statics/slots split ready for %Phoenix.LiveView.Rendered{}. All HTML manipulation (tag tree parsing, marker injection, splitting) happens in the NIF. Sub-blocks for v-if / v-for are recursively split.

Used by PhoenixVapor to render Vue templates as native LiveView output.

SSR Compilation

{:ok, result} = Vize.compile_ssr("<div>{{ msg }}</div>")
result.code      # JS with _push() calls
result.preamble  # Import statements

Template Compilation

{:ok, result} = Vize.compile_template("<div v-if=\"show\">{{ msg }}</div>")
result.code     # Render function
result.helpers  # ["createElementVNode", "toDisplayString", ...]

Lint

{:ok, diagnostics} = Vize.lint("<template><img></template>", "App.vue")

Parse SFC

{:ok, descriptor} = Vize.parse_sfc(source)
descriptor.template      # %{content: "...", lang: nil, ...}
descriptor.script_setup  # %{content: "...", setup: true, ...}
descriptor.styles        # [%{content: "...", scoped: true, ...}]

CSS Compilation

Standalone CSS compilation via LightningCSS — parse, autoprefix, and minify CSS independently of SFC compilation:

{:ok, result} = Vize.compile_css(".foo { color: red; user-select: none }", minify: true)
result.code
# ".foo{color:red;-webkit-user-select:none;user-select:none}"

With Vue scoped styles:

{:ok, result} = Vize.compile_css(".foo { color: red }", scoped: true, scope_id: "data-v-abc123")
result.code
# ".foo[data-v-abc123] { color: red }"

Browser targeting:

{:ok, result} = Vize.compile_css(css, targets: %{chrome: 80, firefox: 78, safari: 14})

License

MIT