- Install
@types/elm
:
npm i -D @types/elm
- Compile
.elm
into.js
elm make src/Main.elm --output=sample.js
- Place
sample.js
in the assets directory, e.g.,/static/elm
for SvelteKit. - Modify
+page.svelte
to embedelm.js
:
<script context="module" lang="ts">
declare let Elm: ElmInstance;
</script>
<script lang="ts">
import { onMount } from "svelte";
let elmRoot: Node;
onMount(() => {
Elm.Main.init({
node: elmRoot,
});
});
</script>
<svelte:head>
<script src="/elm/sample.js"></script>
</svelte:head>
<div bind:this="{elmRoot}" />
Method 2. Using vite-plugin-elm:
- Install
vite-plugin-elm
:
npm i -D vite-plugin-elm
-
Place the Elm project, including the
elm.json
file, in the library directory, e.g.,/src/lib
in SvelteKit." -
Modify
vite.config.ts
:
// vite.config.ts
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import { plugin as elm } from "vite-plugin-elm";
export default defineConfig({
plugins: [sveltekit(), elm()],
});
- Modify
+page.svelte
to embedMain.elm
:
<script lang="ts">
import { onMount } from "svelte";
import { Elm } from "$lib/elm/src/Main.elm";
let elmRoot: Node;
onMount(() => {
Elm.Main.init({
node: elmRoot,
});
});
</script>
<div bind:this="{elmRoot}" />
- States are separated for each component.
- Programmatic loading of scripts for enhanced performance.
- Prevention of multiple script loads that can cause errors in Elm and affect performance.
File Elm.svelte
:
<script context="module" lang="ts">
declare let Elm: ElmInstance;
type Callback = () => void;
const scriptsLoaded = new Set<string>();
const loadingPromises: Record<string, Promise<void>> = {};
const loadScript = (src: string, callback: Callback): void => {
if (scriptsLoaded.has(src)) {
callback();
return;
}
if (!loadingPromises[src]) {
loadingPromises[src] = new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = src;
script.async = true;
script.onload = () => {
scriptsLoaded.add(src);
resolve();
};
script.onerror = (event) => {
console.error(`Error loading script ${src}:`, event);
reject(new Error(`Script load error: ${src}`));
};
document.head.appendChild(script);
});
}
loadingPromises[src]?.then(callback).catch(() => {
console.error(`Failed to load script: ${src}`);
});
};
</script>
<script lang="ts">
import { onMount } from "svelte";
import { assets } from "$app/paths";
export let elmJsFilename: `${string}.js`;
export let moduleName: string;
const elmAssetsDirectory: string = `${assets}/elm`;
const elmJsFilePath: string = `${elmAssetsDirectory}/${elmJsFilename}`;
let elmRoot: Node;
const handleLoad: Callback = () => {
if (Elm && Elm[moduleName]) {
Elm[moduleName].init({ node: elmRoot });
} else {
console.error("Elm module not found or not loaded: ", moduleName);
}
};
onMount(() => {
loadScript(elmJsFilePath, handleLoad);
});
</script>
<div bind:this={elmRoot} />
Build and minify .elm
files
File elm-build.sh
:
#!/bin/sh
project_root=$(pwd)
elm_root=$project_root/src/lib/elm
build_then_uglify() {
local js=$1
local min=$2
shift 2
elm make --output="$js" --optimize "$@"
uglifyjs "$js" \
--compress 'pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe' |
uglifyjs --mangle --output "$min"
rm $js
}
build_example1() {
local js="$project_root/static/elm/elm.unmin.js"
local min="$project_root/static/elm/elm.js"
cd $elm_root/examples1
build_then_uglify $js $min src/*
}
build_example2() {
cd $elm_root/examples2
for elm_file in src/*.elm; do
base_name=$(basename "$elm_file" .elm)
js="${project_root}/static/elm/${base_name}.unmin.js"
min="${project_root}/static/elm/${base_name}.js"
build_then_uglify $js $min $elm_file
done
}
if [ "$1" = "1" ]; then
build_example1
elif [ "$1" = "2" ]; then
build_example2
fi
File: one-elm-js/+page.svelte
:
<script lang="ts">
import Elm from "$lib/elm/Elm.svelte";
const elmJsFilename = "elm.js";
const moduleNames = ["Counter", "TextField"] as const;
</script>
<section>
<hgroup>
<h4>Using one `elm.js` file containing multiple modules</h4>
<h5>Each module is used 3 times.</h5>
</hgroup>
</section>
<section>
{#each moduleNames as moduleName}
{#each Array(3) as _, index (`${moduleName}-${index * 3}`)}
<div>
<Elm {elmJsFilename} {moduleName} />
</div>
{/each}
{/each}
</section>
File: js-per-module/+page.svelte
:
<script lang="ts">
import Elm from "$lib/elm/Elm.svelte";
const moduleNames = ["Hello", "Bye", "Welcome"] as const;
</script>
<hgroup>
<h4>Using multiple `moduleName.js` files each containing one module</h4>
<h5>
Each module is used 3 times.
</h5>
</hgroup>
<div>
{#each moduleNames as moduleName}
{#each Array(3) as _, index (`${moduleName}-${index * 3}`)}
<Elm elmJsFilename={`${moduleName}.js`} {moduleName} />
{/each}
{/each}
</div>
- Go to
method(1|2)
directory:
cd method1 # or method2
- Install npm packages:
npm i
- (Only for Method 1) Compile
.elm
into.js
:
# The command is equivalent to
# `cd ./src/lib/elm/elm-sample \
# && elm make src/Main.elm --output=../../../../static/elm.js`.
npm run elm:build
elm:make
npm script is defined in package.json
.
- Run dev server:
npm run dev
- Go to
method1_deepdive
directory:
cd method1_deepdive
- Install npm packages:
npm i
- Compile
.elm
into.js
:
npm run elm:build
# Equivalent to `npm run elm:build:examples1`
# && npm run elm:build:examples2`
- Run dev server:
npm run dev