svelteesp32 3.0.1 → 3.1.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/dist/cppCode.js CHANGED
@@ -1,970 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCppCode = void 0;
4
- const handlebars_1 = require("handlebars");
3
+ exports.getCppCode = exports.genHook = exports.genManifest = exports.genEtagArrays = exports.genDataArrays = exports.genCommonHeader = exports.cacheCtrl = exports.sw = void 0;
5
4
  const commandLine_1 = require("./commandLine");
5
+ const cppCodeAsync_1 = require("./cppCodeAsync");
6
6
  const cppCodeEspIdf_1 = require("./cppCodeEspIdf");
7
- const commonHeaderSection = `
8
- {{#switch etag}}
9
- {{#case "always"}}
10
- #ifdef {{definePrefix}}_ENABLE_ETAG
11
- #warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched ON
12
- #endif
13
- {{/case}}
14
- {{#case "never"}}
15
- #ifdef {{definePrefix}}_ENABLE_ETAG
16
- #warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched OFF
17
- #endif
18
- {{/case}}
19
- {{/switch}}
20
-
21
- {{#switch gzip}}
22
- {{#case "always"}}
23
- #ifdef {{definePrefix}}_ENABLE_GZIP
24
- #warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched ON
25
- #endif
26
- {{/case}}
27
- {{#case "never"}}
28
- #ifdef {{definePrefix}}_ENABLE_GZIP
29
- #warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched OFF
30
- #endif
31
- {{/case}}
32
- {{/switch}}
33
-
34
- //
35
- {{#if version }}
36
- #define {{definePrefix}}_VERSION "{{version}}"
37
- {{/if}}
38
- #define {{definePrefix}}_COUNT {{fileCount}}
39
- #define {{definePrefix}}_SIZE {{fileSize}}
40
- #define {{definePrefix}}_SIZE_GZIP {{fileGzipSize}}
41
- {{#if isPsychic}}
42
- #define {{definePrefix}}_MAX_URI_HANDLERS {{maxUriHandlers}}
43
- {{/if}}
44
-
45
- //
46
- {{#each sources}}
47
- #define {{../definePrefix}}_FILE_{{this.datanameUpperCase}}
48
- {{/each}}
49
-
50
- //
51
- {{#each filesByExtension}}
52
- #define {{../definePrefix}}_{{this.extension}}_FILES {{this.count}}
53
- {{/each}}
54
- `;
55
- const dataArraysSection = (progmem = false) => {
56
- const memDirective = progmem ? ' PROGMEM' : '';
57
- return `
58
- {{#switch gzip}}
59
- {{#case "always"}}
60
- {{#each sources}}
61
- static const uint8_t datagzip_{{this.dataname}}[{{this.lengthGzip}}]${memDirective} = { {{this.bytesGzip}} };
62
- {{/each}}
63
- {{/case}}
64
- {{#case "never"}}
65
- {{#each sources}}
66
- static const uint8_t data_{{this.dataname}}[{{this.length}}]${memDirective} = { {{this.bytes}} };
67
- {{/each}}
68
- {{/case}}
69
- {{#case "compiler"}}
70
- #ifdef {{definePrefix}}_ENABLE_GZIP
71
- {{#each sources}}
72
- static const uint8_t datagzip_{{this.dataname}}[{{this.lengthGzip}}]${memDirective} = { {{this.bytesGzip}} };
73
- {{/each}}
74
- #else
75
- {{#each sources}}
76
- static const uint8_t data_{{this.dataname}}[{{this.length}}]${memDirective} = { {{this.bytes}} };
77
- {{/each}}
78
- #endif
79
- {{/case}}
80
- {{/switch}}
81
- `;
82
- };
83
- const etagArraysSection = `
84
- {{#switch etag}}
85
- {{#case "always"}}
86
- {{#each sources}}
87
- static const char etag_{{this.dataname}}[] = "{{this.sha256}}";
88
- {{/each}}
89
- {{/case}}
90
- {{#case "never"}}
91
- {{/case}}
92
- {{#case "compiler"}}
93
- #ifdef {{definePrefix}}_ENABLE_ETAG
94
- {{#each sources}}
95
- static const char etag_{{this.dataname}}[] = "{{this.sha256}}";
96
- {{/each}}
97
- #endif
98
- {{/case}}
99
- {{/switch}}
100
- `;
101
- const manifestSection = `
102
- // File manifest struct
103
- struct {{definePrefix}}_FileInfo {
104
- const char* path;
105
- uint32_t size;
106
- uint32_t gzipSize;
107
- const char* etag;
108
- const char* contentType;
109
- };
110
-
111
- // File manifest array
112
- static const {{definePrefix}}_FileInfo {{definePrefix}}_FILES[] = {
113
- {{#each sources}}
114
- { "{{../basePath}}/{{this.filename}}", {{this.length}}, {{this.gzipSizeForManifest}}, {{this.etagForManifest}}, "{{this.mime}}" },
115
- {{/each}}
116
- };
117
- static const size_t {{definePrefix}}_FILE_COUNT = sizeof({{definePrefix}}_FILES) / sizeof({{definePrefix}}_FILES[0]);
118
- `;
119
- const hookSection = `
120
- // File served hook - override with your own implementation
121
- extern "C" void __attribute__((weak)) {{definePrefix}}_onFileServed(const char* path, int statusCode) {}
122
- `;
123
- const psychicTemplate = `
124
- //engine: PsychicHttpServer
125
- //config: {{{config}}}
126
- {{#if created }}
127
- //created: {{now}}
128
- {{/if}}
129
- //
130
- ${commonHeaderSection}
131
-
132
- //
133
- #include <Arduino.h>
134
- #include <PsychicHttp.h>
135
- #include <PsychicHttpsServer.h>
136
-
137
- //
138
- ${dataArraysSection(false)}
139
-
140
- //
141
- ${etagArraysSection}
142
-
143
- //
144
- ${manifestSection}
145
-
146
- //
147
- ${hookSection}
148
-
149
- //
150
- // Http Handlers
151
- void {{methodName}}(PsychicHttpServer * server) {
152
- {{#each sources}}
153
- //
154
- // {{this.filename}}
155
- {{#if this.isDefault}}{{#unless ../basePath}}server->defaultEndpoint = {{/unless}}{{/if}}server->on("{{../basePath}}/{{this.filename}}", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {
156
-
157
- {{#switch ../etag}}
158
- {{#case "always"}}
159
- if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
160
- response->setCode(304);
161
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
162
- return response->send();
163
- }
164
- {{/case}}
165
- {{#case "compiler"}}
166
- #ifdef {{../definePrefix}}_ENABLE_ETAG
167
- if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
168
- response->setCode(304);
169
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
170
- return response->send();
171
- }
172
- #endif
173
- {{/case}}
174
- {{/switch}}
175
-
176
- response->setContentType("{{this.mime}}");
177
-
178
- {{#switch ../gzip}}
179
- {{#case "always"}}
180
- {{#if this.isGzip}}
181
- response->addHeader("Content-Encoding", "gzip");
182
- {{/if}}
183
- {{/case}}
184
- {{#case "compiler"}}
185
- {{#if this.isGzip}}
186
- #ifdef {{../definePrefix}}_ENABLE_GZIP
187
- response->addHeader("Content-Encoding", "gzip");
188
- #endif
189
- {{/if}}
190
- {{/case}}
191
- {{/switch}}
192
-
193
- {{#switch ../etag}}
194
- {{#case "always"}}
195
- {{#this.cacheTime}}
196
- response->addHeader("Cache-Control", "max-age={{value}}");
197
- {{/this.cacheTime}}
198
- {{^this.cacheTime}}
199
- response->addHeader("Cache-Control", "no-cache");
200
- {{/this.cacheTime}}
201
- response->addHeader("ETag", etag_{{this.dataname}});
202
- {{/case}}
203
- {{#case "compiler"}}
204
- #ifdef {{../definePrefix}}_ENABLE_ETAG
205
- {{#this.cacheTime}}
206
- response->addHeader("Cache-Control", "max-age={{value}}");
207
- {{/this.cacheTime}}
208
- {{^this.cacheTime}}
209
- response->addHeader("Cache-Control", "no-cache");
210
- {{/this.cacheTime}}
211
- response->addHeader("ETag", etag_{{this.dataname}});
212
- #endif
213
- {{/case}}
214
- {{/switch}}
215
-
216
- {{#switch ../gzip}}
217
- {{#case "always"}}
218
- response->setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
219
- {{/case}}
220
- {{#case "never"}}
221
- response->setContent(data_{{this.dataname}}, {{this.length}});
222
- {{/case}}
223
- {{#case "compiler"}}
224
- #ifdef {{../definePrefix}}_ENABLE_GZIP
225
- response->setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
226
- #else
227
- response->setContent(data_{{this.dataname}}, {{this.length}});
228
- #endif
229
- {{/case}}
230
- {{/switch}}
231
-
232
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
233
- return response->send();
234
- });
235
- {{#if this.isDefault}}{{#if ../basePath}}
236
- //
237
- // {{this.filename}} (base path route)
238
- server->on("{{../basePath}}", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {
239
-
240
- {{#switch ../etag}}
241
- {{#case "always"}}
242
- if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
243
- response->setCode(304);
244
- {{../definePrefix}}_onFileServed("{{../basePath}}", 304);
245
- return response->send();
246
- }
247
- {{/case}}
248
- {{#case "compiler"}}
249
- #ifdef {{../definePrefix}}_ENABLE_ETAG
250
- if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
251
- response->setCode(304);
252
- {{../definePrefix}}_onFileServed("{{../basePath}}", 304);
253
- return response->send();
254
- }
255
- #endif
256
- {{/case}}
257
- {{/switch}}
258
-
259
- response->setContentType("{{this.mime}}");
260
-
261
- {{#switch ../gzip}}
262
- {{#case "always"}}
263
- {{#if this.isGzip}}
264
- response->addHeader("Content-Encoding", "gzip");
265
- {{/if}}
266
- {{/case}}
267
- {{#case "compiler"}}
268
- {{#if this.isGzip}}
269
- #ifdef {{../definePrefix}}_ENABLE_GZIP
270
- response->addHeader("Content-Encoding", "gzip");
271
- #endif
272
- {{/if}}
273
- {{/case}}
274
- {{/switch}}
275
-
276
- {{#switch ../etag}}
277
- {{#case "always"}}
278
- {{#this.cacheTime}}
279
- response->addHeader("Cache-Control", "max-age={{value}}");
280
- {{/this.cacheTime}}
281
- {{^this.cacheTime}}
282
- response->addHeader("Cache-Control", "no-cache");
283
- {{/this.cacheTime}}
284
- response->addHeader("ETag", etag_{{this.dataname}});
285
- {{/case}}
286
- {{#case "compiler"}}
287
- #ifdef {{../definePrefix}}_ENABLE_ETAG
288
- {{#this.cacheTime}}
289
- response->addHeader("Cache-Control", "max-age={{value}}");
290
- {{/this.cacheTime}}
291
- {{^this.cacheTime}}
292
- response->addHeader("Cache-Control", "no-cache");
293
- {{/this.cacheTime}}
294
- response->addHeader("ETag", etag_{{this.dataname}});
295
- #endif
296
- {{/case}}
297
- {{/switch}}
298
-
299
- {{#switch ../gzip}}
300
- {{#case "always"}}
301
- response->setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
302
- {{/case}}
303
- {{#case "never"}}
304
- response->setContent(data_{{this.dataname}}, {{this.length}});
305
- {{/case}}
306
- {{#case "compiler"}}
307
- #ifdef {{../definePrefix}}_ENABLE_GZIP
308
- response->setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
309
- #else
310
- response->setContent(data_{{this.dataname}}, {{this.length}});
311
- #endif
312
- {{/case}}
313
- {{/switch}}
314
-
315
- {{../definePrefix}}_onFileServed("{{../basePath}}", 200);
316
- return response->send();
317
- });
318
- {{/if}}{{/if}}
319
-
320
- {{/each}}
321
- {{#if spa}}
322
- {{#with spaSource}}
323
- {{#if ../basePath}}
324
- //
325
- // SPA catch-all: unmatched routes serve {{this.filename}}
326
- server->on("{{../basePath}}/*", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {
327
-
328
- {{#switch ../etag}}
329
- {{#case "always"}}
330
- if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
331
- response->setCode(304);
332
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
333
- return response->send();
334
- }
335
- {{/case}}
336
- {{#case "compiler"}}
337
- #ifdef {{../definePrefix}}_ENABLE_ETAG
338
- if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
339
- response->setCode(304);
340
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
341
- return response->send();
342
- }
343
- #endif
344
- {{/case}}
345
- {{/switch}}
346
-
347
- response->setContentType("{{this.mime}}");
348
-
349
- {{#switch ../gzip}}
350
- {{#case "always"}}
351
- {{#if this.isGzip}}
352
- response->addHeader("Content-Encoding", "gzip");
353
- {{/if}}
354
- {{/case}}
355
- {{#case "compiler"}}
356
- {{#if this.isGzip}}
357
- #ifdef {{../definePrefix}}_ENABLE_GZIP
358
- response->addHeader("Content-Encoding", "gzip");
359
- #endif
360
- {{/if}}
361
- {{/case}}
362
- {{/switch}}
363
-
364
- {{#switch ../etag}}
365
- {{#case "always"}}
366
- {{#this.cacheTime}}
367
- response->addHeader("Cache-Control", "max-age={{value}}");
368
- {{/this.cacheTime}}
369
- {{^this.cacheTime}}
370
- response->addHeader("Cache-Control", "no-cache");
371
- {{/this.cacheTime}}
372
- response->addHeader("ETag", etag_{{this.dataname}});
373
- {{/case}}
374
- {{#case "compiler"}}
375
- #ifdef {{../definePrefix}}_ENABLE_ETAG
376
- {{#this.cacheTime}}
377
- response->addHeader("Cache-Control", "max-age={{value}}");
378
- {{/this.cacheTime}}
379
- {{^this.cacheTime}}
380
- response->addHeader("Cache-Control", "no-cache");
381
- {{/this.cacheTime}}
382
- response->addHeader("ETag", etag_{{this.dataname}});
383
- #endif
384
- {{/case}}
385
- {{/switch}}
386
-
387
- {{#switch ../gzip}}
388
- {{#case "always"}}
389
- response->setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
390
- {{/case}}
391
- {{#case "never"}}
392
- response->setContent(data_{{this.dataname}}, {{this.length}});
393
- {{/case}}
394
- {{#case "compiler"}}
395
- #ifdef {{../definePrefix}}_ENABLE_GZIP
396
- response->setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
397
- #else
398
- response->setContent(data_{{this.dataname}}, {{this.length}});
399
- #endif
400
- {{/case}}
401
- {{/switch}}
402
-
403
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
404
- return response->send();
405
- });
406
- {{/if}}
407
- {{/with}}
408
- {{/if}}
409
- }`;
410
- const asyncTemplate = `
411
- //engine: ESPAsyncWebServer
412
- //config: {{{config}}}
413
- {{#if created }}
414
- //created: {{now}}
415
- {{/if}}
416
- //
417
- ${commonHeaderSection}
418
-
419
- //
420
- #include <Arduino.h>
421
- #include <ESPAsyncWebServer.h>
422
-
423
- //
424
- ${dataArraysSection(true)}
425
-
426
- //
427
- ${etagArraysSection}
428
-
429
- //
430
- ${manifestSection}
431
-
432
- //
433
- ${hookSection}
434
-
435
- //
436
- // Http Handlers
437
- void {{methodName}}(AsyncWebServer * server) {
438
- {{#each sources}}
439
- //
440
- // {{this.filename}}
441
- server->on("{{../basePath}}/{{this.filename}}", HTTP_GET, [](AsyncWebServerRequest * request) {
442
-
443
- {{#switch ../etag}}
444
- {{#case "always"}}
445
- const AsyncWebHeader* h = request->getHeader("If-None-Match");
446
- if (h && h->value().equals(etag_{{this.dataname}})) {
447
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
448
- request->send(304);
449
- return;
450
- }
451
- {{/case}}
452
- {{#case "compiler"}}
453
- #ifdef {{../definePrefix}}_ENABLE_ETAG
454
- const AsyncWebHeader* h = request->getHeader("If-None-Match");
455
- if (h && h->value().equals(etag_{{this.dataname}})) {
456
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
457
- request->send(304);
458
- return;
459
- }
460
- #endif
461
- {{/case}}
462
- {{/switch}}
463
-
464
- {{#switch ../gzip}}
465
- {{#case "always"}}
466
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", datagzip_{{this.dataname}}, {{this.lengthGzip}});
467
- {{#if this.isGzip}}
468
- response->addHeader("Content-Encoding", "gzip");
469
- {{/if}}
470
- {{/case}}
471
- {{#case "never"}}
472
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
473
- {{/case}}
474
- {{#case "compiler"}}
475
- #ifdef {{../definePrefix}}_ENABLE_GZIP
476
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", datagzip_{{this.dataname}}, {{this.lengthGzip}});
477
- {{#if this.isGzip}}
478
- response->addHeader("Content-Encoding", "gzip");
479
- {{/if}}
480
- #else
481
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
482
- #endif
483
- {{/case}}
484
- {{/switch}}
485
-
486
- {{#switch ../etag}}
487
- {{#case "always"}}
488
- {{#this.cacheTime}}
489
- response->addHeader("Cache-Control", "max-age={{value}}");
490
- {{/this.cacheTime}}
491
- {{^this.cacheTime}}
492
- response->addHeader("Cache-Control", "no-cache");
493
- {{/this.cacheTime}}
494
- response->addHeader("ETag", etag_{{this.dataname}});
495
- {{/case}}
496
- {{#case "compiler"}}
497
- #ifdef {{../definePrefix}}_ENABLE_ETAG
498
- {{#this.cacheTime}}
499
- response->addHeader("Cache-Control", "max-age={{value}}");
500
- {{/this.cacheTime}}
501
- {{^this.cacheTime}}
502
- response->addHeader("Cache-Control", "no-cache");
503
- {{/this.cacheTime}}
504
- response->addHeader("ETag", etag_{{this.dataname}});
505
- #endif
506
- {{/case}}
507
- {{/switch}}
508
-
509
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
510
- request->send(response);
511
- });
512
- {{#if this.isDefault}}
513
- server->on("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", HTTP_GET, [](AsyncWebServerRequest * request) {
514
-
515
- {{#switch ../etag}}
516
- {{#case "always"}}
517
- const AsyncWebHeader* h = request->getHeader("If-None-Match");
518
- if (h && h->value().equals(etag_{{this.dataname}})) {
519
- {{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 304);
520
- request->send(304);
521
- return;
522
- }
523
- {{/case}}
524
- {{#case "compiler"}}
525
- #ifdef {{../definePrefix}}_ENABLE_ETAG
526
- const AsyncWebHeader* h = request->getHeader("If-None-Match");
527
- if (h && h->value().equals(etag_{{this.dataname}})) {
528
- {{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 304);
529
- request->send(304);
530
- return;
531
- }
532
- #endif
533
- {{/case}}
534
- {{/switch}}
535
-
536
- {{#switch ../gzip}}
537
- {{#case "always"}}
538
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", datagzip_{{this.dataname}}, {{this.lengthGzip}});
539
- {{#if this.isGzip}}
540
- response->addHeader("Content-Encoding", "gzip");
541
- {{/if}}
542
- {{/case}}
543
- {{#case "never"}}
544
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
545
- {{/case}}
546
- {{#case "compiler"}}
547
- #ifdef {{../definePrefix}}_ENABLE_GZIP
548
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", datagzip_{{this.dataname}}, {{this.lengthGzip}});
549
- {{#if this.isGzip}}
550
- response->addHeader("Content-Encoding", "gzip");
551
- {{/if}}
552
- #else
553
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
554
- #endif
555
- {{/case}}
556
- {{/switch}}
557
-
558
- {{#switch ../etag}}
559
- {{#case "always"}}
560
- {{#this.cacheTime}}
561
- response->addHeader("Cache-Control", "max-age={{value}}");
562
- {{/this.cacheTime}}
563
- {{^this.cacheTime}}
564
- response->addHeader("Cache-Control", "no-cache");
565
- {{/this.cacheTime}}
566
- response->addHeader("ETag", etag_{{this.dataname}});
567
- {{/case}}
568
- {{#case "compiler"}}
569
- #ifdef {{../definePrefix}}_ENABLE_ETAG
570
- {{#this.cacheTime}}
571
- response->addHeader("Cache-Control", "max-age={{value}}");
572
- {{/this.cacheTime}}
573
- {{^this.cacheTime}}
574
- response->addHeader("Cache-Control", "no-cache");
575
- {{/this.cacheTime}}
576
- response->addHeader("ETag", etag_{{this.dataname}});
577
- #endif
578
- {{/case}}
579
- {{/switch}}
580
-
581
- {{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 200);
582
- request->send(response);
583
- });
584
- {{/if}}
585
-
586
- {{/each}}
587
- {{#if spa}}
588
- {{#with spaSource}}
589
- //
590
- // SPA catch-all: unmatched routes serve {{this.filename}}
591
- server->onNotFound([](AsyncWebServerRequest * request) {
592
- if (request->method() != HTTP_GET) { request->send(404); return; }
593
- {{#if ../basePath}}
594
- if (!request->url().startsWith("{{../basePath}}/") && request->url() != "{{../basePath}}") { request->send(404); return; }
595
- {{/if}}
596
-
597
- {{#switch ../etag}}
598
- {{#case "always"}}
599
- const AsyncWebHeader* h = request->getHeader("If-None-Match");
600
- if (h && h->value().equals(etag_{{this.dataname}})) {
601
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
602
- request->send(304);
603
- return;
604
- }
605
- {{/case}}
606
- {{#case "compiler"}}
607
- #ifdef {{../definePrefix}}_ENABLE_ETAG
608
- const AsyncWebHeader* h = request->getHeader("If-None-Match");
609
- if (h && h->value().equals(etag_{{this.dataname}})) {
610
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
611
- request->send(304);
612
- return;
613
- }
614
- #endif
615
- {{/case}}
616
- {{/switch}}
617
-
618
- {{#switch ../gzip}}
619
- {{#case "always"}}
620
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", datagzip_{{this.dataname}}, {{this.lengthGzip}});
621
- {{#if this.isGzip}}
622
- response->addHeader("Content-Encoding", "gzip");
623
- {{/if}}
624
- {{/case}}
625
- {{#case "never"}}
626
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
627
- {{/case}}
628
- {{#case "compiler"}}
629
- #ifdef {{../definePrefix}}_ENABLE_GZIP
630
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", datagzip_{{this.dataname}}, {{this.lengthGzip}});
631
- {{#if this.isGzip}}
632
- response->addHeader("Content-Encoding", "gzip");
633
- {{/if}}
634
- #else
635
- AsyncWebServerResponse *response = request->beginResponse(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
636
- #endif
637
- {{/case}}
638
- {{/switch}}
639
-
640
- {{#switch ../etag}}
641
- {{#case "always"}}
642
- {{#this.cacheTime}}
643
- response->addHeader("Cache-Control", "max-age={{value}}");
644
- {{/this.cacheTime}}
645
- {{^this.cacheTime}}
646
- response->addHeader("Cache-Control", "no-cache");
647
- {{/this.cacheTime}}
648
- response->addHeader("ETag", etag_{{this.dataname}});
649
- {{/case}}
650
- {{#case "compiler"}}
651
- #ifdef {{../definePrefix}}_ENABLE_ETAG
652
- {{#this.cacheTime}}
653
- response->addHeader("Cache-Control", "max-age={{value}}");
654
- {{/this.cacheTime}}
655
- {{^this.cacheTime}}
656
- response->addHeader("Cache-Control", "no-cache");
657
- {{/this.cacheTime}}
658
- response->addHeader("ETag", etag_{{this.dataname}});
659
- #endif
660
- {{/case}}
661
- {{/switch}}
662
-
663
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
664
- request->send(response);
665
- });
666
- {{/with}}
667
- {{/if}}
668
- }`;
669
- const webserverTemplate = `
670
- //engine: Arduino WebServer
671
- //config: {{{config}}}
672
- {{#if created }}
673
- //created: {{now}}
674
- {{/if}}
675
- //
676
- ${commonHeaderSection}
677
-
678
- //
679
- #include <Arduino.h>
680
- #include <WebServer.h>
681
-
682
- //
683
- ${dataArraysSection(true)}
684
-
685
- //
686
- ${etagArraysSection}
687
-
688
- //
689
- ${manifestSection}
690
-
691
- //
692
- ${hookSection}
693
-
694
- //
695
- // Chunked send helper for PROGMEM data
696
- static inline void {{definePrefix}}_sendChunked(WebServer * server, const uint8_t * data, size_t len) {
697
- const size_t chunkSize = 4096;
698
- for (size_t offset = 0; offset < len; offset += chunkSize) {
699
- size_t remaining = len - offset;
700
- size_t toSend = remaining < chunkSize ? remaining : chunkSize;
701
- server->sendContent_P((const char *)(data + offset), toSend);
702
- }
703
- }
704
-
705
- //
706
- // Http Handlers
707
- void {{methodName}}(WebServer * server) {
708
- {{#each sources}}
709
- //
710
- // {{this.filename}}
711
- server->on("{{../basePath}}/{{this.filename}}", HTTP_GET, [server]() {
712
-
713
- {{#switch ../etag}}
714
- {{#case "always"}}
715
- if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
716
- server->send(304);
717
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
718
- return;
719
- }
720
- {{/case}}
721
- {{#case "compiler"}}
722
- #ifdef {{../definePrefix}}_ENABLE_ETAG
723
- if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
724
- server->send(304);
725
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
726
- return;
727
- }
728
- #endif
729
- {{/case}}
730
- {{/switch}}
731
-
732
- {{#switch ../etag}}
733
- {{#case "always"}}
734
- {{#this.cacheTime}}
735
- server->sendHeader("Cache-Control", "max-age={{value}}");
736
- {{/this.cacheTime}}
737
- {{^this.cacheTime}}
738
- server->sendHeader("Cache-Control", "no-cache");
739
- {{/this.cacheTime}}
740
- server->sendHeader("ETag", etag_{{this.dataname}});
741
- {{/case}}
742
- {{#case "compiler"}}
743
- #ifdef {{../definePrefix}}_ENABLE_ETAG
744
- {{#this.cacheTime}}
745
- server->sendHeader("Cache-Control", "max-age={{value}}");
746
- {{/this.cacheTime}}
747
- {{^this.cacheTime}}
748
- server->sendHeader("Cache-Control", "no-cache");
749
- {{/this.cacheTime}}
750
- server->sendHeader("ETag", etag_{{this.dataname}});
751
- #endif
752
- {{/case}}
753
- {{/switch}}
754
-
755
- {{#switch ../gzip}}
756
- {{#case "always"}}
757
- {{#if this.isGzip}}
758
- server->sendHeader("Content-Encoding", "gzip");
759
- {{/if}}
760
- server->setContentLength({{this.lengthGzip}});
761
- server->send(200, "{{this.mime}}", "");
762
- {{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
763
- {{/case}}
764
- {{#case "never"}}
765
- server->setContentLength({{this.length}});
766
- server->send(200, "{{this.mime}}", "");
767
- {{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
768
- {{/case}}
769
- {{#case "compiler"}}
770
- #ifdef {{../definePrefix}}_ENABLE_GZIP
771
- {{#if this.isGzip}}
772
- server->sendHeader("Content-Encoding", "gzip");
773
- {{/if}}
774
- server->setContentLength({{this.lengthGzip}});
775
- server->send(200, "{{this.mime}}", "");
776
- {{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
777
- #else
778
- server->setContentLength({{this.length}});
779
- server->send(200, "{{this.mime}}", "");
780
- {{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
781
- #endif
782
- {{/case}}
783
- {{/switch}}
784
-
785
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
786
- });
787
- {{#if this.isDefault}}
788
- server->on("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", HTTP_GET, [server]() {
789
-
790
- {{#switch ../etag}}
791
- {{#case "always"}}
792
- if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
793
- server->send(304);
794
- {{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 304);
795
- return;
796
- }
797
- {{/case}}
798
- {{#case "compiler"}}
799
- #ifdef {{../definePrefix}}_ENABLE_ETAG
800
- if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
801
- server->send(304);
802
- {{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 304);
803
- return;
804
- }
805
- #endif
806
- {{/case}}
807
- {{/switch}}
808
-
809
- {{#switch ../etag}}
810
- {{#case "always"}}
811
- {{#this.cacheTime}}
812
- server->sendHeader("Cache-Control", "max-age={{value}}");
813
- {{/this.cacheTime}}
814
- {{^this.cacheTime}}
815
- server->sendHeader("Cache-Control", "no-cache");
816
- {{/this.cacheTime}}
817
- server->sendHeader("ETag", etag_{{this.dataname}});
818
- {{/case}}
819
- {{#case "compiler"}}
820
- #ifdef {{../definePrefix}}_ENABLE_ETAG
821
- {{#this.cacheTime}}
822
- server->sendHeader("Cache-Control", "max-age={{value}}");
823
- {{/this.cacheTime}}
824
- {{^this.cacheTime}}
825
- server->sendHeader("Cache-Control", "no-cache");
826
- {{/this.cacheTime}}
827
- server->sendHeader("ETag", etag_{{this.dataname}});
828
- #endif
829
- {{/case}}
830
- {{/switch}}
831
-
832
- {{#switch ../gzip}}
833
- {{#case "always"}}
834
- {{#if this.isGzip}}
835
- server->sendHeader("Content-Encoding", "gzip");
836
- {{/if}}
837
- server->setContentLength({{this.lengthGzip}});
838
- server->send(200, "{{this.mime}}", "");
839
- {{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
840
- {{/case}}
841
- {{#case "never"}}
842
- server->setContentLength({{this.length}});
843
- server->send(200, "{{this.mime}}", "");
844
- {{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
845
- {{/case}}
846
- {{#case "compiler"}}
847
- #ifdef {{../definePrefix}}_ENABLE_GZIP
848
- {{#if this.isGzip}}
849
- server->sendHeader("Content-Encoding", "gzip");
850
- {{/if}}
851
- server->setContentLength({{this.lengthGzip}});
852
- server->send(200, "{{this.mime}}", "");
853
- {{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
854
- #else
855
- server->setContentLength({{this.length}});
856
- server->send(200, "{{this.mime}}", "");
857
- {{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
858
- #endif
859
- {{/case}}
860
- {{/switch}}
861
-
862
- {{../definePrefix}}_onFileServed("{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}", 200);
863
- });
864
- {{/if}}
865
-
866
- {{/each}}
867
- {{#if spa}}
868
- {{#with spaSource}}
869
- //
870
- // SPA catch-all: unmatched routes serve {{this.filename}}
871
- server->onNotFound([server]() {
872
- if (server->method() != HTTP_GET) { server->send(404, "text/plain", "Not found"); return; }
873
- {{#if ../basePath}}
874
- if (!server->uri().startsWith("{{../basePath}}/") && server->uri() != "{{../basePath}}") { server->send(404, "text/plain", "Not found"); return; }
875
- {{/if}}
876
-
877
- {{#switch ../etag}}
878
- {{#case "always"}}
879
- if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
880
- server->send(304);
881
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
882
- return;
883
- }
884
- {{/case}}
885
- {{#case "compiler"}}
886
- #ifdef {{../definePrefix}}_ENABLE_ETAG
887
- if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_{{this.dataname}})) {
888
- server->send(304);
889
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
890
- return;
891
- }
892
- #endif
893
- {{/case}}
894
- {{/switch}}
895
-
896
- {{#switch ../etag}}
897
- {{#case "always"}}
898
- {{#this.cacheTime}}
899
- server->sendHeader("Cache-Control", "max-age={{value}}");
900
- {{/this.cacheTime}}
901
- {{^this.cacheTime}}
902
- server->sendHeader("Cache-Control", "no-cache");
903
- {{/this.cacheTime}}
904
- server->sendHeader("ETag", etag_{{this.dataname}});
905
- {{/case}}
906
- {{#case "compiler"}}
907
- #ifdef {{../definePrefix}}_ENABLE_ETAG
908
- {{#this.cacheTime}}
909
- server->sendHeader("Cache-Control", "max-age={{value}}");
910
- {{/this.cacheTime}}
911
- {{^this.cacheTime}}
912
- server->sendHeader("Cache-Control", "no-cache");
913
- {{/this.cacheTime}}
914
- server->sendHeader("ETag", etag_{{this.dataname}});
915
- #endif
916
- {{/case}}
917
- {{/switch}}
918
-
919
- {{#switch ../gzip}}
920
- {{#case "always"}}
921
- {{#if this.isGzip}}
922
- server->sendHeader("Content-Encoding", "gzip");
923
- {{/if}}
924
- server->setContentLength({{this.lengthGzip}});
925
- server->send(200, "{{this.mime}}", "");
926
- {{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
927
- {{/case}}
928
- {{#case "never"}}
929
- server->setContentLength({{this.length}});
930
- server->send(200, "{{this.mime}}", "");
931
- {{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
932
- {{/case}}
933
- {{#case "compiler"}}
934
- #ifdef {{../definePrefix}}_ENABLE_GZIP
935
- {{#if this.isGzip}}
936
- server->sendHeader("Content-Encoding", "gzip");
937
- {{/if}}
938
- server->setContentLength({{this.lengthGzip}});
939
- server->send(200, "{{this.mime}}", "");
940
- {{../definePrefix}}_sendChunked(server, datagzip_{{this.dataname}}, {{this.lengthGzip}});
941
- #else
942
- server->setContentLength({{this.length}});
943
- server->send(200, "{{this.mime}}", "");
944
- {{../definePrefix}}_sendChunked(server, data_{{this.dataname}}, {{this.length}});
945
- #endif
946
- {{/case}}
947
- {{/switch}}
948
-
949
- {{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
950
- });
951
- {{/with}}
952
- {{/if}}
953
- }`;
954
- const getTemplate = (engine) => {
955
- switch (engine) {
956
- case 'psychic':
957
- return psychicTemplate;
958
- case 'async':
959
- return asyncTemplate;
960
- case 'espidf':
961
- return cppCodeEspIdf_1.espidfTemplate;
962
- case 'webserver':
963
- return webserverTemplate;
964
- default:
965
- throw new Error(`Unknown engine: ${engine}`);
966
- }
967
- };
7
+ const cppCodePsychic_1 = require("./cppCodePsychic");
8
+ const cppCodeWebserver_1 = require("./cppCodeWebserver");
9
+ const sw = (value, cases) => cases[value] ?? '';
10
+ exports.sw = sw;
968
11
  const bufferToByteString = (buffer) => {
969
12
  if (buffer.length === 0)
970
13
  return '';
@@ -984,6 +27,106 @@ const transformSourceToTemplateData = (s, etag, effectiveCacheTime) => ({
984
27
  etagForManifest: etag === 'never' ? 'NULL' : `etag_${s.dataname}`,
985
28
  cacheTime: effectiveCacheTime ? { value: effectiveCacheTime } : undefined
986
29
  });
30
+ const cacheCtrl = (source) => source.cacheTime ? `max-age=${source.cacheTime.value}` : 'no-cache';
31
+ exports.cacheCtrl = cacheCtrl;
32
+ const genCommonHeader = (d) => {
33
+ const lines = [];
34
+ const etagWarn = (0, exports.sw)(d.etag, {
35
+ always: [
36
+ `#ifdef ${d.definePrefix}_ENABLE_ETAG`,
37
+ `#warning ${d.definePrefix}_ENABLE_ETAG has no effect because it is permanently switched ON`,
38
+ '#endif'
39
+ ].join('\n'),
40
+ never: [
41
+ `#ifdef ${d.definePrefix}_ENABLE_ETAG`,
42
+ `#warning ${d.definePrefix}_ENABLE_ETAG has no effect because it is permanently switched OFF`,
43
+ '#endif'
44
+ ].join('\n')
45
+ });
46
+ if (etagWarn)
47
+ lines.push(etagWarn);
48
+ const gzipWarn = (0, exports.sw)(d.gzip, {
49
+ always: [
50
+ `#ifdef ${d.definePrefix}_ENABLE_GZIP`,
51
+ `#warning ${d.definePrefix}_ENABLE_GZIP has no effect because it is permanently switched ON`,
52
+ '#endif'
53
+ ].join('\n'),
54
+ never: [
55
+ `#ifdef ${d.definePrefix}_ENABLE_GZIP`,
56
+ `#warning ${d.definePrefix}_ENABLE_GZIP has no effect because it is permanently switched OFF`,
57
+ '#endif'
58
+ ].join('\n')
59
+ });
60
+ if (gzipWarn)
61
+ lines.push(gzipWarn);
62
+ lines.push('//');
63
+ if (d.version)
64
+ lines.push(`#define ${d.definePrefix}_VERSION "${d.version}"`);
65
+ lines.push(`#define ${d.definePrefix}_COUNT ${d.fileCount}`, `#define ${d.definePrefix}_SIZE ${d.fileSize}`, `#define ${d.definePrefix}_SIZE_GZIP ${d.fileGzipSize}`);
66
+ if (d.isPsychic)
67
+ lines.push(`#define ${d.definePrefix}_MAX_URI_HANDLERS ${d.maxUriHandlers}`);
68
+ lines.push('//');
69
+ for (const s of d.sources)
70
+ lines.push(`#define ${d.definePrefix}_FILE_${s.datanameUpperCase}`);
71
+ lines.push('//');
72
+ for (const g of d.filesByExtension)
73
+ lines.push(`#define ${d.definePrefix}_${g.extension}_FILES ${g.count}`);
74
+ return lines.join('\n');
75
+ };
76
+ exports.genCommonHeader = genCommonHeader;
77
+ const genDataArrays = (d, progmem) => {
78
+ const mem = progmem ? ' PROGMEM' : '';
79
+ const gzipArrays = d.sources
80
+ .map((s) => `static const uint8_t datagzip_${s.dataname}[${s.lengthGzip}]${mem} = { ${s.bytesGzip} };`)
81
+ .join('\n');
82
+ const plainArrays = d.sources
83
+ .map((s) => `static const uint8_t data_${s.dataname}[${s.length}]${mem} = { ${s.bytes} };`)
84
+ .join('\n');
85
+ return (0, exports.sw)(d.gzip, {
86
+ always: gzipArrays,
87
+ never: plainArrays,
88
+ compiler: [`#ifdef ${d.definePrefix}_ENABLE_GZIP`, gzipArrays, '#else', plainArrays, '#endif'].join('\n')
89
+ });
90
+ };
91
+ exports.genDataArrays = genDataArrays;
92
+ const genEtagArrays = (d) => {
93
+ const items = d.sources.map((s) => `static const char etag_${s.dataname}[] = "${s.sha256}";`).join('\n');
94
+ return (0, exports.sw)(d.etag, {
95
+ always: items,
96
+ compiler: [`#ifdef ${d.definePrefix}_ENABLE_ETAG`, items, '#endif'].join('\n')
97
+ });
98
+ };
99
+ exports.genEtagArrays = genEtagArrays;
100
+ const genManifest = (d) => [
101
+ `// File manifest struct`,
102
+ `struct ${d.definePrefix}_FileInfo {`,
103
+ ` const char* path;`,
104
+ ` uint32_t size;`,
105
+ ` uint32_t gzipSize;`,
106
+ ` const char* etag;`,
107
+ ` const char* contentType;`,
108
+ `};`,
109
+ `// File manifest array`,
110
+ `static const ${d.definePrefix}_FileInfo ${d.definePrefix}_FILES[] = {`,
111
+ ...d.sources.map((s) => ` { "${d.basePath}/${s.filename}", ${s.length}, ${s.gzipSizeForManifest}, ${s.etagForManifest}, "${s.mime}" },`),
112
+ `};`,
113
+ `static const size_t ${d.definePrefix}_FILE_COUNT = sizeof(${d.definePrefix}_FILES) / sizeof(${d.definePrefix}_FILES[0]);`
114
+ ].join('\n');
115
+ exports.genManifest = genManifest;
116
+ const genHook = (d) => `// File served hook - override with your own implementation\nextern "C" void __attribute__((weak)) ${d.definePrefix}_onFileServed(const char* path, int statusCode) {}`;
117
+ exports.genHook = genHook;
118
+ const getGenerator = (engine) => {
119
+ switch (engine) {
120
+ case 'psychic':
121
+ return cppCodePsychic_1.genPsychicCpp;
122
+ case 'async':
123
+ return cppCodeAsync_1.genAsyncCpp;
124
+ case 'espidf':
125
+ return cppCodeEspIdf_1.genEspIdfCpp;
126
+ case 'webserver':
127
+ return cppCodeWebserver_1.genWebserverCpp;
128
+ }
129
+ };
987
130
  const postProcessCppCode = (code) => code
988
131
  .split('\n')
989
132
  .map((line) => line.trimEnd())
@@ -991,27 +134,7 @@ const postProcessCppCode = (code) => code
991
134
  .map((line) => (line === '//' ? '' : line))
992
135
  .join('\n')
993
136
  .replaceAll(/\n{2,}/g, '\n');
994
- const createHandlebarsHelpers = () => {
995
- let switchValue;
996
- return {
997
- ifeq: function (a, b, options) {
998
- if (a === b)
999
- return options.fn(this);
1000
- return options.inverse(this);
1001
- },
1002
- switch: function (value, options) {
1003
- switchValue = value;
1004
- return options.fn(this);
1005
- },
1006
- case: function (value, options) {
1007
- if (value === switchValue)
1008
- return options.fn(this);
1009
- return options.inverse(this);
1010
- }
1011
- };
1012
- };
1013
137
  const getCppCode = (sources, filesByExtension, options) => {
1014
- const template = (0, handlebars_1.compile)(getTemplate(options.engine));
1015
138
  const transformedSources = sources.map((s) => {
1016
139
  const effectiveCacheTime = s.mime === 'text/html'
1017
140
  ? (options.cachetimeHtml ?? options.cachetime)
@@ -1042,7 +165,6 @@ const getCppCode = (sources, filesByExtension, options) => {
1042
165
  isPsychic: options.engine === 'psychic',
1043
166
  maxUriHandlers: (sources.length + 5).toString()
1044
167
  };
1045
- const rawCode = template(templateData, { helpers: createHandlebarsHelpers() });
1046
- return postProcessCppCode(rawCode);
168
+ return postProcessCppCode(getGenerator(options.engine)(templateData));
1047
169
  };
1048
170
  exports.getCppCode = getCppCode;