svelteesp32 2.1.0 → 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 +43 -21
- package/dist/commandLine.d.ts +2 -0
- package/dist/commandLine.js +15 -2
- package/dist/cppCode.js +261 -2
- package/dist/cppCodeEspIdf.d.ts +1 -1
- package/dist/cppCodeEspIdf.js +19 -0
- package/dist/index.js +2 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -79,8 +79,8 @@ void setup() {
|
|
|
79
79
|
|
|
80
80
|
## What's New
|
|
81
81
|
|
|
82
|
+
- **v2.2.0** — SPA routing catch-all (`--spa`) for client-side routers on all four engines
|
|
82
83
|
- **v2.1.0** — New Arduino WebServer engine (`-e webserver`), dependency updates
|
|
83
|
-
- **v2.0.1** — Dependency updates (ESLint v10, eslint-plugin-unicorn 63), improved error cause chaining
|
|
84
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
|
|
85
85
|
- **v1.16.0** — Size budget constraints (`--maxsize`, `--maxgzipsize`)
|
|
86
86
|
- **v1.15.0** — `--basepath` for multiple frontends (e.g., `/admin`, `/app`)
|
|
@@ -376,6 +376,26 @@ void setup() {
|
|
|
376
376
|
|
|
377
377
|
**Rules:** Must start with `/`, no trailing slash, no double slashes.
|
|
378
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
|
+
|
|
379
399
|
### C++ Build-Time Validation
|
|
380
400
|
|
|
381
401
|
Catch configuration issues at compile time with generated defines:
|
|
@@ -425,25 +445,26 @@ Called for every response (200 = content served, 304 = cache hit).
|
|
|
425
445
|
|
|
426
446
|
## CLI Reference
|
|
427
447
|
|
|
428
|
-
| Option | Description
|
|
429
|
-
| ---------------- |
|
|
430
|
-
| `-s` | Source folder with compiled web files
|
|
431
|
-
| `-e` | Web server engine (psychic/async/espidf/webserver)
|
|
432
|
-
| `-o` | Output header file path
|
|
433
|
-
| `--etag` | ETag caching (true/false/compiler)
|
|
434
|
-
| `--gzip` | Gzip compression (true/false/compiler)
|
|
435
|
-
| `--exclude` | Exclude files by glob pattern
|
|
436
|
-
| `--basepath` | URL prefix for all routes
|
|
437
|
-
| `--maxsize` | Max total uncompressed size (e.g., `400k`, `1m`)
|
|
438
|
-
| `--maxgzipsize` | Max total gzip size (e.g., `150k`, `500k`)
|
|
439
|
-
| `--cachetime` | Cache-Control max-age in seconds
|
|
440
|
-
| `--version` | Version string in header
|
|
441
|
-
| `--define` | C++ define prefix
|
|
442
|
-
| `--espmethod` | Init function name
|
|
443
|
-
| `--config` | Custom RC file path
|
|
444
|
-
| `--dryrun` | Show summary without writing output
|
|
445
|
-
| `--
|
|
446
|
-
|
|
|
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 | |
|
|
447
468
|
|
|
448
469
|
---
|
|
449
470
|
|
|
@@ -463,7 +484,8 @@ Store your settings in `.svelteesp32rc.json` for zero-argument builds:
|
|
|
463
484
|
"maxsize": "400k",
|
|
464
485
|
"maxgzipsize": "150k",
|
|
465
486
|
"noindexcheck": false,
|
|
466
|
-
"dryrun": false
|
|
487
|
+
"dryrun": false,
|
|
488
|
+
"spa": false
|
|
467
489
|
}
|
|
468
490
|
```
|
|
469
491
|
|
package/dist/commandLine.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ 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 {
|
|
@@ -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
|
@@ -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.
|
|
@@ -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,87 @@ 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}}
|
|
496
665
|
}`;
|
|
497
666
|
const webserverTemplate = `
|
|
498
667
|
//engine: Arduino WebServer
|
|
@@ -692,6 +861,92 @@ void {{methodName}}(WebServer * server) {
|
|
|
692
861
|
{{/if}}
|
|
693
862
|
|
|
694
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}}
|
|
695
950
|
}`;
|
|
696
951
|
const getTemplate = (engine) => {
|
|
697
952
|
switch (engine) {
|
|
@@ -753,6 +1008,8 @@ const createHandlebarsHelpers = () => {
|
|
|
753
1008
|
};
|
|
754
1009
|
const getCppCode = (sources, filesByExtension) => {
|
|
755
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;
|
|
756
1013
|
const templateData = {
|
|
757
1014
|
config: (0, commandLine_1.formatConfiguration)(commandLine_1.cmdLine),
|
|
758
1015
|
now: (() => {
|
|
@@ -762,7 +1019,7 @@ const getCppCode = (sources, filesByExtension) => {
|
|
|
762
1019
|
fileCount: sources.length.toString(),
|
|
763
1020
|
fileSize: sources.reduce((previous, current) => previous + current.content.length, 0).toString(),
|
|
764
1021
|
fileGzipSize: sources.reduce((previous, current) => previous + current.contentGzip.length, 0).toString(),
|
|
765
|
-
sources:
|
|
1022
|
+
sources: transformedSources,
|
|
766
1023
|
filesByExtension,
|
|
767
1024
|
etag: commandLine_1.cmdLine.etag,
|
|
768
1025
|
gzip: commandLine_1.cmdLine.gzip,
|
|
@@ -771,7 +1028,9 @@ const getCppCode = (sources, filesByExtension) => {
|
|
|
771
1028
|
methodName: commandLine_1.cmdLine.espmethod,
|
|
772
1029
|
cacheTime: commandLine_1.cmdLine.cachetime ? { value: commandLine_1.cmdLine.cachetime } : undefined,
|
|
773
1030
|
definePrefix: commandLine_1.cmdLine.define,
|
|
774
|
-
basePath: commandLine_1.cmdLine.basePath
|
|
1031
|
+
basePath: commandLine_1.cmdLine.basePath,
|
|
1032
|
+
spa: !!commandLine_1.cmdLine.spa,
|
|
1033
|
+
spaSource
|
|
775
1034
|
};
|
|
776
1035
|
const rawCode = template(templateData, { helpers: createHandlebarsHelpers() });
|
|
777
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/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.
|
|
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",
|
|
@@ -59,19 +59,19 @@
|
|
|
59
59
|
"espasyncwebserver"
|
|
60
60
|
],
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@eslint/eslintrc": "^3.3.
|
|
62
|
+
"@eslint/eslintrc": "^3.3.5",
|
|
63
63
|
"@eslint/js": "^10.0.1",
|
|
64
64
|
"@types/mime-types": "^3.0.1",
|
|
65
|
-
"@types/node": "^25.
|
|
65
|
+
"@types/node": "^25.4.0",
|
|
66
66
|
"@types/picomatch": "^4.0.2",
|
|
67
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
68
|
-
"@typescript-eslint/parser": "^8.
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
68
|
+
"@typescript-eslint/parser": "^8.57.0",
|
|
69
69
|
"@vitest/coverage-v8": "^4.0.18",
|
|
70
|
-
"eslint": "^10.0.
|
|
70
|
+
"eslint": "^10.0.3",
|
|
71
71
|
"eslint-config-prettier": "^10.1.8",
|
|
72
72
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
73
73
|
"eslint-plugin-unicorn": "^63.0.0",
|
|
74
|
-
"memfs": "^4.56.
|
|
74
|
+
"memfs": "^4.56.11",
|
|
75
75
|
"nodemon": "^3.1.14",
|
|
76
76
|
"prettier": "^3.8.1",
|
|
77
77
|
"ts-node": "^10.9.2",
|