Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/lentil32/elm-in-svelte
Embed Elm in Svelte with TypeScript
https://github.com/lentil32/elm-in-svelte
elm svelte sveltekit typescript vite
Last synced: 2 months ago
JSON representation
Embed Elm in Svelte with TypeScript
- Host: GitHub
- URL: https://github.com/lentil32/elm-in-svelte
- Owner: lentil32
- License: mit
- Created: 2023-12-02T16:44:11.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-09-04T07:03:14.000Z (4 months ago)
- Last Synced: 2024-09-30T05:07:48.068Z (3 months ago)
- Topics: elm, svelte, sveltekit, typescript, vite
- Language: Svelte
- Homepage: https://lentil32.github.io/elm-in-svelte/
- Size: 86.9 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# How to Embed Elm into Svelte with TypeScript Setup
[Demo](https://lentil32.github.io/elm-in-svelte/)## Method 1. Using `` tag:
1. Install `@types/elm`:
```sh
npm i -D @types/elm
```2. Compile `.elm` into `.js` `elm make src/Main.elm --output=sample.js`
3. Place `sample.js` in the assets directory, e.g., `/static/elm` for SvelteKit.
4. Modify `+page.svelte` to embed `elm.js`:```svelte
<script context="module" lang="ts">
declare let Elm: ElmInstance;import { onMount } from "svelte";
let elmRoot: Node;
onMount(() => {
Elm.Main.init({
node: elmRoot,
});
});
```## Method 2. Using [vite-plugin-elm](https://github.com/hmsk/vite-plugin-elm):
1. Install `vite-plugin-elm`:
```sh
npm i -D vite-plugin-elm
```2. Place the Elm project, including the `elm.json` file, in the library directory, e.g., `/src/lib` in SvelteKit."
3. Modify `vite.config.ts`:
```ts
// vite.config.tsimport { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import { plugin as elm } from "vite-plugin-elm";export default defineConfig({
plugins: [sveltekit(), elm()],
});
```4. Modify `+page.svelte` to embed `Main.elm`:
```svelte
import { onMount } from "svelte";
import { Elm } from "$lib/elm/src/Main.elm";let elmRoot: Node;
onMount(() => {
Elm.Main.init({
node: elmRoot,
});
});
```## Method 1 deep-dive. Embedding multiple Elm modules with script lifecycle management
### Key Considerations
1. States are separated for each component.
2. Programmatic loading of scripts for enhanced performance.
3. Prevention of multiple script loads that can cause errors in Elm and affect performance.File `Elm.svelte`:
```svelte
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}`);
});
};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);
});
```### Build and [minify](https://guide.elm-lang.org/optimization/asset_size) `.elm` files
File `elm-build.sh`:
```sh
#!/bin/shproject_root=$(pwd)
elm_root=$project_root/src/lib/elmbuild_then_uglify() {
local js=$1
local min=$2
shift 2elm 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/examples2for 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
```### Example 1. Using one `elm.js` file containing multiple modules
File: `one-elm-js/+page.svelte`:
```svelte
import Elm from "$lib/elm/Elm.svelte";
const elmJsFilename = "elm.js";
const moduleNames = ["Counter", "TextField"] as const;
Using one `elm.js` file containing multiple modules
Each module is used 3 times.
{#each moduleNames as moduleName}
{#each Array(3) as _, index (`${moduleName}-${index * 3}`)}
{/each}
{/each}```
### Example 2. Using multiple `moduleName.js` files each containing one
File: `js-per-module/+page.svelte`:
```svelte
import Elm from "$lib/elm/Elm.svelte";
const moduleNames = ["Hello", "Bye", "Welcome"] as const;
Using multiple `moduleName.js` files each containing one module
Each module is used 3 times.
{#each moduleNames as moduleName}
{#each Array(3) as _, index (`${moduleName}-${index * 3}`)}
{/each}
{/each}
```## See also
- [joakin/elm-node: Run Elm + JS programs easily in node](https://github.com/joakin/elm-node)
## Reference
- [JavaScript Interop · An Introduction to Elm](https://guide.elm-lang.org/interop/)
- [example of multiple elm apps on a single page](https://gist.github.com/epequeno/2d12d021bd865582b0fcb1509373ba25)---
## Running examples - Method 1, 2
1. Go to `method(1|2)` directory:
```sh
cd method1 # or method2
```2. Install npm packages:
```sh
npm i
```3. (Only for Method 1) Compile `.elm` into `.js`:
```sh
# 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`.
4. Run dev server:
```sh
npm run dev
```## Running examples - Method 1 deep-dive
1. Go to `method1_deepdive` directory:
```sh
cd method1_deepdive
```2. Install npm packages:
```sh
npm i
```3. Compile `.elm` into `.js`:
```sh
npm run elm:build
# Equivalent to `npm run elm:build:examples1`
# && npm run elm:build:examples2`
```4. Run dev server:
```sh
npm run dev
```