Home
Softono
b

bcsabaengine

Professional software vendor delivering innovative solutions on the Softono platform. Specialized in both open-source and proprietary software development.

Total Products
1

Software by bcsabaengine

svelteesp32
Open Source

svelteesp32

# svelteesp32 ![image](https://badges.github.io/stability-badges/dist/stable.svg) ### Embed Any Web App in Your ESP32 — One Binary, Zero Filesystem Hassle **Turn your Svelte, React, Angular, or Vue frontend into a single C++ header file.** Serve beautiful web interfaces directly from ESP32/ESP8266 flash memory with automatic gzip compression, ETag caching, and seamless OTA updates. [Changelog](CHANGELOG.md) <p align="center"> <img src="svelteesp32.png" alt="svelteesp32" /> </p> --- ## Why SvelteESP32? **The problem:** Traditional approaches like SPIFFS and LittleFS require separate partition uploads, complex OTA workflows, and manual compression. Your users end up managing multiple files, and your CI/CD pipeline becomes a mess. **The solution:** SvelteESP32 compiles your entire web application into a single C++ header file. One firmware binary. One OTA update. Done. ### Key Benefits - **Single Binary OTA** — Everything embedded in firmware. No partition juggling, no separate uploads. - **Automatic Optimization** — Build-time gzip compression with intelligent thresholds (>1KB, >15% reduction). - **Smart Caching** — Built-in SHA256 ETags deliver HTTP 304 responses, slashing bandwidth on constrained devices. - **CI/CD Ready** — Simple npm package that slots into any build pipeline. - **Zero Runtime Overhead** — Data served directly from flash. No filesystem reads, no RAM allocation. - **4 Web Server Engines** — PsychicHttpServer V2, ESPAsyncWebServer, Arduino WebServer, and native ESP-IDF supported. --- ## SvelteESP32 vs Traditional Filesystem | Feature | SvelteESP32 | SPIFFS / LittleFS | | --------------------- | --------------------------------- | ---------------------------------------- | | **Single Binary OTA** | ✓ Everything in firmware | ✗ Separate partition upload required | | **Gzip Compression** | ✓ Automatic at build time | Manual or runtime compression | | **ETag Support** | ✓ Built-in SHA256 + 304 responses | Manual implementation required | | **CI/CD Integration** | ✓ One npm command | Complex upload_fs tooling | | **Memory Efficiency** | Flash only (PROGMEM/const arrays) | Filesystem partition + overhead | | **Performance** | Direct byte array serving | Filesystem read latency | | **Setup Complexity** | Include header, call one function | Partition tables, upload tools, handlers | **Best for:** Single-binary OTA, CI/CD pipelines, static web UIs that ship with firmware. **Consider SPIFFS/LittleFS for:** User-uploadable files, runtime-editable configs, dynamic content. --- ## Quick Start ```bash npm install -D svelteesp32 ``` After building your frontend (Vite/Rollup/Webpack): ```bash npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=always ``` Include in your ESP32 project: ```c #include <PsychicHttp.h> #include "svelteesp32.h" PsychicHttpServer server; void setup() { server.listen(80); initSvelteStaticFiles(&server); } ``` **That's it.** Your entire web app is now embedded and ready to serve. > **Just want production-safe defaults?** > > ```bash > npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h \ > --etag=always --gzip=always --cachetimehtml=0 --cachetimeassets=31536000 > ``` > > ETags for instant 304s, gzip for smaller transfers, `no-cache` for HTML so updates are always picked up, and 1-year caching for content-hashed JS/CSS assets. --- ## What's New - **v3.1.0** — Removed `handlebars`, `picomatch`, and `mime-types` dependencies; C++ generation is now pure TypeScript with a built-in MIME type map and direct `tinyglobby` exclude handling. `--cachetime-html` → `--cachetimehtml`, `--cachetime-assets` → `--cachetimeassets` (CLI now matches RC file keys); `--dry-run` alias removed — use `--dryrun` - **v3.0.0** — **Vite plugin** (`import { svelteESP32 } from 'svelteesp32/vite'`) generates the header automatically after every build — call with no argument for RC file mode or pass an options object for plugin-options mode; `npx svelteesp32 init` interactive RC file wizard; Node.js >= 22 required - **v2.4.0** — `--analyze` for CI size budget checks (per-file table, exits 1 on over-budget); `--manifest` to write a companion JSON manifest alongside the header - **v2.3.0** — `--cachetimehtml` and `--cachetimeassets` for per-type cache control (e.g. `no-cache` for HTML, 1-year for content-hashed JS/CSS) - **v2.2.0** — SPA routing catch-all (`--spa`) for client-side routers on all four engines - **v2.1.0** — New Arduino WebServer engine (`-e webserver`), dependency updates - **v2.0.0** — **BREAKING**: PsychicHttpServer V2 is now the default `psychic` engine. The `psychic2` engine has been removed. Dry run mode, C++ identifier validation, improved MIME type warnings - **v1.16.0** — Size budget constraints (`--maxsize`, `--maxgzipsize`) - **v1.15.0** — `--basepath` for multiple frontends (e.g., `/admin`, `/app`) - **v1.13.0** — npm package variable interpolation in RC files - **v1.12.0** — RC file configuration support - **v1.11.0** — File exclusion patterns - **v1.9.0** — Native ESP-IDF engine --- ## Requirements - Node.js >= 22 - npm >= 10 --- ## Installation & Usage ### Install ```bash npm install -D svelteesp32 ``` ### Quick Setup with `init` The `init` command creates a `.svelteesp32rc.json` configuration file interactively so you never have to remember CLI flags: ```bash npx svelteesp32 init ``` It asks for engine, source path, output path, and ETag preference, writes the RC file, and optionally runs the tool immediately. ### Vite Plugin For Vite-based projects (SvelteKit, React, Vue, Vanilla) you can skip the manual CLI step entirely — the plugin hooks into the build pipeline and regenerates the C++ header automatically after every `vite build`. The plugin has two exclusive modes — pick one: **RC file mode** — call with no argument (or a string path to a custom RC file). All settings come from `.svelteesp32rc.json`; `outputfile` in the RC file is required. ```ts import { svelteKit } from '@sveltejs/kit/vite'; import { svelteESP32 } from 'svelteesp32/vite'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [ svelteKit(), svelteESP32() // auto-discover .svelteesp32rc.json // svelteESP32('/path/to/custom.rc.json') // or specify path explicitly ] }); ``` **Plugin options mode** — call with an options object. The RC file is completely ignored; `output` is required. ```ts export default defineConfig({ plugins: [ svelteKit(), svelteESP32({ output: '../firmware/include/svelteesp32.h', engine: 'psychic', etag: 'always', gzip: 'always', cachetimehtml: 0, cachetimeassets: 31536000 }) ] }); ``` `sourcepath` defaults to Vite's `build.outDir` in both modes. **Plugin options** | Option | Type | Default | Description | | ----------------- | ------------------------------------------- | ------------------------- | -------------------------------------------------- | | `output` | `string` | RC `outputfile` | Output `.h` file path | | `sourcepath` | `string` | Vite's `build.outDir` | Source directory (compiled web files) | | `engine` | `'psychic'\|'async'\|'espidf'\|'webserver'` | `'psychic'` | Target web server engine | | `etag` | `'always'\|'never'\|'compiler'` | `'never'` | ETag generation mode | | `gzip` | `'always'\|'never'\|'compiler'` | `'always'` | Gzip compression mode | | `cachetime` | `number` | `0` | `Cache-Control: max-age` in seconds (all files) | | `cachetimehtml` | `number` | (unset) | max-age for HTML files (overrides `cachetime`) | | `cachetimeassets` | `number` | (unset) | max-age for non-HTML files (overrides `cachetime`) | | `exclude` | `string[]` | `[]` | Glob patterns to exclude | | `basepath` | `string` | (none) | URL prefix for all routes | | `espmethod` | `string` | `'initSvelteStaticFiles'` | Generated init function name | | `define` | `string` | `'SVELTEESP32'` | C++ `#define` prefix | | `version` | `string` | (none) | Version string embedded in header | | `created` | `boolean` | `false` | Include creation timestamp | | `spa` | `boolean` | `false` | Serve `index.html` for unmatched routes | | `manifest` | `boolean` | `false` | Write companion `.manifest.json` | | `noindexcheck` | `boolean` | `false` | Skip `index.html` validation | | `maxsize` | `number` | (none) | Max total uncompressed size in bytes | | `maxgzipsize` | `number` | (none) | Max total gzip size in bytes | ### Generate Header File (CLI) Choose your web server engine: ```bash # PsychicHttpServer (recommended for ESP32) npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=always # ESPAsyncWebServer (ESP32 + ESP8266) npx svelteesp32 -e async -s ./dist -o ./esp32/svelteesp32.h --etag=always # Arduino WebServer (ESP32, synchronous, no dependencies) npx svelteesp32 -e webserver -s ./dist -o ./esp32/svelteesp32.h --etag=always # Native ESP-IDF npx svelteesp32 -e espidf -s ./dist -o ./esp32/svelteesp32.h --etag=always ``` ### Build Output Watch your files get optimized in real-time: ``` [assets/index-KwubEIf-.js] ✓ gzip used (38850 -> 12547 = 32%) [assets/index-Soe6cpLA.css] ✓ gzip used (32494 -> 5368 = 17%) [favicon.png] x gzip unused (33249 -> 33282 = 100%) [index.html] x gzip unused (too small) (472 -> 308 = 65%) [roboto_regular.json] ✓ gzip used (363757 -> 93567 = 26%) 5 files, 458kB original size, 142kB gzip size ../../../Arduino/EspSvelte/svelteesp32.h 842kB size ``` **Automatic optimizations:** - Gzip level 9 compression when beneficial (>1KB, >15% size reduction) - Duplicate file detection via SHA256 hashing - Smart skip of pre-compressed files (.gz, .br) when originals exist ### ESP32 Integration **PsychicHttpServer V2 (Recommended)** ```c #include <PsychicHttp.h> #include "svelteesp32.h" PsychicHttpServer server; void setup() { server.listen(80); initSvelteStaticFiles(&server); // One line. Done. } ``` **ESPAsyncWebServer** ```c #include <ESPAsyncWebServer.h> #include "svelteesp32.h" AsyncWebServer server(80); void setup() { initSvelteStaticFiles(&server); server.begin(); } ``` **Arduino WebServer (built-in, no dependencies)** ```c #include <WebServer.h> #include "svelteesp32.h" WebServer server(80); void setup() { initSvelteStaticFiles(&server); server.begin(); } void loop() { server.handleClient(); } ``` **Native ESP-IDF** ```c #include <esp_http_server.h> #include "svelteesp32.h" httpd_handle_t server = NULL; void app_main() { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); httpd_start(&server, &config); initSvelteStaticFiles(server); } ``` Working examples with LED control via web interface: [Arduino/PlatformIO](demo/esp32) | [ESP-IDF](demo/esp32idf) ### What Gets Generated The generated header file includes everything your ESP needs: ```c //engine: PsychicHttpServer V2 //config: engine=psychic sourcepath=./dist outputfile=./output.h etag=always gzip=always cachetime=0 espmethod=initSvelteStaticFiles define=SVELTEESP32 // #define SVELTEESP32_COUNT 5 #define SVELTEESP32_SIZE 468822 #define SVELTEESP32_SIZE_GZIP 145633 #define SVELTEESP32_FILE_INDEX_HTML #define SVELTEESP32_HTML_FILES 1 #define SVELTEESP32_CSS_FILES 1 #define SVELTEESP32_JS_FILES 1 ... #include <Arduino.h> #include <PsychicHttp.h> #include <PsychicHttpsServer.h> static const uint8_t datagzip_assets_index_KwubEIf__js[12547] = {0x1f, 0x8b, 0x8, 0x0, ... static const uint8_t datagzip_assets_index_Soe6cpLA_css[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ... static const char etag_assets_index_KwubEIf__js[] = "387b88e345cc56ef9091..."; static const char etag_assets_index_Soe6cpLA_css[] = "d4f23bc45ef67890ab12..."; // File manifest for runtime introspection struct SVELTEESP32_FileInfo { const char* path; uint32_t size; uint32_t gzipSize; const char* etag; const char* contentType; }; const SVELTEESP32_FileInfo SVELTEESP32_FILES[] = { { "/assets/index-KwubEIf-.js", 38850, 12547, etag_assets_index_KwubEIf__js, "text/javascript" }, { "/assets/index-Soe6cpLA.css", 32494, 5368, etag_assets_index_Soe6cpLA_css, "text/css" }, ... }; const size_t SVELTEESP32_FILE_COUNT = sizeof(SVELTEESP32_FILES) / sizeof(SVELTEESP32_FILES[0]); ... // File served hook - override with your own implementation for metrics/logging extern "C" void __attribute__((weak)) SVELTEESP32_onFileServed(const char* path, int statusCode) {} void initSvelteStaticFiles(PsychicHttpServer * server) { server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) { if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_assets_index_KwubEIf__js)) { response->setCode(304); SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 304); return response->send(); } response->setContentType("text/javascript"); response->addHeader("Content-Encoding", "gzip"); response->addHeader("Cache-Control", "no-cache"); response->addHeader("ETag", etag_assets_index_KwubEIf__js); response->setContent(datagzip_assets_index_KwubEIf__js, 12547); SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 200); return response->send(); }); // ... more routes } ``` --- ## Supported Web Server Engines | Engine | Flag | Best For | Platform | | ------------------------ | -------------- | ---------------------------- | --------------- | | **PsychicHttpServer V2** | `-e psychic` | Maximum performance | ESP32 only | | **ESPAsyncWebServer** | `-e async` | Cross-platform compatibility | ESP32 + ESP8266 | | **Arduino WebServer** | `-e webserver` | No dependencies, simplicity | ESP32 only | | **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only | **Recommendation:** For ESP32-only projects, use PsychicHttpServer V2 (`-e psychic`) for the fastest, most stable experience. **Note:** For PsychicHttp, configure `server.config.max_uri_handlers`. The generated header provides `SVELTEESP32_MAX_URI_HANDLERS` (file count + 5 safety margin) for use directly in your sketch. --- ## Features ### Automatic Gzip Compression Your JS, CSS, and HTML files are automatically compressed at build time — not on the ESP32. Files are gzipped when they're >1KB and achieve >15% size reduction. - **Enabled by default** — disable with `--gzip=never` - **Compiler mode** — use `--gzip=compiler` and control via `-D SVELTEESP32_ENABLE_GZIP` in PlatformIO ### Smart ETag Caching Reduce bandwidth dramatically with HTTP 304 "Not Modified" responses. When a browser has a cached file, the ESP32 sends just a status code instead of the entire file — perfect for bandwidth-constrained IoT devices. - **Enable with** `--etag=always` (recommended) - **Minimal overhead** — adds ~1-3% code size for significant bandwidth savings - **Compiler mode** — use `--etag=compiler` and control via `-D SVELTEESP32_ENABLE_ETAG` All four engines support full ETag validation. > **Browser compatibility note:** ETags and `Cache-Control: max-age` are universally supported in all modern browsers. Very old clients (IE6/7, early Android 2.x WebViews) may ignore `must-revalidate` or mishandle 304 responses. If you target these environments, set `--etag=never` and `--cachetime=0` to force full downloads on every request. ### Browser Cache Control Fine-tune how browsers cache your content: - **Default:** `no-cache` — browsers always validate with server (ETag check) - **Long-term caching:** `--cachetime=86400` — cache for 24 hours without any server requests - **Per-type caching:** Use `--cachetimehtml` and `--cachetimeassets` independently Vite and webpack produce content-hashed filenames for JS/CSS (e.g., `app.a1b2c3.js`). Those can be cached for up to a year because the hash changes with every build, but `index.html` must stay `no-cache` since it's the entry point that references them: ```bash npx svelteesp32 -e psychic -s ./dist -o ./output.h \ --etag=always --cachetimehtml=0 --cachetimeassets=31536000 ``` This emits `Cache-Control: no-cache` for every `text/html` file and `Cache-Control: max-age=31536000` for all other assets in the same header, with no per-file configuration needed. | Option | Applies to | Falls back to | | ------------------- | -------------------------------- | -------------- | | `--cachetimehtml` | `text/html` only | `--cachetime` | | `--cachetimeassets` | everything else | `--cachetime` | | `--cachetime` | all files (when no override set) | `0` (no-cache) | ### Automatic Index Handling Your `index.html` is automatically served at the root URL — just like any web server. Visit `http://esp32.local/` and your app loads. **API-only projects?** Skip index validation with `--noindexcheck`: ```bash npx svelteesp32 -e psychic -s ./dist -o ./output.h --noindexcheck ``` ### File Exclusion Keep source maps, docs, and test files out of your firmware: ```bash # Single pattern npx svelteesp32 -s ./dist -o ./output.h --exclude="*.map" # Multiple patterns npx svelteesp32 -s ./dist -o ./output.h --exclude="*.map,*.md,test/**/*" ``` No patterns are excluded by default — specify everything you need explicitly. Build output shows exactly what's excluded: ``` Excluded 3 file(s): - assets/index.js.map - assets/vendor.js.map - README.md ``` ### Multiple Frontends (Base Path) Serve multiple web apps from one ESP32 using URL prefixes: ```bash npx svelteesp32 -s ./admin-dist -o ./admin.h --basepath=/admin npx svelteesp32 -s ./user-dist -o ./user.h --basepath=/app ``` ```c #include "admin.h" // Serves at /admin/* #include "user.h" // Serves at /app/* void setup() { server.listen(80); initSvelteStaticFiles_admin(&server); initSvelteStaticFiles_user(&server); server.on("/api/data", HTTP_GET, handleApiData); } ``` **Rules:** Must start with `/`, no trailing slash, no double slashes. ### SPA Routing (Client-Side Routers) Modern JS frameworks use client-side routing. Without a catch-all, refreshing `/settings` on your ESP32 returns nothing. Add `--spa` to make all unmatched GET requests fall through to `index.html`: ```bash npx svelteesp32 -e async -s ./dist -o ./output.h --spa npx svelteesp32 -e psychic -s ./dist -o ./output.h --spa --basepath=/app ``` What gets generated per engine: | Engine | Catch-all mechanism | | ----------- | ---------------------------------------------------------------------------------------------------------------------------- | | `psychic` | `server->on("/*", ...)` (no basePath) already handled by `defaultEndpoint`; `server->on("/app/*", ...)` when basePath is set | | `async` | `server->onNotFound(...)` with optional basePath prefix check | | `webserver` | `server->onNotFound(...)` with optional basePath prefix check | | `espidf` | `httpd_register_err_handler(HTTPD_404_NOT_FOUND, ...)` | **Note:** `--spa` requires `index.html` or `index.htm` in the source directory — a warning is printed if it is missing. ### Analyze Mode (CI Size Budget Checks) Use `--analyze` in CI to validate firmware size budgets without producing any output file: ```bash npx svelteesp32 -e psychic -s ./dist --maxsize=400k --maxgzipsize=150k --analyze ``` Sample output: ``` index.html Original Gzip ──────────────────────────────────── ──────── ──────── assets/index-KwubEIf-.js 37.9kB 12.3kB assets/index-Soe6cpLA.css 31.7kB 5.2kB favicon.png 32.5kB 32.5kB [no gzip] index.html 0.5kB 0.3kB ──────────────────────────────────────────────────────── Total 102.6kB 50.3kB Budget (maxsize) 400.0kB - ✓ PASS Budget (maxgzipsize) - 150.0kB ✓ PASS ``` Exits with code **1** if any budget is exceeded — CI fails automatically. Mutually exclusive with `--dryrun`. ### JSON Manifest Add `--manifest` to write a companion `.manifest.json` file alongside the header (same directory, same base name): ```bash npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --manifest # also writes ./esp32/svelteesp32.manifest.json ``` The manifest records build metadata and per-file details for tooling and dashboards: ```json { "generated": "2026-04-26T12:00:00.000Z", "engine": "psychic", "etag": "never", "gzip": "always", "filecount": 4, "size": 104960, "gzipSize": 51507, "files": [ { "path": "/assets/index-KwubEIf-.js", "mime": "text/javascript", "size": 38850, "gzipSize": 12547, "isGzip": true } ] } ``` ### C++ Build-Time Validation Catch configuration issues at compile time with generated defines: ```c #include "svelteesp32.h" #if SVELTEESP32_COUNT != 5 #error Unexpected file count - check your build #endif #ifndef SVELTEESP32_FILE_INDEX_HTML #error Missing index.html - frontend build failed? #endif ``` **Available defines:** `SVELTEESP32_COUNT`, `SVELTEESP32_SIZE`, `SVELTEESP32_SIZE_GZIP`, `SVELTEESP32_FILE_*`, `SVELTEESP32_*_FILES` ### Runtime File Manifest Query embedded files at runtime for logging, diagnostics, or API endpoints: ```c // List all embedded files for (size_t i = 0; i < SVELTEESP32_FILE_COUNT; i++) { const auto& f = SVELTEESP32_FILES[i]; Serial.printf("%s (%d bytes, gzip: %d)\n", f.path, f.size, f.gzipSize); } ``` Each file entry includes: `path`, `size`, `gzipSize`, `etag`, `contentType` ### Request Hook (Metrics & Logging) Track every request with zero overhead when unused (weak linkage): ```c extern "C" void SVELTEESP32_onFileServed(const char* path, int statusCode) { Serial.printf("[HTTP] %s -> %d\n", path, statusCode); if (statusCode == 304) cacheHits++; } ``` Called for every response (200 = content served, 304 = cache hit). --- ## CLI Reference | Option | Description | Default | | ------------------- | -------------------------------------------------------------------------------------- | ----------------------- | | `-s` | Source folder with compiled web files | (required) | | `-e` | Web server engine (psychic/async/espidf/webserver) | `psychic` | | `-o` | Output header file path | `svelteesp32.h` | | `--etag` | ETag caching (always/never/compiler) | `never` | | `--gzip` | Gzip compression (always/never/compiler) | `always` | | `--created` | Include creation timestamp in header | `false` | | `--exclude` | Exclude files by glob pattern | (none) | | `--basepath` | URL prefix for all routes | (none) | | `--maxsize` | Max total uncompressed size (e.g., `400k`, `1m`) | (none) | | `--maxgzipsize` | Max total gzip size (e.g., `150k`, `500k`) | (none) | | `--cachetime` | Cache-Control max-age in seconds (all files) | `0` | | `--cachetimehtml` | max-age for HTML files (overrides `--cachetime`) | (unset) | | `--cachetimeassets` | max-age for non-HTML files (overrides `--cachetime`) | (unset) | | `--version` | Version string in header | (none) | | `--define` | C++ define prefix | `SVELTEESP32` | | `--espmethod` | Init function name | `initSvelteStaticFiles` | | `--config` | Custom RC file path | `.svelteesp32rc.json` | | `--dryrun` | Show route table + summary without writing output | `false` | | `--analyze` | Print per-file size table and budget status, no output written; exits 1 if over budget | `false` | | `--manifest` | Write companion `.manifest.json` alongside the header | `false` | | `--spa` | Serve index.html for unmatched routes (SPA routing) | `false` | | `--noindexcheck` | Skip index.html validation | `false` | | `-h` | Show help | | --- ## Configuration File Store your settings in `.svelteesp32rc.json` for zero-argument builds: ```json { "engine": "psychic", "sourcepath": "./dist", "outputfile": "./esp32/svelteesp32.h", "etag": "always", "gzip": "always", "exclude": ["*.map", "*.md"], "basepath": "/ui", "maxsize": "400k", "maxgzipsize": "150k", "cachetime": 0, "cachetimehtml": 0, "cachetimeassets": 31536000, "noindexcheck": false, "dryrun": false, "analyze": false, "spa": false, "manifest": false } ``` Boolean fields (`noindexcheck`, `dryrun`, `analyze`, `spa`, `manifest`, `created`) accept native JSON booleans (`true`/`false`) or their string equivalents (`"true"`/`"false"`), matching the existing behaviour of `etag` and `gzip`. Then just run: ```bash npx svelteesp32 ``` ### npm Variable Interpolation Sync versions and names automatically from your `package.json`: ```json { "version": "v$npm_package_version", "define": "$npm_package_name" } ``` With `package.json` containing `"version": "2.1.0"`, this becomes `"version": "v2.1.0"`. ### Multiple Environments ```bash npx svelteesp32 --config=.svelteesp32rc.prod.json ``` CLI arguments always override RC file values. > **Security note:** When svelteesp32 auto-loads an RC file from the current directory it prints a warning. If you cloned a project that includes `.svelteesp32rc.json`, review it before running. `outputfile` in RC files must be a relative path — use `--output` on the CLI for absolute paths. --- ## Common Recipes ### Recipe A — Arduino IDE + Built-in WebServer No PlatformIO required. Build your frontend, run svelteesp32 manually, then compile in Arduino IDE. ```bash # 1. Build your frontend npm run build # 2. Generate the header npx svelteesp32 -e webserver -s ./dist -o ./MyProject/svelteesp32.h \ --gzip=always --etag=never ``` > **Note:** The Arduino `WebServer` library does not support ETag validation, so `--etag=never` is the correct setting here. Remember to call `server.handleClient()` in your `loop()`. ```c #include <WebServer.h> #include "svelteesp32.h" WebServer server(80); void setup() { WiFi.begin("ssid", "password"); while (WiFi.status() != WL_CONNECTED) delay(500); initSvelteStaticFiles(&server); server.begin(); } void loop() { server.handleClient(); } ``` --- ### Recipe B — PlatformIO + Vite/SvelteKit, Auto-Generated Header on `pio run` Run svelteesp32 automatically as a PlatformIO pre-build step so your header is always up to date. **`platformio.ini`** ```ini [env:esp32dev] platform = espressif32 board = esp32dev framework = arduino lib_deps = ESP Async WebServer extra_scripts = pre:scripts/build_frontend.py ``` **`scripts/build_frontend.py`** ```python Import("env") import subprocess def build_frontend(source, target, env): print("Building frontend...") subprocess.run(["npm", "run", "build"], cwd="frontend", check=True) subprocess.run([ "npx", "svelteesp32", "-e", "async", "-s", "frontend/dist", "-o", "src/svelteesp32.h", "--etag=always", "--gzip=always", "--cachetimehtml=0", "--cachetimeassets=31536000" ], check=True) env.AddPreAction("buildprog", build_frontend) ``` This ensures the C++ header is regenerated from the latest frontend build every time you run `pio run`. --- ### Recipe C — ESP-IDF CMake, 1-Year Cached Content-Hashed Assets Integrate svelteesp32 into a CMake build so the header is regenerated automatically. **`CMakeLists.txt` (component or top-level)** ```cmake add_custom_command( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h COMMAND npm run build COMMAND npx svelteesp32 -e espidf -s ${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist -o ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h --etag=always --gzip=always --cachetimehtml=0 --cachetimeassets=31536000 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating svelteesp32.h from frontend build" VERBATIM ) add_custom_target(frontend_header DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h ) # Make your main component depend on the generated header add_dependencies(${COMPONENT_LIB} frontend_header) ``` **`main/app_main.c`** ```c #include <esp_http_server.h> #include "svelteesp32.h" void app_main(void) { // ... WiFi init ... httpd_config_t config = HTTPD_DEFAULT_CONFIG(); httpd_handle_t server = NULL; httpd_start(&server, &config); initSvelteStaticFiles(server); } ``` The `--cachetimehtml=0 --cachetimeassets=31536000` combination gives you `no-cache` for `index.html` (so browser always checks for updates) and a 1-year `max-age` for content-hashed JS/CSS bundles. --- ## FAQ **How large can my web app be?** With gzip compression, 3-4MB asset directories work comfortably. That's enough for a full-featured SPA. **Does this use RAM or Flash?** Flash only. Data is stored in program memory (PROGMEM on ESP8266, const arrays on ESP32), leaving your heap and stack free for application logic. **Why is the .h file so large?** The text representation (comma-separated bytes) is larger than binary. Check `SVELTEESP32_SIZE_GZIP` for actual flash usage. **Does compilation take forever?** No. Large headers compile in seconds, and incremental builds skip recompilation if your frontend hasn't changed. **Can frontend and firmware teams work separately?** Absolutely. Frontend builds the app, runs svelteesp32, commits the header. Firmware team includes it and ships. Version sync via npm variables keeps everyone aligned. --- ## Development ```bash npm run build # Build TypeScript npm run test # Run unit tests npm run test:watch # Watch mode npm run fix # Fix formatting & linting ``` --- **Ready to ship your web UI in a single binary?** ```bash npm install -D svelteesp32 ```

IoT & Embedded Mobile Development
240 Github Stars