{"id":14963422,"url":"https://github.com/bcsabaengine/svelteesp32","last_synced_at":"2026-05-11T19:05:02.243Z","repository":{"id":222196797,"uuid":"756065585","full_name":"BCsabaEngine/svelteesp32","owner":"BCsabaEngine","description":"Convert Svelte (or React/Angular/Vue) JS application to serve it from ESP32/ESP8266 webserver","archived":false,"fork":false,"pushed_at":"2026-01-31T22:49:29.000Z","size":1972,"stargazers_count":98,"open_issues_count":4,"forks_count":15,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-02-01T00:57:02.126Z","etag":null,"topics":["angular","arduino","esp-idf","esp32","esp8266","espasyncwebserver","platformio","psychichttp","react","svelte","vue","webserver"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BCsabaEngine.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-02-11T21:44:25.000Z","updated_at":"2026-01-31T22:49:03.000Z","dependencies_parsed_at":"2024-03-11T21:25:00.279Z","dependency_job_id":"e83f4593-289c-411e-b9dd-d0f20f09ccfa","html_url":"https://github.com/BCsabaEngine/svelteesp32","commit_stats":{"total_commits":95,"total_committers":4,"mean_commits":23.75,"dds":"0.052631578947368474","last_synced_commit":"e2e0e4ea5d517c4c05ec3a07a5936074dcb93d08"},"previous_names":["bcsabaengine/svelteesp32"],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/BCsabaEngine/svelteesp32","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BCsabaEngine%2Fsvelteesp32","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BCsabaEngine%2Fsvelteesp32/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BCsabaEngine%2Fsvelteesp32/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BCsabaEngine%2Fsvelteesp32/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BCsabaEngine","download_url":"https://codeload.github.com/BCsabaEngine/svelteesp32/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BCsabaEngine%2Fsvelteesp32/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28977316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T09:57:52.632Z","status":"ssl_error","status_checked_at":"2026-02-01T09:57:49.143Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","arduino","esp-idf","esp32","esp8266","espasyncwebserver","platformio","psychichttp","react","svelte","vue","webserver"],"created_at":"2024-09-24T13:31:29.065Z","updated_at":"2026-05-11T19:05:02.223Z","avatar_url":"https://github.com/BCsabaEngine.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# svelteesp32 ![image](https://badges.github.io/stability-badges/dist/stable.svg)\n\n### Embed Any Web App in Your ESP32 — One Binary, Zero Filesystem Hassle\n\n**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.\n\n[Changelog](CHANGELOG.md)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"svelteesp32.png\" alt=\"svelteesp32\" /\u003e\n\u003c/p\u003e\n\n---\n\n## Why SvelteESP32?\n\n**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.\n\n**The solution:** SvelteESP32 compiles your entire web application into a single C++ header file. One firmware binary. One OTA update. Done.\n\n### Key Benefits\n\n- **Single Binary OTA** — Everything embedded in firmware. No partition juggling, no separate uploads.\n- **Automatic Optimization** — Build-time gzip compression with intelligent thresholds (\u003e1KB, \u003e15% reduction).\n- **Smart Caching** — Built-in SHA256 ETags deliver HTTP 304 responses, slashing bandwidth on constrained devices.\n- **CI/CD Ready** — Simple npm package that slots into any build pipeline.\n- **Zero Runtime Overhead** — Data served directly from flash. No filesystem reads, no RAM allocation.\n- **4 Web Server Engines** — PsychicHttpServer V2, ESPAsyncWebServer, Arduino WebServer, and native ESP-IDF supported.\n\n---\n\n## SvelteESP32 vs Traditional Filesystem\n\n| Feature               | SvelteESP32                       | SPIFFS / LittleFS                        |\n| --------------------- | --------------------------------- | ---------------------------------------- |\n| **Single Binary OTA** | ✓ Everything in firmware          | ✗ Separate partition upload required     |\n| **Gzip Compression**  | ✓ Automatic at build time         | Manual or runtime compression            |\n| **ETag Support**      | ✓ Built-in SHA256 + 304 responses | Manual implementation required           |\n| **CI/CD Integration** | ✓ One npm command                 | Complex upload_fs tooling                |\n| **Memory Efficiency** | Flash only (PROGMEM/const arrays) | Filesystem partition + overhead          |\n| **Performance**       | Direct byte array serving         | Filesystem read latency                  |\n| **Setup Complexity**  | Include header, call one function | Partition tables, upload tools, handlers |\n\n**Best for:** Single-binary OTA, CI/CD pipelines, static web UIs that ship with firmware.\n\n**Consider SPIFFS/LittleFS for:** User-uploadable files, runtime-editable configs, dynamic content.\n\n---\n\n## Quick Start\n\n```bash\nnpm install -D svelteesp32\n```\n\nAfter building your frontend (Vite/Rollup/Webpack):\n\n```bash\nnpx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=always\n```\n\nInclude in your ESP32 project:\n\n```c\n#include \u003cPsychicHttp.h\u003e\n#include \"svelteesp32.h\"\n\nPsychicHttpServer server;\n\nvoid setup() {\n    server.listen(80);\n    initSvelteStaticFiles(\u0026server);\n}\n```\n\n**That's it.** Your entire web app is now embedded and ready to serve.\n\n\u003e **Just want production-safe defaults?**\n\u003e\n\u003e ```bash\n\u003e npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h \\\n\u003e   --etag=always --gzip=always --cachetimehtml=0 --cachetimeassets=31536000\n\u003e ```\n\u003e\n\u003e 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.\n\n---\n\n## What's New\n\n- **v3.1.0** — Removed `handlebars` dependency; C++ generation is now pure TypeScript. `--cachetime-html` → `--cachetimehtml`, `--cachetime-assets` → `--cachetimeassets` (CLI now matches RC file keys); `--dry-run` alias removed — use `--dryrun`\n- **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 \u003e= 22 required\n- **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\n- **v2.3.0** — `--cachetimehtml` and `--cachetimeassets` for per-type cache control (e.g. `no-cache` for HTML, 1-year for content-hashed JS/CSS)\n- **v2.2.0** — SPA routing catch-all (`--spa`) for client-side routers on all four engines\n- **v2.1.0** — New Arduino WebServer engine (`-e webserver`), dependency updates\n- **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\n- **v1.16.0** — Size budget constraints (`--maxsize`, `--maxgzipsize`)\n- **v1.15.0** — `--basepath` for multiple frontends (e.g., `/admin`, `/app`)\n- **v1.13.0** — npm package variable interpolation in RC files\n- **v1.12.0** — RC file configuration support\n- **v1.11.0** — File exclusion patterns\n- **v1.9.0** — Native ESP-IDF engine\n\n---\n\n## Requirements\n\n- Node.js \u003e= 22\n- npm \u003e= 10\n\n---\n\n## Installation \u0026 Usage\n\n### Install\n\n```bash\nnpm install -D svelteesp32\n```\n\n### Quick Setup with `init`\n\nThe `init` command creates a `.svelteesp32rc.json` configuration file interactively so you never have to remember CLI flags:\n\n```bash\nnpx svelteesp32 init\n```\n\nIt asks for engine, source path, output path, and ETag preference, writes the RC file, and optionally runs the tool immediately.\n\n### Vite Plugin\n\nFor 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`.\n\nThe plugin has two exclusive modes — pick one:\n\n**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.\n\n```ts\nimport { svelteKit } from '@sveltejs/kit/vite';\nimport { svelteESP32 } from 'svelteesp32/vite';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n  plugins: [\n    svelteKit(),\n    svelteESP32() // auto-discover .svelteesp32rc.json\n    // svelteESP32('/path/to/custom.rc.json')  // or specify path explicitly\n  ]\n});\n```\n\n**Plugin options mode** — call with an options object. The RC file is completely ignored; `output` is required.\n\n```ts\nexport default defineConfig({\n  plugins: [\n    svelteKit(),\n    svelteESP32({\n      output: '../firmware/include/svelteesp32.h',\n      engine: 'psychic',\n      etag: 'always',\n      gzip: 'always',\n      cachetimehtml: 0,\n      cachetimeassets: 31536000\n    })\n  ]\n});\n```\n\n`sourcepath` defaults to Vite's `build.outDir` in both modes.\n\n**Plugin options**\n\n| Option            | Type                                        | Default                   | Description                                        |\n| ----------------- | ------------------------------------------- | ------------------------- | -------------------------------------------------- |\n| `output`          | `string`                                    | RC `outputfile`           | Output `.h` file path                              |\n| `sourcepath`      | `string`                                    | Vite's `build.outDir`     | Source directory (compiled web files)              |\n| `engine`          | `'psychic'\\|'async'\\|'espidf'\\|'webserver'` | `'psychic'`               | Target web server engine                           |\n| `etag`            | `'always'\\|'never'\\|'compiler'`             | `'never'`                 | ETag generation mode                               |\n| `gzip`            | `'always'\\|'never'\\|'compiler'`             | `'always'`                | Gzip compression mode                              |\n| `cachetime`       | `number`                                    | `0`                       | `Cache-Control: max-age` in seconds (all files)    |\n| `cachetimehtml`   | `number`                                    | (unset)                   | max-age for HTML files (overrides `cachetime`)     |\n| `cachetimeassets` | `number`                                    | (unset)                   | max-age for non-HTML files (overrides `cachetime`) |\n| `exclude`         | `string[]`                                  | `[]`                      | Glob patterns to exclude                           |\n| `basepath`        | `string`                                    | (none)                    | URL prefix for all routes                          |\n| `espmethod`       | `string`                                    | `'initSvelteStaticFiles'` | Generated init function name                       |\n| `define`          | `string`                                    | `'SVELTEESP32'`           | C++ `#define` prefix                               |\n| `version`         | `string`                                    | (none)                    | Version string embedded in header                  |\n| `created`         | `boolean`                                   | `false`                   | Include creation timestamp                         |\n| `spa`             | `boolean`                                   | `false`                   | Serve `index.html` for unmatched routes            |\n| `manifest`        | `boolean`                                   | `false`                   | Write companion `.manifest.json`                   |\n| `noindexcheck`    | `boolean`                                   | `false`                   | Skip `index.html` validation                       |\n| `maxsize`         | `number`                                    | (none)                    | Max total uncompressed size in bytes               |\n| `maxgzipsize`     | `number`                                    | (none)                    | Max total gzip size in bytes                       |\n\n### Generate Header File (CLI)\n\nChoose your web server engine:\n\n```bash\n# PsychicHttpServer (recommended for ESP32)\nnpx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=always\n\n# ESPAsyncWebServer (ESP32 + ESP8266)\nnpx svelteesp32 -e async -s ./dist -o ./esp32/svelteesp32.h --etag=always\n\n# Arduino WebServer (ESP32, synchronous, no dependencies)\nnpx svelteesp32 -e webserver -s ./dist -o ./esp32/svelteesp32.h --etag=always\n\n# Native ESP-IDF\nnpx svelteesp32 -e espidf -s ./dist -o ./esp32/svelteesp32.h --etag=always\n```\n\n### Build Output\n\nWatch your files get optimized in real-time:\n\n```\n[assets/index-KwubEIf-.js]  ✓ gzip used (38850 -\u003e 12547 = 32%)\n[assets/index-Soe6cpLA.css] ✓ gzip used (32494 -\u003e 5368 = 17%)\n[favicon.png]               x gzip unused (33249 -\u003e 33282 = 100%)\n[index.html]                x gzip unused (too small) (472 -\u003e 308 = 65%)\n[roboto_regular.json]       ✓ gzip used (363757 -\u003e 93567 = 26%)\n\n5 files, 458kB original size, 142kB gzip size\n../../../Arduino/EspSvelte/svelteesp32.h 842kB size\n```\n\n**Automatic optimizations:**\n\n- Gzip level 9 compression when beneficial (\u003e1KB, \u003e15% size reduction)\n- Duplicate file detection via SHA256 hashing\n- Smart skip of pre-compressed files (.gz, .br) when originals exist\n\n### ESP32 Integration\n\n**PsychicHttpServer V2 (Recommended)**\n\n```c\n#include \u003cPsychicHttp.h\u003e\n#include \"svelteesp32.h\"\n\nPsychicHttpServer server;\n\nvoid setup() {\n    server.listen(80);\n    initSvelteStaticFiles(\u0026server);  // One line. Done.\n}\n```\n\n**ESPAsyncWebServer**\n\n```c\n#include \u003cESPAsyncWebServer.h\u003e\n#include \"svelteesp32.h\"\n\nAsyncWebServer server(80);\n\nvoid setup() {\n    initSvelteStaticFiles(\u0026server);\n    server.begin();\n}\n```\n\n**Arduino WebServer (built-in, no dependencies)**\n\n```c\n#include \u003cWebServer.h\u003e\n#include \"svelteesp32.h\"\n\nWebServer server(80);\n\nvoid setup() {\n    initSvelteStaticFiles(\u0026server);\n    server.begin();\n}\n\nvoid loop() {\n    server.handleClient();\n}\n```\n\n**Native ESP-IDF**\n\n```c\n#include \u003cesp_http_server.h\u003e\n#include \"svelteesp32.h\"\n\nhttpd_handle_t server = NULL;\n\nvoid app_main() {\n    httpd_config_t config = HTTPD_DEFAULT_CONFIG();\n    httpd_start(\u0026server, \u0026config);\n    initSvelteStaticFiles(server);\n}\n```\n\nWorking examples with LED control via web interface: [Arduino/PlatformIO](demo/esp32) | [ESP-IDF](demo/esp32idf)\n\n### What Gets Generated\n\nThe generated header file includes everything your ESP needs:\n\n```c\n//engine:   PsychicHttpServer V2\n//config:   engine=psychic sourcepath=./dist outputfile=./output.h etag=always gzip=always cachetime=0 espmethod=initSvelteStaticFiles define=SVELTEESP32\n//\n#define SVELTEESP32_COUNT 5\n#define SVELTEESP32_SIZE 468822\n#define SVELTEESP32_SIZE_GZIP 145633\n#define SVELTEESP32_FILE_INDEX_HTML\n#define SVELTEESP32_HTML_FILES 1\n#define SVELTEESP32_CSS_FILES 1\n#define SVELTEESP32_JS_FILES 1\n...\n\n#include \u003cArduino.h\u003e\n#include \u003cPsychicHttp.h\u003e\n#include \u003cPsychicHttpsServer.h\u003e\n\nstatic const uint8_t datagzip_assets_index_KwubEIf__js[12547] = {0x1f, 0x8b, 0x8, 0x0, ...\nstatic const uint8_t datagzip_assets_index_Soe6cpLA_css[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ...\nstatic const char etag_assets_index_KwubEIf__js[] = \"387b88e345cc56ef9091...\";\nstatic const char etag_assets_index_Soe6cpLA_css[] = \"d4f23bc45ef67890ab12...\";\n\n// File manifest for runtime introspection\nstruct SVELTEESP32_FileInfo {\n  const char* path;\n  uint32_t size;\n  uint32_t gzipSize;\n  const char* etag;\n  const char* contentType;\n};\nconst SVELTEESP32_FileInfo SVELTEESP32_FILES[] = {\n  { \"/assets/index-KwubEIf-.js\", 38850, 12547, etag_assets_index_KwubEIf__js, \"text/javascript\" },\n  { \"/assets/index-Soe6cpLA.css\", 32494, 5368, etag_assets_index_Soe6cpLA_css, \"text/css\" },\n  ...\n};\nconst size_t SVELTEESP32_FILE_COUNT = sizeof(SVELTEESP32_FILES) / sizeof(SVELTEESP32_FILES[0]);\n...\n\n// File served hook - override with your own implementation for metrics/logging\nextern \"C\" void __attribute__((weak)) SVELTEESP32_onFileServed(const char* path, int statusCode) {}\n\nvoid initSvelteStaticFiles(PsychicHttpServer * server) {\n  server-\u003eon(\"/assets/index-KwubEIf-.js\", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {\n    if (request-\u003ehasHeader(\"If-None-Match\") \u0026\u0026\n        request-\u003eheader(\"If-None-Match\").equals(etag_assets_index_KwubEIf__js)) {\n      response-\u003esetCode(304);\n      SVELTEESP32_onFileServed(\"/assets/index-KwubEIf-.js\", 304);\n      return response-\u003esend();\n    }\n\n    response-\u003esetContentType(\"text/javascript\");\n    response-\u003eaddHeader(\"Content-Encoding\", \"gzip\");\n    response-\u003eaddHeader(\"Cache-Control\", \"no-cache\");\n    response-\u003eaddHeader(\"ETag\", etag_assets_index_KwubEIf__js);\n    response-\u003esetContent(datagzip_assets_index_KwubEIf__js, 12547);\n    SVELTEESP32_onFileServed(\"/assets/index-KwubEIf-.js\", 200);\n    return response-\u003esend();\n  });\n\n  // ... more routes\n}\n```\n\n---\n\n## Supported Web Server Engines\n\n| Engine                   | Flag           | Best For                     | Platform        |\n| ------------------------ | -------------- | ---------------------------- | --------------- |\n| **PsychicHttpServer V2** | `-e psychic`   | Maximum performance          | ESP32 only      |\n| **ESPAsyncWebServer**    | `-e async`     | Cross-platform compatibility | ESP32 + ESP8266 |\n| **Arduino WebServer**    | `-e webserver` | No dependencies, simplicity  | ESP32 only      |\n| **Native ESP-IDF**       | `-e espidf`    | Pure ESP-IDF projects        | ESP32 only      |\n\n**Recommendation:** For ESP32-only projects, use PsychicHttpServer V2 (`-e psychic`) for the fastest, most stable experience.\n\n**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.\n\n---\n\n## Features\n\n### Automatic Gzip Compression\n\nYour JS, CSS, and HTML files are automatically compressed at build time — not on the ESP32. Files are gzipped when they're \u003e1KB and achieve \u003e15% size reduction.\n\n- **Enabled by default** — disable with `--gzip=never`\n- **Compiler mode** — use `--gzip=compiler` and control via `-D SVELTEESP32_ENABLE_GZIP` in PlatformIO\n\n### Smart ETag Caching\n\nReduce 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.\n\n- **Enable with** `--etag=always` (recommended)\n- **Minimal overhead** — adds ~1-3% code size for significant bandwidth savings\n- **Compiler mode** — use `--etag=compiler` and control via `-D SVELTEESP32_ENABLE_ETAG`\n\nAll four engines support full ETag validation.\n\n\u003e **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.\n\n### Browser Cache Control\n\nFine-tune how browsers cache your content:\n\n- **Default:** `no-cache` — browsers always validate with server (ETag check)\n- **Long-term caching:** `--cachetime=86400` — cache for 24 hours without any server requests\n- **Per-type caching:** Use `--cachetimehtml` and `--cachetimeassets` independently\n\nVite 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:\n\n```bash\nnpx svelteesp32 -e psychic -s ./dist -o ./output.h \\\n  --etag=always --cachetimehtml=0 --cachetimeassets=31536000\n```\n\nThis 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.\n\n| Option              | Applies to                       | Falls back to  |\n| ------------------- | -------------------------------- | -------------- |\n| `--cachetimehtml`   | `text/html` only                 | `--cachetime`  |\n| `--cachetimeassets` | everything else                  | `--cachetime`  |\n| `--cachetime`       | all files (when no override set) | `0` (no-cache) |\n\n### Automatic Index Handling\n\nYour `index.html` is automatically served at the root URL — just like any web server. Visit `http://esp32.local/` and your app loads.\n\n**API-only projects?** Skip index validation with `--noindexcheck`:\n\n```bash\nnpx svelteesp32 -e psychic -s ./dist -o ./output.h --noindexcheck\n```\n\n### File Exclusion\n\nKeep source maps, docs, and test files out of your firmware:\n\n```bash\n# Single pattern\nnpx svelteesp32 -s ./dist -o ./output.h --exclude=\"*.map\"\n\n# Multiple patterns\nnpx svelteesp32 -s ./dist -o ./output.h --exclude=\"*.map,*.md,test/**/*\"\n```\n\nNo patterns are excluded by default — specify everything you need explicitly.\n\nBuild output shows exactly what's excluded:\n\n```\nExcluded 3 file(s):\n  - assets/index.js.map\n  - assets/vendor.js.map\n  - README.md\n```\n\n### Multiple Frontends (Base Path)\n\nServe multiple web apps from one ESP32 using URL prefixes:\n\n```bash\nnpx svelteesp32 -s ./admin-dist -o ./admin.h --basepath=/admin\nnpx svelteesp32 -s ./user-dist -o ./user.h --basepath=/app\n```\n\n```c\n#include \"admin.h\"  // Serves at /admin/*\n#include \"user.h\"   // Serves at /app/*\n\nvoid setup() {\n    server.listen(80);\n    initSvelteStaticFiles_admin(\u0026server);\n    initSvelteStaticFiles_user(\u0026server);\n    server.on(\"/api/data\", HTTP_GET, handleApiData);\n}\n```\n\n**Rules:** Must start with `/`, no trailing slash, no double slashes.\n\n### SPA Routing (Client-Side Routers)\n\nModern 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`:\n\n```bash\nnpx svelteesp32 -e async -s ./dist -o ./output.h --spa\nnpx svelteesp32 -e psychic -s ./dist -o ./output.h --spa --basepath=/app\n```\n\nWhat gets generated per engine:\n\n| Engine      | Catch-all mechanism                                                                                                          |\n| ----------- | ---------------------------------------------------------------------------------------------------------------------------- |\n| `psychic`   | `server-\u003eon(\"/*\", ...)` (no basePath) already handled by `defaultEndpoint`; `server-\u003eon(\"/app/*\", ...)` when basePath is set |\n| `async`     | `server-\u003eonNotFound(...)` with optional basePath prefix check                                                                |\n| `webserver` | `server-\u003eonNotFound(...)` with optional basePath prefix check                                                                |\n| `espidf`    | `httpd_register_err_handler(HTTPD_404_NOT_FOUND, ...)`                                                                       |\n\n**Note:** `--spa` requires `index.html` or `index.htm` in the source directory — a warning is printed if it is missing.\n\n### Analyze Mode (CI Size Budget Checks)\n\nUse `--analyze` in CI to validate firmware size budgets without producing any output file:\n\n```bash\nnpx svelteesp32 -e psychic -s ./dist --maxsize=400k --maxgzipsize=150k --analyze\n```\n\nSample output:\n\n```\nindex.html                           Original  Gzip\n──────────────────────────────────── ────────  ────────\nassets/index-KwubEIf-.js             37.9kB    12.3kB\nassets/index-Soe6cpLA.css            31.7kB    5.2kB\nfavicon.png                          32.5kB    32.5kB  [no gzip]\nindex.html                           0.5kB     0.3kB\n────────────────────────────────────────────────────────\nTotal                                102.6kB   50.3kB\nBudget (maxsize)                     400.0kB   -         ✓ PASS\nBudget (maxgzipsize)                 -         150.0kB   ✓ PASS\n```\n\nExits with code **1** if any budget is exceeded — CI fails automatically. Mutually exclusive with `--dryrun`.\n\n### JSON Manifest\n\nAdd `--manifest` to write a companion `.manifest.json` file alongside the header (same directory, same base name):\n\n```bash\nnpx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --manifest\n# also writes ./esp32/svelteesp32.manifest.json\n```\n\nThe manifest records build metadata and per-file details for tooling and dashboards:\n\n```json\n{\n  \"generated\": \"2026-04-26T12:00:00.000Z\",\n  \"engine\": \"psychic\",\n  \"etag\": \"never\",\n  \"gzip\": \"always\",\n  \"filecount\": 4,\n  \"size\": 104960,\n  \"gzipSize\": 51507,\n  \"files\": [\n    {\n      \"path\": \"/assets/index-KwubEIf-.js\",\n      \"mime\": \"text/javascript\",\n      \"size\": 38850,\n      \"gzipSize\": 12547,\n      \"isGzip\": true\n    }\n  ]\n}\n```\n\n### C++ Build-Time Validation\n\nCatch configuration issues at compile time with generated defines:\n\n```c\n#include \"svelteesp32.h\"\n\n#if SVELTEESP32_COUNT != 5\n  #error Unexpected file count - check your build\n#endif\n\n#ifndef SVELTEESP32_FILE_INDEX_HTML\n  #error Missing index.html - frontend build failed?\n#endif\n```\n\n**Available defines:** `SVELTEESP32_COUNT`, `SVELTEESP32_SIZE`, `SVELTEESP32_SIZE_GZIP`, `SVELTEESP32_FILE_*`, `SVELTEESP32_*_FILES`\n\n### Runtime File Manifest\n\nQuery embedded files at runtime for logging, diagnostics, or API endpoints:\n\n```c\n// List all embedded files\nfor (size_t i = 0; i \u003c SVELTEESP32_FILE_COUNT; i++) {\n    const auto\u0026 f = SVELTEESP32_FILES[i];\n    Serial.printf(\"%s (%d bytes, gzip: %d)\\n\", f.path, f.size, f.gzipSize);\n}\n```\n\nEach file entry includes: `path`, `size`, `gzipSize`, `etag`, `contentType`\n\n### Request Hook (Metrics \u0026 Logging)\n\nTrack every request with zero overhead when unused (weak linkage):\n\n```c\nextern \"C\" void SVELTEESP32_onFileServed(const char* path, int statusCode) {\n    Serial.printf(\"[HTTP] %s -\u003e %d\\n\", path, statusCode);\n    if (statusCode == 304) cacheHits++;\n}\n```\n\nCalled for every response (200 = content served, 304 = cache hit).\n\n---\n\n## CLI Reference\n\n| Option              | Description                                                                            | Default                 |\n| ------------------- | -------------------------------------------------------------------------------------- | ----------------------- |\n| `-s`                | Source folder with compiled web files                                                  | (required)              |\n| `-e`                | Web server engine (psychic/async/espidf/webserver)                                     | `psychic`               |\n| `-o`                | Output header file path                                                                | `svelteesp32.h`         |\n| `--etag`            | ETag caching (always/never/compiler)                                                   | `never`                 |\n| `--gzip`            | Gzip compression (always/never/compiler)                                               | `always`                |\n| `--created`         | Include creation timestamp in header                                                   | `false`                 |\n| `--exclude`         | Exclude files by glob pattern                                                          | (none)                  |\n| `--basepath`        | URL prefix for all routes                                                              | (none)                  |\n| `--maxsize`         | Max total uncompressed size (e.g., `400k`, `1m`)                                       | (none)                  |\n| `--maxgzipsize`     | Max total gzip size (e.g., `150k`, `500k`)                                             | (none)                  |\n| `--cachetime`       | Cache-Control max-age in seconds (all files)                                           | `0`                     |\n| `--cachetimehtml`   | max-age for HTML files (overrides `--cachetime`)                                       | (unset)                 |\n| `--cachetimeassets` | max-age for non-HTML files (overrides `--cachetime`)                                   | (unset)                 |\n| `--version`         | Version string in header                                                               | (none)                  |\n| `--define`          | C++ define prefix                                                                      | `SVELTEESP32`           |\n| `--espmethod`       | Init function name                                                                     | `initSvelteStaticFiles` |\n| `--config`          | Custom RC file path                                                                    | `.svelteesp32rc.json`   |\n| `--dryrun`          | Show route table + summary without writing output                                      | `false`                 |\n| `--analyze`         | Print per-file size table and budget status, no output written; exits 1 if over budget | `false`                 |\n| `--manifest`        | Write companion `.manifest.json` alongside the header                                  | `false`                 |\n| `--spa`             | Serve index.html for unmatched routes (SPA routing)                                    | `false`                 |\n| `--noindexcheck`    | Skip index.html validation                                                             | `false`                 |\n| `-h`                | Show help                                                                              |                         |\n\n---\n\n## Configuration File\n\nStore your settings in `.svelteesp32rc.json` for zero-argument builds:\n\n```json\n{\n  \"engine\": \"psychic\",\n  \"sourcepath\": \"./dist\",\n  \"outputfile\": \"./esp32/svelteesp32.h\",\n  \"etag\": \"always\",\n  \"gzip\": \"always\",\n  \"exclude\": [\"*.map\", \"*.md\"],\n  \"basepath\": \"/ui\",\n  \"maxsize\": \"400k\",\n  \"maxgzipsize\": \"150k\",\n  \"cachetime\": 0,\n  \"cachetimehtml\": 0,\n  \"cachetimeassets\": 31536000,\n  \"noindexcheck\": false,\n  \"dryrun\": false,\n  \"analyze\": false,\n  \"spa\": false,\n  \"manifest\": false\n}\n```\n\nBoolean 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`.\n\nThen just run:\n\n```bash\nnpx svelteesp32\n```\n\n### npm Variable Interpolation\n\nSync versions and names automatically from your `package.json`:\n\n```json\n{\n  \"version\": \"v$npm_package_version\",\n  \"define\": \"$npm_package_name\"\n}\n```\n\nWith `package.json` containing `\"version\": \"2.1.0\"`, this becomes `\"version\": \"v2.1.0\"`.\n\n### Multiple Environments\n\n```bash\nnpx svelteesp32 --config=.svelteesp32rc.prod.json\n```\n\nCLI arguments always override RC file values.\n\n\u003e **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.\n\n---\n\n## Common Recipes\n\n### Recipe A — Arduino IDE + Built-in WebServer\n\nNo PlatformIO required. Build your frontend, run svelteesp32 manually, then compile in Arduino IDE.\n\n```bash\n# 1. Build your frontend\nnpm run build\n\n# 2. Generate the header\nnpx svelteesp32 -e webserver -s ./dist -o ./MyProject/svelteesp32.h \\\n  --gzip=always --etag=never\n```\n\n\u003e **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()`.\n\n```c\n#include \u003cWebServer.h\u003e\n#include \"svelteesp32.h\"\n\nWebServer server(80);\n\nvoid setup() {\n    WiFi.begin(\"ssid\", \"password\");\n    while (WiFi.status() != WL_CONNECTED) delay(500);\n    initSvelteStaticFiles(\u0026server);\n    server.begin();\n}\n\nvoid loop() {\n    server.handleClient();\n}\n```\n\n---\n\n### Recipe B — PlatformIO + Vite/SvelteKit, Auto-Generated Header on `pio run`\n\nRun svelteesp32 automatically as a PlatformIO pre-build step so your header is always up to date.\n\n**`platformio.ini`**\n\n```ini\n[env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\nlib_deps =\n    ESP Async WebServer\nextra_scripts = pre:scripts/build_frontend.py\n```\n\n**`scripts/build_frontend.py`**\n\n```python\nImport(\"env\")\nimport subprocess\n\ndef build_frontend(source, target, env):\n    print(\"Building frontend...\")\n    subprocess.run([\"npm\", \"run\", \"build\"], cwd=\"frontend\", check=True)\n    subprocess.run([\n        \"npx\", \"svelteesp32\",\n        \"-e\", \"async\",\n        \"-s\", \"frontend/dist\",\n        \"-o\", \"src/svelteesp32.h\",\n        \"--etag=always\", \"--gzip=always\",\n        \"--cachetimehtml=0\", \"--cachetimeassets=31536000\"\n    ], check=True)\n\nenv.AddPreAction(\"buildprog\", build_frontend)\n```\n\nThis ensures the C++ header is regenerated from the latest frontend build every time you run `pio run`.\n\n---\n\n### Recipe C — ESP-IDF CMake, 1-Year Cached Content-Hashed Assets\n\nIntegrate svelteesp32 into a CMake build so the header is regenerated automatically.\n\n**`CMakeLists.txt` (component or top-level)**\n\n```cmake\nadd_custom_command(\n    OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h\n    COMMAND npm run build\n    COMMAND npx svelteesp32\n        -e espidf\n        -s ${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist\n        -o ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h\n        --etag=always --gzip=always\n        --cachetimehtml=0 --cachetimeassets=31536000\n    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n    COMMENT \"Generating svelteesp32.h from frontend build\"\n    VERBATIM\n)\n\nadd_custom_target(frontend_header\n    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h\n)\n\n# Make your main component depend on the generated header\nadd_dependencies(${COMPONENT_LIB} frontend_header)\n```\n\n**`main/app_main.c`**\n\n```c\n#include \u003cesp_http_server.h\u003e\n#include \"svelteesp32.h\"\n\nvoid app_main(void) {\n    // ... WiFi init ...\n    httpd_config_t config = HTTPD_DEFAULT_CONFIG();\n    httpd_handle_t server = NULL;\n    httpd_start(\u0026server, \u0026config);\n    initSvelteStaticFiles(server);\n}\n```\n\nThe `--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.\n\n---\n\n## FAQ\n\n**How large can my web app be?**\nWith gzip compression, 3-4MB asset directories work comfortably. That's enough for a full-featured SPA.\n\n**Does this use RAM or Flash?**\nFlash only. Data is stored in program memory (PROGMEM on ESP8266, const arrays on ESP32), leaving your heap and stack free for application logic.\n\n**Why is the .h file so large?**\nThe text representation (comma-separated bytes) is larger than binary. Check `SVELTEESP32_SIZE_GZIP` for actual flash usage.\n\n**Does compilation take forever?**\nNo. Large headers compile in seconds, and incremental builds skip recompilation if your frontend hasn't changed.\n\n**Can frontend and firmware teams work separately?**\nAbsolutely. Frontend builds the app, runs svelteesp32, commits the header. Firmware team includes it and ships. Version sync via npm variables keeps everyone aligned.\n\n---\n\n## Development\n\n```bash\nnpm run build        # Build TypeScript\nnpm run test         # Run unit tests\nnpm run test:watch   # Watch mode\nnpm run fix          # Fix formatting \u0026 linting\n```\n\n---\n\n**Ready to ship your web UI in a single binary?**\n\n```bash\nnpm install -D svelteesp32\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbcsabaengine%2Fsvelteesp32","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbcsabaengine%2Fsvelteesp32","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbcsabaengine%2Fsvelteesp32/lists"}