svelteesp32 1.14.0 → 1.16.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 +231 -485
- package/dist/commandLine.d.ts +8 -1
- package/dist/commandLine.js +103 -3
- package/dist/cppCode.js +209 -12
- package/dist/cppCodeEspIdf.d.ts +1 -1
- package/dist/cppCodeEspIdf.js +12 -4
- package/dist/errorMessages.d.ts +1 -0
- package/dist/errorMessages.js +28 -1
- package/dist/index.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,135 +1,125 @@
|
|
|
1
|
-
#
|
|
1
|
+
# svelteesp32 
|
|
2
2
|
|
|
3
|
-
|
|
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**.
|
|
14
|
-
|
|
15
|
-
**Quick Comparison:**
|
|
3
|
+
### Embed Any Web App in Your ESP32 — One Binary, Zero Filesystem Hassle
|
|
16
4
|
|
|
17
|
-
|
|
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 SHA256 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 |
|
|
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.
|
|
26
6
|
|
|
27
|
-
|
|
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
|
-
|
|
9
|
+
---
|
|
35
10
|
|
|
36
|
-
|
|
11
|
+
## Why SvelteESP32?
|
|
37
12
|
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
+
### Key Benefits
|
|
43
18
|
|
|
44
|
-
|
|
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
|
-
|
|
26
|
+
---
|
|
47
27
|
|
|
48
|
-
|
|
28
|
+
## SvelteESP32 vs Traditional Filesystem
|
|
49
29
|
|
|
50
|
-
|
|
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
|
-
|
|
40
|
+
**Best for:** Single-binary OTA, CI/CD pipelines, static web UIs that ship with firmware.
|
|
53
41
|
|
|
54
|
-
|
|
42
|
+
**Consider SPIFFS/LittleFS for:** User-uploadable files, runtime-editable configs, dynamic content.
|
|
55
43
|
|
|
56
|
-
|
|
44
|
+
---
|
|
57
45
|
|
|
58
|
-
|
|
46
|
+
## Quick Start
|
|
59
47
|
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
```bash
|
|
49
|
+
npm install -D svelteesp32
|
|
50
|
+
```
|
|
62
51
|
|
|
63
|
-
|
|
52
|
+
After building your frontend (Vite/Rollup/Webpack):
|
|
64
53
|
|
|
65
|
-
|
|
54
|
+
```bash
|
|
55
|
+
npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
56
|
+
```
|
|
66
57
|
|
|
67
|
-
|
|
58
|
+
Include in your ESP32 project:
|
|
68
59
|
|
|
69
|
-
```
|
|
70
|
-
#
|
|
71
|
-
|
|
60
|
+
```c
|
|
61
|
+
#include <PsychicHttp.h>
|
|
62
|
+
#include "svelteesp32.h"
|
|
72
63
|
|
|
73
|
-
|
|
74
|
-
npm run test:watch
|
|
64
|
+
PsychicHttpServer server;
|
|
75
65
|
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
void setup() {
|
|
67
|
+
server.listen(80);
|
|
68
|
+
initSvelteStaticFiles(&server);
|
|
69
|
+
}
|
|
78
70
|
```
|
|
79
71
|
|
|
80
|
-
**
|
|
72
|
+
**That's it.** Your entire web app is now embedded and ready to serve.
|
|
81
73
|
|
|
82
|
-
|
|
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
|
|
74
|
+
---
|
|
86
75
|
|
|
87
|
-
|
|
76
|
+
## What's New
|
|
88
77
|
|
|
89
|
-
|
|
78
|
+
- **v1.15.0** — `--basepath` 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
|
|
90
84
|
|
|
91
|
-
|
|
92
|
-
# Check formatting
|
|
93
|
-
npm run format:check
|
|
85
|
+
---
|
|
94
86
|
|
|
95
|
-
|
|
96
|
-
npm run format:fix
|
|
87
|
+
## Requirements
|
|
97
88
|
|
|
98
|
-
|
|
99
|
-
npm
|
|
89
|
+
- Node.js >= 20
|
|
90
|
+
- npm >= 9
|
|
100
91
|
|
|
101
|
-
|
|
102
|
-
npm run lint:fix
|
|
92
|
+
---
|
|
103
93
|
|
|
104
|
-
|
|
105
|
-
npm run fix
|
|
106
|
-
```
|
|
94
|
+
## Installation & Usage
|
|
107
95
|
|
|
108
|
-
###
|
|
109
|
-
|
|
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
|
-
|
|
102
|
+
### Generate Header File
|
|
103
|
+
|
|
104
|
+
Choose your web server engine:
|
|
117
105
|
|
|
118
106
|
```bash
|
|
119
|
-
|
|
120
|
-
npx svelteesp32 -e psychic -s
|
|
107
|
+
# PsychicHttpServer (recommended for ESP32)
|
|
108
|
+
npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
121
109
|
|
|
122
|
-
|
|
123
|
-
npx svelteesp32 -e psychic2 -s
|
|
110
|
+
# PsychicHttpServer V2
|
|
111
|
+
npx svelteesp32 -e psychic2 -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
124
112
|
|
|
125
|
-
|
|
126
|
-
npx svelteesp32 -e async -s
|
|
113
|
+
# ESPAsyncWebServer (ESP32 + ESP8266)
|
|
114
|
+
npx svelteesp32 -e async -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
127
115
|
|
|
128
|
-
|
|
129
|
-
npx svelteesp32 -e espidf -s
|
|
116
|
+
# Native ESP-IDF
|
|
117
|
+
npx svelteesp32 -e espidf -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
130
118
|
```
|
|
131
119
|
|
|
132
|
-
|
|
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
|
-
|
|
135
|
+
**Automatic optimizations:**
|
|
146
136
|
|
|
147
|
-
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
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
|
-
|
|
141
|
+
### ESP32 Integration
|
|
152
142
|
|
|
153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
186
|
+
Working examples: [Arduino/PlatformIO](demo/esp32) | [ESP-IDF](demo/esp32idf)
|
|
187
|
+
|
|
188
|
+
### What Gets Generated
|
|
219
189
|
|
|
220
|
-
The
|
|
190
|
+
The generated header file includes everything your ESP needs:
|
|
221
191
|
|
|
222
192
|
```c
|
|
223
193
|
//engine: PsychicHttpServer
|
|
@@ -257,12 +227,16 @@ const SVELTEESP32_FileInfo SVELTEESP32_FILES[] = {
|
|
|
257
227
|
const size_t SVELTEESP32_FILE_COUNT = sizeof(SVELTEESP32_FILES) / sizeof(SVELTEESP32_FILES[0]);
|
|
258
228
|
...
|
|
259
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
|
+
|
|
260
233
|
void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
261
234
|
server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request) {
|
|
262
235
|
if (request->hasHeader("If-None-Match") &&
|
|
263
236
|
request->header("If-None-Match").equals(etag_assets_index_KwubEIf__js)) {
|
|
264
237
|
PsychicResponse response304(request);
|
|
265
238
|
response304.setCode(304);
|
|
239
|
+
SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 304);
|
|
266
240
|
return response304.send();
|
|
267
241
|
}
|
|
268
242
|
|
|
@@ -272,23 +246,7 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
|
272
246
|
response.addHeader("Cache-Control", "no-cache");
|
|
273
247
|
response.addHeader("ETag", etag_assets_index_KwubEIf__js);
|
|
274
248
|
response.setContent(datagzip_assets_index_KwubEIf__js, 12547);
|
|
275
|
-
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
server->on("/assets/index-Soe6cpLA.css", HTTP_GET, [](PsychicRequest * request) {
|
|
279
|
-
if (request->hasHeader("If-None-Match") &&
|
|
280
|
-
request->header("If-None-Match").equals(etag_assets_index_Soe6cpLA_css)) {
|
|
281
|
-
PsychicResponse response304(request);
|
|
282
|
-
response304.setCode(304);
|
|
283
|
-
return response304.send();
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
PsychicResponse response(request);
|
|
287
|
-
response.setContentType("text/css");
|
|
288
|
-
response.addHeader("Content-Encoding", "gzip");
|
|
289
|
-
response.addHeader("Cache-Control", "no-cache");
|
|
290
|
-
response.addHeader("ETag", etag_assets_index_Soe6cpLA_css);
|
|
291
|
-
response.setContent(datagzip_assets_index_Soe6cpLA_css, 5368);
|
|
249
|
+
SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 200);
|
|
292
250
|
return response.send();
|
|
293
251
|
});
|
|
294
252
|
|
|
@@ -296,461 +254,249 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
|
296
254
|
}
|
|
297
255
|
```
|
|
298
256
|
|
|
299
|
-
|
|
257
|
+
---
|
|
300
258
|
|
|
301
|
-
|
|
259
|
+
## Supported Web Server Engines
|
|
302
260
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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 |
|
|
307
267
|
|
|
308
|
-
|
|
268
|
+
**Recommendation:** For ESP32-only projects, use PsychicHttpServer for the fastest, most stable experience.
|
|
309
269
|
|
|
310
|
-
**Note
|
|
270
|
+
**Note:** For PsychicHttp, configure `server.config.max_uri_handlers` to match your file count.
|
|
311
271
|
|
|
312
|
-
|
|
272
|
+
---
|
|
313
273
|
|
|
314
|
-
|
|
274
|
+
## Features
|
|
315
275
|
|
|
316
|
-
|
|
276
|
+
### Automatic Gzip Compression
|
|
317
277
|
|
|
318
|
-
|
|
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.
|
|
319
279
|
|
|
320
|
-
|
|
280
|
+
- **Enabled by default** — disable with `--gzip=false`
|
|
281
|
+
- **Compiler mode** — use `--gzip=compiler` and control via `-D SVELTEESP32_ENABLE_GZIP` in PlatformIO
|
|
321
282
|
|
|
322
|
-
### ETag
|
|
283
|
+
### Smart ETag Caching
|
|
323
284
|
|
|
324
|
-
|
|
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.
|
|
325
286
|
|
|
326
|
-
|
|
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`
|
|
327
290
|
|
|
328
|
-
|
|
291
|
+
All four engines support full ETag validation.
|
|
329
292
|
|
|
330
|
-
|
|
293
|
+
### Browser Cache Control
|
|
331
294
|
|
|
332
|
-
|
|
295
|
+
Fine-tune how browsers cache your content:
|
|
333
296
|
|
|
334
|
-
|
|
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
|
|
335
299
|
|
|
336
|
-
|
|
300
|
+
### Automatic Index Handling
|
|
337
301
|
|
|
338
|
-
|
|
302
|
+
Your `index.html` is automatically served at the root URL — just like any web server. Visit `http://esp32.local/` and your app loads.
|
|
339
303
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
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.
|
|
343
|
-
|
|
344
|
-
**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.
|
|
345
|
-
|
|
346
|
-
**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 `--noindexcheck`:
|
|
347
305
|
|
|
348
306
|
```bash
|
|
349
|
-
|
|
350
|
-
npx svelteesp32 -e psychic -s ./dist -o ./output.h --no-index-check
|
|
351
|
-
|
|
352
|
-
# Custom entry point (users must visit /main.html explicitly)
|
|
353
|
-
npx svelteesp32 -e psychic -s ./dist -o ./output.h --no-index-check
|
|
307
|
+
npx svelteesp32 -e psychic -s ./dist -o ./output.h --noindexcheck
|
|
354
308
|
```
|
|
355
309
|
|
|
356
310
|
### File Exclusion
|
|
357
311
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
#### Basic Usage
|
|
312
|
+
Keep source maps, docs, and test files out of your firmware:
|
|
361
313
|
|
|
362
314
|
```bash
|
|
363
|
-
#
|
|
364
|
-
npx svelteesp32 -
|
|
365
|
-
|
|
366
|
-
# Exclude documentation files
|
|
367
|
-
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.md"
|
|
315
|
+
# Single pattern
|
|
316
|
+
npx svelteesp32 -s ./dist -o ./output.h --exclude="*.map"
|
|
368
317
|
|
|
369
|
-
#
|
|
370
|
-
npx svelteesp32 -
|
|
371
|
-
|
|
372
|
-
# Exclude using multiple flags
|
|
373
|
-
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map" --exclude="*.md"
|
|
374
|
-
|
|
375
|
-
# Exclude entire directories
|
|
376
|
-
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="test/**/*"
|
|
377
|
-
|
|
378
|
-
# Combine multiple approaches
|
|
379
|
-
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/**/*"
|
|
380
320
|
```
|
|
381
321
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
The exclude patterns use standard glob syntax:
|
|
385
|
-
|
|
386
|
-
- `*.map` - Match all files ending with `.map`
|
|
387
|
-
- `**/*.test.js` - Match all `.test.js` files in any directory
|
|
388
|
-
- `test/**/*` - Match all files in the `test` directory and subdirectories
|
|
389
|
-
- `.DS_Store` - Match specific filename
|
|
390
|
-
|
|
391
|
-
#### Default Exclusions
|
|
392
|
-
|
|
393
|
-
By default, the following system and development files are automatically excluded:
|
|
322
|
+
**Default exclusions:** `.DS_Store`, `Thumbs.db`, `.git`, `.svn`, `*.swp`, `*~`, `.gitignore`, `.gitattributes`
|
|
394
323
|
|
|
395
|
-
|
|
396
|
-
- `Thumbs.db` (Windows thumbnail cache)
|
|
397
|
-
- `.git` (Git directory)
|
|
398
|
-
- `.svn` (SVN directory)
|
|
399
|
-
- `*.swp` (Vim swap files)
|
|
400
|
-
- `*~` (Backup files)
|
|
401
|
-
- `.gitignore` (Git ignore file)
|
|
402
|
-
- `.gitattributes` (Git attributes file)
|
|
403
|
-
|
|
404
|
-
Custom exclude patterns are added to these defaults.
|
|
405
|
-
|
|
406
|
-
#### Exclusion Output
|
|
407
|
-
|
|
408
|
-
When files are excluded, you'll see a summary in the build output:
|
|
324
|
+
Build output shows exactly what's excluded:
|
|
409
325
|
|
|
410
326
|
```
|
|
411
|
-
Excluded
|
|
327
|
+
Excluded 3 file(s):
|
|
412
328
|
- assets/index.js.map
|
|
413
329
|
- assets/vendor.js.map
|
|
414
330
|
- README.md
|
|
415
|
-
- docs/guide.md
|
|
416
|
-
- test/unit.test.js
|
|
417
331
|
```
|
|
418
332
|
|
|
419
|
-
|
|
333
|
+
### Multiple Frontends (Base Path)
|
|
420
334
|
|
|
421
|
-
|
|
335
|
+
Serve multiple web apps from one ESP32 using URL prefixes:
|
|
422
336
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
337
|
+
```bash
|
|
338
|
+
npx svelteesp32 -s ./admin-dist -o ./admin.h --basepath=/admin
|
|
339
|
+
npx svelteesp32 -s ./user-dist -o ./user.h --basepath=/app
|
|
340
|
+
```
|
|
426
341
|
|
|
427
342
|
```c
|
|
428
|
-
|
|
429
|
-
#include "
|
|
343
|
+
#include "admin.h" // Serves at /admin/*
|
|
344
|
+
#include "user.h" // Serves at /app/*
|
|
430
345
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
346
|
+
void setup() {
|
|
347
|
+
server.listen(80);
|
|
348
|
+
initSvelteStaticFiles_admin(&server);
|
|
349
|
+
initSvelteStaticFiles_user(&server);
|
|
350
|
+
server.on("/api/data", HTTP_GET, handleApiData);
|
|
351
|
+
}
|
|
435
352
|
```
|
|
436
353
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
```c
|
|
440
|
-
...
|
|
441
|
-
#include "svelteesp32.h"
|
|
354
|
+
**Rules:** Must start with `/`, no trailing slash, no double slashes.
|
|
442
355
|
|
|
443
|
-
|
|
444
|
-
#error Missing index file
|
|
445
|
-
#endif
|
|
446
|
-
...
|
|
447
|
-
```
|
|
356
|
+
### C++ Build-Time Validation
|
|
448
357
|
|
|
449
|
-
|
|
358
|
+
Catch configuration issues at compile time with generated defines:
|
|
450
359
|
|
|
451
360
|
```c
|
|
452
|
-
...
|
|
453
361
|
#include "svelteesp32.h"
|
|
454
362
|
|
|
455
|
-
#if
|
|
456
|
-
#error
|
|
363
|
+
#if SVELTEESP32_COUNT != 5
|
|
364
|
+
#error Unexpected file count - check your build
|
|
457
365
|
#endif
|
|
458
|
-
...
|
|
459
|
-
```
|
|
460
366
|
|
|
461
|
-
|
|
367
|
+
#ifndef SVELTEESP32_FILE_INDEX_HTML
|
|
368
|
+
#error Missing index.html - frontend build failed?
|
|
369
|
+
#endif
|
|
370
|
+
```
|
|
462
371
|
|
|
463
|
-
|
|
372
|
+
**Available defines:** `SVELTEESP32_COUNT`, `SVELTEESP32_SIZE`, `SVELTEESP32_SIZE_GZIP`, `SVELTEESP32_FILE_*`, `SVELTEESP32_*_FILES`
|
|
464
373
|
|
|
465
|
-
|
|
374
|
+
### Runtime File Manifest
|
|
466
375
|
|
|
467
|
-
|
|
376
|
+
Query embedded files at runtime for logging, diagnostics, or API endpoints:
|
|
468
377
|
|
|
469
378
|
```c
|
|
470
|
-
//
|
|
471
|
-
|
|
472
|
-
const char* path; // URL path (e.g., "/index.html")
|
|
473
|
-
uint32_t size; // Original file size in bytes
|
|
474
|
-
uint32_t gzipSize; // Compressed size (0 if not gzipped)
|
|
475
|
-
const char* etag; // ETag pointer (nullptr if etag disabled)
|
|
476
|
-
const char* contentType; // MIME type (e.g., "text/html")
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
// File manifest array
|
|
480
|
-
const SVELTEESP32_FileInfo SVELTEESP32_FILES[] = { ... };
|
|
481
|
-
const size_t SVELTEESP32_FILE_COUNT = sizeof(SVELTEESP32_FILES) / sizeof(SVELTEESP32_FILES[0]);
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
**Usage Example - List All Files:**
|
|
485
|
-
|
|
486
|
-
```c
|
|
487
|
-
#include "svelteesp32.h"
|
|
488
|
-
|
|
489
|
-
void listFiles() {
|
|
490
|
-
Serial.printf("Embedded files (%d):\n", SVELTEESP32_FILE_COUNT);
|
|
491
|
-
for (size_t i = 0; i < SVELTEESP32_FILE_COUNT; i++) {
|
|
379
|
+
// List all embedded files
|
|
380
|
+
for (size_t i = 0; i < SVELTEESP32_FILE_COUNT; i++) {
|
|
492
381
|
const auto& f = SVELTEESP32_FILES[i];
|
|
493
|
-
Serial.printf("
|
|
494
|
-
if (f.gzipSize > 0) {
|
|
495
|
-
Serial.printf(", gzip: %d bytes", f.gzipSize);
|
|
496
|
-
}
|
|
497
|
-
Serial.printf(", type: %s)\n", f.contentType);
|
|
498
|
-
}
|
|
382
|
+
Serial.printf("%s (%d bytes, gzip: %d)\n", f.path, f.size, f.gzipSize);
|
|
499
383
|
}
|
|
500
384
|
```
|
|
501
385
|
|
|
502
|
-
|
|
386
|
+
Each file entry includes: `path`, `size`, `gzipSize`, `etag`, `contentType`
|
|
387
|
+
|
|
388
|
+
### Request Hook (Metrics & Logging)
|
|
389
|
+
|
|
390
|
+
Track every request with zero overhead when unused (weak linkage):
|
|
503
391
|
|
|
504
392
|
```c
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if (i > 0) json += ",";
|
|
510
|
-
json += "{\"path\":\"" + String(f.path) + "\",";
|
|
511
|
-
json += "\"size\":" + String(f.size) + ",";
|
|
512
|
-
json += "\"gzipSize\":" + String(f.gzipSize) + ",";
|
|
513
|
-
json += "\"contentType\":\"" + String(f.contentType) + "\"}";
|
|
514
|
-
}
|
|
515
|
-
json += "]";
|
|
516
|
-
PsychicResponse response(request);
|
|
517
|
-
response.setContentType("application/json");
|
|
518
|
-
response.setContent(json.c_str(), json.length());
|
|
519
|
-
return response.send();
|
|
520
|
-
});
|
|
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
|
+
}
|
|
521
397
|
```
|
|
522
398
|
|
|
523
|
-
|
|
399
|
+
Called for every response (200 = content served, 304 = cache hit).
|
|
524
400
|
|
|
525
|
-
|
|
526
|
-
- `gzipSize` is 0 when the file is not gzipped (either too small, poor compression ratio, or gzip disabled)
|
|
527
|
-
- `etag` is `NULL` when etag is disabled (`--etag=false`)
|
|
528
|
-
- The struct and array names use the `--define` prefix (default: `SVELTEESP32`)
|
|
529
|
-
- For ESP-IDF engine, C-compatible `typedef struct` syntax is used instead of C++ `struct`
|
|
401
|
+
---
|
|
530
402
|
|
|
531
|
-
|
|
403
|
+
## CLI Reference
|
|
532
404
|
|
|
533
|
-
| Option
|
|
534
|
-
|
|
|
535
|
-
| `-s`
|
|
536
|
-
| `-e`
|
|
537
|
-
| `-o`
|
|
538
|
-
| `--
|
|
539
|
-
| `--
|
|
540
|
-
| `--
|
|
541
|
-
| `--
|
|
542
|
-
| `--
|
|
543
|
-
| `--version`
|
|
544
|
-
| `--
|
|
545
|
-
| `--
|
|
546
|
-
| `--config`
|
|
547
|
-
| `--
|
|
548
|
-
| `-h`
|
|
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
|
+
| `--basepath` | 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
|
+
| `--noindexcheck` | Skip index.html validation | `false` |
|
|
420
|
+
| `-h` | Show help | |
|
|
549
421
|
|
|
550
|
-
|
|
422
|
+
---
|
|
551
423
|
|
|
552
|
-
|
|
424
|
+
## Configuration File
|
|
553
425
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
Create `.svelteesp32rc.json` in your project directory:
|
|
426
|
+
Store your settings in `.svelteesp32rc.json` for zero-argument builds:
|
|
557
427
|
|
|
558
428
|
```json
|
|
559
429
|
{
|
|
560
430
|
"engine": "psychic",
|
|
561
431
|
"sourcepath": "./dist",
|
|
562
|
-
"outputfile": "./esp32/
|
|
432
|
+
"outputfile": "./esp32/svelteesp32.h",
|
|
563
433
|
"etag": "true",
|
|
564
434
|
"gzip": "true",
|
|
565
|
-
"cachetime": 86400,
|
|
566
435
|
"exclude": ["*.map", "*.md"]
|
|
567
436
|
}
|
|
568
437
|
```
|
|
569
438
|
|
|
570
|
-
Then
|
|
439
|
+
Then just run:
|
|
571
440
|
|
|
572
441
|
```bash
|
|
573
442
|
npx svelteesp32
|
|
574
443
|
```
|
|
575
444
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
#### Search Locations
|
|
579
|
-
|
|
580
|
-
The tool automatically searches for `.svelteesp32rc.json` in:
|
|
581
|
-
|
|
582
|
-
1. Current working directory
|
|
583
|
-
2. User home directory
|
|
584
|
-
|
|
585
|
-
Or specify a custom location:
|
|
586
|
-
|
|
587
|
-
```bash
|
|
588
|
-
npx svelteesp32 --config=.svelteesp32rc.prod.json
|
|
589
|
-
```
|
|
590
|
-
|
|
591
|
-
#### Configuration Reference
|
|
592
|
-
|
|
593
|
-
All CLI options can be specified in the RC file using long-form property names:
|
|
594
|
-
|
|
595
|
-
| RC Property | CLI Flag | Type | Example |
|
|
596
|
-
| -------------- | ------------------ | ------- | ------------------------------------------------ |
|
|
597
|
-
| `engine` | `-e` | string | `"psychic"`, `"psychic2"`, `"async"`, `"espidf"` |
|
|
598
|
-
| `sourcepath` | `-s` | string | `"./dist"` |
|
|
599
|
-
| `outputfile` | `-o` | string | `"./output.h"` |
|
|
600
|
-
| `etag` | `--etag` | string | `"true"`, `"false"`, `"compiler"` |
|
|
601
|
-
| `gzip` | `--gzip` | string | `"true"`, `"false"`, `"compiler"` |
|
|
602
|
-
| `cachetime` | `--cachetime` | number | `86400` |
|
|
603
|
-
| `created` | `--created` | boolean | `true`, `false` |
|
|
604
|
-
| `version` | `--version` | string | `"v1.0.0"` |
|
|
605
|
-
| `espmethod` | `--espmethod` | string | `"initSvelteStaticFiles"` |
|
|
606
|
-
| `define` | `--define` | string | `"SVELTEESP32"` |
|
|
607
|
-
| `exclude` | `--exclude` | array | `["*.map", "*.md"]` |
|
|
608
|
-
| `noIndexCheck` | `--no-index-check` | boolean | `true`, `false` |
|
|
609
|
-
|
|
610
|
-
#### CLI Override
|
|
611
|
-
|
|
612
|
-
Command line arguments always take precedence over RC file values:
|
|
613
|
-
|
|
614
|
-
```bash
|
|
615
|
-
# Use RC settings but override etag
|
|
616
|
-
npx svelteesp32 --etag=false
|
|
617
|
-
|
|
618
|
-
# Use RC settings but add different exclude pattern
|
|
619
|
-
npx svelteesp32 --exclude="*.txt"
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
#### Multiple Environments
|
|
623
|
-
|
|
624
|
-
Create different config files for different environments:
|
|
625
|
-
|
|
626
|
-
```bash
|
|
627
|
-
# Development build
|
|
628
|
-
npx svelteesp32 --config=.svelteesp32rc.dev.json
|
|
629
|
-
|
|
630
|
-
# Production build
|
|
631
|
-
npx svelteesp32 --config=.svelteesp32rc.prod.json
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
#### Exclude Pattern Behavior
|
|
635
|
-
|
|
636
|
-
**Replace mode**: When you specify `exclude` patterns in RC file or CLI, they completely replace the defaults.
|
|
445
|
+
### npm Variable Interpolation
|
|
637
446
|
|
|
638
|
-
|
|
639
|
-
- **RC file has exclude**: Replaces defaults with RC patterns
|
|
640
|
-
- **CLI has --exclude**: Replaces RC patterns (or defaults if no RC)
|
|
641
|
-
|
|
642
|
-
Example:
|
|
643
|
-
|
|
644
|
-
```json
|
|
645
|
-
// .svelteesp32rc.json
|
|
646
|
-
{ "exclude": ["*.map"] }
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
Result: Only `*.map` is excluded (default patterns are replaced)
|
|
650
|
-
|
|
651
|
-
To keep defaults, explicitly list them in your RC file:
|
|
447
|
+
Sync versions and names automatically from your `package.json`:
|
|
652
448
|
|
|
653
449
|
```json
|
|
654
450
|
{
|
|
655
|
-
"exclude": [".DS_Store", "Thumbs.db", ".git", "*.map", "*.md"]
|
|
656
|
-
}
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
#### NPM Package Variable Interpolation
|
|
660
|
-
|
|
661
|
-
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.
|
|
662
|
-
|
|
663
|
-
**Syntax:** `$npm_package_<field_name>`
|
|
664
|
-
|
|
665
|
-
**Supported in:** All string fields (`version`, `define`, `sourcepath`, `outputfile`, `espmethod`, `exclude` patterns)
|
|
666
|
-
|
|
667
|
-
**Example:**
|
|
668
|
-
|
|
669
|
-
```json
|
|
670
|
-
// .svelteesp32rc.json
|
|
671
|
-
{
|
|
672
|
-
"engine": "psychic",
|
|
673
451
|
"version": "v$npm_package_version",
|
|
674
|
-
"define": "$npm_package_name"
|
|
675
|
-
"sourcepath": "./dist",
|
|
676
|
-
"outputfile": "./output.h"
|
|
677
|
-
}
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
With `package.json` containing:
|
|
681
|
-
|
|
682
|
-
```json
|
|
683
|
-
{
|
|
684
|
-
"name": "my-esp32-app",
|
|
685
|
-
"version": "2.1.0"
|
|
686
|
-
}
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
The variables are automatically interpolated to:
|
|
690
|
-
|
|
691
|
-
```json
|
|
692
|
-
{
|
|
693
|
-
"version": "v2.1.0",
|
|
694
|
-
"define": "my_esp32_app"
|
|
452
|
+
"define": "$npm_package_name"
|
|
695
453
|
}
|
|
696
454
|
```
|
|
697
455
|
|
|
698
|
-
|
|
456
|
+
With `package.json` containing `"version": "2.1.0"`, this becomes `"version": "v2.1.0"`.
|
|
699
457
|
|
|
700
|
-
|
|
458
|
+
### Multiple Environments
|
|
701
459
|
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
{
|
|
705
|
-
"name": "myapp",
|
|
706
|
-
"repository": {
|
|
707
|
-
"type": "git",
|
|
708
|
-
"url": "https://github.com/user/repo.git"
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// .svelteesp32rc.json
|
|
713
|
-
{
|
|
714
|
-
"version": "$npm_package_repository_type"
|
|
715
|
-
}
|
|
716
|
-
// Results in: "version": "git"
|
|
460
|
+
```bash
|
|
461
|
+
npx svelteesp32 --config=.svelteesp32rc.prod.json
|
|
717
462
|
```
|
|
718
463
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
Combine multiple variables in a single field:
|
|
464
|
+
CLI arguments always override RC file values.
|
|
722
465
|
|
|
723
|
-
|
|
724
|
-
{
|
|
725
|
-
"version": "$npm_package_name-v$npm_package_version-release"
|
|
726
|
-
}
|
|
727
|
-
// Results in: "my-esp32-app-v2.1.0-release"
|
|
728
|
-
```
|
|
466
|
+
---
|
|
729
467
|
|
|
730
|
-
|
|
468
|
+
## FAQ
|
|
731
469
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
- 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.
|
|
735
472
|
|
|
736
|
-
**
|
|
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.
|
|
737
475
|
|
|
738
|
-
|
|
739
|
-
-
|
|
740
|
-
- **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.
|
|
741
478
|
|
|
742
|
-
|
|
479
|
+
**Does compilation take forever?**
|
|
480
|
+
No. Large headers compile in seconds, and incremental builds skip recompilation if your frontend hasn't changed.
|
|
743
481
|
|
|
744
|
-
|
|
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.
|
|
745
484
|
|
|
746
|
-
|
|
485
|
+
---
|
|
747
486
|
|
|
748
|
-
|
|
487
|
+
## Development
|
|
749
488
|
|
|
750
|
-
|
|
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
|
+
```
|
|
751
495
|
|
|
752
|
-
|
|
496
|
+
---
|
|
753
497
|
|
|
754
|
-
|
|
498
|
+
**Ready to ship your web UI in a single binary?**
|
|
755
499
|
|
|
756
|
-
|
|
500
|
+
```bash
|
|
501
|
+
npm install -D svelteesp32
|
|
502
|
+
```
|