svelteesp32 1.13.1 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,135 +1,125 @@
1
- # `svelteesp32` ![image](https://badges.github.io/stability-badges/dist/stable.svg)
1
+ # svelteesp32 ![image](https://badges.github.io/stability-badges/dist/stable.svg)
2
2
 
3
- [Changelog](CHANGELOG.md)
4
-
5
- # Convert Svelte (or React/Angular/Vue) JS application to serve it from ESP32/ESP8266 webserver
6
-
7
- ### Forget SPIFFS and LittleFS now
8
-
9
- I often make small to medium-sized microcontroller solutions that run on ESP32 or ESP8266. If a web interface is needed, I create a Svelte application. The Svelte application is practically served by the ESP32/ESP8266.
10
-
11
- In order to be able to easily update OTA, it is important - from the users' point of view - that the update file **consists of one file**. I can't use the SPIFFS/LittleFS solution for this. It is necessary that the WebUI files are included inline in the Arduino or PlatformIO c++ code.
12
-
13
- This npm package provides a solution for **inserting any JS client application into the ESP web server** (PsychicHttp and also ESPAsyncWebServer (https://github.com/ESP32Async/ESPAsyncWebServer) and ESP-IDF available, PsychicHttp is the default). For this, JS, html, css, font, assets, etc. files must be converted to binary byte array. Npm mode is easy to use and easy to **integrate into your CI/CD pipeline**.
3
+ ### Embed Any Web App in Your ESP32 — One Binary, Zero Filesystem Hassle
14
4
 
15
- **Quick Comparison:**
5
+ **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.
16
6
 
17
- | Feature | SvelteESP32 | Traditional Filesystem (SPIFFS/LittleFS) |
18
- | --------------------- | --------------------------------------- | ---------------------------------------- |
19
- | **Single Binary OTA** | ✓ Everything embedded in firmware | ✗ Requires separate partition upload |
20
- | **Gzip Compression** | ✓ Automatic build-time (>15% reduction) | Manual or runtime compression |
21
- | **ETag Support** | ✓ Built-in MD5 ETags with 304 responses | Manual implementation required |
22
- | **CI/CD Integration** | ✓ npm package, simple build step | Complex with upload_fs tools |
23
- | **Memory Efficiency** | Flash only (PROGMEM/const arrays) | Flash partition + filesystem overhead |
24
- | **Performance** | Direct byte array serving | Filesystem read overhead |
25
- | **Setup Complexity** | Include header, call init function | Partition setup, upload tools, handlers |
26
-
27
- **When to use:**
28
-
29
- - **SvelteESP32**: Single-binary OTA updates, CI/CD pipelines, static web content that doesn't change at runtime
30
- - **SPIFFS/LittleFS**: Dynamic content, user-uploadable files, configuration that changes at runtime
31
-
32
- > Starting with version v1.13.0, RC files support npm package variable interpolation
7
+ [Changelog](CHANGELOG.md)
33
8
 
34
- > Starting with version v1.12.0, you can use RC file for configuration
9
+ ---
35
10
 
36
- > Starting with version v1.11.0, you can exclude files by pattern
11
+ ## Why SvelteESP32?
37
12
 
38
- > Starting with version v1.10.0, we reduced npm dependencies
13
+ **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.
39
14
 
40
- > Starting with version v1.9.0, code generator for esp-idf is available
15
+ **The solution:** SvelteESP32 compiles your entire web application into a single C++ header file. One firmware binary. One OTA update. Done.
41
16
 
42
- > Starting with version v1.8.0, use the new and maintained ESPAsyncWebserver available at https://github.com/ESP32Async/ESPAsyncWebServer
17
+ ### Key Benefits
43
18
 
44
- > Starting with version v1.7.0, with the cachetime command line option, you can set whether the browser can cache pages
19
+ - **Single Binary OTA** Everything embedded in firmware. No partition juggling, no separate uploads.
20
+ - **Automatic Optimization** — Build-time gzip compression with intelligent thresholds (>1KB, >15% reduction).
21
+ - **Smart Caching** — Built-in SHA256 ETags deliver HTTP 304 responses, slashing bandwidth on constrained devices.
22
+ - **CI/CD Ready** — Simple npm package that slots into any build pipeline.
23
+ - **Zero Runtime Overhead** — Data served directly from flash. No filesystem reads, no RAM allocation.
24
+ - **4 Web Server Engines** — PsychicHttp V1/V2, ESPAsyncWebServer, and native ESP-IDF supported.
45
25
 
46
- > Starting with version v1.6.0, mime-types package properly handles MIME types (application/javascript -> text/javascript)
26
+ ---
47
27
 
48
- > Starting with version v1.5.0, PsychicHttp v2 is also supported.
28
+ ## SvelteESP32 vs Traditional Filesystem
49
29
 
50
- > Version v1.4.0 has a breaking change! --no-gzip changed to --gzip. Starting with this version c++ compiler directives are available to setup operation in project level.
30
+ | Feature | SvelteESP32 | SPIFFS / LittleFS |
31
+ | --------------------- | --------------------------------- | ---------------------------------------- |
32
+ | **Single Binary OTA** | ✓ Everything in firmware | ✗ Separate partition upload required |
33
+ | **Gzip Compression** | ✓ Automatic at build time | Manual or runtime compression |
34
+ | **ETag Support** | ✓ Built-in SHA256 + 304 responses | Manual implementation required |
35
+ | **CI/CD Integration** | ✓ One npm command | Complex upload_fs tooling |
36
+ | **Memory Efficiency** | Flash only (PROGMEM/const arrays) | Filesystem partition + overhead |
37
+ | **Performance** | Direct byte array serving | Filesystem read latency |
38
+ | **Setup Complexity** | Include header, call one function | Partition tables, upload tools, handlers |
51
39
 
52
- > Starting with version v1.3.0, c++ defines can be used.
40
+ **Best for:** Single-binary OTA, CI/CD pipelines, static web UIs that ship with firmware.
53
41
 
54
- > Starting with version v1.2.0, ESP8266/ESP8285 is also supported.
42
+ **Consider SPIFFS/LittleFS for:** User-uploadable files, runtime-editable configs, dynamic content.
55
43
 
56
- > Starting with version v1.1.0, the ETag header is also supported.
44
+ ---
57
45
 
58
- ### Requirements
46
+ ## Quick Start
59
47
 
60
- - Node.js >= 20
61
- - npm >= 9
48
+ ```bash
49
+ npm install -D svelteesp32
50
+ ```
62
51
 
63
- ### Development
52
+ After building your frontend (Vite/Rollup/Webpack):
64
53
 
65
- #### Testing
54
+ ```bash
55
+ npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
56
+ ```
66
57
 
67
- The project includes comprehensive unit tests using Vitest:
58
+ Include in your ESP32 project:
68
59
 
69
- ```bash
70
- # Run tests once
71
- npm run test
60
+ ```c
61
+ #include <PsychicHttp.h>
62
+ #include "svelteesp32.h"
72
63
 
73
- # Run tests in watch mode (for development)
74
- npm run test:watch
64
+ PsychicHttpServer server;
75
65
 
76
- # Generate coverage report
77
- npm run test:coverage
66
+ void setup() {
67
+ server.listen(80);
68
+ initSvelteStaticFiles(&server);
69
+ }
78
70
  ```
79
71
 
80
- **Test Coverage:** ~68% overall with focus on core functionality:
81
-
82
- - `commandLine.ts`: 84.56% - CLI argument parsing and validation
83
- - `file.ts`: 100% - File operations and duplicate detection
84
- - `cppCode.ts`: 96.62% - C++ code generation and templates
85
- - `consoleColor.ts`: 100% - Console output utilities
72
+ **That's it.** Your entire web app is now embedded and ready to serve.
86
73
 
87
- Coverage reports are generated in the `coverage/` directory and can be viewed by opening `coverage/index.html` in a browser.
74
+ ---
88
75
 
89
- #### Code Quality
76
+ ## What's New
90
77
 
91
- ```bash
92
- # Check formatting
93
- npm run format:check
78
+ - **v1.15.0** — `--base-path` for multiple frontends (e.g., `/admin`, `/app`)
79
+ - **v1.13.0** — npm package variable interpolation in RC files
80
+ - **v1.12.0** — RC file configuration support
81
+ - **v1.11.0** — File exclusion patterns
82
+ - **v1.9.0** — Native ESP-IDF engine
83
+ - **v1.5.0** — PsychicHttp V2 support
94
84
 
95
- # Fix formatting
96
- npm run format:fix
85
+ ---
97
86
 
98
- # Check linting
99
- npm run lint:check
87
+ ## Requirements
100
88
 
101
- # Fix linting issues
102
- npm run lint:fix
89
+ - Node.js >= 20
90
+ - npm >= 9
103
91
 
104
- # Fix all formatting and linting issues
105
- npm run fix
106
- ```
92
+ ---
107
93
 
108
- ### Usage
94
+ ## Installation & Usage
109
95
 
110
- **Install package** as dev dependency (it is practical if the package is part of the project so that you always receive updates)
96
+ ### Install
111
97
 
112
98
  ```bash
113
99
  npm install -D svelteesp32
114
100
  ```
115
101
 
116
- After a successful Svelte build (rollup/webpack/vite) **create an includable c++ header** file
102
+ ### Generate Header File
103
+
104
+ Choose your web server engine:
117
105
 
118
106
  ```bash
119
- // for PsychicHttpServer
120
- npx svelteesp32 -e psychic -s ../svelteapp/dist -o ../esp32project/svelteesp32.h --etag=true
107
+ # PsychicHttpServer (recommended for ESP32)
108
+ npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
121
109
 
122
- // for PsychicHttpServer V2
123
- npx svelteesp32 -e psychic2 -s ../svelteapp/dist -o ../esp32project/svelteesp32.h --etag=true
110
+ # PsychicHttpServer V2
111
+ npx svelteesp32 -e psychic2 -s ./dist -o ./esp32/svelteesp32.h --etag=true
124
112
 
125
- // for ESPAsyncWebServer
126
- npx svelteesp32 -e async -s ../svelteapp/dist -o ../esp32project/svelteesp32.h --etag=true
113
+ # ESPAsyncWebServer (ESP32 + ESP8266)
114
+ npx svelteesp32 -e async -s ./dist -o ./esp32/svelteesp32.h --etag=true
127
115
 
128
- // for native esp-idf
129
- npx svelteesp32 -e espidf -s ../svelteapp/dist -o ../esp32project/svelteesp32.h --etag=true
116
+ # Native ESP-IDF
117
+ npx svelteesp32 -e espidf -s ./dist -o ./esp32/svelteesp32.h --etag=true
130
118
  ```
131
119
 
132
- During the **translation process**, the processed file details are visible with compression ratios, and at the end, the result shows the ESP's memory allocation (gzip size)
120
+ ### Build Output
121
+
122
+ Watch your files get optimized in real-time:
133
123
 
134
124
  ```
135
125
  [assets/index-KwubEIf-.js] ✓ gzip used (38850 -> 12547 = 32%)
@@ -142,82 +132,62 @@ During the **translation process**, the processed file details are visible with
142
132
  ../../../Arduino/EspSvelte/svelteesp32.h 842kB size
143
133
  ```
144
134
 
145
- The tool automatically:
135
+ **Automatic optimizations:**
146
136
 
147
- - Compresses files with gzip level 9 when beneficial (>1024 bytes and >15% reduction)
148
- - Detects and reports duplicate files using SHA256 hashing
149
- - Skips pre-compressed files (.gz, .br, .brottli) if the original exists
137
+ - Gzip level 9 compression when beneficial (>1KB, >15% size reduction)
138
+ - Duplicate file detection via SHA256 hashing
139
+ - Smart skip of pre-compressed files (.gz, .br) when originals exist
150
140
 
151
- **Include svelteesp32.h** into your Arduino or PlatformIO c++ project (copy it next to the main c++ file)
141
+ ### ESP32 Integration
152
142
 
153
- ```c
154
- ...
143
+ **PsychicHttpServer (Recommended)**
155
144
 
145
+ ```c
156
146
  #include <PsychicHttp.h>
157
147
  #include "svelteesp32.h"
158
148
 
159
149
  PsychicHttpServer server;
160
150
 
161
- ...
162
-
163
- void setup()
164
- {
165
- ...
166
-
151
+ void setup() {
167
152
  server.listen(80);
168
-
169
- initSvelteStaticFiles(&server);
153
+ initSvelteStaticFiles(&server); // One line. Done.
170
154
  }
171
155
  ```
172
156
 
173
- or
157
+ **ESPAsyncWebServer**
174
158
 
175
159
  ```c
176
- ...
177
-
178
160
  #include <ESPAsyncWebServer.h>
179
161
  #include "svelteesp32.h"
180
162
 
181
163
  AsyncWebServer server(80);
182
164
 
183
- ...
184
-
185
- void setup()
186
- {
187
- ...
188
-
165
+ void setup() {
189
166
  initSvelteStaticFiles(&server);
190
-
191
167
  server.begin();
192
168
  }
193
169
  ```
194
170
 
195
- or for ESP-IDF native:
171
+ **Native ESP-IDF**
196
172
 
197
173
  ```c
198
- ...
199
-
200
174
  #include <esp_http_server.h>
201
175
  #include "svelteesp32.h"
202
176
 
203
177
  httpd_handle_t server = NULL;
204
178
 
205
- ...
206
-
207
- void app_main()
208
- {
209
- ...
210
-
179
+ void app_main() {
211
180
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
212
181
  httpd_start(&server, &config);
213
-
214
182
  initSvelteStaticFiles(server);
215
183
  }
216
184
  ```
217
185
 
218
- You can find minimal buildable example projects in [demo/esp32](demo/esp32) (Arduino/PlatformIO) and [demo/esp32idf](demo/esp32idf) (ESP-IDF native) folders.
186
+ Working examples: [Arduino/PlatformIO](demo/esp32) | [ESP-IDF](demo/esp32idf)
187
+
188
+ ### What Gets Generated
219
189
 
220
- The content of **generated file** (do not edit, just use):
190
+ The generated header file includes everything your ESP needs:
221
191
 
222
192
  ```c
223
193
  //engine: PsychicHttpServer
@@ -240,14 +210,33 @@ const uint8_t datagzip_assets_index_KwubEIf__js[12547] = {0x1f, 0x8b, 0x8, 0x0,
240
210
  const uint8_t datagzip_assets_index_Soe6cpLA_css[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ...
241
211
  const char * etag_assets_index_KwubEIf__js = "387b88e345cc56ef9091...";
242
212
  const char * etag_assets_index_Soe6cpLA_css = "d4f23bc45ef67890ab12...";
213
+
214
+ // File manifest for runtime introspection
215
+ struct SVELTEESP32_FileInfo {
216
+ const char* path;
217
+ uint32_t size;
218
+ uint32_t gzipSize;
219
+ const char* etag;
220
+ const char* contentType;
221
+ };
222
+ const SVELTEESP32_FileInfo SVELTEESP32_FILES[] = {
223
+ { "/assets/index-KwubEIf-.js", 38850, 12547, etag_assets_index_KwubEIf__js, "text/javascript" },
224
+ { "/assets/index-Soe6cpLA.css", 32494, 5368, etag_assets_index_Soe6cpLA_css, "text/css" },
225
+ ...
226
+ };
227
+ const size_t SVELTEESP32_FILE_COUNT = sizeof(SVELTEESP32_FILES) / sizeof(SVELTEESP32_FILES[0]);
243
228
  ...
244
229
 
230
+ // File served hook - override with your own implementation for metrics/logging
231
+ extern "C" void __attribute__((weak)) SVELTEESP32_onFileServed(const char* path, int statusCode) {}
232
+
245
233
  void initSvelteStaticFiles(PsychicHttpServer * server) {
246
234
  server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request) {
247
235
  if (request->hasHeader("If-None-Match") &&
248
236
  request->header("If-None-Match").equals(etag_assets_index_KwubEIf__js)) {
249
237
  PsychicResponse response304(request);
250
238
  response304.setCode(304);
239
+ SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 304);
251
240
  return response304.send();
252
241
  }
253
242
 
@@ -257,23 +246,7 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
257
246
  response.addHeader("Cache-Control", "no-cache");
258
247
  response.addHeader("ETag", etag_assets_index_KwubEIf__js);
259
248
  response.setContent(datagzip_assets_index_KwubEIf__js, 12547);
260
- return response.send();
261
- });
262
-
263
- server->on("/assets/index-Soe6cpLA.css", HTTP_GET, [](PsychicRequest * request) {
264
- if (request->hasHeader("If-None-Match") &&
265
- request->header("If-None-Match").equals(etag_assets_index_Soe6cpLA_css)) {
266
- PsychicResponse response304(request);
267
- response304.setCode(304);
268
- return response304.send();
269
- }
270
-
271
- PsychicResponse response(request);
272
- response.setContentType("text/css");
273
- response.addHeader("Content-Encoding", "gzip");
274
- response.addHeader("Cache-Control", "no-cache");
275
- response.addHeader("ETag", etag_assets_index_Soe6cpLA_css);
276
- response.setContent(datagzip_assets_index_Soe6cpLA_css, 5368);
249
+ SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 200);
277
250
  return response.send();
278
251
  });
279
252
 
@@ -281,393 +254,249 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
281
254
  }
282
255
  ```
283
256
 
284
- ### Engines and ESP variants
285
-
286
- Four web server engines are supported:
287
-
288
- - **`-e psychic`** (default): PsychicHttpServer V1 - Fast ESP32-only server using ESP-IDF internally. Recommended for ESP32 projects.
289
- - **`-e psychic2`**: PsychicHttpServer V2 - Updated version with improved API
290
- - **`-e async`**: ESPAsyncWebServer (https://github.com/ESP32Async/ESPAsyncWebServer) - Popular async server supporting both ESP32 and ESP8266/ESP8285
291
- - **`-e espidf`**: Native ESP-IDF web server - For projects using ESP-IDF framework directly (not Arduino)
257
+ ---
292
258
 
293
- If you **only work on ESP32**, we recommend using PsychicHttpServer (`-e psychic` or `-e psychic2`), which uses the native ESP-IDF web server internally for significantly faster and more stable operation.
259
+ ## Supported Web Server Engines
294
260
 
295
- **Note for PsychicHttpServer users:** The generated header includes a comment suggesting to configure `server.config.max_uri_handlers` to match or exceed your file count to ensure all routes are properly registered.
261
+ | Engine | Flag | Best For | Platform |
262
+ | --------------------- | ------------- | ---------------------------- | --------------- |
263
+ | **PsychicHttp V1** | `-e psychic` | Maximum performance | ESP32 only |
264
+ | **PsychicHttp V2** | `-e psychic2` | Modern API + performance | ESP32 only |
265
+ | **ESPAsyncWebServer** | `-e async` | Cross-platform compatibility | ESP32 + ESP8266 |
266
+ | **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only |
296
267
 
297
- ### Gzip
268
+ **Recommendation:** For ESP32-only projects, use PsychicHttpServer for the fastest, most stable experience.
298
269
 
299
- All modern browsers have been able to handle gzip-compressed content for years. For this reason, there is no question that the easily compressed JS and CSS files are stored compressed in the ESP32/ESP8266 and sent to the browser.
270
+ **Note:** For PsychicHttp, configure `server.config.max_uri_handlers` to match your file count.
300
271
 
301
- During the translation process, data in gzip format is generated and will be used if the **size is greater than 1024 bytes** and we experience a **reduction of at least 15%**. In such a case, the compressed data is unconditionally sent to the browser with the appropriate **Content-Encoding** header information.
272
+ ---
302
273
 
303
- Automatic compression can be turned off with the `--gzip=false` option.
274
+ ## Features
304
275
 
305
- > This setting has three states: yes, no, and compiler mode is available. In compiler mode, you can disable/enable Gzip by setting the `SVELTEESP32_ENABLE_GZIP` c++ compiler directive. For example, if using platformio, just write `-D SVELTEESP32_ENABLE_GZIP`.
276
+ ### Automatic Gzip Compression
306
277
 
307
- ### ETag
278
+ 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.
308
279
 
309
- The ETag HTTP header can be used to significantly reduce network traffic. If the server sends ETag information, the client can check the integrity of the file by sending back this ETag (in `If-None-Match` header) without sending the data back again. All browsers use this function, the 304 HTTP response code is clearly visible in the network traffic.
280
+ - **Enabled by default** disable with `--gzip=false`
281
+ - **Compiler mode** — use `--gzip=compiler` and control via `-D SVELTEESP32_ENABLE_GZIP` in PlatformIO
310
282
 
311
- Since microcontroller data traffic is moderately expensive, it is an individual decision whether to use the ETag or not. We **recommend using ETag**, which adds a bit more code (about 1-3%) but results in a much cleaner operation.
283
+ ### Smart ETag Caching
312
284
 
313
- The use of ETag is **not enabled by default**, this can be achieved with the `--etag=true` switch.
285
+ 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.
314
286
 
315
- All four engines (psychic, psychic2, async, espidf) fully support ETag validation with HTTP 304 Not Modified responses, reducing bandwidth usage when clients have valid cached content.
287
+ - **Enable with** `--etag=true` (recommended)
288
+ - **Minimal overhead** — adds ~1-3% code size for significant bandwidth savings
289
+ - **Compiler mode** — use `--etag=compiler` and control via `-D SVELTEESP32_ENABLE_ETAG`
316
290
 
317
- > This setting has three states: yes, no, and compiler mode is available. In compiler mode, you can disable/enable ETag by setting the `SVELTEESP32_ENABLE_ETAG` c++ compiler directive. For example, if using platformio, just type `-D SVELTEESP32_ENABLE_ETAG`.
291
+ All four engines support full ETag validation.
318
292
 
319
- ### Cache-control
293
+ ### Browser Cache Control
320
294
 
321
- By default (when using the ETag), we send no-cache in the cache-control header of the HTTP response. Pages, subpages and other elements are downloaded every time. This is perfectly acceptable when serving small pages with ESP.
295
+ Fine-tune how browsers cache your content:
322
296
 
323
- At the same time, it can be an advantage that the content is cached by the browser and not even the ETag check is performed. For this, you can specify how many seconds the max-age value sent instead of no-cache should be. In the case of `--cachetime=86400` (max-age=86400), the page (and other elements) will not be downloaded by the browser **for one day**.
297
+ - **Default:** `no-cache` browsers always validate with server (ETag check)
298
+ - **Long-term caching:** `--cachetime=86400` — cache for 24 hours without any server requests
324
299
 
325
- ### Main entry point - index.html
300
+ ### Automatic Index Handling
326
301
 
327
- Typically, the entry point for web applications is the **index.htm or index.html** file. This does not need to be listed in the browser's address bar because web servers know that this file should be served by default. Svelteesp32 also does this: if there is an index.htm or index.html file, it sets it as the main file to be served. So using `http://esp_xxx.local` or just entering the `http://x.y.w.z/` IP address will serve this main file.
302
+ Your `index.html` is automatically served at the root URL just like any web server. Visit `http://esp32.local/` and your app loads.
328
303
 
329
- **Validation**: By default, svelteesp32 validates that an `index.html` or `index.htm` file exists in your source directory (in the root or any subdirectory). This ensures users won't get a 404 error when visiting your ESP32's root URL.
330
-
331
- **Skipping Validation**: If you're building an API-only application (REST endpoints without a web UI) or using a different entry point (e.g., `main.html`), you can skip this validation with the `--no-index-check` flag:
304
+ **API-only projects?** Skip index validation with `--no-index-check`:
332
305
 
333
306
  ```bash
334
- # API-only application (no web UI)
335
- npx svelteesp32 -e psychic -s ./dist -o ./output.h --no-index-check
336
-
337
- # Custom entry point (users must visit /main.html explicitly)
338
307
  npx svelteesp32 -e psychic -s ./dist -o ./output.h --no-index-check
339
308
  ```
340
309
 
341
310
  ### File Exclusion
342
311
 
343
- The `--exclude` option allows you to exclude files from being embedded in the ESP32 firmware using glob patterns. This is useful for excluding source maps, documentation, and test files that shouldn't be part of the deployed application.
344
-
345
- #### Basic Usage
312
+ Keep source maps, docs, and test files out of your firmware:
346
313
 
347
314
  ```bash
348
- # Exclude source maps
349
- npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map"
350
-
351
- # Exclude documentation files
352
- npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.md"
353
-
354
- # Exclude multiple file types (comma-separated)
355
- npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map,*.md,*.txt"
315
+ # Single pattern
316
+ npx svelteesp32 -s ./dist -o ./output.h --exclude="*.map"
356
317
 
357
- # Exclude using multiple flags
358
- npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map" --exclude="*.md"
359
-
360
- # Exclude entire directories
361
- npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="test/**/*"
362
-
363
- # Combine multiple approaches
364
- npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map,*.md" --exclude="docs/**/*"
318
+ # Multiple patterns
319
+ npx svelteesp32 -s ./dist -o ./output.h --exclude="*.map,*.md,test/**/*"
365
320
  ```
366
321
 
367
- #### Pattern Syntax
368
-
369
- The exclude patterns use standard glob syntax:
370
-
371
- - `*.map` - Match all files ending with `.map`
372
- - `**/*.test.js` - Match all `.test.js` files in any directory
373
- - `test/**/*` - Match all files in the `test` directory and subdirectories
374
- - `.DS_Store` - Match specific filename
375
-
376
- #### Default Exclusions
377
-
378
- By default, the following system and development files are automatically excluded:
322
+ **Default exclusions:** `.DS_Store`, `Thumbs.db`, `.git`, `.svn`, `*.swp`, `*~`, `.gitignore`, `.gitattributes`
379
323
 
380
- - `.DS_Store` (macOS system file)
381
- - `Thumbs.db` (Windows thumbnail cache)
382
- - `.git` (Git directory)
383
- - `.svn` (SVN directory)
384
- - `*.swp` (Vim swap files)
385
- - `*~` (Backup files)
386
- - `.gitignore` (Git ignore file)
387
- - `.gitattributes` (Git attributes file)
388
-
389
- Custom exclude patterns are added to these defaults.
390
-
391
- #### Exclusion Output
392
-
393
- When files are excluded, you'll see a summary in the build output:
324
+ Build output shows exactly what's excluded:
394
325
 
395
326
  ```
396
- Excluded 5 file(s):
327
+ Excluded 3 file(s):
397
328
  - assets/index.js.map
398
329
  - assets/vendor.js.map
399
330
  - README.md
400
- - docs/guide.md
401
- - test/unit.test.js
402
331
  ```
403
332
 
404
- This helps you verify that the correct files are being excluded from your build.
333
+ ### Multiple Frontends (Base Path)
405
334
 
406
- ### C++ defines
335
+ Serve multiple web apps from one ESP32 using URL prefixes:
407
336
 
408
- To make it easy to integrate into a larger c++ project, we have made a couple of variables available as c++ defines.
409
-
410
- You can use the COUNT and SIZE constants:
337
+ ```bash
338
+ npx svelteesp32 -s ./admin-dist -o ./admin.h --base-path=/admin
339
+ npx svelteesp32 -s ./user-dist -o ./user.h --base-path=/app
340
+ ```
411
341
 
412
342
  ```c
413
- ...
414
- #include "svelteesp32.h"
343
+ #include "admin.h" // Serves at /admin/*
344
+ #include "user.h" // Serves at /app/*
415
345
 
416
- #if SVELTEESP32_COUNT != 5
417
- #error Invalid file count
418
- #endif
419
- ...
346
+ void setup() {
347
+ server.listen(80);
348
+ initSvelteStaticFiles_admin(&server);
349
+ initSvelteStaticFiles_user(&server);
350
+ server.on("/api/data", HTTP_GET, handleApiData);
351
+ }
420
352
  ```
421
353
 
422
- You can include a blocker error if a named file accidentally missing from the build:
423
-
424
- ```c
425
- ...
426
- #include "svelteesp32.h"
354
+ **Rules:** Must start with `/`, no trailing slash, no double slashes.
427
355
 
428
- #ifndef SVELTEESP32_FILE_INDEX_HTML
429
- #error Missing index file
430
- #endif
431
- ...
432
- ```
356
+ ### C++ Build-Time Validation
433
357
 
434
- ...or if there are too many of a file type:
358
+ Catch configuration issues at compile time with generated defines:
435
359
 
436
360
  ```c
437
- ...
438
361
  #include "svelteesp32.h"
439
362
 
440
- #if SVELTEESP32_CSS_FILES > 1
441
- #error Too many CSS files
363
+ #if SVELTEESP32_COUNT != 5
364
+ #error Unexpected file count - check your build
442
365
  #endif
443
- ...
444
- ```
445
-
446
- You can use the following c++ directives at the project level if you want to configure the usage there: `SVELTEESP32_ENABLE_ETAG` and `SVELTEESP32_ENABLE_GZIP`. (Do not forget `--etag=compiler` or `--gzip=compiler` command line arg!)
447
-
448
- ### Command line options
449
-
450
- | Option | Description | default |
451
- | ------------------ | ------------------------------------------------------------------------------------ | ----------------------- |
452
- | `-s` | **Source dist folder contains compiled web files** | (required) |
453
- | `-e` | The engine for which the include file is created (psychic/psychic2/async/espidf) | psychic |
454
- | `-o` | Generated output file with path | `svelteesp32.h` |
455
- | `--exclude` | Exclude files matching glob pattern (repeatable or comma-separated) | Default system files |
456
- | `--etag` | Use ETag header for cache (true/false/compiler) | false |
457
- | `--cachetime` | Override no-cache response with a max-age=\<cachetime\> response (seconds) | 0 |
458
- | `--gzip` | Compress content with gzip (true/false/compiler) | true |
459
- | `--created` | Include creation time in generated header | false |
460
- | `--version` | Include a version string in generated header, e.g. `--version=v$npm_package_version` | '' |
461
- | `--espmethod` | Name of generated initialization method | `initSvelteStaticFiles` |
462
- | `--define` | Prefix of c++ defines (e.g., SVELTEESP32_COUNT) | `SVELTEESP32` |
463
- | `--config` | Use custom RC file path | `.svelteesp32rc.json` |
464
- | `--no-index-check` | Skip validation for index.html/index.htm (for API-only or custom entry points) | false |
465
- | `-h` | Show help | |
466
-
467
- ### Configuration File
468
-
469
- You can store frequently-used options in a configuration file to avoid repeating command line arguments. This is especially useful for CI/CD pipelines and team collaboration.
470
-
471
- #### Quick Start
472
-
473
- Create `.svelteesp32rc.json` in your project directory:
474
-
475
- ```json
476
- {
477
- "engine": "psychic",
478
- "sourcepath": "./dist",
479
- "outputfile": "./esp32/include/svelteesp32.h",
480
- "etag": "true",
481
- "gzip": "true",
482
- "cachetime": 86400,
483
- "exclude": ["*.map", "*.md"]
484
- }
485
- ```
486
-
487
- Then simply run:
488
-
489
- ```bash
490
- npx svelteesp32
491
- ```
492
-
493
- No command line arguments needed!
494
-
495
- #### Search Locations
496
-
497
- The tool automatically searches for `.svelteesp32rc.json` in:
498
-
499
- 1. Current working directory
500
- 2. User home directory
501
-
502
- Or specify a custom location:
503
-
504
- ```bash
505
- npx svelteesp32 --config=.svelteesp32rc.prod.json
506
- ```
507
-
508
- #### Configuration Reference
509
-
510
- All CLI options can be specified in the RC file using long-form property names:
511
-
512
- | RC Property | CLI Flag | Type | Example |
513
- | -------------- | ------------------ | ------- | ------------------------------------------------ |
514
- | `engine` | `-e` | string | `"psychic"`, `"psychic2"`, `"async"`, `"espidf"` |
515
- | `sourcepath` | `-s` | string | `"./dist"` |
516
- | `outputfile` | `-o` | string | `"./output.h"` |
517
- | `etag` | `--etag` | string | `"true"`, `"false"`, `"compiler"` |
518
- | `gzip` | `--gzip` | string | `"true"`, `"false"`, `"compiler"` |
519
- | `cachetime` | `--cachetime` | number | `86400` |
520
- | `created` | `--created` | boolean | `true`, `false` |
521
- | `version` | `--version` | string | `"v1.0.0"` |
522
- | `espmethod` | `--espmethod` | string | `"initSvelteStaticFiles"` |
523
- | `define` | `--define` | string | `"SVELTEESP32"` |
524
- | `exclude` | `--exclude` | array | `["*.map", "*.md"]` |
525
- | `noIndexCheck` | `--no-index-check` | boolean | `true`, `false` |
526
-
527
- #### CLI Override
528
366
 
529
- Command line arguments always take precedence over RC file values:
530
-
531
- ```bash
532
- # Use RC settings but override etag
533
- npx svelteesp32 --etag=false
534
-
535
- # Use RC settings but add different exclude pattern
536
- npx svelteesp32 --exclude="*.txt"
367
+ #ifndef SVELTEESP32_FILE_INDEX_HTML
368
+ #error Missing index.html - frontend build failed?
369
+ #endif
537
370
  ```
538
371
 
539
- #### Multiple Environments
372
+ **Available defines:** `SVELTEESP32_COUNT`, `SVELTEESP32_SIZE`, `SVELTEESP32_SIZE_GZIP`, `SVELTEESP32_FILE_*`, `SVELTEESP32_*_FILES`
540
373
 
541
- Create different config files for different environments:
374
+ ### Runtime File Manifest
542
375
 
543
- ```bash
544
- # Development build
545
- npx svelteesp32 --config=.svelteesp32rc.dev.json
376
+ Query embedded files at runtime for logging, diagnostics, or API endpoints:
546
377
 
547
- # Production build
548
- npx svelteesp32 --config=.svelteesp32rc.prod.json
378
+ ```c
379
+ // List all embedded files
380
+ for (size_t i = 0; i < SVELTEESP32_FILE_COUNT; i++) {
381
+ const auto& f = SVELTEESP32_FILES[i];
382
+ Serial.printf("%s (%d bytes, gzip: %d)\n", f.path, f.size, f.gzipSize);
383
+ }
549
384
  ```
550
385
 
551
- #### Exclude Pattern Behavior
386
+ Each file entry includes: `path`, `size`, `gzipSize`, `etag`, `contentType`
552
387
 
553
- **Replace mode**: When you specify `exclude` patterns in RC file or CLI, they completely replace the defaults.
388
+ ### Request Hook (Metrics & Logging)
554
389
 
555
- - **No exclude specified**: Uses default system exclusions (`.DS_Store`, `Thumbs.db`, `.git`, etc.)
556
- - **RC file has exclude**: Replaces defaults with RC patterns
557
- - **CLI has --exclude**: Replaces RC patterns (or defaults if no RC)
390
+ Track every request with zero overhead when unused (weak linkage):
558
391
 
559
- Example:
560
-
561
- ```json
562
- // .svelteesp32rc.json
563
- { "exclude": ["*.map"] }
392
+ ```c
393
+ extern "C" void SVELTEESP32_onFileServed(const char* path, int statusCode) {
394
+ Serial.printf("[HTTP] %s -> %d\n", path, statusCode);
395
+ if (statusCode == 304) cacheHits++;
396
+ }
564
397
  ```
565
398
 
566
- Result: Only `*.map` is excluded (default patterns are replaced)
567
-
568
- To keep defaults, explicitly list them in your RC file:
399
+ Called for every response (200 = content served, 304 = cache hit).
569
400
 
570
- ```json
571
- {
572
- "exclude": [".DS_Store", "Thumbs.db", ".git", "*.map", "*.md"]
573
- }
574
- ```
401
+ ---
575
402
 
576
- #### NPM Package Variable Interpolation
403
+ ## CLI Reference
577
404
 
578
- RC files support automatic variable interpolation from your `package.json`. This allows you to reference package.json fields in your RC configuration using npm-style variable syntax.
405
+ | Option | Description | Default |
406
+ | ------------------ | ------------------------------------------------- | ----------------------- |
407
+ | `-s` | Source folder with compiled web files | (required) |
408
+ | `-e` | Web server engine (psychic/psychic2/async/espidf) | `psychic` |
409
+ | `-o` | Output header file path | `svelteesp32.h` |
410
+ | `--etag` | ETag caching (true/false/compiler) | `false` |
411
+ | `--gzip` | Gzip compression (true/false/compiler) | `true` |
412
+ | `--exclude` | Exclude files by glob pattern | System files |
413
+ | `--base-path` | URL prefix for all routes | (none) |
414
+ | `--cachetime` | Cache-Control max-age in seconds | `0` |
415
+ | `--version` | Version string in header | (none) |
416
+ | `--define` | C++ define prefix | `SVELTEESP32` |
417
+ | `--espmethod` | Init function name | `initSvelteStaticFiles` |
418
+ | `--config` | Custom RC file path | `.svelteesp32rc.json` |
419
+ | `--no-index-check` | Skip index.html validation | `false` |
420
+ | `-h` | Show help | |
579
421
 
580
- **Syntax:** `$npm_package_<field_name>`
422
+ ---
581
423
 
582
- **Supported in:** All string fields (`version`, `define`, `sourcepath`, `outputfile`, `espmethod`, `exclude` patterns)
424
+ ## Configuration File
583
425
 
584
- **Example:**
426
+ Store your settings in `.svelteesp32rc.json` for zero-argument builds:
585
427
 
586
428
  ```json
587
- // .svelteesp32rc.json
588
429
  {
589
430
  "engine": "psychic",
590
- "version": "v$npm_package_version",
591
- "define": "$npm_package_name",
592
431
  "sourcepath": "./dist",
593
- "outputfile": "./output.h"
432
+ "outputfile": "./esp32/svelteesp32.h",
433
+ "etag": "true",
434
+ "gzip": "true",
435
+ "exclude": ["*.map", "*.md"]
594
436
  }
595
437
  ```
596
438
 
597
- With `package.json` containing:
439
+ Then just run:
598
440
 
599
- ```json
600
- {
601
- "name": "my-esp32-app",
602
- "version": "2.1.0"
603
- }
441
+ ```bash
442
+ npx svelteesp32
604
443
  ```
605
444
 
606
- The variables are automatically interpolated to:
445
+ ### npm Variable Interpolation
446
+
447
+ Sync versions and names automatically from your `package.json`:
607
448
 
608
449
  ```json
609
450
  {
610
- "version": "v2.1.0",
611
- "define": "my_esp32_app"
451
+ "version": "v$npm_package_version",
452
+ "define": "$npm_package_name"
612
453
  }
613
454
  ```
614
455
 
615
- **Nested Fields:**
616
-
617
- You can access nested package.json fields using underscores:
456
+ With `package.json` containing `"version": "2.1.0"`, this becomes `"version": "v2.1.0"`.
618
457
 
619
- ```json
620
- // package.json
621
- {
622
- "name": "myapp",
623
- "repository": {
624
- "type": "git",
625
- "url": "https://github.com/user/repo.git"
626
- }
627
- }
458
+ ### Multiple Environments
628
459
 
629
- // .svelteesp32rc.json
630
- {
631
- "version": "$npm_package_repository_type"
632
- }
633
- // Results in: "version": "git"
460
+ ```bash
461
+ npx svelteesp32 --config=.svelteesp32rc.prod.json
634
462
  ```
635
463
 
636
- **Multiple Variables:**
637
-
638
- Combine multiple variables in a single field:
464
+ CLI arguments always override RC file values.
639
465
 
640
- ```json
641
- {
642
- "version": "$npm_package_name-v$npm_package_version-release"
643
- }
644
- // Results in: "my-esp32-app-v2.1.0-release"
645
- ```
466
+ ---
646
467
 
647
- **Requirements:**
468
+ ## FAQ
648
469
 
649
- - `package.json` must exist in the same directory as the RC file
650
- - If variables are used but `package.json` is not found, an error is thrown with details about which fields contain variables
651
- - Unknown variables are left unchanged (e.g., `$npm_package_nonexistent` stays as-is)
470
+ **How large can my web app be?**
471
+ With gzip compression, 3-4MB asset directories work comfortably. That's enough for a full-featured SPA.
652
472
 
653
- **Use Cases:**
473
+ **Does this use RAM or Flash?**
474
+ Flash only. Data is stored in program memory (PROGMEM on ESP8266, const arrays on ESP32), leaving your heap and stack free for application logic.
654
475
 
655
- - **Version Synchronization:** Keep header version in sync with npm package version
656
- - **Dynamic Naming:** Use package name for C++ defines automatically
657
- - **CI/CD Integration:** Reusable RC files across projects with different package names
476
+ **Why is the .h file so large?**
477
+ The text representation (comma-separated bytes) is larger than binary. Check `SVELTEESP32_SIZE_GZIP` for actual flash usage.
658
478
 
659
- ### Q&A
479
+ **Does compilation take forever?**
480
+ No. Large headers compile in seconds, and incremental builds skip recompilation if your frontend hasn't changed.
660
481
 
661
- - **How big a frontend application can be placed?** If you compress the content with gzip, even a 3-4Mb assets directory can be placed. This is a serious enough amount to serve a complete application.
482
+ **Can frontend and firmware teams work separately?**
483
+ Absolutely. Frontend builds the app, runs svelteesp32, commits the header. Firmware team includes it and ships. Version sync via npm variables keeps everyone aligned.
662
484
 
663
- - **How fast is cpp file compilation?** The cpp (.h) file can be large, but it can be compiled in a few seconds on any machine. If you don't modify your svelte/react app, it will use the already compiled cpp file (not recompile). This does not increase the speed of ESP32/ESP8266 development.
485
+ ---
664
486
 
665
- - **Does the solution use PROGMEM?** It depends on the engine. For PsychicHttpServer (`psychic`, `psychic2`) and ESP-IDF (`espidf`) engines, const arrays are used which are automatically placed in program memory on ESP32. For ESPAsyncWebServer (`async`), PROGMEM directive is explicitly used to support ESP8266/ESP8285. In both cases, the file data is stored in flash memory, not RAM, so your heap and stack remain available for your application.
487
+ ## Development
666
488
 
667
- - **Why is the .h file so big?** The source files are always larger than the binary compiled from them. The .h file contains byte arrays in text format (comma-separated decimal numbers), which takes more space than the binary data itself. The actual memory allocation is shown in the header file defines (SVELTEESP32_SIZE for uncompressed, SVELTEESP32_SIZE_GZIP for compressed).
489
+ ```bash
490
+ npm run build # Build TypeScript
491
+ npm run test # Run unit tests
492
+ npm run test:watch # Watch mode
493
+ npm run fix # Fix formatting & linting
494
+ ```
668
495
 
669
- - **Is collaboration between groups supported?** Yes, the Frontend team produces the application, the use of svelteesp32 is part of the build process. Then, provided with a version number, the .h file is placed in git, which the ESP team translates into the platformio application.
496
+ ---
670
497
 
671
- - **Will you develop it further?** Since I use it myself, I will do my best to make the solution better and better.
498
+ **Ready to ship your web UI in a single binary?**
672
499
 
673
- - **Is this safe to use in production?** I suggest you give it a try! If you find it useful and safe in several different situations, feel free to use it, just like any other free library.
500
+ ```bash
501
+ npm install -D svelteesp32
502
+ ```