svelteesp32 2.0.1 → 2.2.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 +73 -28
- package/dist/commandLine.d.ts +4 -2
- package/dist/commandLine.js +17 -4
- package/dist/cppCode.js +462 -2
- package/dist/cppCodeEspIdf.d.ts +1 -1
- package/dist/cppCodeEspIdf.js +19 -0
- package/dist/errorMessages.js +7 -2
- package/dist/index.js +2 -0
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
- **Smart Caching** — Built-in SHA256 ETags deliver HTTP 304 responses, slashing bandwidth on constrained devices.
|
|
26
26
|
- **CI/CD Ready** — Simple npm package that slots into any build pipeline.
|
|
27
27
|
- **Zero Runtime Overhead** — Data served directly from flash. No filesystem reads, no RAM allocation.
|
|
28
|
-
- **
|
|
28
|
+
- **4 Web Server Engines** — PsychicHttpServer V2, ESPAsyncWebServer, Arduino WebServer, and native ESP-IDF supported.
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
@@ -79,7 +79,8 @@ void setup() {
|
|
|
79
79
|
|
|
80
80
|
## What's New
|
|
81
81
|
|
|
82
|
-
- **v2.0
|
|
82
|
+
- **v2.2.0** — SPA routing catch-all (`--spa`) for client-side routers on all four engines
|
|
83
|
+
- **v2.1.0** — New Arduino WebServer engine (`-e webserver`), dependency updates
|
|
83
84
|
- **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
|
|
84
85
|
- **v1.16.0** — Size budget constraints (`--maxsize`, `--maxgzipsize`)
|
|
85
86
|
- **v1.15.0** — `--basepath` for multiple frontends (e.g., `/admin`, `/app`)
|
|
@@ -116,6 +117,9 @@ npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
|
116
117
|
# ESPAsyncWebServer (ESP32 + ESP8266)
|
|
117
118
|
npx svelteesp32 -e async -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
118
119
|
|
|
120
|
+
# Arduino WebServer (ESP32, synchronous, no dependencies)
|
|
121
|
+
npx svelteesp32 -e webserver -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
122
|
+
|
|
119
123
|
# Native ESP-IDF
|
|
120
124
|
npx svelteesp32 -e espidf -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
121
125
|
```
|
|
@@ -171,6 +175,24 @@ void setup() {
|
|
|
171
175
|
}
|
|
172
176
|
```
|
|
173
177
|
|
|
178
|
+
**Arduino WebServer (built-in, no dependencies)**
|
|
179
|
+
|
|
180
|
+
```c
|
|
181
|
+
#include <WebServer.h>
|
|
182
|
+
#include "svelteesp32.h"
|
|
183
|
+
|
|
184
|
+
WebServer server(80);
|
|
185
|
+
|
|
186
|
+
void setup() {
|
|
187
|
+
initSvelteStaticFiles(&server);
|
|
188
|
+
server.begin();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
void loop() {
|
|
192
|
+
server.handleClient();
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
174
196
|
**Native ESP-IDF**
|
|
175
197
|
|
|
176
198
|
```c
|
|
@@ -259,11 +281,12 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
|
259
281
|
|
|
260
282
|
## Supported Web Server Engines
|
|
261
283
|
|
|
262
|
-
| Engine | Flag
|
|
263
|
-
| ------------------------ |
|
|
264
|
-
| **PsychicHttpServer V2** | `-e psychic`
|
|
265
|
-
| **ESPAsyncWebServer** | `-e async`
|
|
266
|
-
| **
|
|
284
|
+
| Engine | Flag | Best For | Platform |
|
|
285
|
+
| ------------------------ | -------------- | ---------------------------- | --------------- |
|
|
286
|
+
| **PsychicHttpServer V2** | `-e psychic` | Maximum performance | ESP32 only |
|
|
287
|
+
| **ESPAsyncWebServer** | `-e async` | Cross-platform compatibility | ESP32 + ESP8266 |
|
|
288
|
+
| **Arduino WebServer** | `-e webserver` | No dependencies, simplicity | ESP32 only |
|
|
289
|
+
| **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only |
|
|
267
290
|
|
|
268
291
|
**Recommendation:** For ESP32-only projects, use PsychicHttpServer V2 (`-e psychic`) for the fastest, most stable experience.
|
|
269
292
|
|
|
@@ -288,7 +311,7 @@ Reduce bandwidth dramatically with HTTP 304 "Not Modified" responses. When a bro
|
|
|
288
311
|
- **Minimal overhead** — adds ~1-3% code size for significant bandwidth savings
|
|
289
312
|
- **Compiler mode** — use `--etag=compiler` and control via `-D SVELTEESP32_ENABLE_ETAG`
|
|
290
313
|
|
|
291
|
-
All
|
|
314
|
+
All four engines support full ETag validation.
|
|
292
315
|
|
|
293
316
|
### Browser Cache Control
|
|
294
317
|
|
|
@@ -353,6 +376,26 @@ void setup() {
|
|
|
353
376
|
|
|
354
377
|
**Rules:** Must start with `/`, no trailing slash, no double slashes.
|
|
355
378
|
|
|
379
|
+
### SPA Routing (Client-Side Routers)
|
|
380
|
+
|
|
381
|
+
Modern JS frameworks use client-side routing. Without a catch-all, refreshing `/settings` on your ESP32 returns nothing. Add `--spa` to make all unmatched GET requests fall through to `index.html`:
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
npx svelteesp32 -e async -s ./dist -o ./output.h --spa
|
|
385
|
+
npx svelteesp32 -e psychic -s ./dist -o ./output.h --spa --basepath=/app
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
What gets generated per engine:
|
|
389
|
+
|
|
390
|
+
| Engine | Catch-all mechanism |
|
|
391
|
+
| ----------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
392
|
+
| `psychic` | `server->on("/*", ...)` (no basePath) already handled by `defaultEndpoint`; `server->on("/app/*", ...)` when basePath is set |
|
|
393
|
+
| `async` | `server->onNotFound(...)` with optional basePath prefix check |
|
|
394
|
+
| `webserver` | `server->onNotFound(...)` with optional basePath prefix check |
|
|
395
|
+
| `espidf` | `httpd_register_err_handler(HTTPD_404_NOT_FOUND, ...)` |
|
|
396
|
+
|
|
397
|
+
**Note:** `--spa` requires `index.html` or `index.htm` in the source directory — a warning is printed if it is missing.
|
|
398
|
+
|
|
356
399
|
### C++ Build-Time Validation
|
|
357
400
|
|
|
358
401
|
Catch configuration issues at compile time with generated defines:
|
|
@@ -402,25 +445,26 @@ Called for every response (200 = content served, 304 = cache hit).
|
|
|
402
445
|
|
|
403
446
|
## CLI Reference
|
|
404
447
|
|
|
405
|
-
| Option | Description
|
|
406
|
-
| ---------------- |
|
|
407
|
-
| `-s` | Source folder with compiled web files
|
|
408
|
-
| `-e` | Web server engine (psychic/async/espidf)
|
|
409
|
-
| `-o` | Output header file path
|
|
410
|
-
| `--etag` | ETag caching (true/false/compiler)
|
|
411
|
-
| `--gzip` | Gzip compression (true/false/compiler)
|
|
412
|
-
| `--exclude` | Exclude files by glob pattern
|
|
413
|
-
| `--basepath` | URL prefix for all routes
|
|
414
|
-
| `--maxsize` | Max total uncompressed size (e.g., `400k`, `1m`)
|
|
415
|
-
| `--maxgzipsize` | Max total gzip size (e.g., `150k`, `500k`)
|
|
416
|
-
| `--cachetime` | Cache-Control max-age in seconds
|
|
417
|
-
| `--version` | Version string in header
|
|
418
|
-
| `--define` | C++ define prefix
|
|
419
|
-
| `--espmethod` | Init function name
|
|
420
|
-
| `--config` | Custom RC file path
|
|
421
|
-
| `--dryrun` | Show summary without writing output
|
|
422
|
-
| `--
|
|
423
|
-
|
|
|
448
|
+
| Option | Description | Default |
|
|
449
|
+
| ---------------- | --------------------------------------------------- | ----------------------- |
|
|
450
|
+
| `-s` | Source folder with compiled web files | (required) |
|
|
451
|
+
| `-e` | Web server engine (psychic/async/espidf/webserver) | `psychic` |
|
|
452
|
+
| `-o` | Output header file path | `svelteesp32.h` |
|
|
453
|
+
| `--etag` | ETag caching (true/false/compiler) | `false` |
|
|
454
|
+
| `--gzip` | Gzip compression (true/false/compiler) | `true` |
|
|
455
|
+
| `--exclude` | Exclude files by glob pattern | System files |
|
|
456
|
+
| `--basepath` | URL prefix for all routes | (none) |
|
|
457
|
+
| `--maxsize` | Max total uncompressed size (e.g., `400k`, `1m`) | (none) |
|
|
458
|
+
| `--maxgzipsize` | Max total gzip size (e.g., `150k`, `500k`) | (none) |
|
|
459
|
+
| `--cachetime` | Cache-Control max-age in seconds | `0` |
|
|
460
|
+
| `--version` | Version string in header | (none) |
|
|
461
|
+
| `--define` | C++ define prefix | `SVELTEESP32` |
|
|
462
|
+
| `--espmethod` | Init function name | `initSvelteStaticFiles` |
|
|
463
|
+
| `--config` | Custom RC file path | `.svelteesp32rc.json` |
|
|
464
|
+
| `--dryrun` | Show summary without writing output | `false` |
|
|
465
|
+
| `--spa` | Serve index.html for unmatched routes (SPA routing) | `false` |
|
|
466
|
+
| `--noindexcheck` | Skip index.html validation | `false` |
|
|
467
|
+
| `-h` | Show help | |
|
|
424
468
|
|
|
425
469
|
---
|
|
426
470
|
|
|
@@ -440,7 +484,8 @@ Store your settings in `.svelteesp32rc.json` for zero-argument builds:
|
|
|
440
484
|
"maxsize": "400k",
|
|
441
485
|
"maxgzipsize": "150k",
|
|
442
486
|
"noindexcheck": false,
|
|
443
|
-
"dryrun": false
|
|
487
|
+
"dryrun": false,
|
|
488
|
+
"spa": false
|
|
444
489
|
}
|
|
445
490
|
```
|
|
446
491
|
|
package/dist/commandLine.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
interface ICopyFilesArguments {
|
|
2
|
-
engine: 'psychic' | 'async' | 'espidf';
|
|
2
|
+
engine: 'psychic' | 'async' | 'espidf' | 'webserver';
|
|
3
3
|
sourcepath: string;
|
|
4
4
|
outputfile: string;
|
|
5
5
|
espmethod: string;
|
|
@@ -15,10 +15,11 @@ interface ICopyFilesArguments {
|
|
|
15
15
|
maxGzipSize?: number;
|
|
16
16
|
noIndexCheck?: boolean;
|
|
17
17
|
dryRun?: boolean;
|
|
18
|
+
spa?: boolean;
|
|
18
19
|
help?: boolean;
|
|
19
20
|
}
|
|
20
21
|
interface IRcFileConfig {
|
|
21
|
-
engine?: 'psychic' | 'async' | 'espidf';
|
|
22
|
+
engine?: 'psychic' | 'async' | 'espidf' | 'webserver';
|
|
22
23
|
sourcepath?: string;
|
|
23
24
|
outputfile?: string;
|
|
24
25
|
espmethod?: string;
|
|
@@ -34,6 +35,7 @@ interface IRcFileConfig {
|
|
|
34
35
|
maxgzipsize?: number | string;
|
|
35
36
|
noindexcheck?: boolean;
|
|
36
37
|
dryrun?: boolean;
|
|
38
|
+
spa?: boolean;
|
|
37
39
|
}
|
|
38
40
|
declare function validateCppIdentifier(value: string, name: string): string;
|
|
39
41
|
declare function parseSize(value: string, name: string): number;
|
package/dist/commandLine.js
CHANGED
|
@@ -24,7 +24,7 @@ Configuration:
|
|
|
24
24
|
|
|
25
25
|
Options:
|
|
26
26
|
-e, --engine <value> The engine for which the include file is created
|
|
27
|
-
(psychic|async|espidf) (default: "psychic")
|
|
27
|
+
(psychic|async|espidf|webserver) (default: "psychic")
|
|
28
28
|
-s, --sourcepath <path> Source dist folder contains compiled web files (required)
|
|
29
29
|
-o, --outputfile <path> Generated output file with path (default: "svelteesp32.h")
|
|
30
30
|
--etag <value> Use ETAG header for cache (true|false|compiler) (default: "false")
|
|
@@ -40,6 +40,7 @@ Options:
|
|
|
40
40
|
--maxsize <size> Maximum total uncompressed size (e.g., 400k, 1.5m, 409600)
|
|
41
41
|
--maxgzipsize <size> Maximum total gzip size (e.g., 150k, 1m, 153600)
|
|
42
42
|
--dryrun Show summary without writing the output file (default: false)
|
|
43
|
+
--spa Serve index.html for unmatched routes (SPA routing) (default: false)
|
|
43
44
|
-h, --help Shows this help
|
|
44
45
|
|
|
45
46
|
RC File:
|
|
@@ -58,7 +59,8 @@ RC File:
|
|
|
58
59
|
"basepath": "/ui",
|
|
59
60
|
"maxsize": "400k",
|
|
60
61
|
"maxgzipsize": "150k",
|
|
61
|
-
"noindexcheck": false
|
|
62
|
+
"noindexcheck": false,
|
|
63
|
+
"spa": false
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
CLI arguments override RC file values.
|
|
@@ -66,7 +68,7 @@ RC File:
|
|
|
66
68
|
process.exit(0);
|
|
67
69
|
}
|
|
68
70
|
function validateEngine(value) {
|
|
69
|
-
if (value === 'psychic' || value === 'async' || value === 'espidf')
|
|
71
|
+
if (value === 'psychic' || value === 'async' || value === 'espidf' || value === 'webserver')
|
|
70
72
|
return value;
|
|
71
73
|
console.error((0, errorMessages_1.getInvalidEngineError)(value));
|
|
72
74
|
process.exit(1);
|
|
@@ -268,7 +270,8 @@ function validateRcConfig(config, rcPath) {
|
|
|
268
270
|
'maxsize',
|
|
269
271
|
'maxgzipsize',
|
|
270
272
|
'noindexcheck',
|
|
271
|
-
'dryrun'
|
|
273
|
+
'dryrun',
|
|
274
|
+
'spa'
|
|
272
275
|
]);
|
|
273
276
|
for (const key of Object.keys(configObject))
|
|
274
277
|
if (!validKeys.has(key))
|
|
@@ -324,6 +327,8 @@ function validateRcConfig(config, rcPath) {
|
|
|
324
327
|
throw new TypeError(`Invalid noindexcheck in RC file: ${configObject['noindexcheck']} (must be boolean)`);
|
|
325
328
|
if (configObject['dryrun'] !== undefined && typeof configObject['dryrun'] !== 'boolean')
|
|
326
329
|
throw new TypeError(`Invalid dryrun in RC file: ${configObject['dryrun']} (must be boolean)`);
|
|
330
|
+
if (configObject['spa'] !== undefined && typeof configObject['spa'] !== 'boolean')
|
|
331
|
+
throw new TypeError(`Invalid spa in RC file: ${configObject['spa']} (must be boolean)`);
|
|
327
332
|
return configObject;
|
|
328
333
|
}
|
|
329
334
|
function parseArguments() {
|
|
@@ -389,6 +394,8 @@ function parseArguments() {
|
|
|
389
394
|
result.noIndexCheck = rcConfig.noindexcheck;
|
|
390
395
|
if (rcConfig.dryrun !== undefined)
|
|
391
396
|
result.dryRun = rcConfig.dryrun;
|
|
397
|
+
if (rcConfig.spa !== undefined)
|
|
398
|
+
result.spa = rcConfig.spa;
|
|
392
399
|
if (rcConfig.exclude && rcConfig.exclude.length > 0)
|
|
393
400
|
result.exclude = [...rcConfig.exclude];
|
|
394
401
|
const cliExclude = [];
|
|
@@ -475,6 +482,10 @@ function parseArguments() {
|
|
|
475
482
|
result.dryRun = true;
|
|
476
483
|
continue;
|
|
477
484
|
}
|
|
485
|
+
if (argument === '--spa') {
|
|
486
|
+
result.spa = true;
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
478
489
|
if (argument.startsWith('-') && !argument.startsWith('--')) {
|
|
479
490
|
const flag = argument.slice(1);
|
|
480
491
|
const nextArgument = arguments_[index + 1];
|
|
@@ -540,6 +551,8 @@ function formatConfiguration(cmdLine) {
|
|
|
540
551
|
parts.push(`maxSize=${cmdLine.maxSize}`);
|
|
541
552
|
if (cmdLine.maxGzipSize !== undefined)
|
|
542
553
|
parts.push(`maxGzipSize=${cmdLine.maxGzipSize}`);
|
|
554
|
+
if (cmdLine.spa)
|
|
555
|
+
parts.push(`spa=${cmdLine.spa}`);
|
|
543
556
|
if (cmdLine.exclude.length > 0)
|
|
544
557
|
parts.push(`exclude=[${cmdLine.exclude.join(', ')}]`);
|
|
545
558
|
return parts.join(' ');
|
package/dist/cppCode.js
CHANGED
|
@@ -315,6 +315,94 @@ void {{methodName}}(PsychicHttpServer * server) {
|
|
|
315
315
|
{{/if}}{{/if}}
|
|
316
316
|
|
|
317
317
|
{{/each}}
|
|
318
|
+
{{#if spa}}
|
|
319
|
+
{{#with spaSource}}
|
|
320
|
+
{{#if ../basePath}}
|
|
321
|
+
//
|
|
322
|
+
// SPA catch-all: unmatched routes serve {{this.filename}}
|
|
323
|
+
server->on("{{../basePath}}/*", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {
|
|
324
|
+
|
|
325
|
+
{{#switch ../etag}}
|
|
326
|
+
{{#case "true"}}
|
|
327
|
+
if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
328
|
+
response->setCode(304);
|
|
329
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
330
|
+
return response->send();
|
|
331
|
+
}
|
|
332
|
+
{{/case}}
|
|
333
|
+
{{#case "compiler"}}
|
|
334
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
335
|
+
if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
336
|
+
response->setCode(304);
|
|
337
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
338
|
+
return response->send();
|
|
339
|
+
}
|
|
340
|
+
#endif
|
|
341
|
+
{{/case}}
|
|
342
|
+
{{/switch}}
|
|
343
|
+
|
|
344
|
+
response->setContentType("{{this.mime}}");
|
|
345
|
+
|
|
346
|
+
{{#switch ../gzip}}
|
|
347
|
+
{{#case "true"}}
|
|
348
|
+
{{#if this.isGzip}}
|
|
349
|
+
response->addHeader("Content-Encoding", "gzip");
|
|
350
|
+
{{/if}}
|
|
351
|
+
{{/case}}
|
|
352
|
+
{{#case "compiler"}}
|
|
353
|
+
{{#if this.isGzip}}
|
|
354
|
+
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
355
|
+
response->addHeader("Content-Encoding", "gzip");
|
|
356
|
+
#endif
|
|
357
|
+
{{/if}}
|
|
358
|
+
{{/case}}
|
|
359
|
+
{{/switch}}
|
|
360
|
+
|
|
361
|
+
{{#switch ../etag}}
|
|
362
|
+
{{#case "true"}}
|
|
363
|
+
{{#../cacheTime}}
|
|
364
|
+
response->addHeader("Cache-Control", "max-age={{value}}");
|
|
365
|
+
{{/../cacheTime}}
|
|
366
|
+
{{^../cacheTime}}
|
|
367
|
+
response->addHeader("Cache-Control", "no-cache");
|
|
368
|
+
{{/../cacheTime}}
|
|
369
|
+
response->addHeader("ETag", etag_{{this.dataname}});
|
|
370
|
+
{{/case}}
|
|
371
|
+
{{#case "compiler"}}
|
|
372
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
373
|
+
{{#../cacheTime}}
|
|
374
|
+
response->addHeader("Cache-Control", "max-age={{value}}");
|
|
375
|
+
{{/../cacheTime}}
|
|
376
|
+
{{^../cacheTime}}
|
|
377
|
+
response->addHeader("Cache-Control", "no-cache");
|
|
378
|
+
{{/../cacheTime}}
|
|
379
|
+
response->addHeader("ETag", etag_{{this.dataname}});
|
|
380
|
+
#endif
|
|
381
|
+
{{/case}}
|
|
382
|
+
{{/switch}}
|
|
383
|
+
|
|
384
|
+
{{#switch ../gzip}}
|
|
385
|
+
{{#case "true"}}
|
|
386
|
+
response->setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
387
|
+
{{/case}}
|
|
388
|
+
{{#case "false"}}
|
|
389
|
+
response->setContent(data_{{this.dataname}}, {{this.length}});
|
|
390
|
+
{{/case}}
|
|
391
|
+
{{#case "compiler"}}
|
|
392
|
+
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
393
|
+
response->setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
394
|
+
#else
|
|
395
|
+
response->setContent(data_{{this.dataname}}, {{this.length}});
|
|
396
|
+
#endif
|
|
397
|
+
{{/case}}
|
|
398
|
+
{{/switch}}
|
|
399
|
+
|
|
400
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
|
|
401
|
+
return response->send();
|
|
402
|
+
});
|
|
403
|
+
{{/if}}
|
|
404
|
+
{{/with}}
|
|
405
|
+
{{/if}}
|
|
318
406
|
}`;
|
|
319
407
|
const asyncTemplate = `
|
|
320
408
|
//engine: ESPAsyncWebServer
|
|
@@ -493,6 +581,372 @@ void {{methodName}}(AsyncWebServer * server) {
|
|
|
493
581
|
{{/if}}
|
|
494
582
|
|
|
495
583
|
{{/each}}
|
|
584
|
+
{{#if spa}}
|
|
585
|
+
{{#with spaSource}}
|
|
586
|
+
//
|
|
587
|
+
// SPA catch-all: unmatched routes serve {{this.filename}}
|
|
588
|
+
server->onNotFound([](AsyncWebServerRequest * request) {
|
|
589
|
+
if (request->method() != HTTP_GET) { request->send(404); return; }
|
|
590
|
+
{{#if ../basePath}}
|
|
591
|
+
if (!request->url().startsWith("{{../basePath}}/") && request->url() != "{{../basePath}}") { request->send(404); return; }
|
|
592
|
+
{{/if}}
|
|
593
|
+
|
|
594
|
+
{{#switch ../etag}}
|
|
595
|
+
{{#case "true"}}
|
|
596
|
+
const AsyncWebHeader* h = request->getHeader("If-None-Match");
|
|
597
|
+
if (h && h->value().equals(etag_{{this.dataname}})) {
|
|
598
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
599
|
+
request->send(304);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
{{/case}}
|
|
603
|
+
{{#case "compiler"}}
|
|
604
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
605
|
+
const AsyncWebHeader* h = request->getHeader("If-None-Match");
|
|
606
|
+
if (h && h->value().equals(etag_{{this.dataname}})) {
|
|
607
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
608
|
+
request->send(304);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
#endif
|
|
612
|
+
{{/case}}
|
|
613
|
+
{{/switch}}
|
|
614
|
+
|
|
615
|
+
{{#switch ../gzip}}
|
|
616
|
+
{{#case "true"}}
|
|
617
|
+
AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
618
|
+
{{#if this.isGzip}}
|
|
619
|
+
response->addHeader("Content-Encoding", "gzip");
|
|
620
|
+
{{/if}}
|
|
621
|
+
{{/case}}
|
|
622
|
+
{{#case "false"}}
|
|
623
|
+
AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
|
|
624
|
+
{{/case}}
|
|
625
|
+
{{#case "compiler"}}
|
|
626
|
+
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
627
|
+
AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
628
|
+
{{#if this.isGzip}}
|
|
629
|
+
response->addHeader("Content-Encoding", "gzip");
|
|
630
|
+
{{/if}}
|
|
631
|
+
#else
|
|
632
|
+
AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
|
|
633
|
+
#endif
|
|
634
|
+
{{/case}}
|
|
635
|
+
{{/switch}}
|
|
636
|
+
|
|
637
|
+
{{#switch ../etag}}
|
|
638
|
+
{{#case "true"}}
|
|
639
|
+
{{#../cacheTime}}
|
|
640
|
+
response->addHeader("Cache-Control", "max-age={{value}}");
|
|
641
|
+
{{/../cacheTime}}
|
|
642
|
+
{{^../cacheTime}}
|
|
643
|
+
response->addHeader("Cache-Control", "no-cache");
|
|
644
|
+
{{/../cacheTime}}
|
|
645
|
+
response->addHeader("ETag", etag_{{this.dataname}});
|
|
646
|
+
{{/case}}
|
|
647
|
+
{{#case "compiler"}}
|
|
648
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
649
|
+
{{#../cacheTime}}
|
|
650
|
+
response->addHeader("Cache-Control", "max-age={{value}}");
|
|
651
|
+
{{/../cacheTime}}
|
|
652
|
+
{{^../cacheTime}}
|
|
653
|
+
response->addHeader("Cache-Control", "no-cache");
|
|
654
|
+
{{/../cacheTime}}
|
|
655
|
+
response->addHeader("ETag", etag_{{this.dataname}});
|
|
656
|
+
#endif
|
|
657
|
+
{{/case}}
|
|
658
|
+
{{/switch}}
|
|
659
|
+
|
|
660
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
|
|
661
|
+
request->send(response);
|
|
662
|
+
});
|
|
663
|
+
{{/with}}
|
|
664
|
+
{{/if}}
|
|
665
|
+
}`;
|
|
666
|
+
const webserverTemplate = `
|
|
667
|
+
//engine: Arduino WebServer
|
|
668
|
+
//config: {{{config}}}
|
|
669
|
+
{{#if created }}
|
|
670
|
+
//created: {{now}}
|
|
671
|
+
{{/if}}
|
|
672
|
+
//
|
|
673
|
+
${commonHeaderSection}
|
|
674
|
+
|
|
675
|
+
//
|
|
676
|
+
#include <Arduino.h>
|
|
677
|
+
#include <WebServer.h>
|
|
678
|
+
|
|
679
|
+
//
|
|
680
|
+
${dataArraysSection(true)}
|
|
681
|
+
|
|
682
|
+
//
|
|
683
|
+
${etagArraysSection}
|
|
684
|
+
|
|
685
|
+
//
|
|
686
|
+
${manifestSection}
|
|
687
|
+
|
|
688
|
+
//
|
|
689
|
+
${hookSection}
|
|
690
|
+
|
|
691
|
+
//
|
|
692
|
+
// Chunked send helper for PROGMEM data
|
|
693
|
+
static inline void {{definePrefix}}_sendChunked(WebServer * server, const uint8_t * data, size_t len) {
|
|
694
|
+
const size_t chunkSize = 4096;
|
|
695
|
+
for (size_t offset = 0; offset < len; offset += chunkSize) {
|
|
696
|
+
size_t remaining = len - offset;
|
|
697
|
+
size_t toSend = remaining < chunkSize ? remaining : chunkSize;
|
|
698
|
+
server->sendContent_P((const char *)(data + offset), toSend);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
//
|
|
703
|
+
// Http Handlers
|
|
704
|
+
void {{methodName}}(WebServer * server) {
|
|
705
|
+
{{#each sources}}
|
|
706
|
+
//
|
|
707
|
+
// {{this.filename}}
|
|
708
|
+
server->on("{{../basePath}}/{{this.filename}}", HTTP_GET, [server]() {
|
|
709
|
+
|
|
710
|
+
{{#switch ../etag}}
|
|
711
|
+
{{#case "true"}}
|
|
712
|
+
if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
713
|
+
server->send(304);
|
|
714
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
{{/case}}
|
|
718
|
+
{{#case "compiler"}}
|
|
719
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
720
|
+
if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
721
|
+
server->send(304);
|
|
722
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
#endif
|
|
726
|
+
{{/case}}
|
|
727
|
+
{{/switch}}
|
|
728
|
+
|
|
729
|
+
{{#switch ../etag}}
|
|
730
|
+
{{#case "true"}}
|
|
731
|
+
{{#../cacheTime}}
|
|
732
|
+
server->sendHeader("Cache-Control", "max-age={{value}}");
|
|
733
|
+
{{/../cacheTime}}
|
|
734
|
+
{{^../cacheTime}}
|
|
735
|
+
server->sendHeader("Cache-Control", "no-cache");
|
|
736
|
+
{{/../cacheTime}}
|
|
737
|
+
server->sendHeader("ETag", etag_{{this.dataname}});
|
|
738
|
+
{{/case}}
|
|
739
|
+
{{#case "compiler"}}
|
|
740
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
741
|
+
{{#../cacheTime}}
|
|
742
|
+
server->sendHeader("Cache-Control", "max-age={{value}}");
|
|
743
|
+
{{/../cacheTime}}
|
|
744
|
+
{{^../cacheTime}}
|
|
745
|
+
server->sendHeader("Cache-Control", "no-cache");
|
|
746
|
+
{{/../cacheTime}}
|
|
747
|
+
server->sendHeader("ETag", etag_{{this.dataname}});
|
|
748
|
+
#endif
|
|
749
|
+
{{/case}}
|
|
750
|
+
{{/switch}}
|
|
751
|
+
|
|
752
|
+
{{#switch ../gzip}}
|
|
753
|
+
{{#case "true"}}
|
|
754
|
+
{{#if this.isGzip}}
|
|
755
|
+
server->sendHeader("Content-Encoding", "gzip");
|
|
756
|
+
{{/if}}
|
|
757
|
+
server->setContentLength({{this.lengthGzip}});
|
|
758
|
+
server->send(200, "{{this.mime}}", "");
|
|
759
|
+
{{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
760
|
+
{{/case}}
|
|
761
|
+
{{#case "false"}}
|
|
762
|
+
server->setContentLength({{this.length}});
|
|
763
|
+
server->send(200, "{{this.mime}}", "");
|
|
764
|
+
{{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
|
|
765
|
+
{{/case}}
|
|
766
|
+
{{#case "compiler"}}
|
|
767
|
+
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
768
|
+
{{#if this.isGzip}}
|
|
769
|
+
server->sendHeader("Content-Encoding", "gzip");
|
|
770
|
+
{{/if}}
|
|
771
|
+
server->setContentLength({{this.lengthGzip}});
|
|
772
|
+
server->send(200, "{{this.mime}}", "");
|
|
773
|
+
{{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
774
|
+
#else
|
|
775
|
+
server->setContentLength({{this.length}});
|
|
776
|
+
server->send(200, "{{this.mime}}", "");
|
|
777
|
+
{{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
|
|
778
|
+
#endif
|
|
779
|
+
{{/case}}
|
|
780
|
+
{{/switch}}
|
|
781
|
+
|
|
782
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
|
|
783
|
+
});
|
|
784
|
+
{{#if this.isDefault}}
|
|
785
|
+
server->on("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", HTTP_GET, [server]() {
|
|
786
|
+
|
|
787
|
+
{{#switch ../etag}}
|
|
788
|
+
{{#case "true"}}
|
|
789
|
+
if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
790
|
+
server->send(304);
|
|
791
|
+
{{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 304);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
{{/case}}
|
|
795
|
+
{{#case "compiler"}}
|
|
796
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
797
|
+
if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
798
|
+
server->send(304);
|
|
799
|
+
{{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 304);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
#endif
|
|
803
|
+
{{/case}}
|
|
804
|
+
{{/switch}}
|
|
805
|
+
|
|
806
|
+
{{#switch ../etag}}
|
|
807
|
+
{{#case "true"}}
|
|
808
|
+
{{#../cacheTime}}
|
|
809
|
+
server->sendHeader("Cache-Control", "max-age={{value}}");
|
|
810
|
+
{{/../cacheTime}}
|
|
811
|
+
{{^../cacheTime}}
|
|
812
|
+
server->sendHeader("Cache-Control", "no-cache");
|
|
813
|
+
{{/../cacheTime}}
|
|
814
|
+
server->sendHeader("ETag", etag_{{this.dataname}});
|
|
815
|
+
{{/case}}
|
|
816
|
+
{{#case "compiler"}}
|
|
817
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
818
|
+
{{#../cacheTime}}
|
|
819
|
+
server->sendHeader("Cache-Control", "max-age={{value}}");
|
|
820
|
+
{{/../cacheTime}}
|
|
821
|
+
{{^../cacheTime}}
|
|
822
|
+
server->sendHeader("Cache-Control", "no-cache");
|
|
823
|
+
{{/../cacheTime}}
|
|
824
|
+
server->sendHeader("ETag", etag_{{this.dataname}});
|
|
825
|
+
#endif
|
|
826
|
+
{{/case}}
|
|
827
|
+
{{/switch}}
|
|
828
|
+
|
|
829
|
+
{{#switch ../gzip}}
|
|
830
|
+
{{#case "true"}}
|
|
831
|
+
{{#if this.isGzip}}
|
|
832
|
+
server->sendHeader("Content-Encoding", "gzip");
|
|
833
|
+
{{/if}}
|
|
834
|
+
server->setContentLength({{this.lengthGzip}});
|
|
835
|
+
server->send(200, "{{this.mime}}", "");
|
|
836
|
+
{{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
837
|
+
{{/case}}
|
|
838
|
+
{{#case "false"}}
|
|
839
|
+
server->setContentLength({{this.length}});
|
|
840
|
+
server->send(200, "{{this.mime}}", "");
|
|
841
|
+
{{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
|
|
842
|
+
{{/case}}
|
|
843
|
+
{{#case "compiler"}}
|
|
844
|
+
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
845
|
+
{{#if this.isGzip}}
|
|
846
|
+
server->sendHeader("Content-Encoding", "gzip");
|
|
847
|
+
{{/if}}
|
|
848
|
+
server->setContentLength({{this.lengthGzip}});
|
|
849
|
+
server->send(200, "{{this.mime}}", "");
|
|
850
|
+
{{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
851
|
+
#else
|
|
852
|
+
server->setContentLength({{this.length}});
|
|
853
|
+
server->send(200, "{{this.mime}}", "");
|
|
854
|
+
{{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
|
|
855
|
+
#endif
|
|
856
|
+
{{/case}}
|
|
857
|
+
{{/switch}}
|
|
858
|
+
|
|
859
|
+
{{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 200);
|
|
860
|
+
});
|
|
861
|
+
{{/if}}
|
|
862
|
+
|
|
863
|
+
{{/each}}
|
|
864
|
+
{{#if spa}}
|
|
865
|
+
{{#with spaSource}}
|
|
866
|
+
//
|
|
867
|
+
// SPA catch-all: unmatched routes serve {{this.filename}}
|
|
868
|
+
server->onNotFound([server]() {
|
|
869
|
+
if (server->method() != HTTP_GET) { server->send(404, "text/plain", "Not found"); return; }
|
|
870
|
+
{{#if ../basePath}}
|
|
871
|
+
if (!server->uri().startsWith("{{../basePath}}/") && server->uri() != "{{../basePath}}") { server->send(404, "text/plain", "Not found"); return; }
|
|
872
|
+
{{/if}}
|
|
873
|
+
|
|
874
|
+
{{#switch ../etag}}
|
|
875
|
+
{{#case "true"}}
|
|
876
|
+
if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
877
|
+
server->send(304);
|
|
878
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
{{/case}}
|
|
882
|
+
{{#case "compiler"}}
|
|
883
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
884
|
+
if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
885
|
+
server->send(304);
|
|
886
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
#endif
|
|
890
|
+
{{/case}}
|
|
891
|
+
{{/switch}}
|
|
892
|
+
|
|
893
|
+
{{#switch ../etag}}
|
|
894
|
+
{{#case "true"}}
|
|
895
|
+
{{#../cacheTime}}
|
|
896
|
+
server->sendHeader("Cache-Control", "max-age={{value}}");
|
|
897
|
+
{{/../cacheTime}}
|
|
898
|
+
{{^../cacheTime}}
|
|
899
|
+
server->sendHeader("Cache-Control", "no-cache");
|
|
900
|
+
{{/../cacheTime}}
|
|
901
|
+
server->sendHeader("ETag", etag_{{this.dataname}});
|
|
902
|
+
{{/case}}
|
|
903
|
+
{{#case "compiler"}}
|
|
904
|
+
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
905
|
+
{{#../cacheTime}}
|
|
906
|
+
server->sendHeader("Cache-Control", "max-age={{value}}");
|
|
907
|
+
{{/../cacheTime}}
|
|
908
|
+
{{^../cacheTime}}
|
|
909
|
+
server->sendHeader("Cache-Control", "no-cache");
|
|
910
|
+
{{/../cacheTime}}
|
|
911
|
+
server->sendHeader("ETag", etag_{{this.dataname}});
|
|
912
|
+
#endif
|
|
913
|
+
{{/case}}
|
|
914
|
+
{{/switch}}
|
|
915
|
+
|
|
916
|
+
{{#switch ../gzip}}
|
|
917
|
+
{{#case "true"}}
|
|
918
|
+
{{#if this.isGzip}}
|
|
919
|
+
server->sendHeader("Content-Encoding", "gzip");
|
|
920
|
+
{{/if}}
|
|
921
|
+
server->setContentLength({{this.lengthGzip}});
|
|
922
|
+
server->send(200, "{{this.mime}}", "");
|
|
923
|
+
{{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
924
|
+
{{/case}}
|
|
925
|
+
{{#case "false"}}
|
|
926
|
+
server->setContentLength({{this.length}});
|
|
927
|
+
server->send(200, "{{this.mime}}", "");
|
|
928
|
+
{{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
|
|
929
|
+
{{/case}}
|
|
930
|
+
{{#case "compiler"}}
|
|
931
|
+
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
932
|
+
{{#if this.isGzip}}
|
|
933
|
+
server->sendHeader("Content-Encoding", "gzip");
|
|
934
|
+
{{/if}}
|
|
935
|
+
server->setContentLength({{this.lengthGzip}});
|
|
936
|
+
server->send(200, "{{this.mime}}", "");
|
|
937
|
+
{{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
938
|
+
#else
|
|
939
|
+
server->setContentLength({{this.length}});
|
|
940
|
+
server->send(200, "{{this.mime}}", "");
|
|
941
|
+
{{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
|
|
942
|
+
#endif
|
|
943
|
+
{{/case}}
|
|
944
|
+
{{/switch}}
|
|
945
|
+
|
|
946
|
+
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
|
|
947
|
+
});
|
|
948
|
+
{{/with}}
|
|
949
|
+
{{/if}}
|
|
496
950
|
}`;
|
|
497
951
|
const getTemplate = (engine) => {
|
|
498
952
|
switch (engine) {
|
|
@@ -502,6 +956,8 @@ const getTemplate = (engine) => {
|
|
|
502
956
|
return asyncTemplate;
|
|
503
957
|
case 'espidf':
|
|
504
958
|
return cppCodeEspIdf_1.espidfTemplate;
|
|
959
|
+
case 'webserver':
|
|
960
|
+
return webserverTemplate;
|
|
505
961
|
default:
|
|
506
962
|
throw new Error(`Unknown engine: ${engine}`);
|
|
507
963
|
}
|
|
@@ -552,6 +1008,8 @@ const createHandlebarsHelpers = () => {
|
|
|
552
1008
|
};
|
|
553
1009
|
const getCppCode = (sources, filesByExtension) => {
|
|
554
1010
|
const template = (0, handlebars_1.compile)(getTemplate(commandLine_1.cmdLine.engine));
|
|
1011
|
+
const transformedSources = sources.map((s) => transformSourceToTemplateData(s, commandLine_1.cmdLine.etag));
|
|
1012
|
+
const spaSource = commandLine_1.cmdLine.spa ? transformedSources.find((s) => s.isDefault) : undefined;
|
|
555
1013
|
const templateData = {
|
|
556
1014
|
config: (0, commandLine_1.formatConfiguration)(commandLine_1.cmdLine),
|
|
557
1015
|
now: (() => {
|
|
@@ -561,7 +1019,7 @@ const getCppCode = (sources, filesByExtension) => {
|
|
|
561
1019
|
fileCount: sources.length.toString(),
|
|
562
1020
|
fileSize: sources.reduce((previous, current) => previous + current.content.length, 0).toString(),
|
|
563
1021
|
fileGzipSize: sources.reduce((previous, current) => previous + current.contentGzip.length, 0).toString(),
|
|
564
|
-
sources:
|
|
1022
|
+
sources: transformedSources,
|
|
565
1023
|
filesByExtension,
|
|
566
1024
|
etag: commandLine_1.cmdLine.etag,
|
|
567
1025
|
gzip: commandLine_1.cmdLine.gzip,
|
|
@@ -570,7 +1028,9 @@ const getCppCode = (sources, filesByExtension) => {
|
|
|
570
1028
|
methodName: commandLine_1.cmdLine.espmethod,
|
|
571
1029
|
cacheTime: commandLine_1.cmdLine.cachetime ? { value: commandLine_1.cmdLine.cachetime } : undefined,
|
|
572
1030
|
definePrefix: commandLine_1.cmdLine.define,
|
|
573
|
-
basePath: commandLine_1.cmdLine.basePath
|
|
1031
|
+
basePath: commandLine_1.cmdLine.basePath,
|
|
1032
|
+
spa: !!commandLine_1.cmdLine.spa,
|
|
1033
|
+
spaSource
|
|
574
1034
|
};
|
|
575
1035
|
const rawCode = template(templateData, { helpers: createHandlebarsHelpers() });
|
|
576
1036
|
return postProcessCppCode(rawCode);
|
package/dist/cppCodeEspIdf.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const espidfTemplate = "\n//engine: espidf\n//config: {{{config}}}\n{{#if created }}\n//created: {{now}}\n{{/if}}\n//\n\n{{#switch etag}}\n{{#case \"true\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n#warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched ON\n#endif\n{{/case}}\n{{#case \"false\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n#warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched OFF\n#endif\n{{/case}}\n{{/switch}}\n\n{{#switch gzip}}\n{{#case \"true\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n#warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched ON\n#endif\n{{/case}}\n{{#case \"false\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n#warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched OFF\n#endif\n{{/case}}\n{{/switch}}\n\n//\n{{#if version }}\n#define {{definePrefix}}_VERSION \"{{version}}\"\n{{/if}}\n#define {{definePrefix}}_COUNT {{fileCount}}\n#define {{definePrefix}}_SIZE {{fileSize}}\n#define {{definePrefix}}_SIZE_GZIP {{fileGzipSize}}\n\n//\n{{#each sources}}\n#define {{../definePrefix}}_FILE_{{this.datanameUpperCase}}\n{{/each}}\n\n//\n{{#each filesByExtension}}\n#define {{../definePrefix}}_{{this.extension}}_FILES {{this.count}}\n{{/each}}\n\n#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>\n#include <esp_err.h>\n#include <esp_http_server.h>\n\n//\n{{#switch gzip}}\n{{#case \"true\"}}\n {{#each sources}}\nconst unsigned char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };\n {{/each}}\n{{/case}}\n{{#case \"false\"}}\n {{#each sources}}\nconst unsigned char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };\n {{/each}}\n{{/case}}\n{{#case \"compiler\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n {{#each sources}}\nconst unsigned char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };\n {{/each}}\n#else\n {{#each sources}}\nconst unsigned char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };\n {{/each}}\n#endif \n{{/case}}\n{{/switch}}\n\n//\n{{#switch etag}}\n{{#case \"true\"}}\n {{#each sources}}\nstatic const char etag_{{this.dataname}}[] = \"{{this.sha256}}\";\n {{/each}}\n{{/case}}\n{{#case \"false\"}}\n{{/case}}\n{{#case \"compiler\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n {{#each sources}}\nstatic const char etag_{{this.dataname}}[] = \"{{this.sha256}}\";\n {{/each}}\n#endif\n{{/case}}\n{{/switch}}\n\n// File manifest struct (C-compatible typedef)\ntypedef struct {\n const char* path;\n uint32_t size;\n uint32_t gzipSize;\n const char* etag;\n const char* contentType;\n} {{definePrefix}}_FileInfo;\n\n// File manifest array\nstatic const {{definePrefix}}_FileInfo {{definePrefix}}_FILES[] = {\n{{#each sources}}\n { \"{{../basePath}}/{{this.filename}}\", {{this.length}}, {{this.gzipSizeForManifest}}, {{this.etagForManifest}}, \"{{this.mime}}\" },\n{{/each}}\n};\nstatic const size_t {{definePrefix}}_FILE_COUNT = sizeof({{definePrefix}}_FILES) / sizeof({{definePrefix}}_FILES[0]);\n\n// File served hook - override with your own implementation\n__attribute__((weak)) void {{definePrefix}}_onFileServed(const char* path, int statusCode) {}\n\n{{#each sources}}\n\nstatic esp_err_t file_handler_{{this.datanameUpperCase}} (httpd_req_t *req)\n{\n{{#switch ../etag}}\n{{#case \"true\"}}\n size_t hdr_len = httpd_req_get_hdr_value_len(req, \"If-None-Match\");\n if (hdr_len > 0) {\n char* hdr_value = malloc(hdr_len + 1);\n if (hdr_value == NULL) { httpd_resp_send_500(req); return ESP_FAIL; }\n if (httpd_req_get_hdr_value_str(req, \"If-None-Match\", hdr_value, hdr_len + 1) == ESP_OK) {\n if (strcmp(hdr_value, etag_{{this.dataname}}) == 0) {\n free(hdr_value);\n httpd_resp_set_status(req, \"304 Not Modified\");\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 304);\n httpd_resp_send(req, NULL, 0);\n return ESP_OK;\n }\n }\n free(hdr_value);\n }\n{{/case}}\n{{#case \"compiler\"}}\n #ifdef {{../definePrefix}}_ENABLE_ETAG\n size_t hdr_len = httpd_req_get_hdr_value_len(req, \"If-None-Match\");\n if (hdr_len > 0) {\n char* hdr_value = malloc(hdr_len + 1);\n if (hdr_value == NULL) { httpd_resp_send_500(req); return ESP_FAIL; }\n if (httpd_req_get_hdr_value_str(req, \"If-None-Match\", hdr_value, hdr_len + 1) == ESP_OK) {\n if (strcmp(hdr_value, etag_{{this.dataname}}) == 0) {\n free(hdr_value);\n httpd_resp_set_status(req, \"304 Not Modified\");\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 304);\n httpd_resp_send(req, NULL, 0);\n return ESP_OK;\n }\n }\n free(hdr_value);\n }\n #endif\n{{/case}}\n{{/switch}}\n httpd_resp_set_type(req, \"{{this.mime}}\");\n{{#switch ../gzip}}\n{{#case \"true\"}}\n{{#if this.isGzip}}\n httpd_resp_set_hdr(req, \"Content-Encoding\", \"gzip\");\n{{/if}}\n{{/case}}\n{{#case \"compiler\"}}\n {{#if this.isGzip}}\n #ifdef {{../definePrefix}}_ENABLE_GZIP\n httpd_resp_set_hdr(req, \"Content-Encoding\", \"gzip\");\n #endif \n {{/if}}\n{{/case}}\n{{/switch}}\n\n{{#switch ../etag}}\n{{#case \"true\"}}\n{{#../cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"max-age={{value}}\");\n{{/../cacheTime}}\n{{^../cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"no-cache\");\n{{/../cacheTime}}\n httpd_resp_set_hdr(req, \"ETag\", etag_{{this.dataname}});\n{{/case}}\n{{#case \"compiler\"}}\n #ifdef {{../definePrefix}}_ENABLE_ETAG\n{{#../cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"max-age={{value}}\");\n{{/../cacheTime}}\n{{^../cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"no-cache\");\n{{/../cacheTime}}\n httpd_resp_set_hdr(req, \"ETag\", etag_{{this.dataname}});\n #endif \n{{/case}}\n{{/switch}}\n\n{{#switch ../gzip}}\n{{#case \"true\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n httpd_resp_send(req, (const char *)datagzip_{{this.dataname}}, {{this.lengthGzip}});\n{{/case}}\n{{#case \"false\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n httpd_resp_send(req, (const char *)data_{{this.dataname}}, {{this.length}});\n{{/case}}\n{{#case \"compiler\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n #ifdef {{../definePrefix}}_ENABLE_GZIP\n httpd_resp_send(req, (const char *)datagzip_{{this.dataname}}, {{this.lengthGzip}});\n #else\n httpd_resp_send(req, (const char *)data_{{this.dataname}}, {{this.length}});\n #endif\n{{/case}}\n{{/switch}}\n return ESP_OK;\n}\n\n{{#if this.isDefault}}\nstatic const httpd_uri_t route_def_{{this.datanameUpperCase}} = {\n .uri = \"{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}\",\n .method = HTTP_GET,\n .handler = file_handler_{{this.datanameUpperCase}},\n};\n{{/if}}\n\nstatic const httpd_uri_t route_{{this.datanameUpperCase}} = {\n .uri = \"{{../basePath}}/{{this.filename}}\",\n .method = HTTP_GET,\n .handler = file_handler_{{this.datanameUpperCase}},\n};\n\n{{/each}}\n\n\n\nstatic inline void {{methodName}}(httpd_handle_t server) {\n{{#each sources}}\n{{#if this.isDefault}}\n httpd_register_uri_handler(server, &route_def_{{this.datanameUpperCase}});\n{{/if}}\n httpd_register_uri_handler(server, &route_{{this.datanameUpperCase}});\n{{/each}}\n\n}";
|
|
1
|
+
export declare const espidfTemplate = "\n//engine: espidf\n//config: {{{config}}}\n{{#if created }}\n//created: {{now}}\n{{/if}}\n//\n\n{{#switch etag}}\n{{#case \"true\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n#warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched ON\n#endif\n{{/case}}\n{{#case \"false\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n#warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched OFF\n#endif\n{{/case}}\n{{/switch}}\n\n{{#switch gzip}}\n{{#case \"true\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n#warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched ON\n#endif\n{{/case}}\n{{#case \"false\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n#warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched OFF\n#endif\n{{/case}}\n{{/switch}}\n\n//\n{{#if version }}\n#define {{definePrefix}}_VERSION \"{{version}}\"\n{{/if}}\n#define {{definePrefix}}_COUNT {{fileCount}}\n#define {{definePrefix}}_SIZE {{fileSize}}\n#define {{definePrefix}}_SIZE_GZIP {{fileGzipSize}}\n\n//\n{{#each sources}}\n#define {{../definePrefix}}_FILE_{{this.datanameUpperCase}}\n{{/each}}\n\n//\n{{#each filesByExtension}}\n#define {{../definePrefix}}_{{this.extension}}_FILES {{this.count}}\n{{/each}}\n\n#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>\n#include <esp_err.h>\n#include <esp_http_server.h>\n\n//\n{{#switch gzip}}\n{{#case \"true\"}}\n {{#each sources}}\nconst unsigned char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };\n {{/each}}\n{{/case}}\n{{#case \"false\"}}\n {{#each sources}}\nconst unsigned char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };\n {{/each}}\n{{/case}}\n{{#case \"compiler\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n {{#each sources}}\nconst unsigned char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };\n {{/each}}\n#else\n {{#each sources}}\nconst unsigned char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };\n {{/each}}\n#endif \n{{/case}}\n{{/switch}}\n\n//\n{{#switch etag}}\n{{#case \"true\"}}\n {{#each sources}}\nstatic const char etag_{{this.dataname}}[] = \"{{this.sha256}}\";\n {{/each}}\n{{/case}}\n{{#case \"false\"}}\n{{/case}}\n{{#case \"compiler\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n {{#each sources}}\nstatic const char etag_{{this.dataname}}[] = \"{{this.sha256}}\";\n {{/each}}\n#endif\n{{/case}}\n{{/switch}}\n\n// File manifest struct (C-compatible typedef)\ntypedef struct {\n const char* path;\n uint32_t size;\n uint32_t gzipSize;\n const char* etag;\n const char* contentType;\n} {{definePrefix}}_FileInfo;\n\n// File manifest array\nstatic const {{definePrefix}}_FileInfo {{definePrefix}}_FILES[] = {\n{{#each sources}}\n { \"{{../basePath}}/{{this.filename}}\", {{this.length}}, {{this.gzipSizeForManifest}}, {{this.etagForManifest}}, \"{{this.mime}}\" },\n{{/each}}\n};\nstatic const size_t {{definePrefix}}_FILE_COUNT = sizeof({{definePrefix}}_FILES) / sizeof({{definePrefix}}_FILES[0]);\n\n// File served hook - override with your own implementation\n__attribute__((weak)) void {{definePrefix}}_onFileServed(const char* path, int statusCode) {}\n\n{{#each sources}}\n\nstatic esp_err_t file_handler_{{this.datanameUpperCase}} (httpd_req_t *req)\n{\n{{#switch ../etag}}\n{{#case \"true\"}}\n size_t hdr_len = httpd_req_get_hdr_value_len(req, \"If-None-Match\");\n if (hdr_len > 0) {\n char* hdr_value = malloc(hdr_len + 1);\n if (hdr_value == NULL) { httpd_resp_send_500(req); return ESP_FAIL; }\n if (httpd_req_get_hdr_value_str(req, \"If-None-Match\", hdr_value, hdr_len + 1) == ESP_OK) {\n if (strcmp(hdr_value, etag_{{this.dataname}}) == 0) {\n free(hdr_value);\n httpd_resp_set_status(req, \"304 Not Modified\");\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 304);\n httpd_resp_send(req, NULL, 0);\n return ESP_OK;\n }\n }\n free(hdr_value);\n }\n{{/case}}\n{{#case \"compiler\"}}\n #ifdef {{../definePrefix}}_ENABLE_ETAG\n size_t hdr_len = httpd_req_get_hdr_value_len(req, \"If-None-Match\");\n if (hdr_len > 0) {\n char* hdr_value = malloc(hdr_len + 1);\n if (hdr_value == NULL) { httpd_resp_send_500(req); return ESP_FAIL; }\n if (httpd_req_get_hdr_value_str(req, \"If-None-Match\", hdr_value, hdr_len + 1) == ESP_OK) {\n if (strcmp(hdr_value, etag_{{this.dataname}}) == 0) {\n free(hdr_value);\n httpd_resp_set_status(req, \"304 Not Modified\");\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 304);\n httpd_resp_send(req, NULL, 0);\n return ESP_OK;\n }\n }\n free(hdr_value);\n }\n #endif\n{{/case}}\n{{/switch}}\n httpd_resp_set_type(req, \"{{this.mime}}\");\n{{#switch ../gzip}}\n{{#case \"true\"}}\n{{#if this.isGzip}}\n httpd_resp_set_hdr(req, \"Content-Encoding\", \"gzip\");\n{{/if}}\n{{/case}}\n{{#case \"compiler\"}}\n {{#if this.isGzip}}\n #ifdef {{../definePrefix}}_ENABLE_GZIP\n httpd_resp_set_hdr(req, \"Content-Encoding\", \"gzip\");\n #endif \n {{/if}}\n{{/case}}\n{{/switch}}\n\n{{#switch ../etag}}\n{{#case \"true\"}}\n{{#../cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"max-age={{value}}\");\n{{/../cacheTime}}\n{{^../cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"no-cache\");\n{{/../cacheTime}}\n httpd_resp_set_hdr(req, \"ETag\", etag_{{this.dataname}});\n{{/case}}\n{{#case \"compiler\"}}\n #ifdef {{../definePrefix}}_ENABLE_ETAG\n{{#../cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"max-age={{value}}\");\n{{/../cacheTime}}\n{{^../cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"no-cache\");\n{{/../cacheTime}}\n httpd_resp_set_hdr(req, \"ETag\", etag_{{this.dataname}});\n #endif \n{{/case}}\n{{/switch}}\n\n{{#switch ../gzip}}\n{{#case \"true\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n httpd_resp_send(req, (const char *)datagzip_{{this.dataname}}, {{this.lengthGzip}});\n{{/case}}\n{{#case \"false\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n httpd_resp_send(req, (const char *)data_{{this.dataname}}, {{this.length}});\n{{/case}}\n{{#case \"compiler\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n #ifdef {{../definePrefix}}_ENABLE_GZIP\n httpd_resp_send(req, (const char *)datagzip_{{this.dataname}}, {{this.lengthGzip}});\n #else\n httpd_resp_send(req, (const char *)data_{{this.dataname}}, {{this.length}});\n #endif\n{{/case}}\n{{/switch}}\n return ESP_OK;\n}\n\n{{#if this.isDefault}}\nstatic const httpd_uri_t route_def_{{this.datanameUpperCase}} = {\n .uri = \"{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}\",\n .method = HTTP_GET,\n .handler = file_handler_{{this.datanameUpperCase}},\n};\n{{/if}}\n\nstatic const httpd_uri_t route_{{this.datanameUpperCase}} = {\n .uri = \"{{../basePath}}/{{this.filename}}\",\n .method = HTTP_GET,\n .handler = file_handler_{{this.datanameUpperCase}},\n};\n\n{{/each}}\n\n{{#if spa}}\n{{#with spaSource}}\nstatic esp_err_t spa_handler_{{this.datanameUpperCase}}(httpd_req_t *req, httpd_err_code_t err) {\n{{#if ../basePath}}\n const char* prefix = \"{{../basePath}}/\";\n if (strncmp(req->uri, prefix, strlen(prefix)) != 0 && strcmp(req->uri, \"{{../basePath}}\") != 0) {\n httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, \"Not found\");\n return ESP_FAIL;\n }\n{{/if}}\n return file_handler_{{this.datanameUpperCase}}(req);\n}\n{{/with}}\n{{/if}}\n\n\nstatic inline void {{methodName}}(httpd_handle_t server) {\n{{#each sources}}\n{{#if this.isDefault}}\n httpd_register_uri_handler(server, &route_def_{{this.datanameUpperCase}});\n{{/if}}\n httpd_register_uri_handler(server, &route_{{this.datanameUpperCase}});\n{{/each}}\n{{#if spa}}\n{{#with spaSource}}\n httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, spa_handler_{{this.datanameUpperCase}});\n{{/with}}\n{{/if}}\n\n}";
|
package/dist/cppCodeEspIdf.js
CHANGED
|
@@ -240,6 +240,20 @@ static const httpd_uri_t route_{{this.datanameUpperCase}} = {
|
|
|
240
240
|
|
|
241
241
|
{{/each}}
|
|
242
242
|
|
|
243
|
+
{{#if spa}}
|
|
244
|
+
{{#with spaSource}}
|
|
245
|
+
static esp_err_t spa_handler_{{this.datanameUpperCase}}(httpd_req_t *req, httpd_err_code_t err) {
|
|
246
|
+
{{#if ../basePath}}
|
|
247
|
+
const char* prefix = "{{../basePath}}/";
|
|
248
|
+
if (strncmp(req->uri, prefix, strlen(prefix)) != 0 && strcmp(req->uri, "{{../basePath}}") != 0) {
|
|
249
|
+
httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Not found");
|
|
250
|
+
return ESP_FAIL;
|
|
251
|
+
}
|
|
252
|
+
{{/if}}
|
|
253
|
+
return file_handler_{{this.datanameUpperCase}}(req);
|
|
254
|
+
}
|
|
255
|
+
{{/with}}
|
|
256
|
+
{{/if}}
|
|
243
257
|
|
|
244
258
|
|
|
245
259
|
static inline void {{methodName}}(httpd_handle_t server) {
|
|
@@ -249,5 +263,10 @@ static inline void {{methodName}}(httpd_handle_t server) {
|
|
|
249
263
|
{{/if}}
|
|
250
264
|
httpd_register_uri_handler(server, &route_{{this.datanameUpperCase}});
|
|
251
265
|
{{/each}}
|
|
266
|
+
{{#if spa}}
|
|
267
|
+
{{#with spaSource}}
|
|
268
|
+
httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, spa_handler_{{this.datanameUpperCase}});
|
|
269
|
+
{{/with}}
|
|
270
|
+
{{/if}}
|
|
252
271
|
|
|
253
272
|
}`;
|
package/dist/errorMessages.js
CHANGED
|
@@ -14,7 +14,8 @@ function getEngineName(engine) {
|
|
|
14
14
|
const names = {
|
|
15
15
|
psychic: 'PsychicHttpServer',
|
|
16
16
|
async: 'ESPAsyncWebServer',
|
|
17
|
-
espidf: 'ESP-IDF'
|
|
17
|
+
espidf: 'ESP-IDF',
|
|
18
|
+
webserver: 'Arduino WebServer'
|
|
18
19
|
};
|
|
19
20
|
return names[engine] ?? engine;
|
|
20
21
|
}
|
|
@@ -28,7 +29,10 @@ function getMissingIndexError(engine) {
|
|
|
28
29
|
3. ESPAsyncWebServer uses: server.on("/", HTTP_GET, ...)`,
|
|
29
30
|
espidf: ` 1. Add an index.html file to your source directory
|
|
30
31
|
2. The file will register both "/" and "/index.html" routes
|
|
31
|
-
3. ESP-IDF uses: httpd_register_uri_handler(server, &route_def_...)
|
|
32
|
+
3. ESP-IDF uses: httpd_register_uri_handler(server, &route_def_...)`,
|
|
33
|
+
webserver: ` 1. Add an index.html file to your source directory
|
|
34
|
+
2. The file will automatically create a "/" route handler
|
|
35
|
+
3. Arduino WebServer uses: server.on("/", HTTP_GET, handler)`
|
|
32
36
|
};
|
|
33
37
|
const hint = hints[engine] ?? hints['psychic'];
|
|
34
38
|
return ((0, consoleColor_1.redLog)('[ERROR] No index.html or index.htm found in source files') +
|
|
@@ -53,6 +57,7 @@ Valid engines are:
|
|
|
53
57
|
${(0, consoleColor_1.cyanLog)('• psychic')} - PsychicHttpServer (ESP32 only, fastest performance)
|
|
54
58
|
${(0, consoleColor_1.cyanLog)('• async')} - ESPAsyncWebServer (ESP32/ESP8266 compatible)
|
|
55
59
|
${(0, consoleColor_1.cyanLog)('• espidf')} - Native ESP-IDF web server (ESP32 only, no Arduino)
|
|
60
|
+
${(0, consoleColor_1.cyanLog)('• webserver')} - Arduino WebServer (ESP32, synchronous, built-in)
|
|
56
61
|
|
|
57
62
|
How to fix:
|
|
58
63
|
npx svelteesp32 --engine=psychic --sourcepath=./dist
|
package/dist/index.js
CHANGED
|
@@ -68,6 +68,8 @@ function main() {
|
|
|
68
68
|
console.error(`Directory ${commandLine_1.cmdLine.sourcepath} is empty`);
|
|
69
69
|
process.exit(1);
|
|
70
70
|
}
|
|
71
|
+
if (commandLine_1.cmdLine.spa && ![...files.keys()].some((f) => f === 'index.html' || f === 'index.htm'))
|
|
72
|
+
console.warn((0, consoleColor_1.yellowLog)('[SvelteESP32] Warning: --spa is set but no index.html/index.htm found; catch-all will not be generated.'));
|
|
71
73
|
console.log();
|
|
72
74
|
console.log('Translation to header file');
|
|
73
75
|
const longestFilename = [...files.keys()].reduce((p, c) => Math.max(c.length, p), 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteesp32",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Convert Svelte (or any frontend) JS application to serve it from ESP32 webserver (PsychicHttp)",
|
|
5
5
|
"author": "BCsabaEngine",
|
|
6
6
|
"license": "ISC",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"scripts": {
|
|
31
31
|
"dev:async": "nodemon src/index.ts -- -e async -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32.h --etag=true --gzip=true --cachetime=86400 --version=v$npm_package_version",
|
|
32
32
|
"dev:psychic": "nodemon src/index.ts -- -e psychic -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32.h --etag=false --gzip=false --version=v$npm_package_version",
|
|
33
|
+
"dev:webserver": "nodemon src/index.ts -- -e webserver -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32.h --etag=true --gzip=true --cachetime=86400 --version=v$npm_package_version",
|
|
33
34
|
"test": "vitest run",
|
|
34
35
|
"test:watch": "vitest",
|
|
35
36
|
"test:coverage": "vitest run --coverage",
|
|
@@ -58,20 +59,20 @@
|
|
|
58
59
|
"espasyncwebserver"
|
|
59
60
|
],
|
|
60
61
|
"devDependencies": {
|
|
61
|
-
"@eslint/eslintrc": "^3.3.
|
|
62
|
-
"@eslint/js": "^10.0.
|
|
62
|
+
"@eslint/eslintrc": "^3.3.5",
|
|
63
|
+
"@eslint/js": "^10.0.1",
|
|
63
64
|
"@types/mime-types": "^3.0.1",
|
|
64
|
-
"@types/node": "^25.
|
|
65
|
+
"@types/node": "^25.4.0",
|
|
65
66
|
"@types/picomatch": "^4.0.2",
|
|
66
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
67
|
-
"@typescript-eslint/parser": "^8.
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
68
|
+
"@typescript-eslint/parser": "^8.57.0",
|
|
68
69
|
"@vitest/coverage-v8": "^4.0.18",
|
|
69
|
-
"eslint": "^10.0.
|
|
70
|
+
"eslint": "^10.0.3",
|
|
70
71
|
"eslint-config-prettier": "^10.1.8",
|
|
71
72
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
72
73
|
"eslint-plugin-unicorn": "^63.0.0",
|
|
73
|
-
"memfs": "^4.56.
|
|
74
|
-
"nodemon": "^3.1.
|
|
74
|
+
"memfs": "^4.56.11",
|
|
75
|
+
"nodemon": "^3.1.14",
|
|
75
76
|
"prettier": "^3.8.1",
|
|
76
77
|
"ts-node": "^10.9.2",
|
|
77
78
|
"tsx": "^4.21.0",
|