svelteesp32 1.16.1 → 2.0.1
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 +47 -46
- package/dist/commandLine.d.ts +6 -3
- package/dist/commandLine.js +92 -127
- package/dist/cppCode.js +19 -214
- package/dist/cppCodeEspIdf.d.ts +1 -1
- package/dist/cppCodeEspIdf.js +10 -8
- package/dist/errorMessages.d.ts +1 -1
- package/dist/errorMessages.js +3 -12
- package/dist/file.d.ts +5 -1
- package/dist/file.js +9 -6
- package/dist/index.d.ts +5 -3
- package/dist/index.js +71 -53
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
[Changelog](CHANGELOG.md)
|
|
8
8
|
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="svelteesp32.png" alt="svelteesp32" />
|
|
11
|
+
</p>
|
|
12
|
+
|
|
9
13
|
---
|
|
10
14
|
|
|
11
15
|
## Why SvelteESP32?
|
|
@@ -21,7 +25,7 @@
|
|
|
21
25
|
- **Smart Caching** — Built-in SHA256 ETags deliver HTTP 304 responses, slashing bandwidth on constrained devices.
|
|
22
26
|
- **CI/CD Ready** — Simple npm package that slots into any build pipeline.
|
|
23
27
|
- **Zero Runtime Overhead** — Data served directly from flash. No filesystem reads, no RAM allocation.
|
|
24
|
-
- **
|
|
28
|
+
- **3 Web Server Engines** — PsychicHttpServer, ESPAsyncWebServer, and native ESP-IDF supported.
|
|
25
29
|
|
|
26
30
|
---
|
|
27
31
|
|
|
@@ -75,13 +79,14 @@ void setup() {
|
|
|
75
79
|
|
|
76
80
|
## What's New
|
|
77
81
|
|
|
82
|
+
- **v2.0.1** — Dependency updates (ESLint v10, eslint-plugin-unicorn 63), improved error cause chaining
|
|
83
|
+
- **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
|
|
78
84
|
- **v1.16.0** — Size budget constraints (`--maxsize`, `--maxgzipsize`)
|
|
79
85
|
- **v1.15.0** — `--basepath` for multiple frontends (e.g., `/admin`, `/app`)
|
|
80
86
|
- **v1.13.0** — npm package variable interpolation in RC files
|
|
81
87
|
- **v1.12.0** — RC file configuration support
|
|
82
88
|
- **v1.11.0** — File exclusion patterns
|
|
83
89
|
- **v1.9.0** — Native ESP-IDF engine
|
|
84
|
-
- **v1.5.0** — PsychicHttp V2 support
|
|
85
90
|
|
|
86
91
|
---
|
|
87
92
|
|
|
@@ -108,9 +113,6 @@ Choose your web server engine:
|
|
|
108
113
|
# PsychicHttpServer (recommended for ESP32)
|
|
109
114
|
npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
110
115
|
|
|
111
|
-
# PsychicHttpServer V2
|
|
112
|
-
npx svelteesp32 -e psychic2 -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
113
|
-
|
|
114
116
|
# ESPAsyncWebServer (ESP32 + ESP8266)
|
|
115
117
|
npx svelteesp32 -e async -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
116
118
|
|
|
@@ -141,7 +143,7 @@ Watch your files get optimized in real-time:
|
|
|
141
143
|
|
|
142
144
|
### ESP32 Integration
|
|
143
145
|
|
|
144
|
-
**PsychicHttpServer (Recommended)**
|
|
146
|
+
**PsychicHttpServer V2 (Recommended)**
|
|
145
147
|
|
|
146
148
|
```c
|
|
147
149
|
#include <PsychicHttp.h>
|
|
@@ -184,14 +186,14 @@ void app_main() {
|
|
|
184
186
|
}
|
|
185
187
|
```
|
|
186
188
|
|
|
187
|
-
Working examples: [Arduino/PlatformIO](demo/esp32) | [ESP-IDF](demo/esp32idf)
|
|
189
|
+
Working examples with LED control via web interface: [Arduino/PlatformIO](demo/esp32) | [ESP-IDF](demo/esp32idf)
|
|
188
190
|
|
|
189
191
|
### What Gets Generated
|
|
190
192
|
|
|
191
193
|
The generated header file includes everything your ESP needs:
|
|
192
194
|
|
|
193
195
|
```c
|
|
194
|
-
//engine: PsychicHttpServer
|
|
196
|
+
//engine: PsychicHttpServer V2
|
|
195
197
|
//config: engine=psychic sourcepath=./dist outputfile=./output.h etag=true gzip=true cachetime=0 espmethod=initSvelteStaticFiles define=SVELTEESP32
|
|
196
198
|
//
|
|
197
199
|
#define SVELTEESP32_COUNT 5
|
|
@@ -232,23 +234,21 @@ const size_t SVELTEESP32_FILE_COUNT = sizeof(SVELTEESP32_FILES) / sizeof(SVELTEE
|
|
|
232
234
|
extern "C" void __attribute__((weak)) SVELTEESP32_onFileServed(const char* path, int statusCode) {}
|
|
233
235
|
|
|
234
236
|
void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
235
|
-
server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request) {
|
|
237
|
+
server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {
|
|
236
238
|
if (request->hasHeader("If-None-Match") &&
|
|
237
239
|
request->header("If-None-Match").equals(etag_assets_index_KwubEIf__js)) {
|
|
238
|
-
|
|
239
|
-
response304.setCode(304);
|
|
240
|
+
response->setCode(304);
|
|
240
241
|
SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 304);
|
|
241
|
-
return
|
|
242
|
+
return response->send();
|
|
242
243
|
}
|
|
243
244
|
|
|
244
|
-
|
|
245
|
-
response
|
|
246
|
-
response
|
|
247
|
-
response
|
|
248
|
-
response
|
|
249
|
-
response.setContent(datagzip_assets_index_KwubEIf__js, 12547);
|
|
245
|
+
response->setContentType("text/javascript");
|
|
246
|
+
response->addHeader("Content-Encoding", "gzip");
|
|
247
|
+
response->addHeader("Cache-Control", "no-cache");
|
|
248
|
+
response->addHeader("ETag", etag_assets_index_KwubEIf__js);
|
|
249
|
+
response->setContent(datagzip_assets_index_KwubEIf__js, 12547);
|
|
250
250
|
SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 200);
|
|
251
|
-
return response
|
|
251
|
+
return response->send();
|
|
252
252
|
});
|
|
253
253
|
|
|
254
254
|
// ... more routes
|
|
@@ -259,14 +259,13 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
|
259
259
|
|
|
260
260
|
## Supported Web Server Engines
|
|
261
261
|
|
|
262
|
-
| Engine
|
|
263
|
-
|
|
|
264
|
-
| **
|
|
265
|
-
| **
|
|
266
|
-
| **
|
|
267
|
-
| **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only |
|
|
262
|
+
| Engine | Flag | Best For | Platform |
|
|
263
|
+
| ------------------------ | ------------ | ---------------------------- | --------------- |
|
|
264
|
+
| **PsychicHttpServer V2** | `-e psychic` | Maximum performance | ESP32 only |
|
|
265
|
+
| **ESPAsyncWebServer** | `-e async` | Cross-platform compatibility | ESP32 + ESP8266 |
|
|
266
|
+
| **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only |
|
|
268
267
|
|
|
269
|
-
**Recommendation:** For ESP32-only projects, use PsychicHttpServer for the fastest, most stable experience.
|
|
268
|
+
**Recommendation:** For ESP32-only projects, use PsychicHttpServer V2 (`-e psychic`) for the fastest, most stable experience.
|
|
270
269
|
|
|
271
270
|
**Note:** For PsychicHttp, configure `server.config.max_uri_handlers` to match your file count.
|
|
272
271
|
|
|
@@ -289,7 +288,7 @@ Reduce bandwidth dramatically with HTTP 304 "Not Modified" responses. When a bro
|
|
|
289
288
|
- **Minimal overhead** — adds ~1-3% code size for significant bandwidth savings
|
|
290
289
|
- **Compiler mode** — use `--etag=compiler` and control via `-D SVELTEESP32_ENABLE_ETAG`
|
|
291
290
|
|
|
292
|
-
All
|
|
291
|
+
All three engines support full ETag validation.
|
|
293
292
|
|
|
294
293
|
### Browser Cache Control
|
|
295
294
|
|
|
@@ -403,24 +402,25 @@ Called for every response (200 = content served, 304 = cache hit).
|
|
|
403
402
|
|
|
404
403
|
## CLI Reference
|
|
405
404
|
|
|
406
|
-
| Option | Description
|
|
407
|
-
| ---------------- |
|
|
408
|
-
| `-s` | Source folder with compiled web files
|
|
409
|
-
| `-e` | Web server engine (psychic/
|
|
410
|
-
| `-o` | Output header file path
|
|
411
|
-
| `--etag` | ETag caching (true/false/compiler)
|
|
412
|
-
| `--gzip` | Gzip compression (true/false/compiler)
|
|
413
|
-
| `--exclude` | Exclude files by glob pattern
|
|
414
|
-
| `--basepath` | URL prefix for all routes
|
|
415
|
-
| `--maxsize` | Max total uncompressed size (e.g., `400k`, `1m`)
|
|
416
|
-
| `--maxgzipsize` | Max total gzip size (e.g., `150k`, `500k`)
|
|
417
|
-
| `--cachetime` | Cache-Control max-age in seconds
|
|
418
|
-
| `--version` | Version string in header
|
|
419
|
-
| `--define` | C++ define prefix
|
|
420
|
-
| `--espmethod` | Init function name
|
|
421
|
-
| `--config` | Custom RC file path
|
|
422
|
-
| `--
|
|
423
|
-
|
|
|
405
|
+
| Option | Description | Default |
|
|
406
|
+
| ---------------- | ------------------------------------------------ | ----------------------- |
|
|
407
|
+
| `-s` | Source folder with compiled web files | (required) |
|
|
408
|
+
| `-e` | Web server engine (psychic/async/espidf) | `psychic` |
|
|
409
|
+
| `-o` | Output header file path | `svelteesp32.h` |
|
|
410
|
+
| `--etag` | ETag caching (true/false/compiler) | `false` |
|
|
411
|
+
| `--gzip` | Gzip compression (true/false/compiler) | `true` |
|
|
412
|
+
| `--exclude` | Exclude files by glob pattern | System files |
|
|
413
|
+
| `--basepath` | URL prefix for all routes | (none) |
|
|
414
|
+
| `--maxsize` | Max total uncompressed size (e.g., `400k`, `1m`) | (none) |
|
|
415
|
+
| `--maxgzipsize` | Max total gzip size (e.g., `150k`, `500k`) | (none) |
|
|
416
|
+
| `--cachetime` | Cache-Control max-age in seconds | `0` |
|
|
417
|
+
| `--version` | Version string in header | (none) |
|
|
418
|
+
| `--define` | C++ define prefix | `SVELTEESP32` |
|
|
419
|
+
| `--espmethod` | Init function name | `initSvelteStaticFiles` |
|
|
420
|
+
| `--config` | Custom RC file path | `.svelteesp32rc.json` |
|
|
421
|
+
| `--dryrun` | Show summary without writing output | `false` |
|
|
422
|
+
| `--noindexcheck` | Skip index.html validation | `false` |
|
|
423
|
+
| `-h` | Show help | |
|
|
424
424
|
|
|
425
425
|
---
|
|
426
426
|
|
|
@@ -439,7 +439,8 @@ Store your settings in `.svelteesp32rc.json` for zero-argument builds:
|
|
|
439
439
|
"basepath": "/ui",
|
|
440
440
|
"maxsize": "400k",
|
|
441
441
|
"maxgzipsize": "150k",
|
|
442
|
-
"noindexcheck": false
|
|
442
|
+
"noindexcheck": false,
|
|
443
|
+
"dryrun": false
|
|
443
444
|
}
|
|
444
445
|
```
|
|
445
446
|
|
package/dist/commandLine.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
interface ICopyFilesArguments {
|
|
2
|
-
engine: 'psychic' | '
|
|
2
|
+
engine: 'psychic' | 'async' | 'espidf';
|
|
3
3
|
sourcepath: string;
|
|
4
4
|
outputfile: string;
|
|
5
5
|
espmethod: string;
|
|
@@ -14,10 +14,11 @@ interface ICopyFilesArguments {
|
|
|
14
14
|
maxSize?: number;
|
|
15
15
|
maxGzipSize?: number;
|
|
16
16
|
noIndexCheck?: boolean;
|
|
17
|
+
dryRun?: boolean;
|
|
17
18
|
help?: boolean;
|
|
18
19
|
}
|
|
19
20
|
interface IRcFileConfig {
|
|
20
|
-
engine?: 'psychic' | '
|
|
21
|
+
engine?: 'psychic' | 'async' | 'espidf';
|
|
21
22
|
sourcepath?: string;
|
|
22
23
|
outputfile?: string;
|
|
23
24
|
espmethod?: string;
|
|
@@ -32,11 +33,13 @@ interface IRcFileConfig {
|
|
|
32
33
|
maxsize?: number | string;
|
|
33
34
|
maxgzipsize?: number | string;
|
|
34
35
|
noindexcheck?: boolean;
|
|
36
|
+
dryrun?: boolean;
|
|
35
37
|
}
|
|
38
|
+
declare function validateCppIdentifier(value: string, name: string): string;
|
|
36
39
|
declare function parseSize(value: string, name: string): number;
|
|
37
40
|
declare function getNpmPackageVariable(packageJson: Record<string, unknown>, variableName: string): string | undefined;
|
|
38
41
|
declare function hasNpmVariables(config: IRcFileConfig): boolean;
|
|
39
42
|
declare function interpolateNpmVariables(config: IRcFileConfig, rcFilePath: string): IRcFileConfig;
|
|
40
|
-
export { getNpmPackageVariable, hasNpmVariables, interpolateNpmVariables, parseSize };
|
|
43
|
+
export { getNpmPackageVariable, hasNpmVariables, interpolateNpmVariables, parseSize, validateCppIdentifier };
|
|
41
44
|
export declare function formatConfiguration(cmdLine: ICopyFilesArguments): string;
|
|
42
45
|
export declare const cmdLine: ICopyFilesArguments;
|
package/dist/commandLine.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.getNpmPackageVariable = getNpmPackageVariable;
|
|
|
8
8
|
exports.hasNpmVariables = hasNpmVariables;
|
|
9
9
|
exports.interpolateNpmVariables = interpolateNpmVariables;
|
|
10
10
|
exports.parseSize = parseSize;
|
|
11
|
+
exports.validateCppIdentifier = validateCppIdentifier;
|
|
11
12
|
exports.formatConfiguration = formatConfiguration;
|
|
12
13
|
const node_fs_1 = require("node:fs");
|
|
13
14
|
const node_os_1 = require("node:os");
|
|
@@ -23,7 +24,7 @@ Configuration:
|
|
|
23
24
|
|
|
24
25
|
Options:
|
|
25
26
|
-e, --engine <value> The engine for which the include file is created
|
|
26
|
-
(psychic|
|
|
27
|
+
(psychic|async|espidf) (default: "psychic")
|
|
27
28
|
-s, --sourcepath <path> Source dist folder contains compiled web files (required)
|
|
28
29
|
-o, --outputfile <path> Generated output file with path (default: "svelteesp32.h")
|
|
29
30
|
--etag <value> Use ETAG header for cache (true|false|compiler) (default: "false")
|
|
@@ -38,6 +39,7 @@ Options:
|
|
|
38
39
|
--basepath <path> URL prefix for all routes (e.g., "/ui") (default: "")
|
|
39
40
|
--maxsize <size> Maximum total uncompressed size (e.g., 400k, 1.5m, 409600)
|
|
40
41
|
--maxgzipsize <size> Maximum total gzip size (e.g., 150k, 1m, 153600)
|
|
42
|
+
--dryrun Show summary without writing the output file (default: false)
|
|
41
43
|
-h, --help Shows this help
|
|
42
44
|
|
|
43
45
|
RC File:
|
|
@@ -64,7 +66,7 @@ RC File:
|
|
|
64
66
|
process.exit(0);
|
|
65
67
|
}
|
|
66
68
|
function validateEngine(value) {
|
|
67
|
-
if (value === 'psychic' || value === '
|
|
69
|
+
if (value === 'psychic' || value === 'async' || value === 'espidf')
|
|
68
70
|
return value;
|
|
69
71
|
console.error((0, errorMessages_1.getInvalidEngineError)(value));
|
|
70
72
|
process.exit(1);
|
|
@@ -74,6 +76,11 @@ function validateTriState(value, name) {
|
|
|
74
76
|
return value;
|
|
75
77
|
throw new Error(`Invalid ${name}: ${value}`);
|
|
76
78
|
}
|
|
79
|
+
function validateCppIdentifier(value, name) {
|
|
80
|
+
if (!/^[A-Z_a-z]\w*$/.test(value))
|
|
81
|
+
throw new Error(`${name} must be a valid C++ identifier (letters, digits, underscores, not starting with a digit): ${value}`);
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
77
84
|
function validateBasePath(value) {
|
|
78
85
|
if (value === '')
|
|
79
86
|
return value;
|
|
@@ -144,7 +151,9 @@ function parsePackageJson(packageJsonPath) {
|
|
|
144
151
|
return JSON.parse(content);
|
|
145
152
|
}
|
|
146
153
|
catch (error) {
|
|
147
|
-
throw new Error(`Failed to parse package.json at ${packageJsonPath}: ${error.message}
|
|
154
|
+
throw new Error(`Failed to parse package.json at ${packageJsonPath}: ${error.message}`, {
|
|
155
|
+
cause: error
|
|
156
|
+
});
|
|
148
157
|
}
|
|
149
158
|
}
|
|
150
159
|
function getNpmPackageVariable(packageJson, variableName) {
|
|
@@ -235,7 +244,7 @@ function loadRcFile(rcPath) {
|
|
|
235
244
|
}
|
|
236
245
|
catch (error) {
|
|
237
246
|
if (error instanceof SyntaxError)
|
|
238
|
-
throw new Error(`Invalid JSON in RC file ${rcPath}: ${error.message}
|
|
247
|
+
throw new Error(`Invalid JSON in RC file ${rcPath}: ${error.message}`, { cause: error });
|
|
239
248
|
throw error;
|
|
240
249
|
}
|
|
241
250
|
}
|
|
@@ -258,7 +267,8 @@ function validateRcConfig(config, rcPath) {
|
|
|
258
267
|
'basepath',
|
|
259
268
|
'maxsize',
|
|
260
269
|
'maxgzipsize',
|
|
261
|
-
'noindexcheck'
|
|
270
|
+
'noindexcheck',
|
|
271
|
+
'dryrun'
|
|
262
272
|
]);
|
|
263
273
|
for (const key of Object.keys(configObject))
|
|
264
274
|
if (!validKeys.has(key))
|
|
@@ -269,9 +279,16 @@ function validateRcConfig(config, rcPath) {
|
|
|
269
279
|
configObject['etag'] = validateTriState(configObject['etag'], 'etag');
|
|
270
280
|
if (configObject['gzip'] !== undefined)
|
|
271
281
|
configObject['gzip'] = validateTriState(configObject['gzip'], 'gzip');
|
|
272
|
-
if (configObject['
|
|
273
|
-
(
|
|
274
|
-
|
|
282
|
+
if (configObject['espmethod'] !== undefined)
|
|
283
|
+
validateCppIdentifier(configObject['espmethod'], 'espmethod');
|
|
284
|
+
if (configObject['define'] !== undefined)
|
|
285
|
+
validateCppIdentifier(configObject['define'], 'define');
|
|
286
|
+
if (configObject['cachetime'] !== undefined) {
|
|
287
|
+
if (typeof configObject['cachetime'] !== 'number' || Number.isNaN(configObject['cachetime']))
|
|
288
|
+
throw new TypeError(`Invalid cachetime in RC file: ${configObject['cachetime']}`);
|
|
289
|
+
if (configObject['cachetime'] < 0)
|
|
290
|
+
throw new TypeError(`Invalid cachetime in RC file: ${configObject['cachetime']} (must be non-negative)`);
|
|
291
|
+
}
|
|
275
292
|
if (configObject['exclude'] !== undefined) {
|
|
276
293
|
if (!Array.isArray(configObject['exclude']))
|
|
277
294
|
throw new TypeError("'exclude' in RC file must be an array");
|
|
@@ -305,6 +322,8 @@ function validateRcConfig(config, rcPath) {
|
|
|
305
322
|
}
|
|
306
323
|
if (configObject['noindexcheck'] !== undefined && typeof configObject['noindexcheck'] !== 'boolean')
|
|
307
324
|
throw new TypeError(`Invalid noindexcheck in RC file: ${configObject['noindexcheck']} (must be boolean)`);
|
|
325
|
+
if (configObject['dryrun'] !== undefined && typeof configObject['dryrun'] !== 'boolean')
|
|
326
|
+
throw new TypeError(`Invalid dryrun in RC file: ${configObject['dryrun']} (must be boolean)`);
|
|
308
327
|
return configObject;
|
|
309
328
|
}
|
|
310
329
|
function parseArguments() {
|
|
@@ -368,9 +387,67 @@ function parseArguments() {
|
|
|
368
387
|
result.maxGzipSize = rcConfig.maxgzipsize;
|
|
369
388
|
if (rcConfig.noindexcheck !== undefined)
|
|
370
389
|
result.noIndexCheck = rcConfig.noindexcheck;
|
|
390
|
+
if (rcConfig.dryrun !== undefined)
|
|
391
|
+
result.dryRun = rcConfig.dryrun;
|
|
371
392
|
if (rcConfig.exclude && rcConfig.exclude.length > 0)
|
|
372
393
|
result.exclude = [...rcConfig.exclude];
|
|
373
394
|
const cliExclude = [];
|
|
395
|
+
function applyFlag(flagName, value) {
|
|
396
|
+
switch (flagName) {
|
|
397
|
+
case 'config':
|
|
398
|
+
break;
|
|
399
|
+
case 'engine':
|
|
400
|
+
result.engine = validateEngine(value);
|
|
401
|
+
break;
|
|
402
|
+
case 'sourcepath':
|
|
403
|
+
result.sourcepath = value;
|
|
404
|
+
break;
|
|
405
|
+
case 'outputfile':
|
|
406
|
+
result.outputfile = value;
|
|
407
|
+
break;
|
|
408
|
+
case 'etag':
|
|
409
|
+
result.etag = validateTriState(value, 'etag');
|
|
410
|
+
break;
|
|
411
|
+
case 'gzip':
|
|
412
|
+
result.gzip = validateTriState(value, 'gzip');
|
|
413
|
+
break;
|
|
414
|
+
case 'version':
|
|
415
|
+
result.version = value;
|
|
416
|
+
break;
|
|
417
|
+
case 'espmethod':
|
|
418
|
+
result.espmethod = validateCppIdentifier(value, 'espmethod');
|
|
419
|
+
break;
|
|
420
|
+
case 'define':
|
|
421
|
+
result.define = validateCppIdentifier(value, 'define');
|
|
422
|
+
break;
|
|
423
|
+
case 'cachetime':
|
|
424
|
+
result.cachetime = Number.parseInt(value, 10);
|
|
425
|
+
if (Number.isNaN(result.cachetime))
|
|
426
|
+
throw new TypeError(`Invalid cachetime: ${value}`);
|
|
427
|
+
if (result.cachetime < 0)
|
|
428
|
+
throw new TypeError(`Invalid cachetime: ${value} (must be non-negative)`);
|
|
429
|
+
break;
|
|
430
|
+
case 'exclude': {
|
|
431
|
+
const patterns = value
|
|
432
|
+
.split(',')
|
|
433
|
+
.map((p) => p.trim())
|
|
434
|
+
.filter(Boolean);
|
|
435
|
+
cliExclude.push(...patterns);
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
case 'basepath':
|
|
439
|
+
result.basePath = validateBasePath(value);
|
|
440
|
+
break;
|
|
441
|
+
case 'maxsize':
|
|
442
|
+
result.maxSize = parseSize(value, '--maxsize');
|
|
443
|
+
break;
|
|
444
|
+
case 'maxgzipsize':
|
|
445
|
+
result.maxGzipSize = parseSize(value, '--maxgzipsize');
|
|
446
|
+
break;
|
|
447
|
+
default:
|
|
448
|
+
throw new Error(`Unknown flag: --${flagName}`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
374
451
|
for (let index = 0; index < arguments_.length; index++) {
|
|
375
452
|
const argument = arguments_[index];
|
|
376
453
|
if (!argument)
|
|
@@ -383,59 +460,7 @@ function parseArguments() {
|
|
|
383
460
|
const value = parts.slice(1).join('=');
|
|
384
461
|
if (!flag || !value)
|
|
385
462
|
throw new Error(`Invalid argument format: ${argument}`);
|
|
386
|
-
|
|
387
|
-
switch (flagName) {
|
|
388
|
-
case 'config':
|
|
389
|
-
break;
|
|
390
|
-
case 'engine':
|
|
391
|
-
result.engine = validateEngine(value);
|
|
392
|
-
break;
|
|
393
|
-
case 'sourcepath':
|
|
394
|
-
result.sourcepath = value;
|
|
395
|
-
break;
|
|
396
|
-
case 'outputfile':
|
|
397
|
-
result.outputfile = value;
|
|
398
|
-
break;
|
|
399
|
-
case 'etag':
|
|
400
|
-
result.etag = validateTriState(value, 'etag');
|
|
401
|
-
break;
|
|
402
|
-
case 'gzip':
|
|
403
|
-
result.gzip = validateTriState(value, 'gzip');
|
|
404
|
-
break;
|
|
405
|
-
case 'version':
|
|
406
|
-
result.version = value;
|
|
407
|
-
break;
|
|
408
|
-
case 'espmethod':
|
|
409
|
-
result.espmethod = value;
|
|
410
|
-
break;
|
|
411
|
-
case 'define':
|
|
412
|
-
result.define = value;
|
|
413
|
-
break;
|
|
414
|
-
case 'cachetime':
|
|
415
|
-
result.cachetime = Number.parseInt(value, 10);
|
|
416
|
-
if (Number.isNaN(result.cachetime))
|
|
417
|
-
throw new TypeError(`Invalid cachetime: ${value}`);
|
|
418
|
-
break;
|
|
419
|
-
case 'exclude': {
|
|
420
|
-
const patterns = value
|
|
421
|
-
.split(',')
|
|
422
|
-
.map((p) => p.trim())
|
|
423
|
-
.filter(Boolean);
|
|
424
|
-
cliExclude.push(...patterns);
|
|
425
|
-
break;
|
|
426
|
-
}
|
|
427
|
-
case 'basepath':
|
|
428
|
-
result.basePath = validateBasePath(value);
|
|
429
|
-
break;
|
|
430
|
-
case 'maxsize':
|
|
431
|
-
result.maxSize = parseSize(value, '--maxsize');
|
|
432
|
-
break;
|
|
433
|
-
case 'maxgzipsize':
|
|
434
|
-
result.maxGzipSize = parseSize(value, '--maxgzipsize');
|
|
435
|
-
break;
|
|
436
|
-
default:
|
|
437
|
-
throw new Error(`Unknown flag: ${flag}`);
|
|
438
|
-
}
|
|
463
|
+
applyFlag(flag.slice(2), value);
|
|
439
464
|
continue;
|
|
440
465
|
}
|
|
441
466
|
if (argument === '--created') {
|
|
@@ -446,6 +471,10 @@ function parseArguments() {
|
|
|
446
471
|
result.noIndexCheck = true;
|
|
447
472
|
continue;
|
|
448
473
|
}
|
|
474
|
+
if (argument === '--dryrun' || argument === '--dry-run') {
|
|
475
|
+
result.dryRun = true;
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
449
478
|
if (argument.startsWith('-') && !argument.startsWith('--')) {
|
|
450
479
|
const flag = argument.slice(1);
|
|
451
480
|
const nextArgument = arguments_[index + 1];
|
|
@@ -474,72 +503,8 @@ function parseArguments() {
|
|
|
474
503
|
const nextArgument = arguments_[index + 1];
|
|
475
504
|
if (!nextArgument || nextArgument.startsWith('-'))
|
|
476
505
|
throw new Error(`Missing value for flag: ${argument}`);
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
index++;
|
|
480
|
-
break;
|
|
481
|
-
case 'engine':
|
|
482
|
-
result.engine = validateEngine(nextArgument);
|
|
483
|
-
index++;
|
|
484
|
-
break;
|
|
485
|
-
case 'sourcepath':
|
|
486
|
-
result.sourcepath = nextArgument;
|
|
487
|
-
index++;
|
|
488
|
-
break;
|
|
489
|
-
case 'outputfile':
|
|
490
|
-
result.outputfile = nextArgument;
|
|
491
|
-
index++;
|
|
492
|
-
break;
|
|
493
|
-
case 'etag':
|
|
494
|
-
result.etag = validateTriState(nextArgument, 'etag');
|
|
495
|
-
index++;
|
|
496
|
-
break;
|
|
497
|
-
case 'gzip':
|
|
498
|
-
result.gzip = validateTriState(nextArgument, 'gzip');
|
|
499
|
-
index++;
|
|
500
|
-
break;
|
|
501
|
-
case 'version':
|
|
502
|
-
result.version = nextArgument;
|
|
503
|
-
index++;
|
|
504
|
-
break;
|
|
505
|
-
case 'espmethod':
|
|
506
|
-
result.espmethod = nextArgument;
|
|
507
|
-
index++;
|
|
508
|
-
break;
|
|
509
|
-
case 'define':
|
|
510
|
-
result.define = nextArgument;
|
|
511
|
-
index++;
|
|
512
|
-
break;
|
|
513
|
-
case 'cachetime':
|
|
514
|
-
result.cachetime = Number.parseInt(nextArgument, 10);
|
|
515
|
-
if (Number.isNaN(result.cachetime))
|
|
516
|
-
throw new TypeError(`Invalid cachetime: ${nextArgument}`);
|
|
517
|
-
index++;
|
|
518
|
-
break;
|
|
519
|
-
case 'exclude': {
|
|
520
|
-
const patterns = nextArgument
|
|
521
|
-
.split(',')
|
|
522
|
-
.map((p) => p.trim())
|
|
523
|
-
.filter(Boolean);
|
|
524
|
-
cliExclude.push(...patterns);
|
|
525
|
-
index++;
|
|
526
|
-
break;
|
|
527
|
-
}
|
|
528
|
-
case 'basepath':
|
|
529
|
-
result.basePath = validateBasePath(nextArgument);
|
|
530
|
-
index++;
|
|
531
|
-
break;
|
|
532
|
-
case 'maxsize':
|
|
533
|
-
result.maxSize = parseSize(nextArgument, '--maxsize');
|
|
534
|
-
index++;
|
|
535
|
-
break;
|
|
536
|
-
case 'maxgzipsize':
|
|
537
|
-
result.maxGzipSize = parseSize(nextArgument, '--maxgzipsize');
|
|
538
|
-
index++;
|
|
539
|
-
break;
|
|
540
|
-
default:
|
|
541
|
-
throw new Error(`Unknown flag: --${flag}`);
|
|
542
|
-
}
|
|
506
|
+
applyFlag(flag, nextArgument);
|
|
507
|
+
index++;
|
|
543
508
|
continue;
|
|
544
509
|
}
|
|
545
510
|
throw new Error(`Unknown argument: ${argument}`);
|
package/dist/cppCode.js
CHANGED
|
@@ -120,212 +120,6 @@ extern "C" void __attribute__((weak)) {{definePrefix}}_onFileServed(const char*
|
|
|
120
120
|
const psychicTemplate = `
|
|
121
121
|
//engine: PsychicHttpServer
|
|
122
122
|
//config: {{{config}}}
|
|
123
|
-
//You should use server.config.max_uri_handlers = {{fileCount}}; or higher value to proper handles all files
|
|
124
|
-
{{#if created }}
|
|
125
|
-
//created: {{now}}
|
|
126
|
-
{{/if}}
|
|
127
|
-
//
|
|
128
|
-
${commonHeaderSection}
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
#include <Arduino.h>
|
|
132
|
-
#include <PsychicHttp.h>
|
|
133
|
-
#include <PsychicHttpsServer.h>
|
|
134
|
-
|
|
135
|
-
//
|
|
136
|
-
${dataArraysSection(false)}
|
|
137
|
-
|
|
138
|
-
//
|
|
139
|
-
${etagArraysSection}
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
${manifestSection}
|
|
143
|
-
|
|
144
|
-
//
|
|
145
|
-
${hookSection}
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
// Http Handlers
|
|
149
|
-
void {{methodName}}(PsychicHttpServer * server) {
|
|
150
|
-
{{#each sources}}
|
|
151
|
-
//
|
|
152
|
-
// {{this.filename}}
|
|
153
|
-
{{#if this.isDefault}}{{#unless ../basePath}}server->defaultEndpoint = {{/unless}}{{/if}}server->on("{{../basePath}}/{{this.filename}}", HTTP_GET, [](PsychicRequest * request) {
|
|
154
|
-
|
|
155
|
-
{{#switch ../etag}}
|
|
156
|
-
{{#case "true"}}
|
|
157
|
-
if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
158
|
-
PsychicResponse response304(request);
|
|
159
|
-
response304.setCode(304);
|
|
160
|
-
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
161
|
-
return response304.send();
|
|
162
|
-
}
|
|
163
|
-
{{/case}}
|
|
164
|
-
{{#case "compiler"}}
|
|
165
|
-
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
166
|
-
if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
167
|
-
PsychicResponse response304(request);
|
|
168
|
-
response304.setCode(304);
|
|
169
|
-
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 304);
|
|
170
|
-
return response304.send();
|
|
171
|
-
}
|
|
172
|
-
#endif
|
|
173
|
-
{{/case}}
|
|
174
|
-
{{/switch}}
|
|
175
|
-
|
|
176
|
-
PsychicResponse response(request);
|
|
177
|
-
response.setContentType("{{this.mime}}");
|
|
178
|
-
|
|
179
|
-
{{#switch ../gzip}}
|
|
180
|
-
{{#case "true"}}
|
|
181
|
-
{{#if this.isGzip}}
|
|
182
|
-
response.addHeader("Content-Encoding", "gzip");
|
|
183
|
-
{{/if}}
|
|
184
|
-
{{/case}}
|
|
185
|
-
{{#case "compiler"}}
|
|
186
|
-
{{#if this.isGzip}}
|
|
187
|
-
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
188
|
-
response.addHeader("Content-Encoding", "gzip");
|
|
189
|
-
#endif
|
|
190
|
-
{{/if}}
|
|
191
|
-
{{/case}}
|
|
192
|
-
{{/switch}}
|
|
193
|
-
|
|
194
|
-
{{#switch ../etag}}
|
|
195
|
-
{{#case "true"}}
|
|
196
|
-
{{#../cacheTime}}
|
|
197
|
-
response.addHeader("Cache-Control", "max-age={{value}}");
|
|
198
|
-
{{/../cacheTime}}
|
|
199
|
-
{{^../cacheTime}}
|
|
200
|
-
response.addHeader("Cache-Control", "no-cache");
|
|
201
|
-
{{/../cacheTime}}
|
|
202
|
-
response.addHeader("ETag", etag_{{this.dataname}});
|
|
203
|
-
{{/case}}
|
|
204
|
-
{{#case "compiler"}}
|
|
205
|
-
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
206
|
-
{{#../cacheTime}}
|
|
207
|
-
response.addHeader("Cache-Control", "max-age={{value}}");
|
|
208
|
-
{{/../cacheTime}}
|
|
209
|
-
{{^../cacheTime}}
|
|
210
|
-
response.addHeader("Cache-Control", "no-cache");
|
|
211
|
-
{{/../cacheTime}}
|
|
212
|
-
response.addHeader("ETag", etag_{{this.dataname}});
|
|
213
|
-
#endif
|
|
214
|
-
{{/case}}
|
|
215
|
-
{{/switch}}
|
|
216
|
-
|
|
217
|
-
{{#switch ../gzip}}
|
|
218
|
-
{{#case "true"}}
|
|
219
|
-
response.setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
220
|
-
{{/case}}
|
|
221
|
-
{{#case "false"}}
|
|
222
|
-
response.setContent(data_{{this.dataname}}, {{this.length}});
|
|
223
|
-
{{/case}}
|
|
224
|
-
{{#case "compiler"}}
|
|
225
|
-
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
226
|
-
response.setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
227
|
-
#else
|
|
228
|
-
response.setContent(data_{{this.dataname}}, {{this.length}});
|
|
229
|
-
#endif
|
|
230
|
-
{{/case}}
|
|
231
|
-
{{/switch}}
|
|
232
|
-
|
|
233
|
-
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
|
|
234
|
-
return response.send();
|
|
235
|
-
});
|
|
236
|
-
{{#if this.isDefault}}{{#if ../basePath}}
|
|
237
|
-
//
|
|
238
|
-
// {{this.filename}} (base path route)
|
|
239
|
-
server->on("{{../basePath}}", HTTP_GET, [](PsychicRequest * request) {
|
|
240
|
-
|
|
241
|
-
{{#switch ../etag}}
|
|
242
|
-
{{#case "true"}}
|
|
243
|
-
if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
244
|
-
PsychicResponse response304(request);
|
|
245
|
-
response304.setCode(304);
|
|
246
|
-
{{../definePrefix}}_onFileServed("{{../basePath}}", 304);
|
|
247
|
-
return response304.send();
|
|
248
|
-
}
|
|
249
|
-
{{/case}}
|
|
250
|
-
{{#case "compiler"}}
|
|
251
|
-
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
252
|
-
if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_{{this.dataname}})) {
|
|
253
|
-
PsychicResponse response304(request);
|
|
254
|
-
response304.setCode(304);
|
|
255
|
-
{{../definePrefix}}_onFileServed("{{../basePath}}", 304);
|
|
256
|
-
return response304.send();
|
|
257
|
-
}
|
|
258
|
-
#endif
|
|
259
|
-
{{/case}}
|
|
260
|
-
{{/switch}}
|
|
261
|
-
|
|
262
|
-
PsychicResponse response(request);
|
|
263
|
-
response.setContentType("{{this.mime}}");
|
|
264
|
-
|
|
265
|
-
{{#switch ../gzip}}
|
|
266
|
-
{{#case "true"}}
|
|
267
|
-
{{#if this.isGzip}}
|
|
268
|
-
response.addHeader("Content-Encoding", "gzip");
|
|
269
|
-
{{/if}}
|
|
270
|
-
{{/case}}
|
|
271
|
-
{{#case "compiler"}}
|
|
272
|
-
{{#if this.isGzip}}
|
|
273
|
-
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
274
|
-
response.addHeader("Content-Encoding", "gzip");
|
|
275
|
-
#endif
|
|
276
|
-
{{/if}}
|
|
277
|
-
{{/case}}
|
|
278
|
-
{{/switch}}
|
|
279
|
-
|
|
280
|
-
{{#switch ../etag}}
|
|
281
|
-
{{#case "true"}}
|
|
282
|
-
{{#../cacheTime}}
|
|
283
|
-
response.addHeader("Cache-Control", "max-age={{value}}");
|
|
284
|
-
{{/../cacheTime}}
|
|
285
|
-
{{^../cacheTime}}
|
|
286
|
-
response.addHeader("Cache-Control", "no-cache");
|
|
287
|
-
{{/../cacheTime}}
|
|
288
|
-
response.addHeader("ETag", etag_{{this.dataname}});
|
|
289
|
-
{{/case}}
|
|
290
|
-
{{#case "compiler"}}
|
|
291
|
-
#ifdef {{../definePrefix}}_ENABLE_ETAG
|
|
292
|
-
{{#../cacheTime}}
|
|
293
|
-
response.addHeader("Cache-Control", "max-age={{value}}");
|
|
294
|
-
{{/../cacheTime}}
|
|
295
|
-
{{^../cacheTime}}
|
|
296
|
-
response.addHeader("Cache-Control", "no-cache");
|
|
297
|
-
{{/../cacheTime}}
|
|
298
|
-
response.addHeader("ETag", etag_{{this.dataname}});
|
|
299
|
-
#endif
|
|
300
|
-
{{/case}}
|
|
301
|
-
{{/switch}}
|
|
302
|
-
|
|
303
|
-
{{#switch ../gzip}}
|
|
304
|
-
{{#case "true"}}
|
|
305
|
-
response.setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
306
|
-
{{/case}}
|
|
307
|
-
{{#case "false"}}
|
|
308
|
-
response.setContent(data_{{this.dataname}}, {{this.length}});
|
|
309
|
-
{{/case}}
|
|
310
|
-
{{#case "compiler"}}
|
|
311
|
-
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
312
|
-
response.setContent(datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
313
|
-
#else
|
|
314
|
-
response.setContent(data_{{this.dataname}}, {{this.length}});
|
|
315
|
-
#endif
|
|
316
|
-
{{/case}}
|
|
317
|
-
{{/switch}}
|
|
318
|
-
|
|
319
|
-
{{../definePrefix}}_onFileServed("{{../basePath}}", 200);
|
|
320
|
-
return response.send();
|
|
321
|
-
});
|
|
322
|
-
{{/if}}{{/if}}
|
|
323
|
-
|
|
324
|
-
{{/each}}
|
|
325
|
-
}`;
|
|
326
|
-
const psychic2Template = `
|
|
327
|
-
//engine: PsychicHttpServerV2
|
|
328
|
-
//config: {{{config}}}
|
|
329
123
|
{{#if created }}
|
|
330
124
|
//created: {{now}}
|
|
331
125
|
{{/if}}
|
|
@@ -704,21 +498,29 @@ const getTemplate = (engine) => {
|
|
|
704
498
|
switch (engine) {
|
|
705
499
|
case 'psychic':
|
|
706
500
|
return psychicTemplate;
|
|
707
|
-
case '
|
|
708
|
-
return
|
|
501
|
+
case 'async':
|
|
502
|
+
return asyncTemplate;
|
|
709
503
|
case 'espidf':
|
|
710
504
|
return cppCodeEspIdf_1.espidfTemplate;
|
|
711
505
|
default:
|
|
712
|
-
|
|
506
|
+
throw new Error(`Unknown engine: ${engine}`);
|
|
713
507
|
}
|
|
714
508
|
};
|
|
509
|
+
const bufferToByteString = (buffer) => {
|
|
510
|
+
if (buffer.length === 0)
|
|
511
|
+
return '';
|
|
512
|
+
let result = buffer[0].toString(10);
|
|
513
|
+
for (let index = 1; index < buffer.length; index++)
|
|
514
|
+
result += ',' + buffer[index].toString(10);
|
|
515
|
+
return result;
|
|
516
|
+
};
|
|
715
517
|
const transformSourceToTemplateData = (s, etag) => ({
|
|
716
518
|
...s,
|
|
717
519
|
length: s.content.length,
|
|
718
|
-
bytes:
|
|
520
|
+
bytes: bufferToByteString(s.content),
|
|
719
521
|
lengthGzip: s.contentGzip.length,
|
|
720
|
-
bytesGzip:
|
|
721
|
-
isDefault: s.filename.
|
|
522
|
+
bytesGzip: bufferToByteString(s.contentGzip),
|
|
523
|
+
isDefault: s.filename === 'index.html' || s.filename === 'index.htm',
|
|
722
524
|
gzipSizeForManifest: s.isGzip ? s.contentGzip.length : 0,
|
|
723
525
|
etagForManifest: etag === 'false' ? 'NULL' : `etag_${s.dataname}`
|
|
724
526
|
});
|
|
@@ -728,7 +530,7 @@ const postProcessCppCode = (code) => code
|
|
|
728
530
|
.filter(Boolean)
|
|
729
531
|
.map((line) => (line === '//' ? '' : line))
|
|
730
532
|
.join('\n')
|
|
731
|
-
.replace(
|
|
533
|
+
.replace(/\n{2,}/g, '\n');
|
|
732
534
|
const createHandlebarsHelpers = () => {
|
|
733
535
|
let switchValue;
|
|
734
536
|
return {
|
|
@@ -752,7 +554,10 @@ const getCppCode = (sources, filesByExtension) => {
|
|
|
752
554
|
const template = (0, handlebars_1.compile)(getTemplate(commandLine_1.cmdLine.engine));
|
|
753
555
|
const templateData = {
|
|
754
556
|
config: (0, commandLine_1.formatConfiguration)(commandLine_1.cmdLine),
|
|
755
|
-
now:
|
|
557
|
+
now: (() => {
|
|
558
|
+
const d = new Date();
|
|
559
|
+
return `${d.toLocaleDateString()} ${d.toLocaleTimeString()}`;
|
|
560
|
+
})(),
|
|
756
561
|
fileCount: sources.length.toString(),
|
|
757
562
|
fileSize: sources.reduce((previous, current) => previous + current.content.length, 0).toString(),
|
|
758
563
|
fileGzipSize: sources.reduce((previous, current) => previous + current.contentGzip.length, 0).toString(),
|
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 char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };\n {{/each}}\n{{/case}}\n{{#case \"false\"}}\n {{#each sources}}\nconst char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };\n {{/each}}\n{{/case}}\n{{#case \"compiler\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n {{#each sources}}\nconst char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };\n {{/each}}\n#else\n {{#each sources}}\nconst 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 (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 (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, datagzip_{{this.dataname}}, {{this.lengthGzip}});\n{{/case}}\n{{#case \"false\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n httpd_resp_send(req, 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, datagzip_{{this.dataname}}, {{this.lengthGzip}});\n #else\n httpd_resp_send(req, 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\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}";
|
package/dist/cppCodeEspIdf.js
CHANGED
|
@@ -63,22 +63,22 @@ exports.espidfTemplate = `
|
|
|
63
63
|
{{#switch gzip}}
|
|
64
64
|
{{#case "true"}}
|
|
65
65
|
{{#each sources}}
|
|
66
|
-
const char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };
|
|
66
|
+
const unsigned char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };
|
|
67
67
|
{{/each}}
|
|
68
68
|
{{/case}}
|
|
69
69
|
{{#case "false"}}
|
|
70
70
|
{{#each sources}}
|
|
71
|
-
const char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };
|
|
71
|
+
const unsigned char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };
|
|
72
72
|
{{/each}}
|
|
73
73
|
{{/case}}
|
|
74
74
|
{{#case "compiler"}}
|
|
75
75
|
#ifdef {{definePrefix}}_ENABLE_GZIP
|
|
76
76
|
{{#each sources}}
|
|
77
|
-
const char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };
|
|
77
|
+
const unsigned char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };
|
|
78
78
|
{{/each}}
|
|
79
79
|
#else
|
|
80
80
|
{{#each sources}}
|
|
81
|
-
const char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };
|
|
81
|
+
const unsigned char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };
|
|
82
82
|
{{/each}}
|
|
83
83
|
#endif
|
|
84
84
|
{{/case}}
|
|
@@ -131,6 +131,7 @@ static esp_err_t file_handler_{{this.datanameUpperCase}} (httpd_req_t *req)
|
|
|
131
131
|
size_t hdr_len = httpd_req_get_hdr_value_len(req, "If-None-Match");
|
|
132
132
|
if (hdr_len > 0) {
|
|
133
133
|
char* hdr_value = malloc(hdr_len + 1);
|
|
134
|
+
if (hdr_value == NULL) { httpd_resp_send_500(req); return ESP_FAIL; }
|
|
134
135
|
if (httpd_req_get_hdr_value_str(req, "If-None-Match", hdr_value, hdr_len + 1) == ESP_OK) {
|
|
135
136
|
if (strcmp(hdr_value, etag_{{this.dataname}}) == 0) {
|
|
136
137
|
free(hdr_value);
|
|
@@ -148,6 +149,7 @@ static esp_err_t file_handler_{{this.datanameUpperCase}} (httpd_req_t *req)
|
|
|
148
149
|
size_t hdr_len = httpd_req_get_hdr_value_len(req, "If-None-Match");
|
|
149
150
|
if (hdr_len > 0) {
|
|
150
151
|
char* hdr_value = malloc(hdr_len + 1);
|
|
152
|
+
if (hdr_value == NULL) { httpd_resp_send_500(req); return ESP_FAIL; }
|
|
151
153
|
if (httpd_req_get_hdr_value_str(req, "If-None-Match", hdr_value, hdr_len + 1) == ESP_OK) {
|
|
152
154
|
if (strcmp(hdr_value, etag_{{this.dataname}}) == 0) {
|
|
153
155
|
free(hdr_value);
|
|
@@ -204,18 +206,18 @@ static esp_err_t file_handler_{{this.datanameUpperCase}} (httpd_req_t *req)
|
|
|
204
206
|
{{#switch ../gzip}}
|
|
205
207
|
{{#case "true"}}
|
|
206
208
|
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
|
|
207
|
-
httpd_resp_send(req, datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
209
|
+
httpd_resp_send(req, (const char *)datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
208
210
|
{{/case}}
|
|
209
211
|
{{#case "false"}}
|
|
210
212
|
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
|
|
211
|
-
httpd_resp_send(req, data_{{this.dataname}}, {{this.length}});
|
|
213
|
+
httpd_resp_send(req, (const char *)data_{{this.dataname}}, {{this.length}});
|
|
212
214
|
{{/case}}
|
|
213
215
|
{{#case "compiler"}}
|
|
214
216
|
{{../definePrefix}}_onFileServed("{{../basePath}}/{{this.filename}}", 200);
|
|
215
217
|
#ifdef {{../definePrefix}}_ENABLE_GZIP
|
|
216
|
-
httpd_resp_send(req, datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
218
|
+
httpd_resp_send(req, (const char *)datagzip_{{this.dataname}}, {{this.lengthGzip}});
|
|
217
219
|
#else
|
|
218
|
-
httpd_resp_send(req, data_{{this.dataname}}, {{this.length}});
|
|
220
|
+
httpd_resp_send(req, (const char *)data_{{this.dataname}}, {{this.length}});
|
|
219
221
|
#endif
|
|
220
222
|
{{/case}}
|
|
221
223
|
{{/switch}}
|
package/dist/errorMessages.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ export declare function getMissingIndexError(engine: string): string;
|
|
|
2
2
|
export declare function getInvalidEngineError(attempted: string): string;
|
|
3
3
|
export declare function getSourcepathNotFoundError(sourcepath: string, reason: 'not_found' | 'not_directory'): string;
|
|
4
4
|
export declare function getSizeBudgetExceededError(type: 'size' | 'gzipSize', limit: number, actual: number): string;
|
|
5
|
-
export declare function getMaxUriHandlersHint(engine: string, routeCount: number): string;
|
|
5
|
+
export declare function getMaxUriHandlersHint(engine: string, routeCount: number, espmethod?: string): string;
|
package/dist/errorMessages.js
CHANGED
|
@@ -13,7 +13,6 @@ const consoleColor_1 = require("./consoleColor");
|
|
|
13
13
|
function getEngineName(engine) {
|
|
14
14
|
const names = {
|
|
15
15
|
psychic: 'PsychicHttpServer',
|
|
16
|
-
psychic2: 'PsychicHttpServer V2',
|
|
17
16
|
async: 'ESPAsyncWebServer',
|
|
18
17
|
espidf: 'ESP-IDF'
|
|
19
18
|
};
|
|
@@ -24,9 +23,6 @@ function getMissingIndexError(engine) {
|
|
|
24
23
|
psychic: ` 1. Add an index.html file to your source directory
|
|
25
24
|
2. The file will automatically be set as the default route ("/")
|
|
26
25
|
3. PsychicHttpServer uses: server->defaultEndpoint = ...`,
|
|
27
|
-
psychic2: ` 1. Add an index.html file to your source directory
|
|
28
|
-
2. The file will automatically be set as the default route ("/")
|
|
29
|
-
3. PsychicHttpServer V2 uses: server->defaultEndpoint = ...`,
|
|
30
26
|
async: ` 1. Add an index.html file to your source directory
|
|
31
27
|
2. The file will automatically create a "/" route handler
|
|
32
28
|
3. ESPAsyncWebServer uses: server.on("/", HTTP_GET, ...)`,
|
|
@@ -55,7 +51,6 @@ function getInvalidEngineError(attempted) {
|
|
|
55
51
|
|
|
56
52
|
Valid engines are:
|
|
57
53
|
${(0, consoleColor_1.cyanLog)('• psychic')} - PsychicHttpServer (ESP32 only, fastest performance)
|
|
58
|
-
${(0, consoleColor_1.cyanLog)('• psychic2')} - PsychicHttpServer V2 (ESP32 only, modern API)
|
|
59
54
|
${(0, consoleColor_1.cyanLog)('• async')} - ESPAsyncWebServer (ESP32/ESP8266 compatible)
|
|
60
55
|
${(0, consoleColor_1.cyanLog)('• espidf')} - Native ESP-IDF web server (ESP32 only, no Arduino)
|
|
61
56
|
|
|
@@ -136,22 +131,18 @@ CI integration:
|
|
|
136
131
|
This non-zero exit code allows build pipelines to fail early when
|
|
137
132
|
the frontend exceeds allocated flash space.`);
|
|
138
133
|
}
|
|
139
|
-
function getMaxUriHandlersHint(engine, routeCount) {
|
|
134
|
+
function getMaxUriHandlersHint(engine, routeCount, espmethod = 'initSvelteStaticFiles') {
|
|
140
135
|
const recommended = routeCount + 5;
|
|
141
136
|
const hints = {
|
|
142
137
|
psychic: `PsychicHttpServer server;
|
|
143
138
|
server.config.max_uri_handlers = ${recommended}; // Default is 8, you need at least ${routeCount}
|
|
144
|
-
|
|
145
|
-
server.listen(80);`,
|
|
146
|
-
psychic2: `PsychicHttpServer server;
|
|
147
|
-
server.config.max_uri_handlers = ${recommended}; // Default is 8, you need at least ${routeCount}
|
|
148
|
-
initSvelteStaticFiles(&server);
|
|
139
|
+
${espmethod}(&server);
|
|
149
140
|
server.listen(80);`,
|
|
150
141
|
espidf: `httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
151
142
|
config.max_uri_handlers = ${recommended}; // Default is 8, you need at least ${routeCount}
|
|
152
143
|
httpd_handle_t server = NULL;
|
|
153
144
|
httpd_start(&server, &config);
|
|
154
|
-
|
|
145
|
+
${espmethod}(server);`
|
|
155
146
|
};
|
|
156
147
|
const hint = hints[engine];
|
|
157
148
|
if (!hint)
|
package/dist/file.d.ts
CHANGED
package/dist/file.js
CHANGED
|
@@ -14,13 +14,12 @@ const consoleColor_1 = require("./consoleColor");
|
|
|
14
14
|
const errorMessages_1 = require("./errorMessages");
|
|
15
15
|
const findSimilarFiles = (files) => {
|
|
16
16
|
const contentComparer = new Map();
|
|
17
|
-
for (const [filename,
|
|
18
|
-
const
|
|
19
|
-
const existingFiles = contentComparer.get(hash);
|
|
17
|
+
for (const [filename, fileData] of files.entries()) {
|
|
18
|
+
const existingFiles = contentComparer.get(fileData.hash);
|
|
20
19
|
if (existingFiles)
|
|
21
20
|
existingFiles.push(filename);
|
|
22
21
|
else
|
|
23
|
-
contentComparer.set(hash, [filename]);
|
|
22
|
+
contentComparer.set(fileData.hash, [filename]);
|
|
24
23
|
}
|
|
25
24
|
const result = [];
|
|
26
25
|
for (const filenames of contentComparer.values())
|
|
@@ -30,7 +29,7 @@ const findSimilarFiles = (files) => {
|
|
|
30
29
|
};
|
|
31
30
|
const shouldSkipFile = (filename, allFilenames) => {
|
|
32
31
|
const extension = node_path_1.default.extname(filename);
|
|
33
|
-
const compressedExtensions = ['.gz', '.
|
|
32
|
+
const compressedExtensions = ['.gz', '.brotli', '.br'];
|
|
34
33
|
if (compressedExtensions.includes(extension)) {
|
|
35
34
|
const original = filename.slice(0, -1 * extension.length);
|
|
36
35
|
if (allFilenames.includes(original)) {
|
|
@@ -72,10 +71,14 @@ const getFiles = () => {
|
|
|
72
71
|
console.log((0, consoleColor_1.cyanLog)(`... and ${excludedFiles.length - displayLimit} more`));
|
|
73
72
|
console.log();
|
|
74
73
|
}
|
|
74
|
+
else if (excludePatterns.length > 0)
|
|
75
|
+
console.log((0, consoleColor_1.yellowLog)(`Warning: --exclude patterns matched no files: ${excludePatterns.join(', ')}`));
|
|
75
76
|
const result = new Map();
|
|
76
77
|
for (const filename of filenames) {
|
|
77
78
|
const filePath = node_path_1.default.join(commandLine_1.cmdLine.sourcepath, filename);
|
|
78
|
-
|
|
79
|
+
const content = (0, node_fs_1.readFileSync)(filePath, { flag: 'r' });
|
|
80
|
+
const hash = (0, node_crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
81
|
+
result.set(filename, { content, hash });
|
|
79
82
|
}
|
|
80
83
|
const duplicates = findSimilarFiles(result);
|
|
81
84
|
for (const sameFiles of duplicates)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { CppCodeSource } from './cppCode';
|
|
1
|
+
import { CppCodeSource, ExtensionGroups } from './cppCode';
|
|
2
2
|
declare const shouldUseGzip: (originalSize: number, compressedSize: number) => boolean;
|
|
3
3
|
declare const calculateCompressionRatio: (originalSize: number, compressedSize: number) => number;
|
|
4
4
|
declare const formatCompressionLog: (filename: string, padding: string, originalSize: number, compressedSize: number, useGzip: boolean) => string;
|
|
5
|
+
declare const formatSize: (bytes: number) => string;
|
|
5
6
|
declare const createSourceEntry: (filename: string, dataname: string, content: Buffer, contentGzip: Buffer, mimeType: string, sha256: string, isGzip: boolean) => CppCodeSource;
|
|
6
|
-
declare const updateExtensionGroup: (extension: string) => void;
|
|
7
|
-
export
|
|
7
|
+
declare const updateExtensionGroup: (filesByExtension: ExtensionGroups, extension: string) => void;
|
|
8
|
+
export declare function main(): void;
|
|
9
|
+
export { calculateCompressionRatio, createSourceEntry, formatCompressionLog, formatSize, shouldUseGzip, updateExtensionGroup };
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.updateExtensionGroup = exports.shouldUseGzip = exports.formatCompressionLog = exports.createSourceEntry = exports.calculateCompressionRatio = void 0;
|
|
7
|
-
|
|
6
|
+
exports.updateExtensionGroup = exports.shouldUseGzip = exports.formatSize = exports.formatCompressionLog = exports.createSourceEntry = exports.calculateCompressionRatio = void 0;
|
|
7
|
+
exports.main = main;
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const node_zlib_1 = require("node:zlib");
|
|
@@ -16,13 +16,6 @@ const errorMessages_1 = require("./errorMessages");
|
|
|
16
16
|
const file_1 = require("./file");
|
|
17
17
|
const GZIP_MIN_SIZE = 1024;
|
|
18
18
|
const GZIP_MIN_REDUCTION_RATIO = 0.85;
|
|
19
|
-
const summary = {
|
|
20
|
-
filecount: 0,
|
|
21
|
-
size: 0,
|
|
22
|
-
gzipsize: 0
|
|
23
|
-
};
|
|
24
|
-
const sources = [];
|
|
25
|
-
const filesByExtension = [];
|
|
26
19
|
const shouldUseGzip = (originalSize, compressedSize) => originalSize > GZIP_MIN_SIZE && compressedSize < originalSize * GZIP_MIN_REDUCTION_RATIO;
|
|
27
20
|
exports.shouldUseGzip = shouldUseGzip;
|
|
28
21
|
const calculateCompressionRatio = (originalSize, compressedSize) => Math.round((compressedSize / originalSize) * 100);
|
|
@@ -36,6 +29,12 @@ const formatCompressionLog = (filename, padding, originalSize, compressedSize, u
|
|
|
36
29
|
return (0, consoleColor_1.yellowLog)(` [${filename}] ${padding} x gzip unused ${tooSmall}${sizeInfo}`);
|
|
37
30
|
};
|
|
38
31
|
exports.formatCompressionLog = formatCompressionLog;
|
|
32
|
+
const formatSize = (bytes) => {
|
|
33
|
+
if (bytes < 1024)
|
|
34
|
+
return `${bytes}B`;
|
|
35
|
+
return `${Math.round(bytes / 1024)}kB`;
|
|
36
|
+
};
|
|
37
|
+
exports.formatSize = formatSize;
|
|
39
38
|
const createSourceEntry = (filename, dataname, content, contentGzip, mimeType, sha256, isGzip) => ({
|
|
40
39
|
filename,
|
|
41
40
|
dataname,
|
|
@@ -47,7 +46,7 @@ const createSourceEntry = (filename, dataname, content, contentGzip, mimeType, s
|
|
|
47
46
|
sha256
|
|
48
47
|
});
|
|
49
48
|
exports.createSourceEntry = createSourceEntry;
|
|
50
|
-
const updateExtensionGroup = (extension) => {
|
|
49
|
+
const updateExtensionGroup = (filesByExtension, extension) => {
|
|
51
50
|
const group = filesByExtension.find((fe) => fe.extension === extension);
|
|
52
51
|
if (group)
|
|
53
52
|
group.count += 1;
|
|
@@ -55,47 +54,66 @@ const updateExtensionGroup = (extension) => {
|
|
|
55
54
|
filesByExtension.push({ extension, count: 1 });
|
|
56
55
|
};
|
|
57
56
|
exports.updateExtensionGroup = updateExtensionGroup;
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
57
|
+
function main() {
|
|
58
|
+
const summary = {
|
|
59
|
+
filecount: 0,
|
|
60
|
+
size: 0,
|
|
61
|
+
gzipsize: 0
|
|
62
|
+
};
|
|
63
|
+
const sources = [];
|
|
64
|
+
const filesByExtension = [];
|
|
65
|
+
console.log('Collecting source files');
|
|
66
|
+
const files = (0, file_1.getFiles)();
|
|
67
|
+
if (files.size === 0) {
|
|
68
|
+
console.error(`Directory ${commandLine_1.cmdLine.sourcepath} is empty`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
console.log();
|
|
72
|
+
console.log('Translation to header file');
|
|
73
|
+
const longestFilename = [...files.keys()].reduce((p, c) => Math.max(c.length, p), 0);
|
|
74
|
+
for (const [originalFilename, fileData] of files) {
|
|
75
|
+
const { content, hash: sha256 } = fileData;
|
|
76
|
+
const mimeType = (0, mime_types_1.lookup)(originalFilename) || 'text/plain';
|
|
77
|
+
if (!(0, mime_types_1.lookup)(originalFilename))
|
|
78
|
+
console.log((0, consoleColor_1.yellowLog)(` [${originalFilename}] unknown MIME type for extension '${node_path_1.default.extname(originalFilename)}', using text/plain`));
|
|
79
|
+
summary.filecount++;
|
|
80
|
+
const filename = originalFilename.replace(/\\/g, '/');
|
|
81
|
+
let dataname = filename.replace(/\W/g, '_');
|
|
82
|
+
if (/^\d/.test(dataname))
|
|
83
|
+
dataname = '_' + dataname;
|
|
84
|
+
let extension = node_path_1.default.extname(filename).toUpperCase();
|
|
85
|
+
if (extension.startsWith('.'))
|
|
86
|
+
extension = extension.slice(1);
|
|
87
|
+
updateExtensionGroup(filesByExtension, extension);
|
|
88
|
+
summary.size += content.length;
|
|
89
|
+
const zipContent = (0, node_zlib_1.gzipSync)(content, { level: 9 });
|
|
90
|
+
const useGzip = shouldUseGzip(content.length, zipContent.length);
|
|
91
|
+
summary.gzipsize += useGzip ? zipContent.length : content.length;
|
|
92
|
+
sources.push(createSourceEntry(filename, dataname, content, zipContent, mimeType, sha256, useGzip));
|
|
93
|
+
const padding = ' '.repeat(longestFilename - originalFilename.length);
|
|
94
|
+
console.log(formatCompressionLog(originalFilename, padding, content.length, zipContent.length, useGzip));
|
|
95
|
+
}
|
|
96
|
+
console.log('');
|
|
97
|
+
filesByExtension.sort((left, right) => left.extension.localeCompare(right.extension));
|
|
98
|
+
if (commandLine_1.cmdLine.maxSize !== undefined && summary.size > commandLine_1.cmdLine.maxSize) {
|
|
99
|
+
console.error((0, errorMessages_1.getSizeBudgetExceededError)('size', commandLine_1.cmdLine.maxSize, summary.size));
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
if (commandLine_1.cmdLine.maxGzipSize !== undefined && summary.gzipsize > commandLine_1.cmdLine.maxGzipSize) {
|
|
103
|
+
console.error((0, errorMessages_1.getSizeBudgetExceededError)('gzipSize', commandLine_1.cmdLine.maxGzipSize, summary.gzipsize));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
if (commandLine_1.cmdLine.dryRun) {
|
|
107
|
+
console.log(`[DRY RUN] ${summary.filecount} files, ${formatSize(summary.size)} original size, ${formatSize(summary.gzipsize)} gzip size`);
|
|
108
|
+
console.log(`[DRY RUN] Would write to ${commandLine_1.cmdLine.outputfile}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const cppFile = (0, cppCode_1.getCppCode)(sources, filesByExtension);
|
|
112
|
+
(0, node_fs_1.mkdirSync)(node_path_1.default.normalize(node_path_1.default.dirname(commandLine_1.cmdLine.outputfile)), { recursive: true });
|
|
113
|
+
(0, node_fs_1.writeFileSync)(commandLine_1.cmdLine.outputfile, cppFile, { flush: true, encoding: 'utf8' });
|
|
114
|
+
console.log(`${summary.filecount} files, ${formatSize(summary.size)} original size, ${formatSize(summary.gzipsize)} gzip size`);
|
|
115
|
+
console.log(`${commandLine_1.cmdLine.outputfile} ${formatSize(cppFile.length)} size`);
|
|
116
|
+
if (commandLine_1.cmdLine.engine === 'psychic' || commandLine_1.cmdLine.engine === 'espidf')
|
|
117
|
+
console.log('\n' + (0, errorMessages_1.getMaxUriHandlersHint)(commandLine_1.cmdLine.engine, sources.length, commandLine_1.cmdLine.espmethod));
|
|
94
118
|
}
|
|
95
|
-
|
|
96
|
-
(0, node_fs_1.mkdirSync)(node_path_1.default.normalize(node_path_1.default.dirname(commandLine_1.cmdLine.outputfile)), { recursive: true });
|
|
97
|
-
(0, node_fs_1.writeFileSync)(commandLine_1.cmdLine.outputfile, cppFile, { flush: true, encoding: 'utf8' });
|
|
98
|
-
console.log(`${summary.filecount} files, ${Math.round(summary.size / 1024)}kB original size, ${Math.round(summary.gzipsize / 1024)}kB gzip size`);
|
|
99
|
-
console.log(`${commandLine_1.cmdLine.outputfile} ${Math.round(cppFile.length / 1024)}kB size`);
|
|
100
|
-
if (commandLine_1.cmdLine.engine === 'psychic' || commandLine_1.cmdLine.engine === 'psychic2' || commandLine_1.cmdLine.engine === 'espidf')
|
|
101
|
-
console.log('\n' + (0, errorMessages_1.getMaxUriHandlersHint)(commandLine_1.cmdLine.engine, sources.length));
|
|
119
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteesp32",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
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,7 +30,6 @@
|
|
|
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:psychic2": "nodemon src/index.ts -- -e psychic2 -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32.h --etag=false --gzip=false --version=v$npm_package_version",
|
|
34
33
|
"test": "vitest run",
|
|
35
34
|
"test:watch": "vitest",
|
|
36
35
|
"test:coverage": "vitest run --coverage",
|
|
@@ -56,20 +55,21 @@
|
|
|
56
55
|
"esp8266",
|
|
57
56
|
"webserver",
|
|
58
57
|
"psychichttpserver",
|
|
59
|
-
"psychichttpserverV2",
|
|
60
58
|
"espasyncwebserver"
|
|
61
59
|
],
|
|
62
60
|
"devDependencies": {
|
|
61
|
+
"@eslint/eslintrc": "^3.3.3",
|
|
62
|
+
"@eslint/js": "^10.0.0",
|
|
63
63
|
"@types/mime-types": "^3.0.1",
|
|
64
|
-
"@types/node": "^25.
|
|
64
|
+
"@types/node": "^25.3.0",
|
|
65
65
|
"@types/picomatch": "^4.0.2",
|
|
66
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
67
|
-
"@typescript-eslint/parser": "^8.
|
|
66
|
+
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
67
|
+
"@typescript-eslint/parser": "^8.56.0",
|
|
68
68
|
"@vitest/coverage-v8": "^4.0.18",
|
|
69
|
-
"eslint": "^
|
|
69
|
+
"eslint": "^10.0.0",
|
|
70
70
|
"eslint-config-prettier": "^10.1.8",
|
|
71
71
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
72
|
-
"eslint-plugin-unicorn": "^
|
|
72
|
+
"eslint-plugin-unicorn": "^63.0.0",
|
|
73
73
|
"memfs": "^4.56.10",
|
|
74
74
|
"nodemon": "^3.1.11",
|
|
75
75
|
"prettier": "^3.8.1",
|