svelteesp32 2.3.1 → 2.3.2

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
@@ -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
@@ -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/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)('**/*', { cwd: commandLine_1.cmdLine.sourcepath, onlyFiles: true, dot: false });
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.1",
3
+ "version": "2.3.2",
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",
@@ -64,25 +64,25 @@
64
64
  "@types/mime-types": "^3.0.1",
65
65
  "@types/node": "^25.5.0",
66
66
  "@types/picomatch": "^4.0.2",
67
- "@typescript-eslint/eslint-plugin": "^8.57.0",
68
- "@typescript-eslint/parser": "^8.57.0",
69
- "@vitest/coverage-v8": "^4.1.0",
70
- "eslint": "^10.0.3",
67
+ "@typescript-eslint/eslint-plugin": "^8.57.2",
68
+ "@typescript-eslint/parser": "^8.57.2",
69
+ "@vitest/coverage-v8": "^4.1.2",
70
+ "eslint": "^10.1.0",
71
71
  "eslint-config-prettier": "^10.1.8",
72
72
  "eslint-plugin-simple-import-sort": "^12.1.1",
73
- "eslint-plugin-unicorn": "^63.0.0",
74
- "memfs": "^4.56.11",
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
79
  "typescript": "^5.9.3",
80
- "vitest": "^4.1.0"
80
+ "vitest": "^4.1.2"
81
81
  },
82
82
  "dependencies": {
83
- "handlebars": "^4.7.8",
83
+ "handlebars": "^4.7.9",
84
84
  "mime-types": "^3.0.2",
85
- "picomatch": "^4.0.3",
85
+ "picomatch": "^4.0.4",
86
86
  "tinyglobby": "^0.2.15"
87
87
  }
88
88
  }