svelteesp32 1.0.4 → 1.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/README.md +31 -9
- package/dist/commandLine.d.ts +1 -0
- package/dist/commandLine.js +5 -0
- package/dist/cppCode.d.ts +1 -0
- package/dist/cppCode.js +25 -5
- package/dist/index.js +8 -3
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ In order to be able to easily update OTA, it is important - from the users' poin
|
|
|
10
10
|
|
|
11
11
|
This npm package provides a solution for **inserting any JS client application into the ESP32 web server** (PsychicHttp is my favorite, faster than ESPAsyncWebServer). For this, JS, html, css, font, assets, etc. files must be converted to binary byte array. Npm mode is easy to use and easy to **integrate into your CI/CD pipeline**.
|
|
12
12
|
|
|
13
|
+
> Starting with version v1.1.0, the ETag header is also supported.
|
|
14
|
+
|
|
13
15
|
### Usage
|
|
14
16
|
|
|
15
17
|
**Install package** as devDependency (it is practical if the package is part of the project so that you always receive updates)
|
|
@@ -21,7 +23,7 @@ npm install -D svelteesp32
|
|
|
21
23
|
After a successful Svelte build (rollup/webpack/vite) **create an includeable c++ header** file
|
|
22
24
|
|
|
23
25
|
```bash
|
|
24
|
-
npx svelteesp32 -s ../svelteapp/dist -o ../esp32project/svelteesp32.h
|
|
26
|
+
npx svelteesp32 -s ../svelteapp/dist -o ../esp32project/svelteesp32.h --etag
|
|
25
27
|
```
|
|
26
28
|
|
|
27
29
|
During the **translation process**, the processed file details are visible, and at the end, the result shows the ESP32's memory allocation (gzip size)
|
|
@@ -73,10 +75,17 @@ The content of **generated file** (do not edit, just use)
|
|
|
73
75
|
```c
|
|
74
76
|
const uint8_t data0[12547] = {0x1f, 0x8b, 0x8, 0x0, ...
|
|
75
77
|
const uint8_t data1[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ...
|
|
78
|
+
const char * etag0 = "387b88e345cc56ef9091...";
|
|
76
79
|
|
|
77
80
|
void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
78
81
|
server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request)
|
|
79
82
|
{
|
|
83
|
+
if (request->hasHeader("If-None-Match") && ...) {
|
|
84
|
+
PsychicResponse response304(request);
|
|
85
|
+
response304.setCode(304);
|
|
86
|
+
return response304.send();
|
|
87
|
+
}
|
|
88
|
+
|
|
80
89
|
PsychicResponse response(request);
|
|
81
90
|
response.setContentType("application/javascript");
|
|
82
91
|
response.addHeader("Content-Encoding", "gzip");
|
|
@@ -86,6 +95,10 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
|
86
95
|
|
|
87
96
|
server->on("/assets/index-Soe6cpLA.css", HTTP_GET, [](PsychicRequest * request)
|
|
88
97
|
{
|
|
98
|
+
if (request->hasHeader("If-None-Match") && ...) {
|
|
99
|
+
...
|
|
100
|
+
}
|
|
101
|
+
|
|
89
102
|
PsychicResponse response(request);
|
|
90
103
|
response.setContentType("text/css");
|
|
91
104
|
response.addHeader("Content-Encoding", "gzip");
|
|
@@ -101,23 +114,32 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
|
101
114
|
|
|
102
115
|
All modern browsers have been able to handle gzip-compressed content for years. For this reason, there is no question that the easily compressed JS and CSS files are stored compressed in the ESP32 and sent to the browser.
|
|
103
116
|
|
|
104
|
-
During the translation process, data in gzip format is generated and will be used if the size is greater than 100 bytes and we experience a reduction of at least 15
|
|
117
|
+
During the translation process, data in gzip format is generated and will be used if the **size is greater than 100 bytes** and we experience a **reduction of at least 15%**. In such a case, the compressed data is unconditionally sent to the browser with the appropriate **Content-Encoding** header information.
|
|
105
118
|
|
|
106
119
|
Automatic compression can be turned off with the `--no-gzip` option.
|
|
107
120
|
|
|
121
|
+
### ETag
|
|
122
|
+
|
|
123
|
+
The ETag HTTP header can be used to significantly reduce network traffic. If the server sends ETag information, the client can check the integrity of the file by sending back this ETag (in `If-None-Match` header) without sending the data back again. All browsers use this function, the 304 HTTP response code is clearly visible in the network traffic.
|
|
124
|
+
|
|
125
|
+
Since ESP32 data traffic is moderately expensive, it is an individual decision whether to use the ETag or not. We **recommend using ETag**, which adds a bit more code (about 1-3%) but results in a much cleaner operation.
|
|
126
|
+
|
|
127
|
+
The use of ETag is **not enabled by default**, this can be achieved with the `--etag` switch.
|
|
128
|
+
|
|
108
129
|
### Main entry point - index.html
|
|
109
130
|
|
|
110
131
|
Typically, the entry point for web applications is the **index.htm or index.html** file. This does not need to be listed in the browser's address bar because web servers know that this file should be served by default. Svelteesp32 also does this: if there is an index.htm or index.html file, it sets it as the main file to be served. So using `http://esp32_xxx.local` or just entering the `http://x.y.w.z/` IP address will serve this main file.
|
|
111
132
|
|
|
112
133
|
### Command line options
|
|
113
134
|
|
|
114
|
-
| Option |
|
|
115
|
-
| ------------- |
|
|
116
|
-
| `-s` |
|
|
117
|
-
| `-o` |
|
|
118
|
-
| `--
|
|
119
|
-
| `--
|
|
120
|
-
|
|
|
135
|
+
| Option | Required | Description | default |
|
|
136
|
+
| ------------- | :------: | ---------------------------------------------- | ----------------------- |
|
|
137
|
+
| `-s` | x | Source dist folder contains compiled web files | |
|
|
138
|
+
| `-o` | x | Generated output file with path | `svelteesp32.h` |
|
|
139
|
+
| `--etag` | | Use ETag header for cache | false |
|
|
140
|
+
| `--no-gzip` | | Do not compress content with gzip | false -> gzip used |
|
|
141
|
+
| `--espmethod` | x | Name of generated method | `initSvelteStaticFiles` |
|
|
142
|
+
| `-h` | | Show help | |
|
|
121
143
|
|
|
122
144
|
### Q&A
|
|
123
145
|
|
package/dist/commandLine.d.ts
CHANGED
package/dist/commandLine.js
CHANGED
|
@@ -20,6 +20,11 @@ exports.cmdLine = (0, ts_command_line_args_1.parse)({
|
|
|
20
20
|
description: 'Do not compress content with gzip',
|
|
21
21
|
defaultValue: false
|
|
22
22
|
},
|
|
23
|
+
etag: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
description: 'Use ETAG header for cache',
|
|
26
|
+
defaultValue: false
|
|
27
|
+
},
|
|
23
28
|
espmethod: {
|
|
24
29
|
type: String,
|
|
25
30
|
description: 'Name of generated method',
|
package/dist/cppCode.d.ts
CHANGED
package/dist/cppCode.js
CHANGED
|
@@ -7,12 +7,20 @@ const replaceAll = (s, from, to) => {
|
|
|
7
7
|
s = s.replace(from, to);
|
|
8
8
|
return s;
|
|
9
9
|
};
|
|
10
|
+
const blockTemplateETag = `if (request->hasHeader("If-None-Match") && request->header("If-None-Match") == String(etag$index$)) {
|
|
11
|
+
PsychicResponse response304(request);
|
|
12
|
+
response304.setCode(304);
|
|
13
|
+
return response304.send();
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
10
16
|
const blockTemplate = `
|
|
11
17
|
$default$server->on("/$filename$", HTTP_GET, [](PsychicRequest * request)
|
|
12
18
|
{
|
|
19
|
+
${commandLine_1.cmdLine.etag ? blockTemplateETag : ''}
|
|
13
20
|
PsychicResponse response(request);
|
|
14
21
|
response.setContentType("$mime$");
|
|
15
22
|
response.addHeader("Content-Encoding", "$encoding$");
|
|
23
|
+
${commandLine_1.cmdLine.etag ? `response.addHeader("ETag", etag$index$);` : ''}
|
|
16
24
|
response.setContent(data$index$, sizeof(data$index$));
|
|
17
25
|
return response.send();
|
|
18
26
|
});
|
|
@@ -27,22 +35,34 @@ const getCppBlock = (source) => {
|
|
|
27
35
|
result = replaceAll(result, '$encoding$', source.isGzip ? 'gzip' : 'identity');
|
|
28
36
|
return result;
|
|
29
37
|
};
|
|
30
|
-
const fileTemplate =
|
|
31
|
-
$
|
|
38
|
+
const fileTemplate = `//cmdline: $commandline$
|
|
39
|
+
//created: $now$
|
|
40
|
+
//files: $filecount$
|
|
41
|
+
//memory: $filesize$
|
|
42
|
+
|
|
43
|
+
$filedataarrays$
|
|
44
|
+
$hashaarrays$
|
|
32
45
|
|
|
33
46
|
void $method$(PsychicHttpServer * server) {
|
|
34
47
|
$code$
|
|
35
48
|
}`;
|
|
36
49
|
const getCppCode = (sources) => {
|
|
37
|
-
const
|
|
50
|
+
const fileDataArrays = [];
|
|
51
|
+
const hashArrays = [];
|
|
38
52
|
const blocks = [];
|
|
39
53
|
for (const source of sources) {
|
|
40
54
|
const bytesString = [...source.content].map((v) => `0x${v.toString(16)}`).join(', ');
|
|
41
|
-
|
|
55
|
+
fileDataArrays.push(`const uint8_t data${source.index}[${source.content.length}] = {${bytesString}};`);
|
|
56
|
+
hashArrays.push(`const char * etag${source.index} = "${source.md5}";`);
|
|
42
57
|
blocks.push(getCppBlock(source));
|
|
43
58
|
}
|
|
44
59
|
let result = fileTemplate;
|
|
45
|
-
result = replaceAll(result, '$
|
|
60
|
+
result = replaceAll(result, '$commandline$', process.argv.slice(2).join(' '));
|
|
61
|
+
result = replaceAll(result, '$now$', `${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}`);
|
|
62
|
+
result = replaceAll(result, '$filecount$', sources.length.toString());
|
|
63
|
+
result = replaceAll(result, '$filesize$', sources.reduce((previous, current) => previous + current.content.length, 0).toString());
|
|
64
|
+
result = replaceAll(result, '$filedataarrays$', fileDataArrays.join('\n'));
|
|
65
|
+
result = replaceAll(result, '$hashaarrays$', commandLine_1.cmdLine.etag ? hashArrays.join('\n') : '');
|
|
46
66
|
result = replaceAll(result, '$method$', commandLine_1.cmdLine.espmethod);
|
|
47
67
|
result = replaceAll(result, '$code$', blocks.join(''));
|
|
48
68
|
return result;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_crypto_1 = require("node:crypto");
|
|
3
4
|
const node_fs_1 = require("node:fs");
|
|
4
5
|
const node_path_1 = require("node:path");
|
|
5
6
|
const node_zlib_1 = require("node:zlib");
|
|
@@ -19,6 +20,7 @@ for (const file of (0, file_1.getFiles)()) {
|
|
|
19
20
|
summary.filecount++;
|
|
20
21
|
console.log(`[${file}]`);
|
|
21
22
|
const rawContent = (0, node_fs_1.readFileSync)((0, node_path_1.join)(commandLine_1.cmdLine.sourcepath, file), { flag: 'r' });
|
|
23
|
+
const md5 = (0, node_crypto_1.createHash)('md5').update(rawContent).digest('hex');
|
|
22
24
|
summary.size += rawContent.length;
|
|
23
25
|
if (commandLine_1.cmdLine['no-gzip']) {
|
|
24
26
|
sources.push({
|
|
@@ -26,7 +28,8 @@ for (const file of (0, file_1.getFiles)()) {
|
|
|
26
28
|
filename: file,
|
|
27
29
|
content: rawContent,
|
|
28
30
|
isGzip: false,
|
|
29
|
-
mime
|
|
31
|
+
mime,
|
|
32
|
+
md5
|
|
30
33
|
});
|
|
31
34
|
console.log(`- gzip skipped (${rawContent.length})`);
|
|
32
35
|
}
|
|
@@ -39,7 +42,8 @@ for (const file of (0, file_1.getFiles)()) {
|
|
|
39
42
|
filename: file,
|
|
40
43
|
content: zipContent,
|
|
41
44
|
isGzip: true,
|
|
42
|
-
mime
|
|
45
|
+
mime,
|
|
46
|
+
md5
|
|
43
47
|
});
|
|
44
48
|
console.log(`✓ gzip used (${rawContent.length} -> ${zipContent.length})`);
|
|
45
49
|
}
|
|
@@ -49,7 +53,8 @@ for (const file of (0, file_1.getFiles)()) {
|
|
|
49
53
|
filename: file,
|
|
50
54
|
content: rawContent,
|
|
51
55
|
isGzip: false,
|
|
52
|
-
mime
|
|
56
|
+
mime,
|
|
57
|
+
md5
|
|
53
58
|
});
|
|
54
59
|
console.log(`x gzip unused (${rawContent.length} -> ${zipContent.length})`);
|
|
55
60
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteesp32",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Convert Svelte (or any frontend) JS application to serve it from ESP32 webserver (PsychicHttp)",
|
|
5
5
|
"author": "BCsabaEngine",
|
|
6
6
|
"license": "ISC",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/BCsabaEngine/svelteesp32",
|
|
30
30
|
"scripts": {
|
|
31
|
-
"dev": "nodemon src/index.ts -- -s ./svelte/dist -o ../../../Arduino/EspSvelte/svelteesp32.h",
|
|
31
|
+
"dev": "nodemon src/index.ts -- -s ./svelte/dist -o ../../../Arduino/EspSvelte/svelteesp32.h --etag",
|
|
32
|
+
"dev:local": "nodemon src/index.ts -- -s ./svelte/dist -o ./svelteesp32.h --etag",
|
|
32
33
|
"clean": "tsc --build --clean",
|
|
33
34
|
"build": "tsc --build --clean && tsc --build --force",
|
|
34
35
|
"format:check": "prettier --check .",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
],
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@types/mime-types": "^2.1.4",
|
|
50
|
-
"@types/node": "^20.11.
|
|
51
|
+
"@types/node": "^20.11.19",
|
|
51
52
|
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
|
52
53
|
"@typescript-eslint/parser": "^7.0.1",
|
|
53
54
|
"eslint": "^8.56.0",
|