<script>
tag:@types/elm
:npm i -D @types/elm
.elm
into .js
elm make src/Main.elm --output=sample.js
sample.js
in the assets directory, e.g., /static/elm
for SvelteKit.+page.svelte
to embed elm.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}" />
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()],
});
+page.svelte
to embed Main.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}" />
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} />
.elm
filesFile 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
elm.js
file containing multiple modulesFile: 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>
moduleName.js
files each containing oneFile: 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>
method(1|2)
directory:cd method1 # or method2
npm i
.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
.
npm run dev
method1_deepdive
directory:cd method1_deepdive
npm i
.elm
into .js
:npm run elm:build
# Equivalent to `npm run elm:build:examples1`
# && npm run elm:build:examples2`
npm run dev