svelteesp32 1.2.7 → 1.3.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 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,16 @@ 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
+ ...
112
+
104
113
  const uint8_t data0[12547] = {0x1f, 0x8b, 0x8, 0x0, ...
105
114
  const uint8_t data1[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ...
106
115
  const char * etag0 = "387b88e345cc56ef9091...";
@@ -164,6 +173,34 @@ The use of ETag is **not enabled by default**, this can be achieved with the `--
164
173
 
165
174
  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
175
 
176
+ ### C++ defines
177
+
178
+ To make it easy to integrate into a larger c++ project, we have made a couple of variables available as c++ defines.
179
+
180
+ You can use the COUNT and SIZE constants:
181
+
182
+ ```c
183
+ ...
184
+ #include "svelteesp32.h"
185
+
186
+ #if SVELTEESP32_COUNT != 5
187
+ #error Invalid file count
188
+ #endif
189
+ ...
190
+ ```
191
+
192
+ You can include a warning if a named file accidentally missing from the build:
193
+
194
+ ```c
195
+ ...
196
+ #include "svelteesp32.h"
197
+
198
+ #ifndef SVELTEESP32_FILE_index_html
199
+ #error Missing index file
200
+ #endif
201
+ ...
202
+ ```
203
+
167
204
  ### Command line options
168
205
 
169
206
  | Option | Required | Description | default |
@@ -174,6 +211,7 @@ Typically, the entry point for web applications is the **index.htm or index.html
174
211
  | `--etag` | | Use ETag header for cache | false |
175
212
  | `--no-gzip` | | Do not compress content with gzip | false -> gzip used |
176
213
  | `--espmethod` | x | Name of generated method | `initSvelteStaticFiles` |
214
+ | `--define` | x | Prefix of c++ defines | `SVELTEESP32` |
177
215
  | `-h` | | Show help | |
178
216
 
179
217
  ### Q&A
@@ -3,6 +3,7 @@ interface ICopyFilesArguments {
3
3
  sourcepath: string;
4
4
  outputfile: string;
5
5
  espmethod: string;
6
+ define: string;
6
7
  'no-gzip': boolean;
7
8
  etag: boolean;
8
9
  help?: boolean;
@@ -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,6 +1,6 @@
1
1
  export type cppCodeSource = {
2
- index: number;
3
2
  filename: string;
3
+ dataname: string;
4
4
  mime: string;
5
5
  content: Buffer;
6
6
  isGzip: boolean;
package/dist/cppCode.js CHANGED
@@ -14,17 +14,16 @@ const psychicTemplate = `
14
14
  #include <PsychicHttp.h>
15
15
  #include <PsychicHttpsServer.h>
16
16
 
17
+ #define {{definePrefix}}_COUNT {{fileCount}}
18
+ #define {{definePrefix}}_SIZE {{fileSize}}
17
19
  {{#each sources}}
18
- {{#if this.isDefault}}
19
- const uint8_t dataDefaultDocument[{{this.length}}] = { {{this.bytes}} };
20
- {{#if ../isEtag}}
21
- const char * etagDefaultDocument = "{{this.md5}}";
22
- {{/if}}
23
- {{else}}
24
- const uint8_t data{{this.index}}[{{this.length}}] = { {{this.bytes}} };
20
+ #define {{../definePrefix}}_FILE_{{this.dataname}}
21
+ {{/each}}
22
+
23
+ {{#each sources}}
24
+ const uint8_t data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };
25
25
  {{#if ../isEtag}}
26
- const char * etag{{this.index}} = "{{this.md5}}";
27
- {{/if}}
26
+ const char * etag_{{this.dataname}} = "{{this.md5}}";
28
27
  {{/if}}
29
28
  {{/each}}
30
29
 
@@ -32,7 +31,7 @@ void {{methodName}}(PsychicHttpServer * server) {
32
31
  {{#each sources}}
33
32
  {{#if this.isDefault}}server->defaultEndpoint = {{/if}}server->on("/{{this.filename}}", HTTP_GET, [](PsychicRequest * request) {
34
33
  {{#if ../isEtag}}
35
- if (request->hasHeader("If-None-Match") && request->header("If-None-Match") == String({{#if this.isDefault}}etagDefaultDocument{{else}}etag{{this.index}}{{/if}})) {
34
+ if (request->hasHeader("If-None-Match") && request->header("If-None-Match") == String(etag_{{this.dataname}})) {
36
35
  PsychicResponse response304(request);
37
36
  response304.setCode(304);
38
37
  return response304.send();
@@ -44,9 +43,9 @@ void {{methodName}}(PsychicHttpServer * server) {
44
43
  response.addHeader("Content-Encoding", "gzip");
45
44
  {{/if}}
46
45
  {{#if ../isEtag}}
47
- response.addHeader("ETag", {{#if this.isDefault}}etagDefaultDocument{{else}}etag{{this.index}}{{/if}});
46
+ response.addHeader("ETag", etag_{{this.dataname}});
48
47
  {{/if}}
49
- response.setContent({{#if this.isDefault}}dataDefaultDocument{{else}}data{{this.index}}{{/if}}, sizeof({{#if this.isDefault}}dataDefaultDocument{{else}}data{{this.index}}{{/if}}));
48
+ response.setContent(data_{{this.dataname}}, sizeof(data_{{this.dataname}}));
50
49
  return response.send();
51
50
  });
52
51
 
@@ -62,43 +61,40 @@ const asyncTemplate = `
62
61
  #include <Arduino.h>
63
62
  #include <ESPAsyncWebServer.h>
64
63
 
64
+ #define {{definePrefix}}_COUNT {{fileCount}}
65
+ #define {{definePrefix}}_SIZE {{fileSize}}
65
66
  {{#each sources}}
66
- {{#if this.isDefault}}
67
- const uint8_t dataDefaultDocument[{{this.length}}] = { {{this.bytes}} };
68
- {{#if ../isEtag}}
69
- const char * etagDefaultDocument = "{{this.md5}}";
70
- {{/if}}
71
- {{else}}
72
- const uint8_t data{{this.index}}[{{this.length}}] PROGMEM = { {{this.bytes}} };
67
+ #define {{../definePrefix}}_FILE_{{this.dataname}}
68
+ {{/each}}
69
+
70
+ {{#each sources}}
71
+ const uint8_t data_{{this.dataname}}[{{this.length}}] PROGMEM = { {{this.bytes}} };
73
72
  {{#if ../isEtag}}
74
- const char * etag{{this.index}} = "{{this.md5}}";
75
- {{/if}}
73
+ const char * etag_{{this.dataname}} = "{{this.md5}}";
76
74
  {{/if}}
77
75
  {{/each}}
78
76
 
79
77
  void {{methodName}}(AsyncWebServer * server) {
80
78
  {{#each sources}}
81
- ArRequestHandlerFunction {{#if this.isDefault}}funcDefaultDocument{{else}}func{{this.index}}{{/if}} = [](AsyncWebServerRequest * request) {
79
+ ArRequestHandlerFunction func_{{this.dataname}} = [](AsyncWebServerRequest * request) {
82
80
  {{#if ../isEtag}}
83
- if (request->hasHeader("If-None-Match") && request->getHeader("If-None-Match")->value() == String({{#if this.isDefault}}etagDefaultDocument{{else}}etag{{this.index}}{{/if}})) {
81
+ if (request->hasHeader("If-None-Match") && request->getHeader("If-None-Match")->value() == String(etag_{{this.dataname}})) {
84
82
  request->send(304);
85
83
  return;
86
84
  }
87
85
  {{/if}}
88
- AsyncWebServerResponse *response = request->beginResponse_P(200, "{{this.mime}}", {{#if this.isDefault}}dataDefaultDocument{{else}}data{{this.index}}{{/if}}, {{this.length}});
86
+ AsyncWebServerResponse *response = request->beginResponse_P(200, "{{this.mime}}", data_{{this.dataname}}, {{this.length}});
89
87
  {{#if this.isGzip}}
90
88
  response->addHeader("Content-Encoding", "gzip");
91
89
  {{/if}}
92
90
  {{#if ../isEtag}}
93
- response->addHeader("ETag", {{#if this.isDefault}}etagDefaultDocument{{else}}etag{{this.index}}{{/if}});
91
+ response->addHeader("ETag", etag_{{this.dataname}});
94
92
  {{/if}}
95
93
  request->send(response);
96
94
  };
95
+ server->on("/{{this.filename}}", HTTP_GET, func_{{this.dataname}});
97
96
  {{#if this.isDefault}}
98
- server->on("/{{this.filename}}", HTTP_GET, funcDefaultDocument);
99
- server->on("/", HTTP_GET, funcDefaultDocument);
100
- {{else}}
101
- server->on("/{{this.filename}}", HTTP_GET, func{{this.index}});
97
+ server->on("/", HTTP_GET, func_{{this.dataname}});
102
98
  {{/if}}
103
99
 
104
100
  {{/each}}
@@ -115,6 +111,7 @@ const getCppCode = (sources) => (0, handlebars_1.compile)(commandLine_1.cmdLine.
115
111
  isDefault: s.filename.startsWith('index.htm')
116
112
  })),
117
113
  isEtag: commandLine_1.cmdLine.etag,
118
- methodName: commandLine_1.cmdLine.espmethod
114
+ methodName: commandLine_1.cmdLine.espmethod,
115
+ definePrefix: commandLine_1.cmdLine.define
119
116
  }).trim();
120
117
  exports.getCppCode = getCppCode;
package/dist/index.js CHANGED
@@ -17,7 +17,6 @@ const summary = {
17
17
  gzipsize: 0
18
18
  };
19
19
  const sources = [];
20
- let sourceIndex = 0;
21
20
  const files = (0, file_1.getFiles)();
22
21
  if (files.length === 0) {
23
22
  console.error(`Directory ${commandLine_1.cmdLine.sourcepath} is empty`);
@@ -27,13 +26,15 @@ for (const file of files) {
27
26
  const mime = (0, mime_types_1.lookup)(file) || 'text/plain';
28
27
  summary.filecount++;
29
28
  console.log(`[${file}]`);
29
+ const filename = file.replace(/\\/g, '/');
30
+ const dataname = filename.replace(/[./-]/g, '_');
30
31
  const rawContent = (0, node_fs_1.readFileSync)(node_path_1.default.join(commandLine_1.cmdLine.sourcepath, file), { flag: 'r' });
31
32
  const md5 = (0, node_crypto_1.createHash)('md5').update(rawContent).digest('hex');
32
33
  summary.size += rawContent.length;
33
34
  if (commandLine_1.cmdLine['no-gzip']) {
34
35
  sources.push({
35
- index: sourceIndex++,
36
- filename: file.replace(/\\/g, '/'),
36
+ filename: filename,
37
+ dataname,
37
38
  content: rawContent,
38
39
  isGzip: false,
39
40
  mime,
@@ -46,8 +47,8 @@ for (const file of files) {
46
47
  summary.gzipsize += zipContent.length;
47
48
  if (rawContent.length > 100 && zipContent.length < rawContent.length * 0.85) {
48
49
  sources.push({
49
- index: sourceIndex++,
50
- filename: file.replace(/\\/g, '/'),
50
+ filename: filename,
51
+ dataname,
51
52
  content: zipContent,
52
53
  isGzip: true,
53
54
  mime,
@@ -57,8 +58,8 @@ for (const file of files) {
57
58
  }
58
59
  else {
59
60
  sources.push({
60
- index: sourceIndex++,
61
- filename: file.replace(/\\/g, '/'),
61
+ filename: filename,
62
+ dataname,
62
63
  content: rawContent,
63
64
  isGzip: false,
64
65
  mime,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelteesp32",
3
- "version": "1.2.7",
3
+ "version": "1.3.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,10 +28,13 @@
28
28
  },
29
29
  "homepage": "https://github.com/BCsabaEngine/svelteesp32",
30
30
  "scripts": {
31
- "dev:psychic": "nodemon src/index.ts -- -e psychic -s ./svelte/dist -o ../../../Arduino/SvelteEsp32/svelteesp32.h --etag",
32
- "dev:psychic:local": "nodemon src/index.ts -- -e psychic -s ./svelte/dist -o ./svelteesp32.h --etag",
33
- "dev:async": "nodemon src/index.ts -- -e async -s ./svelte/dist -o ../../../Arduino/SvelteEsp8266/svelteesp32.h --etag",
34
- "dev:async:local": "nodemon src/index.ts -- -e async -s ./svelte/dist -o ./svelteesp32.h --etag",
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": "^20.14.12",
57
- "@typescript-eslint/eslint-plugin": "^7.17.0",
58
- "@typescript-eslint/parser": "^7.17.0",
59
- "eslint": "^8.57.0",
59
+ "@types/node": "^22.2.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": {