svelteesp32 2.3.1 → 2.3.3
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 +139 -0
- package/dist/commandLine.js +7 -2
- package/dist/cppCode.js +1 -1
- package/dist/file.js +10 -1
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -75,6 +75,15 @@ void setup() {
|
|
|
75
75
|
|
|
76
76
|
**That's it.** Your entire web app is now embedded and ready to serve.
|
|
77
77
|
|
|
78
|
+
> **Just want production-safe defaults?**
|
|
79
|
+
>
|
|
80
|
+
> ```bash
|
|
81
|
+
> npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h \
|
|
82
|
+
> --etag=true --gzip=true --cachetime-html=0 --cachetime-assets=31536000
|
|
83
|
+
> ```
|
|
84
|
+
>
|
|
85
|
+
> ETags for instant 304s, gzip for smaller transfers, `no-cache` for HTML so updates are always picked up, and 1-year caching for content-hashed JS/CSS assets.
|
|
86
|
+
|
|
78
87
|
---
|
|
79
88
|
|
|
80
89
|
## What's New
|
|
@@ -314,6 +323,8 @@ Reduce bandwidth dramatically with HTTP 304 "Not Modified" responses. When a bro
|
|
|
314
323
|
|
|
315
324
|
All four engines support full ETag validation.
|
|
316
325
|
|
|
326
|
+
> **Browser compatibility note:** ETags and `Cache-Control: max-age` are universally supported in all modern browsers. Very old clients (IE6/7, early Android 2.x WebViews) may ignore `must-revalidate` or mishandle 304 responses. If you target these environments, set `--etag=false` and `--cachetime=0` to force full downloads on every request.
|
|
327
|
+
|
|
317
328
|
### Browser Cache Control
|
|
318
329
|
|
|
319
330
|
Fine-tune how browsers cache your content:
|
|
@@ -538,6 +549,134 @@ npx svelteesp32 --config=.svelteesp32rc.prod.json
|
|
|
538
549
|
|
|
539
550
|
CLI arguments always override RC file values.
|
|
540
551
|
|
|
552
|
+
> **Security note:** When svelteesp32 auto-loads an RC file from the current directory it prints a warning. If you cloned a project that includes `.svelteesp32rc.json`, review it before running. `outputfile` in RC files must be a relative path — use `--output` on the CLI for absolute paths.
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Common Recipes
|
|
557
|
+
|
|
558
|
+
### Recipe A — Arduino IDE + Built-in WebServer
|
|
559
|
+
|
|
560
|
+
No PlatformIO required. Build your frontend, run svelteesp32 manually, then compile in Arduino IDE.
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
# 1. Build your frontend
|
|
564
|
+
npm run build
|
|
565
|
+
|
|
566
|
+
# 2. Generate the header
|
|
567
|
+
npx svelteesp32 -e webserver -s ./dist -o ./MyProject/svelteesp32.h \
|
|
568
|
+
--gzip=true --etag=false
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
> **Note:** The Arduino `WebServer` library does not support ETag validation, so `--etag=false` is the correct setting here. Remember to call `server.handleClient()` in your `loop()`.
|
|
572
|
+
|
|
573
|
+
```c
|
|
574
|
+
#include <WebServer.h>
|
|
575
|
+
#include "svelteesp32.h"
|
|
576
|
+
|
|
577
|
+
WebServer server(80);
|
|
578
|
+
|
|
579
|
+
void setup() {
|
|
580
|
+
WiFi.begin("ssid", "password");
|
|
581
|
+
while (WiFi.status() != WL_CONNECTED) delay(500);
|
|
582
|
+
initSvelteStaticFiles(&server);
|
|
583
|
+
server.begin();
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
void loop() {
|
|
587
|
+
server.handleClient();
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
### Recipe B — PlatformIO + Vite/SvelteKit, Auto-Generated Header on `pio run`
|
|
594
|
+
|
|
595
|
+
Run svelteesp32 automatically as a PlatformIO pre-build step so your header is always up to date.
|
|
596
|
+
|
|
597
|
+
**`platformio.ini`**
|
|
598
|
+
|
|
599
|
+
```ini
|
|
600
|
+
[env:esp32dev]
|
|
601
|
+
platform = espressif32
|
|
602
|
+
board = esp32dev
|
|
603
|
+
framework = arduino
|
|
604
|
+
lib_deps =
|
|
605
|
+
ESP Async WebServer
|
|
606
|
+
extra_scripts = pre:scripts/build_frontend.py
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
**`scripts/build_frontend.py`**
|
|
610
|
+
|
|
611
|
+
```python
|
|
612
|
+
Import("env")
|
|
613
|
+
import subprocess
|
|
614
|
+
|
|
615
|
+
def build_frontend(source, target, env):
|
|
616
|
+
print("Building frontend...")
|
|
617
|
+
subprocess.run(["npm", "run", "build"], cwd="frontend", check=True)
|
|
618
|
+
subprocess.run([
|
|
619
|
+
"npx", "svelteesp32",
|
|
620
|
+
"-e", "async",
|
|
621
|
+
"-s", "frontend/dist",
|
|
622
|
+
"-o", "src/svelteesp32.h",
|
|
623
|
+
"--etag=true", "--gzip=true",
|
|
624
|
+
"--cachetime-html=0", "--cachetime-assets=31536000"
|
|
625
|
+
], check=True)
|
|
626
|
+
|
|
627
|
+
env.AddPreAction("buildprog", build_frontend)
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
This ensures the C++ header is regenerated from the latest frontend build every time you run `pio run`.
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
### Recipe C — ESP-IDF CMake, 1-Year Cached Content-Hashed Assets
|
|
635
|
+
|
|
636
|
+
Integrate svelteesp32 into a CMake build so the header is regenerated automatically.
|
|
637
|
+
|
|
638
|
+
**`CMakeLists.txt` (component or top-level)**
|
|
639
|
+
|
|
640
|
+
```cmake
|
|
641
|
+
add_custom_command(
|
|
642
|
+
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h
|
|
643
|
+
COMMAND npm run build
|
|
644
|
+
COMMAND npx svelteesp32
|
|
645
|
+
-e espidf
|
|
646
|
+
-s ${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist
|
|
647
|
+
-o ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h
|
|
648
|
+
--etag=true --gzip=true
|
|
649
|
+
--cachetime-html=0 --cachetime-assets=31536000
|
|
650
|
+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
651
|
+
COMMENT "Generating svelteesp32.h from frontend build"
|
|
652
|
+
VERBATIM
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
add_custom_target(frontend_header
|
|
656
|
+
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/main/svelteesp32.h
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
# Make your main component depend on the generated header
|
|
660
|
+
add_dependencies(${COMPONENT_LIB} frontend_header)
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**`main/app_main.c`**
|
|
664
|
+
|
|
665
|
+
```c
|
|
666
|
+
#include <esp_http_server.h>
|
|
667
|
+
#include "svelteesp32.h"
|
|
668
|
+
|
|
669
|
+
void app_main(void) {
|
|
670
|
+
// ... WiFi init ...
|
|
671
|
+
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
672
|
+
httpd_handle_t server = NULL;
|
|
673
|
+
httpd_start(&server, &config);
|
|
674
|
+
initSvelteStaticFiles(server);
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
The `--cachetime-html=0 --cachetime-assets=31536000` combination gives you `no-cache` for `index.html` (so browser always checks for updates) and a 1-year `max-age` for content-hashed JS/CSS bundles.
|
|
679
|
+
|
|
541
680
|
---
|
|
542
681
|
|
|
543
682
|
## FAQ
|
package/dist/commandLine.js
CHANGED
|
@@ -218,7 +218,7 @@ function interpolateNpmVariables(config, rcFilePath) {
|
|
|
218
218
|
const packageJson = parsePackageJson(packageJsonPath);
|
|
219
219
|
const interpolateString = (value) => {
|
|
220
220
|
const regex = /\$npm_package_[\dA-Za-z]+(?:_[a-z][\dA-Za-z]*)*/g;
|
|
221
|
-
return value.
|
|
221
|
+
return value.replaceAll(regex, (match) => getNpmPackageVariable(packageJson, match) ?? match);
|
|
222
222
|
};
|
|
223
223
|
const result = { ...config };
|
|
224
224
|
if (result.sourcepath)
|
|
@@ -335,6 +335,8 @@ function validateRcConfig(config, rcPath) {
|
|
|
335
335
|
throw new TypeError(`Invalid dryrun in RC file: ${configObject['dryrun']} (must be boolean)`);
|
|
336
336
|
if (configObject['spa'] !== undefined && typeof configObject['spa'] !== 'boolean')
|
|
337
337
|
throw new TypeError(`Invalid spa in RC file: ${configObject['spa']} (must be boolean)`);
|
|
338
|
+
if (configObject['outputfile'] !== undefined && node_path_1.default.isAbsolute(configObject['outputfile']))
|
|
339
|
+
throw new Error(`'outputfile' in RC file must be a relative path (use --output CLI flag for absolute paths): ${configObject['outputfile']}`);
|
|
338
340
|
return configObject;
|
|
339
341
|
}
|
|
340
342
|
function parseArguments() {
|
|
@@ -355,8 +357,11 @@ function parseArguments() {
|
|
|
355
357
|
}
|
|
356
358
|
const rcPath = findRcFile(customConfigPath);
|
|
357
359
|
const rcConfig = rcPath ? loadRcFile(rcPath) : {};
|
|
358
|
-
if (rcPath)
|
|
360
|
+
if (rcPath) {
|
|
359
361
|
console.log((0, consoleColor_1.cyanLog)(`[SvelteESP32] Using config from: ${rcPath}`));
|
|
362
|
+
if (!customConfigPath && node_path_1.default.dirname(rcPath) === process.cwd())
|
|
363
|
+
console.warn((0, consoleColor_1.yellowLog)(`Warning: Loading config from current directory — verify this file is trusted before proceeding.`));
|
|
364
|
+
}
|
|
360
365
|
const result = {
|
|
361
366
|
engine: 'psychic',
|
|
362
367
|
outputfile: 'svelteesp32.h',
|
package/dist/cppCode.js
CHANGED
|
@@ -990,7 +990,7 @@ const postProcessCppCode = (code) => code
|
|
|
990
990
|
.filter(Boolean)
|
|
991
991
|
.map((line) => (line === '//' ? '' : line))
|
|
992
992
|
.join('\n')
|
|
993
|
-
.
|
|
993
|
+
.replaceAll(/\n{2,}/g, '\n');
|
|
994
994
|
const createHandlebarsHelpers = () => {
|
|
995
995
|
let switchValue;
|
|
996
996
|
return {
|
package/dist/file.js
CHANGED
|
@@ -51,7 +51,12 @@ const isExcluded = (filename, excludePatterns) => {
|
|
|
51
51
|
return isMatch(normalizedFilename);
|
|
52
52
|
};
|
|
53
53
|
const getFiles = () => {
|
|
54
|
-
const allFilenames = (0, tinyglobby_1.globSync)('**/*', {
|
|
54
|
+
const allFilenames = (0, tinyglobby_1.globSync)('**/*', {
|
|
55
|
+
cwd: commandLine_1.cmdLine.sourcepath,
|
|
56
|
+
onlyFiles: true,
|
|
57
|
+
dot: false,
|
|
58
|
+
followSymbolicLinks: false
|
|
59
|
+
});
|
|
55
60
|
const withoutCompressed = allFilenames.filter((filename) => !shouldSkipFile(filename, allFilenames));
|
|
56
61
|
const excludePatterns = commandLine_1.cmdLine.exclude || [];
|
|
57
62
|
const excludedFiles = [];
|
|
@@ -73,9 +78,13 @@ const getFiles = () => {
|
|
|
73
78
|
}
|
|
74
79
|
else if (excludePatterns.length > 0)
|
|
75
80
|
console.log((0, consoleColor_1.yellowLog)(`Warning: --exclude patterns matched no files: ${excludePatterns.join(', ')}`));
|
|
81
|
+
const MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
76
82
|
const result = new Map();
|
|
77
83
|
for (const filename of filenames) {
|
|
78
84
|
const filePath = node_path_1.default.join(commandLine_1.cmdLine.sourcepath, filename);
|
|
85
|
+
const fileSize = (0, node_fs_1.statSync)(filePath).size;
|
|
86
|
+
if (fileSize > MAX_FILE_SIZE)
|
|
87
|
+
throw new Error(`File too large (${Math.round(fileSize / 1024)}kB): ${filename}. Maximum single-file size is 50MB.`);
|
|
79
88
|
const content = (0, node_fs_1.readFileSync)(filePath, { flag: 'r' });
|
|
80
89
|
const hash = (0, node_crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
81
90
|
result.set(filename, { content, hash });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteesp32",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
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",
|
|
@@ -62,27 +62,27 @@
|
|
|
62
62
|
"@eslint/eslintrc": "^3.3.5",
|
|
63
63
|
"@eslint/js": "^10.0.1",
|
|
64
64
|
"@types/mime-types": "^3.0.1",
|
|
65
|
-
"@types/node": "^25.5.
|
|
66
|
-
"@types/picomatch": "^4.0.
|
|
67
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
68
|
-
"@typescript-eslint/parser": "^8.
|
|
69
|
-
"@vitest/coverage-v8": "^4.1.
|
|
70
|
-
"eslint": "^10.0
|
|
65
|
+
"@types/node": "^25.5.2",
|
|
66
|
+
"@types/picomatch": "^4.0.3",
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^8.58.0",
|
|
68
|
+
"@typescript-eslint/parser": "^8.58.0",
|
|
69
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
70
|
+
"eslint": "^10.2.0",
|
|
71
71
|
"eslint-config-prettier": "^10.1.8",
|
|
72
|
-
"eslint-plugin-simple-import-sort": "^
|
|
73
|
-
"eslint-plugin-unicorn": "^
|
|
74
|
-
"memfs": "^4.
|
|
72
|
+
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
73
|
+
"eslint-plugin-unicorn": "^64.0.0",
|
|
74
|
+
"memfs": "^4.57.1",
|
|
75
75
|
"nodemon": "^3.1.14",
|
|
76
76
|
"prettier": "^3.8.1",
|
|
77
77
|
"ts-node": "^10.9.2",
|
|
78
78
|
"tsx": "^4.21.0",
|
|
79
|
-
"typescript": "^
|
|
80
|
-
"vitest": "^4.1.
|
|
79
|
+
"typescript": "^6.0.2",
|
|
80
|
+
"vitest": "^4.1.2"
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"handlebars": "^4.7.
|
|
83
|
+
"handlebars": "^4.7.9",
|
|
84
84
|
"mime-types": "^3.0.2",
|
|
85
|
-
"picomatch": "^4.0.
|
|
85
|
+
"picomatch": "^4.0.4",
|
|
86
86
|
"tinyglobby": "^0.2.15"
|
|
87
87
|
}
|
|
88
88
|
}
|