svelteesp32 1.2.7 → 1.3.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 +60 -9
- package/dist/commandLine.d.ts +1 -0
- package/dist/commandLine.js +5 -0
- package/dist/cppCode.d.ts +10 -3
- package/dist/cppCode.js +39 -31
- package/dist/index.js +22 -8
- package/package.json +13 -9
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ This npm package provides a solution for **inserting any JS client application i
|
|
|
14
14
|
|
|
15
15
|
> Starting with version v1.2.0, ESP8266/ESP8285 is also supported.
|
|
16
16
|
|
|
17
|
+
> Starting with version v1.3.0, c++ defines can be used.
|
|
18
|
+
|
|
17
19
|
### Usage
|
|
18
20
|
|
|
19
21
|
**Install package** as devDependency (it is practical if the package is part of the project so that you always receive updates)
|
|
@@ -98,9 +100,17 @@ void setup()
|
|
|
98
100
|
}
|
|
99
101
|
```
|
|
100
102
|
|
|
103
|
+
You can find a minimal buildable example platformio project in [demo/esp32](demo/esp32) folder.
|
|
104
|
+
|
|
101
105
|
The content of **generated file** (do not edit, just use)
|
|
102
106
|
|
|
103
107
|
```c
|
|
108
|
+
#define SVELTEESP32_COUNT 5
|
|
109
|
+
#define SVELTEESP32_SIZE 145633
|
|
110
|
+
#define SVELTEESP32_FILE_INDEX_HTML
|
|
111
|
+
#define SVELTEESP32_HTML_FILES 1
|
|
112
|
+
...
|
|
113
|
+
|
|
104
114
|
const uint8_t data0[12547] = {0x1f, 0x8b, 0x8, 0x0, ...
|
|
105
115
|
const uint8_t data1[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ...
|
|
106
116
|
const char * etag0 = "387b88e345cc56ef9091...";
|
|
@@ -164,17 +174,58 @@ The use of ETag is **not enabled by default**, this can be achieved with the `--
|
|
|
164
174
|
|
|
165
175
|
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://esp_xxx.local` or just entering the `http://x.y.w.z/` IP address will serve this main file.
|
|
166
176
|
|
|
177
|
+
### C++ defines
|
|
178
|
+
|
|
179
|
+
To make it easy to integrate into a larger c++ project, we have made a couple of variables available as c++ defines.
|
|
180
|
+
|
|
181
|
+
You can use the COUNT and SIZE constants:
|
|
182
|
+
|
|
183
|
+
```c
|
|
184
|
+
...
|
|
185
|
+
#include "svelteesp32.h"
|
|
186
|
+
|
|
187
|
+
#if SVELTEESP32_COUNT != 5
|
|
188
|
+
#error Invalid file count
|
|
189
|
+
#endif
|
|
190
|
+
...
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
You can include a blocker error if a named file accidentally missing from the build:
|
|
194
|
+
|
|
195
|
+
```c
|
|
196
|
+
...
|
|
197
|
+
#include "svelteesp32.h"
|
|
198
|
+
|
|
199
|
+
#ifndef SVELTEESP32_FILE_INDEX_HTML
|
|
200
|
+
#error Missing index file
|
|
201
|
+
#endif
|
|
202
|
+
...
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
...or if there are too many of a file type:
|
|
206
|
+
|
|
207
|
+
```c
|
|
208
|
+
...
|
|
209
|
+
#include "svelteesp32.h"
|
|
210
|
+
|
|
211
|
+
#if SVELTEESP32_CSS_FILES > 1
|
|
212
|
+
#error Too many CSS files
|
|
213
|
+
#endif
|
|
214
|
+
...
|
|
215
|
+
```
|
|
216
|
+
|
|
167
217
|
### Command line options
|
|
168
218
|
|
|
169
|
-
| Option |
|
|
170
|
-
| ------------- |
|
|
171
|
-
| `-
|
|
172
|
-
| `-
|
|
173
|
-
| `-o` |
|
|
174
|
-
| `--etag` |
|
|
175
|
-
| `--no-gzip` |
|
|
176
|
-
| `--espmethod` |
|
|
177
|
-
|
|
|
219
|
+
| Option | Description | default |
|
|
220
|
+
| ------------- | --------------------------------------------------------- | ----------------------- | ------- |
|
|
221
|
+
| `-s` | **Source dist folder contains compiled web files** | |
|
|
222
|
+
| `-e` | The engine for which the include file is created [psychic | async] | psychic |
|
|
223
|
+
| `-o` | Generated output file with path | `svelteesp32.h` |
|
|
224
|
+
| `--etag` | Use ETag header for cache | false |
|
|
225
|
+
| `--no-gzip` | Do not compress content with gzip | false -> gzip used |
|
|
226
|
+
| `--espmethod` | Name of generated method | `initSvelteStaticFiles` |
|
|
227
|
+
| `--define` | Prefix of c++ defines | `SVELTEESP32` |
|
|
228
|
+
| `-h` | Show help | |
|
|
178
229
|
|
|
179
230
|
### Q&A
|
|
180
231
|
|
package/dist/commandLine.d.ts
CHANGED
package/dist/commandLine.js
CHANGED
|
@@ -42,6 +42,11 @@ exports.cmdLine = (0, ts_command_line_args_1.parse)({
|
|
|
42
42
|
description: 'Name of generated method',
|
|
43
43
|
defaultValue: 'initSvelteStaticFiles'
|
|
44
44
|
},
|
|
45
|
+
define: {
|
|
46
|
+
type: String,
|
|
47
|
+
description: 'Prefix of c++ defines',
|
|
48
|
+
defaultValue: 'SVELTEESP32'
|
|
49
|
+
},
|
|
45
50
|
help: { type: Boolean, optional: true, alias: 'h', description: 'Shows this help' }
|
|
46
51
|
}, {
|
|
47
52
|
helpArg: 'help',
|
package/dist/cppCode.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
export type
|
|
2
|
-
index: number;
|
|
1
|
+
export type CppCodeSource = {
|
|
3
2
|
filename: string;
|
|
3
|
+
dataname: string;
|
|
4
|
+
datanameUpperCase: string;
|
|
4
5
|
mime: string;
|
|
5
6
|
content: Buffer;
|
|
6
7
|
isGzip: boolean;
|
|
7
8
|
md5: string;
|
|
8
9
|
};
|
|
9
|
-
export
|
|
10
|
+
export type CppCodeSources = CppCodeSource[];
|
|
11
|
+
export type ExtensionGroup = {
|
|
12
|
+
extension: string;
|
|
13
|
+
count: number;
|
|
14
|
+
};
|
|
15
|
+
export type ExtensionGroups = ExtensionGroup[];
|
|
16
|
+
export declare const getCppCode: (sources: CppCodeSources, filesByExtension: ExtensionGroups) => string;
|
package/dist/cppCode.js
CHANGED
|
@@ -14,17 +14,21 @@ const psychicTemplate = `
|
|
|
14
14
|
#include <PsychicHttp.h>
|
|
15
15
|
#include <PsychicHttpsServer.h>
|
|
16
16
|
|
|
17
|
+
#define {{definePrefix}}_COUNT {{fileCount}}
|
|
18
|
+
#define {{definePrefix}}_SIZE {{fileSize}}
|
|
19
|
+
|
|
17
20
|
{{#each sources}}
|
|
18
|
-
{{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{{
|
|
23
|
-
{{
|
|
24
|
-
|
|
21
|
+
#define {{../definePrefix}}_FILE_{{this.datanameUpperCase}}
|
|
22
|
+
{{/each}}
|
|
23
|
+
|
|
24
|
+
{{#each filesByExtension}}
|
|
25
|
+
#define {{../definePrefix}}_{{this.extension}}_FILES {{this.count}}
|
|
26
|
+
{{/each}}
|
|
27
|
+
|
|
28
|
+
{{#each sources}}
|
|
29
|
+
const uint8_t data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };
|
|
25
30
|
{{#if ../isEtag}}
|
|
26
|
-
const char *
|
|
27
|
-
{{/if}}
|
|
31
|
+
const char * etag_{{this.dataname}} = "{{this.md5}}";
|
|
28
32
|
{{/if}}
|
|
29
33
|
{{/each}}
|
|
30
34
|
|
|
@@ -32,7 +36,7 @@ void {{methodName}}(PsychicHttpServer * server) {
|
|
|
32
36
|
{{#each sources}}
|
|
33
37
|
{{#if this.isDefault}}server->defaultEndpoint = {{/if}}server->on("/{{this.filename}}", HTTP_GET, [](PsychicRequest * request) {
|
|
34
38
|
{{#if ../isEtag}}
|
|
35
|
-
if (request->hasHeader("If-None-Match") && request->header("If-None-Match") == String({{
|
|
39
|
+
if (request->hasHeader("If-None-Match") && request->header("If-None-Match") == String(etag_{{this.dataname}})) {
|
|
36
40
|
PsychicResponse response304(request);
|
|
37
41
|
response304.setCode(304);
|
|
38
42
|
return response304.send();
|
|
@@ -44,9 +48,9 @@ void {{methodName}}(PsychicHttpServer * server) {
|
|
|
44
48
|
response.addHeader("Content-Encoding", "gzip");
|
|
45
49
|
{{/if}}
|
|
46
50
|
{{#if ../isEtag}}
|
|
47
|
-
response.addHeader("ETag", {{
|
|
51
|
+
response.addHeader("ETag", etag_{{this.dataname}});
|
|
48
52
|
{{/if}}
|
|
49
|
-
response.setContent({{
|
|
53
|
+
response.setContent(data_{{this.dataname}}, sizeof(data_{{this.dataname}}));
|
|
50
54
|
return response.send();
|
|
51
55
|
});
|
|
52
56
|
|
|
@@ -62,48 +66,50 @@ const asyncTemplate = `
|
|
|
62
66
|
#include <Arduino.h>
|
|
63
67
|
#include <ESPAsyncWebServer.h>
|
|
64
68
|
|
|
69
|
+
#define {{definePrefix}}_COUNT {{fileCount}}
|
|
70
|
+
#define {{definePrefix}}_SIZE {{fileSize}}
|
|
71
|
+
|
|
65
72
|
{{#each sources}}
|
|
66
|
-
{{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
{{
|
|
71
|
-
{{
|
|
72
|
-
|
|
73
|
+
#define {{../definePrefix}}_FILE_{{this.datanameUpperCase}}
|
|
74
|
+
{{/each}}
|
|
75
|
+
|
|
76
|
+
{{#each filesByExtension}}
|
|
77
|
+
#define {{../definePrefix}}_{{this.extension}}_FILES {{this.count}}
|
|
78
|
+
{{/each}}
|
|
79
|
+
|
|
80
|
+
{{#each sources}}
|
|
81
|
+
const uint8_t data_{{this.dataname}}[{{this.length}}] PROGMEM = { {{this.bytes}} };
|
|
73
82
|
{{#if ../isEtag}}
|
|
74
|
-
const char *
|
|
75
|
-
{{/if}}
|
|
83
|
+
const char * etag_{{this.dataname}} = "{{this.md5}}";
|
|
76
84
|
{{/if}}
|
|
77
85
|
{{/each}}
|
|
78
86
|
|
|
79
87
|
void {{methodName}}(AsyncWebServer * server) {
|
|
80
88
|
{{#each sources}}
|
|
81
|
-
ArRequestHandlerFunction {{
|
|
89
|
+
ArRequestHandlerFunction func_{{this.dataname}} = [](AsyncWebServerRequest * request) {
|
|
82
90
|
{{#if ../isEtag}}
|
|
83
|
-
if (request->hasHeader("If-None-Match") && request->getHeader("If-None-Match")->value() == String({{
|
|
91
|
+
if (request->hasHeader("If-None-Match") && request->getHeader("If-None-Match")->value() == String(etag_{{this.dataname}})) {
|
|
84
92
|
request->send(304);
|
|
85
93
|
return;
|
|
86
94
|
}
|
|
87
95
|
{{/if}}
|
|
88
|
-
AsyncWebServerResponse *response = request->beginResponse_P(200, "{{this.mime}}", {{
|
|
96
|
+
AsyncWebServerResponse *response = request->beginResponse_P(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
|
|
89
97
|
{{#if this.isGzip}}
|
|
90
98
|
response->addHeader("Content-Encoding", "gzip");
|
|
91
99
|
{{/if}}
|
|
92
100
|
{{#if ../isEtag}}
|
|
93
|
-
response->addHeader("ETag", {{
|
|
101
|
+
response->addHeader("ETag", etag_{{this.dataname}});
|
|
94
102
|
{{/if}}
|
|
95
103
|
request->send(response);
|
|
96
104
|
};
|
|
105
|
+
server->on("/{{this.filename}}", HTTP_GET, func_{{this.dataname}});
|
|
97
106
|
{{#if this.isDefault}}
|
|
98
|
-
server->on("/{{this.
|
|
99
|
-
server->on("/", HTTP_GET, funcDefaultDocument);
|
|
100
|
-
{{else}}
|
|
101
|
-
server->on("/{{this.filename}}", HTTP_GET, func{{this.index}});
|
|
107
|
+
server->on("/", HTTP_GET, func_{{this.dataname}});
|
|
102
108
|
{{/if}}
|
|
103
109
|
|
|
104
110
|
{{/each}}
|
|
105
111
|
}`;
|
|
106
|
-
const getCppCode = (sources) => (0, handlebars_1.compile)(commandLine_1.cmdLine.engine === 'psychic' ? psychicTemplate : asyncTemplate)({
|
|
112
|
+
const getCppCode = (sources, filesByExtension) => (0, handlebars_1.compile)(commandLine_1.cmdLine.engine === 'psychic' ? psychicTemplate : asyncTemplate)({
|
|
107
113
|
commandLine: process.argv.slice(2).join(' '),
|
|
108
114
|
now: `${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}`,
|
|
109
115
|
fileCount: sources.length.toString(),
|
|
@@ -114,7 +120,9 @@ const getCppCode = (sources) => (0, handlebars_1.compile)(commandLine_1.cmdLine.
|
|
|
114
120
|
bytes: [...s.content].map((v) => `0x${v.toString(16)}`).join(', '),
|
|
115
121
|
isDefault: s.filename.startsWith('index.htm')
|
|
116
122
|
})),
|
|
123
|
+
filesByExtension,
|
|
117
124
|
isEtag: commandLine_1.cmdLine.etag,
|
|
118
|
-
methodName: commandLine_1.cmdLine.espmethod
|
|
125
|
+
methodName: commandLine_1.cmdLine.espmethod,
|
|
126
|
+
definePrefix: commandLine_1.cmdLine.define
|
|
119
127
|
}).trim();
|
|
120
128
|
exports.getCppCode = getCppCode;
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ const summary = {
|
|
|
17
17
|
gzipsize: 0
|
|
18
18
|
};
|
|
19
19
|
const sources = [];
|
|
20
|
-
|
|
20
|
+
const filesByExtension = [];
|
|
21
21
|
const files = (0, file_1.getFiles)();
|
|
22
22
|
if (files.length === 0) {
|
|
23
23
|
console.error(`Directory ${commandLine_1.cmdLine.sourcepath} is empty`);
|
|
@@ -27,13 +27,24 @@ for (const file of files) {
|
|
|
27
27
|
const mime = (0, mime_types_1.lookup)(file) || 'text/plain';
|
|
28
28
|
summary.filecount++;
|
|
29
29
|
console.log(`[${file}]`);
|
|
30
|
+
const filename = file.replace(/\\/g, '/');
|
|
31
|
+
const dataname = filename.replace(/[./-]/g, '_');
|
|
32
|
+
let extension = node_path_1.default.extname(filename).toUpperCase();
|
|
33
|
+
if (extension.startsWith('.'))
|
|
34
|
+
extension = extension.slice(1);
|
|
35
|
+
const group = filesByExtension.find((fe) => fe.extension === extension);
|
|
36
|
+
if (group)
|
|
37
|
+
group.count += 1;
|
|
38
|
+
else
|
|
39
|
+
filesByExtension.push({ extension, count: 1 });
|
|
30
40
|
const rawContent = (0, node_fs_1.readFileSync)(node_path_1.default.join(commandLine_1.cmdLine.sourcepath, file), { flag: 'r' });
|
|
31
41
|
const md5 = (0, node_crypto_1.createHash)('md5').update(rawContent).digest('hex');
|
|
32
42
|
summary.size += rawContent.length;
|
|
33
43
|
if (commandLine_1.cmdLine['no-gzip']) {
|
|
34
44
|
sources.push({
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
filename: filename,
|
|
46
|
+
dataname,
|
|
47
|
+
datanameUpperCase: dataname.toUpperCase(),
|
|
37
48
|
content: rawContent,
|
|
38
49
|
isGzip: false,
|
|
39
50
|
mime,
|
|
@@ -46,8 +57,9 @@ for (const file of files) {
|
|
|
46
57
|
summary.gzipsize += zipContent.length;
|
|
47
58
|
if (rawContent.length > 100 && zipContent.length < rawContent.length * 0.85) {
|
|
48
59
|
sources.push({
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
filename: filename,
|
|
61
|
+
dataname,
|
|
62
|
+
datanameUpperCase: dataname.toUpperCase(),
|
|
51
63
|
content: zipContent,
|
|
52
64
|
isGzip: true,
|
|
53
65
|
mime,
|
|
@@ -57,8 +69,9 @@ for (const file of files) {
|
|
|
57
69
|
}
|
|
58
70
|
else {
|
|
59
71
|
sources.push({
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
filename: filename,
|
|
73
|
+
dataname,
|
|
74
|
+
datanameUpperCase: dataname.toUpperCase(),
|
|
62
75
|
content: rawContent,
|
|
63
76
|
isGzip: false,
|
|
64
77
|
mime,
|
|
@@ -69,7 +82,8 @@ for (const file of files) {
|
|
|
69
82
|
}
|
|
70
83
|
console.log('');
|
|
71
84
|
}
|
|
72
|
-
|
|
85
|
+
filesByExtension.sort((left, right) => left.extension.localeCompare(right.extension));
|
|
86
|
+
const cppFile = (0, cppCode_1.getCppCode)(sources, filesByExtension);
|
|
73
87
|
(0, node_fs_1.writeFileSync)(commandLine_1.cmdLine.outputfile, cppFile);
|
|
74
88
|
if (commandLine_1.cmdLine['no-gzip']) {
|
|
75
89
|
console.log(`${summary.filecount} files, ${Math.round(summary.size / 1024)}kB original size`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteesp32",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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",
|
|
@@ -28,10 +28,13 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/BCsabaEngine/svelteesp32",
|
|
30
30
|
"scripts": {
|
|
31
|
-
"dev:
|
|
32
|
-
"dev:psychic
|
|
33
|
-
"
|
|
34
|
-
"
|
|
31
|
+
"dev:async": "nodemon src/index.ts -- -e async -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32async.h --etag",
|
|
32
|
+
"dev:psychic": "nodemon src/index.ts -- -e psychic -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32psychic.h --etag",
|
|
33
|
+
"run:async": "tsx src/index.ts -e async -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32async.h --etag",
|
|
34
|
+
"run:psychic": "tsx src/index.ts -e psychic -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32psychic.h --etag",
|
|
35
|
+
"run:both": "npm run run:async && npm run run:psychic",
|
|
36
|
+
"platformio": "~/.platformio/penv/bin/pio run -d ./demo/esp32",
|
|
37
|
+
"run:both:build": "npm run run:both && npm run platformio",
|
|
35
38
|
"clean": "tsc --build --clean",
|
|
36
39
|
"build": "tsc --build --clean && tsc --build --force",
|
|
37
40
|
"format:check": "prettier --check .",
|
|
@@ -53,16 +56,17 @@
|
|
|
53
56
|
],
|
|
54
57
|
"devDependencies": {
|
|
55
58
|
"@types/mime-types": "^2.1.4",
|
|
56
|
-
"@types/node": "^
|
|
57
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
58
|
-
"@typescript-eslint/parser": "^
|
|
59
|
-
"eslint": "^
|
|
59
|
+
"@types/node": "^22.3.0",
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
|
61
|
+
"@typescript-eslint/parser": "^8.1.0",
|
|
62
|
+
"eslint": "^9.9.0",
|
|
60
63
|
"eslint-config-prettier": "^9.1.0",
|
|
61
64
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
62
65
|
"eslint-plugin-unicorn": "^55.0.0",
|
|
63
66
|
"nodemon": "^3.1.4",
|
|
64
67
|
"prettier": "^3.3.3",
|
|
65
68
|
"ts-node": "^10.9.2",
|
|
69
|
+
"tsx": "^4.17.0",
|
|
66
70
|
"typescript": "^5.5.4"
|
|
67
71
|
},
|
|
68
72
|
"dependencies": {
|