# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [3.2.3] - 2026-07-31 ### Changed - Updated npm dependencies: `@typescript-eslint/eslint-plugin`, `@typescript-eslint/parser`, `eslint`, `eslint-plugin-simple-import-sort`, `eslint-plugin-unicorn`, `globals`, `prettier`, and transitive packages - Pinned `@types/node` back to `^22.20.1` to match the `engines.node >= 22` requirement (was drifting ahead to `^26`) ## [3.2.2] - 2026-07-14 ### Fixed - **Source files that collide on the same C++ identifier are now rejected at generation time.** Filenames become C symbols by mapping every character outside `[A-Za-z0-9_]` to `_`, so `app-v1.js` and `app_v1.js` — or `assets/app.js` and `assets-app.js` — both produced `data_app_v1_js` / `assets_app_js`. The header then declared the same array twice and the firmware failed to compile with a redefinition error pointing at generated code, giving no hint that the real cause was a pair of filenames in `dist/`. The pipeline now names both files and exits before writing anything. Identifiers are compared case-insensitively, since the header also emits uppercased symbols (`{PREFIX}_FILE_`, and ESP-IDF's `file_handler_`/`route_` functions), where `App.js` and `app.js` collide too. - **The Vite plugin no longer runs on the dev server.** Its only work happens in `closeBundle()`, a Rollup hook that Vite's dev server also fires on every plugin when it shuts down the plugin container. A dev session therefore regenerated the header from `build.outDir` — a directory that during development is stale from an earlier build, half-written, or missing altogether — silently overwriting a good header or throwing a "no index.html" error on exit. The plugin is now `apply: 'build'`, so Vite leaves it out of the dev-server plugin container entirely. In practice the misfire hit hardest on a **config-file change**: editing `vite.config.ts` restarts the dev server, and the restart closes the plugin container, so the header was rewritten from a stale `dist/` in the middle of an ordinary editing session. A `SIGTERM` shutdown did the same. (Plain Ctrl-C did not — Vite leaves `SIGINT` on Node's default handling, so the process dies before any hook runs.) - **The Vite plugin now runs last (`enforce: 'post'`).** Its `closeBundle` is sorted after that of any other user plugin, so plugins that emit files into `outDir` from their own `closeBundle` — `vite-plugin-pwa`'s service worker, compression and static-copy plugins — have finished writing by the time the directory is scanned. Their output now lands in the header regardless of where `svelteESP32()` sits in the `plugins` array. ## [3.2.1] - 2026-07-13 ### Fixed - **`Cache-Control` is no longer coupled to the ETag mode.** In every engine the cache header block was emitted inside a `switch` over `etag` that had `always` and `compiler` arms but no `never` arm, so the `Cache-Control` header disappeared along with the `ETag` whenever ETag validation was off: - `--etag=never --cachetimeassets=31536000` emitted **no `Cache-Control` header at all** — the cache flags were silently inert. This is the combination the README's Arduino `WebServer` recipe recommended. - `--etag=compiler` compiled _without_ `-D SVELTEESP32_ENABLE_ETAG` also dropped caching, as a side effect of a compile-time _ETag_ switch. The two headers are independent: `Cache-Control` sets a freshness lifetime, `ETag` provides the validator used to revalidate afterwards. `Cache-Control` is now emitted on every `200` in every etag mode, and only the `ETag` line stays gated. Note the consequence for `--etag=never` builds with no cache time set: they now carry `Cache-Control: no-cache`, the honest directive for an uncacheable response (it also stops proxies applying heuristic freshness), where previously they carried nothing. - **`304 Not Modified` responses now repeat the `ETag` and `Cache-Control` headers that the matching `200` would carry.** A 304 previously went out as a bare status line with no headers at all, violating RFC 7232 §4.1 (now RFC 9110 §15.4.5): _"The server generating a 304 response MUST generate any of the following header fields that would have been sent in a 200 (OK) response to the same request: Cache-Control, Content-Location, Date, ETag, Expires, and Vary."_ The practical cost is spelled out in RFC 7234 §4.3.4 (now RFC 9111 §4.3.4): a client refreshes its stored response's freshness lifetime from the headers on the 304. With none present, the lifetime was never refreshed — so a browser revalidated on _every_ load, and `--cachetime`, `--cachetimehtml` and `--cachetimeassets` had no effect at all on any client that already held the file. All four engines are fixed. `Content-Encoding` and `Content-Type` are deliberately left off the 304: they describe a representation, and a 304 has no body. ### Changed - **ETags are now quoted and truncated to 16 hex characters.** The generated tag was the raw 64-character SHA256 hex with no quotes, which is not a valid entity-tag: RFC 9110 defines `opaque-tag = DQUOTE *etagc DQUOTE`, and some proxies and stricter clients mishandle an unquoted value. Headers now emit `static const char etag_index_html[] = "\"387b88e345cc56ef\"";` and send `ETag: "387b88e345cc56ef"`. 16 hex characters is 64 bits of collision resistance — far beyond what the few dozen files that fit in flash need — and costs 18 bytes per file instead of 64, on every response header and every conditional request. The **full 64-character hash is still written to the `--manifest` JSON**, so change detection between builds is unaffected. Clients holding an ETag from an older build will miss once and get a fresh `200`; there is no other migration. - **`If-None-Match` is matched with `strstr()` instead of exact string equality** (`.equals()` on the Arduino engines, `strcmp()` on espidf). A browser may send a comma-separated list (`If-None-Match: "a", "b"`) or a weak validator (`W/"a"`), and both previously failed to match — silently costing a full `200` where a `304` was correct. Because `etagc` excludes `"`, the quoted tag can only match a complete entity-tag within a list, and matching through a `W/` prefix is exactly the weak comparison RFC 9110 mandates for `If-None-Match`. ## [3.2.0] - 2026-07-13 ### Added - **HTTP `HEAD` support (psychic, async)**: Generated routes now answer `HEAD` as well as `GET` — same status code and headers, no body. `curl -I`, health checks and uptime monitors previously received a `404`. Always on; there is no flag, because it costs no extra route on either engine. psychic registers file routes as `HTTP_ANY` and dispatches on the method inside the handler; async registers them as `HTTP_GET | HTTP_HEAD`. The `--spa` catch-all answers `HEAD` too. `HEAD` responses report `Content-Length: 0`. ESP-IDF's `httpd_resp_send()` derives `Content-Length` from the buffer it is handed and offers no way to override it, so a body-less response cannot carry the real length; async reports `0` for consistency. The **webserver** and **espidf** engines remain `GET`-only — HEAD would require a second handler per file there. The README shows how to add it by hand. ### Fixed - **ETag/`304` never fired on the `webserver` engine**: the generated handler tests `server->hasHeader("If-None-Match")`, but Arduino `WebServer` only retains request headers it has been told to collect, and nothing called `collectHeaders()`. `hasHeader()` therefore always returned false and the entire `304` branch was unreachable — every request re-sent the full body. The generated init function now calls `server->collectAllHeaders()`. (Calling `server->collectHeaders(...)` yourself afterwards re-initialises the header list and will disable ETag again.) ### Changed - **psychic: `POST`/`PUT`/`DELETE` to a static file path now returns `405 Method Not Allowed`** with an `Allow: GET, HEAD` header. Previously these fell through psychic's 404 path into `defaultEndpoint`, so e.g. `POST /app.js` answered `200` with the contents of `index.html`. - **psychic: `SVELTEESP32_MAX_URI_HANDLERS` is informational only.** PsychicHttp 3.x registers one wildcard esp-idf handler per HTTP method and routes endpoints internally, overwriting `server.config.max_uri_handlers` in `start()` — so assigning the define was already a no-op. The defines are still emitted (and remain load-bearing for `espidf`), but the psychic demo and README no longer tell you to assign them. - Updated dev dependencies: `eslint-plugin-unicorn` 71 (major), `eslint` 10.7, `@typescript-eslint/*` 8.64, `vitest` and `@vitest/coverage-v8` 4.1.10, `memfs` 4.64, `prettier` 3.9.5, `tsx` 4.23.1, `@types/node` 26.1.1 ## [3.1.3] - 2026-07-05 ### Fixed - **Accurate `max_uri_handlers` count**: The generated `SVELTEESP32_MAX_URI_HANDLERS` (and new `SVELTEESP32_URI_HANDLERS`) defines now account for the default `/` route and the `--spa` catch-all handler instead of just the raw file count, for the `psychic` and `espidf` engines. - Removed the `[CONFIG TIP] max_uri_handlers configuration` console hint printed after generation — the generated header defines are sufficient. ### Changed - Updated ESP32 demo library versions and PlatformIO dependencies ## [3.1.2] - 2026-06-15 ### Changed - Updated ESP32 demo library versions: ESPAsyncWebServer v3.11.1, PsychicHttp 3.1.1 - Updated npm dependencies; resolved ESLint rule violations ## [3.1.1] - 2026-05-12 ### Changed - **Removed `picomatch` dependency**: `--exclude` glob patterns are now passed directly to `tinyglobby`'s `ignore` option. No separate `picomatch` call is made, removing the dependency entirely. - **Removed `mime-types` dependency**: MIME type lookup is now handled by a built-in static map in `pipeline.ts`, eliminating the `mime-types` runtime dependency and its `@types/mime-types` dev dependency. - Updated dependencies ## [3.1.0] - 2026-05-11 ### BREAKING CHANGES - **`--cachetime-html` renamed to `--cachetimehtml`**: The CLI flag no longer accepts the hyphenated form. RC file key (`cachetimehtml`) and Vite plugin option were already using the no-dash form since v2.3.0 — now the CLI matches. - **`--cachetime-assets` renamed to `--cachetimeassets`**: Same alignment as above. - **`--dry-run` alias removed**: Only `--dryrun` is accepted. `--dry-run` now produces an unknown flag error. ### Changed - **Removed `handlebars` dependency**: C++ code generation is now pure TypeScript string building — no template engine. Eliminates the `handlebars` runtime dependency, reducing install size and startup overhead. - **Engine code generation split into dedicated modules**: `cppCode.ts` is now shared utilities only; each engine has its own file — `cppCodePsychic.ts` (`genPsychicCpp`), `cppCodeAsync.ts` (`genAsyncCpp`), `cppCodeWebserver.ts` (`genWebserverCpp`), `cppCodeEspIdf.ts` (`genEspIdfCpp`). - Updated dependencies ## [3.0.2] - 2026-05-06 ### Changed - **Node.js 26 support**: CI now tests against Node.js 22, 24, and 26. - **Vite plugin two-mode API**: `svelteESP32()` now has two exclusive modes — pass no argument (or a string path) for RC file mode, or pass an options object for plugin options mode. The two modes no longer merge: - **RC file mode** — `svelteESP32()` or `svelteESP32('/path/to/.svelteesp32rc.json')`: all settings come from the RC file; `outputfile` in the RC file is required. - **Plugin options mode** — `svelteESP32({ output: '...', ... })`: all settings come from the options object; the RC file is completely ignored. `output` is required. - **`config` option removed**: The `config` property on `SvelteESP32PluginOptions` has been removed. To point at a custom RC file, pass the path as the first argument: `svelteESP32('/path/to/custom.rc.json')`. ## [3.0.1] - 2026-05-04 ### Fixed - **Vite plugin RC file support**: The plugin now loads `.svelteesp32rc.json` automatically; `output` is optional when `outputfile` is set in the RC file; plugin option names aligned to lowercase (`cachetimehtml`, `cachetimeassets`, `noindexcheck`, `maxsize`, `maxgzipsize`) to match RC file keys; new `config` option to specify a custom RC file path. ## [3.0.0] - 2026-05-01 ### BREAKING CHANGES - **Node.js >= 22 required**: Minimum runtime bumped from Node.js 20 to Node.js 22; npm >= 10 is now required. Node.js 20 is no longer supported. ### Added - **Vite plugin**: New `svelteESP32(options)` plugin imported from `svelteesp32/vite`. Hook it into `vite.config.ts` and the C++ header is regenerated automatically after every `vite build` — no manual CLI step needed. `sourcepath` defaults to Vite's `build.outDir`; all other options mirror the CLI flags. Vite is an optional peer dependency. - **`init` command**: `npx svelteesp32 init` launches an interactive wizard that creates `.svelteesp32rc.json`. Asks for engine, source path, output path, and ETag preference, then offers to run the tool immediately. - **Programmatic API**: Core pipeline extracted to `src/pipeline.ts` and exported as `runPipeline(options)` from the package root. Used internally by both the CLI and the Vite plugin. - **Enhanced summary when `--manifest` is used**: The final console summary line now also reports the manifest file path when `--manifest` is active. ### Changed - Updated dependencies: `typescript` 6, `@types/node` 25, `vitest` 4, `eslint` 10, `eslint-plugin-unicorn` 64, and others ## [2.4.1] - 2026-04-26 ### Fixed - **RC file boolean fields accept string `"true"` / `"false"`**: The boolean flags `created`, `noindexcheck`, `dryrun`, `analyze`, `spa`, and `manifest` in `.svelteesp32rc.json` now accept the string values `"true"` and `"false"` in addition to native JSON booleans. This matches the existing behaviour of `etag` and `gzip` (which are already string-based), making RC files more forgiving and consistent when generated by tooling or copied from CLI argument strings. ## [2.4.0] - 2026-04-26 ### Added - **`--analyze` mode**: Prints a formatted per-file size table (original size and gzip size) plus budget pass/fail status without writing any output file. Exits with code 1 if a `--maxsize` or `--maxgzipsize` budget is exceeded — designed for CI budget checks. Mutually exclusive with `--dryrun`. Supported in RC files (`analyze: true`). - **`--manifest` option**: After generating the header, writes a companion `.manifest.json` file in the same directory (same base name). The manifest records `engine`, `etag`, `gzip`, total `filecount`/`size`/`gzipSize`, and per-file entries with `path`, `mime`, `size`, `gzipSize`, and `isGzip`. Supported in RC files (`manifest: true`). ## [2.3.3] - 2026-04-06 ### Changed - **TypeScript 6**: Upgraded compiler to TypeScript 6; `module` and `moduleResolution` updated to `Node16`, target raised to `ES2023` - **`replaceAll` now used directly**: ES2023 target makes `String.prototype.replaceAll` available — removed the `eslint-disable` workarounds that previously suppressed the `unicorn/prefer-string-replace-all` rule ## [2.3.2] - 2026-03-29 ### Security - **Symlink traversal blocked**: `tinyglobby` now runs with `followSymbolicLinks: false` — symlinks inside the source directory are no longer followed, preventing accidental (or malicious) inclusion of files outside the intended directory - **RC file CWD warning**: A yellow warning is printed when a config file is auto-loaded from the current working directory (as opposed to `--config` or the home directory), making it visible when a cloned project supplies its own RC file - **Absolute `outputfile` rejected in RC files**: Setting `"outputfile"` to an absolute path in `.svelteesp32rc.json` now throws an error — output paths in RC files must be relative. Absolute paths remain valid via the `--output` CLI flag where the user is explicitly in control - **Per-file 50 MB size limit**: Files larger than 50 MB are rejected before `readFileSync` / `gzipSync`, preventing OOM when the source directory contains unexpectedly large files (the existing `--maxsize`/`--maxgzipsize` budget checks run after all files are processed and do not protect against this) ## [2.3.1] - 2026-03-15 ### Fixed - **`--version` validation**: Values are now validated against `/^[\w.+-]+$/`; strings containing `"`, newlines, or other characters that would break the generated `#define` line are rejected with a clear error - **`--basepath` validation**: Rejects `"` and `\` characters which are invalid in URL paths and would corrupt C++ string literals in generated code - **`formatConfiguration()` newline safety**: Newlines and carriage returns in any user-supplied value (e.g. from RC file JSON) are replaced with spaces, preventing injection of content after the `//config:` comment line - **Double MIME lookup eliminated**: `mimeLookup()` was called twice per file in the hot loop (once to get the value, once to test it for the warning); now called once ## [2.3.0] - 2026-03-15 ### Added - **`--cachetime-html` and `--cachetime-assets` options**: Per-type `Cache-Control` overrides so Vite/webpack content-hashed assets can be cached for up to 1 year while `index.html` stays `no-cache` - `--cachetime-html ` — `Cache-Control: max-age=N` applied only to `text/html` files; overrides `--cachetime` - `--cachetime-assets ` — `Cache-Control: max-age=N` applied to all non-HTML files; overrides `--cachetime` - When neither override is set, behaviour is identical to the existing `--cachetime` (fully backwards-compatible) - `--cachetime-html=0` disables caching for HTML even when `--cachetime` sets a positive value - Cache time is resolved **per source file** instead of globally, so a single run can produce `no-cache` for `index.html` and `max-age=31536000` for JS/CSS in the same header - Both options supported in RC file (`cachetimehtml`, `cachetimeassets`) and via CLI (`--cachetime-html`, `--cachetime-assets`) - Shown in `formatConfiguration` / `//config:` header comment only when set - All four engines (psychic, async, webserver, espidf) supported ## [2.2.2] - 2026-03-13 ### Added - **`SVELTEESP32_MAX_URI_HANDLERS` define** (psychic engine only): The generated header now includes a `#define SVELTEESP32_MAX_URI_HANDLERS ` constant (file count + 5 safety margin), so `server.config.max_uri_handlers` can reference this value directly instead of a magic number ### Changed - **Removed built-in default exclude patterns**: `--exclude` no longer pre-populates with `.DS_Store`, `Thumbs.db`, `.git`, `*.swp`, etc. Exclusions are now fully explicit — add patterns you need - **Data arrays declared `static`**: All generated `uint8_t` data arrays are now `static const` (was `const`), preventing linker collisions when the header is included in multiple translation units - **ETag strings changed to char arrays**: ETag variables are now `static const char etag_[]` (was `const char * etag_`), eliminating pointer indirection and allowing correct `static` linkage - **`validateSizeOption()` helper extracted**: Duplicated `maxsize`/`maxgzipsize` RC file validation logic in `commandLine.ts` is now a shared helper function - **Demo PlatformIO dependencies updated** ## [2.2.1] - 2026-03-13 ### Changed - **Enhanced `--dryrun` output**: Dry-run mode now prints a full route table instead of a single summary line - Header line shows engine, ETag mode, gzip mode, base path, and SPA flag at a glance - Route table lists every `GET` route with its MIME type and original → gzip size - `[default]` tag marks the root/basePath catch for `index.html`/`index.htm` - `[no gzip]` tag flags files where gzip was skipped - `[SPA catch-all → index.html]` tag shows the SPA fallback route (psychic engine with basePath shows the real `basePath/*` URL; others show `(SPA catch-all)`) - `formatDryRunRoutes` is now exported for programmatic use and unit-tested ## [2.2.0] - 2026-03-11 ### Added - **SPA routing support**: New `--spa` flag enables client-side routing catch-all for single-page applications - When set, unmatched GET requests fall through to `index.html` so the JS router handles navigation - Browser refresh on `/settings` or `/dashboard` now works without a 404 - Psychic engine (no basePath): already SPA-compatible via `defaultEndpoint` — no extra route added - Psychic engine (with basePath): registers `server->on("{{basePath}}/*", ...)` catch-all - Async engine: adds `server->onNotFound(...)` with optional basePath prefix guard - Arduino WebServer engine: adds `server->onNotFound(...)` with optional basePath prefix guard - ESP-IDF engine: registers `spa_handler_*` via `httpd_register_err_handler(HTTPD_404_NOT_FOUND)` - Configurable via RC file as `"spa": true` - Warns if `--spa` is set but no `index.html`/`index.htm` is found in the source directory ## [2.1.0] - 2026-02-26 ### Added - **Arduino WebServer engine**: New `-e webserver` engine for the built-in Arduino WebServer library - No external library dependencies — uses the WebServer bundled with the ESP32 Arduino core - Synchronous request handling (requires `handleClient()` in your `loop()`) - Full ETag, gzip, and cache-time support - PROGMEM storage for data arrays - File manifest and `onFileServed` hook included - Demo project in `demo/esp32/` updated with `main_webserver.cpp` - Contributed by Michael Haberler via [#74](https://github.com/BCsabaEngine/svelteesp32/pull/74) ### Changed - Updated dependencies: `@types/node` 25, `@vitest/coverage-v8` 4, `eslint-config-prettier` 10, `memfs` 4.56, `nodemon` 3.1.14, `prettier` 3.8, `tsx` 4.21, `typescript` 5.9, `vitest` 4 ## [2.0.1] - 2026-02-19 ### Fixed - **Error cause chaining**: `Error` objects in `commandLine.ts` now pass the original error as `cause` for better stack trace propagation ### Changed - Updated dependencies: ESLint v10, `@typescript-eslint` 8.56, `eslint-plugin-unicorn` 63, added `@eslint/eslintrc` and `@eslint/js` - Updated demo Svelte app dependencies ## [2.0.0] - 2026-02-09 ### BREAKING CHANGES - **Removed `psychic2` engine**: The `psychic` engine now uses PsychicHttpServer V2 API exclusively. The old `-e psychic` (V1) engine has been removed. If you were using `-e psychic2`, change to `-e psychic`. If you were using `-e psychic` with PsychicHttpServer V1, you must upgrade to PsychicHttpServer V2. - **3 engines instead of 4**: Valid engines are now `psychic`, `async`, and `espidf` ### Added - **Dry run mode**: New `--dryrun` flag shows file summary without writing the output file. Useful for CI/CD validation and size budget checks - **C++ identifier validation**: `--define` and `--espmethod` values are now validated to be valid C++ identifiers (letters, digits, underscores, cannot start with a digit) - **Unknown MIME type warnings**: Files with unrecognized extensions now log a warning with the fallback MIME type (`text/plain`) - **Negative cachetime validation**: `--cachetime` now rejects negative values both in CLI arguments and RC files - RC file support for `dryrun` as a boolean property ### Changed - **PsychicHttpServer V2 is now the default**: The `psychic` engine generates PsychicHttpServer V2 API code (response passed as parameter instead of return value) - **Improved gzip size tracking**: `gzipsize` summary now reflects actual bytes stored (uncompressed size for files where gzip was not beneficial) - **Optimized byte array generation**: `bufferToByteString()` uses string concatenation instead of intermediate array allocation for better memory efficiency - **Improved C++ dataname generation**: Uses `\W` regex for broader special character replacement; identifiers starting with a digit are prefixed with `_` - **Strict `isDefault` matching**: Only exact `index.html` or `index.htm` filenames are treated as default endpoints (previously matched any filename starting with `index.htm`) - **Code cleanup**: Extracted `applyFlag()` helper to eliminate duplicated flag-handling code in CLI parser - **Better output formatting**: File sizes displayed as `kB` instead of raw bytes - Wrapped pipeline in exported `main()` function for better testability - **Complete runnable demo projects**: Arduino/PlatformIO and ESP-IDF demos are now fully working examples with a Svelte web interface that controls the built-in LED on the ESP32 - Updated dependencies ## [1.16.1] - 2026-02-01 ### Fixed - **RC File Property Naming Consistency**: Standardized all RC file properties to lowercase for consistency with CLI flag naming - `basePath` → `basepath` - `maxSize` → `maxsize` - `maxGzipSize` → `maxgzipsize` - Note: This is a breaking change if using these properties in RC files with camelCase names - **Added `noindexcheck` to RC File Support**: The `noindexcheck` option can now be set in RC files as a boolean - Updated help text RC file example to show all available options including `basepath`, `maxsize`, `maxgzipsize`, and `noindexcheck` ## [1.16.0] - 2026-02-01 ### Added - **Size Budget Constraints**: New `--maxsize` and `--maxgzipsize` options to enforce file size limits - Supports human-readable suffixes: `k`/`K` (×1024) and `m`/`M` (×1024²) - Examples: `--maxsize=400k`, `--maxgzipsize=1.5m`, `--maxsize=409600` - Validates total uncompressed size (`--maxsize`) and total gzip size (`--maxgzipsize`) - Exits with non-zero code when budget exceeded, enabling CI/CD pipeline failures - Detailed error message shows budget, actual size, overage amount, and percentage - RC file support: `maxsize` and `maxgzipsize` properties (accept strings with k/m suffix or numbers) - Works with all 4 engines (psychic, psychic2, async, espidf) - New `getSizeBudgetExceededError()` function in `src/errorMessages.ts` with actionable hints - New `parseSize()` function in `src/commandLine.ts` for parsing size values with k/m suffixes - 40+ new unit tests for size budget feature: - Size parsing (k/m suffixes, decimal values, edge cases) - CLI argument parsing (`--maxsize`, `--maxgzipsize`) - RC file validation for size properties - Error message content validation ### Changed - **CLI Option Naming**: Simplified command-line option names (breaking change for scripts using old names) - `--base-path` → `--basepath` - `--no-index-check` → `--noindexcheck` - Updated help text with new size budget options and corrected flag names - Updated README.md with corrected flag names ## [1.15.0] - 2026-02-01 - **Base Path Support**: New `--base-path` option to prefix all generated routes with a URL path - Usage: `--base-path=/ui` or `--base-path=/admin` - All routes are prefixed (e.g., `/index.html` becomes `/ui/index.html`) - File manifest paths include the basePath prefix - When basePath is set, creates explicit route for basePath instead of using `defaultEndpoint` - Validation: Must start with `/`, must not end with `/`, no double slashes allowed - RC file support: Can be set via `basePath` property in `.svelteesp32rc.json` - Supports `$npm_package_*` variable interpolation - Enables hosting multiple frontends in a single ESP32 firmware (e.g., `/admin` and `/user`) - **onFileServed Hook**: Weak function called whenever a file is served for metrics, logging, or telemetry - Signature: `void {{definePrefix}}_onFileServed(const char* path, int statusCode)` - Parameters: `path` (URL being served, e.g., "/index.html"), `statusCode` (200 or 304) - Always generated with zero overhead when unused (weak linkage) - Hook name uses `--define` prefix: default `SVELTEESP32_onFileServed`, custom with `--define=MYAPP` becomes `MYAPP_onFileServed` - Called before every `send()`: both 200 (content) and 304 (cache hit) responses - Works with all 4 engines (psychic, psychic2, async, espidf) - ESP-IDF uses C-compatible syntax (no `extern "C"`) ## [1.14.0] - 2026-01-31 ### Added - **File Manifest Feature**: Runtime introspection of all embedded files in generated C++ headers - New `FileInfo` struct with `path`, `size`, `gzipSize`, `etag`, and `contentType` fields - Auto-generated `FILES[]` array and `FILE_COUNT` constant for iterating over embedded files - `gzipSize` contains actual compressed size when gzip enabled, 0 otherwise - `etag` references the ETag variable when enabled, `NULL` when `--etag=false` - Works with all 4 engines (psychic, psychic2, async, espidf) - ESP-IDF uses C-compatible `typedef struct` syntax for compatibility - Always generated (no CLI flag needed) - Enables runtime features like file listings, size reporting, and cache management ### Changed - **SHA256 for ETags**: Replaced MD5 with SHA256 for ETag hash generation - Improved security with cryptographically stronger hash algorithm - SHA256 is more collision-resistant than MD5 - Generated ETag format remains compatible with HTTP standards ### Fixed - Updated dependencies to latest versions ## [1.13.1] - 2025-12-11 ### Added - **Enhanced Error Messages with Framework-Specific Hints**: Comprehensive error messages with actionable "How to fix" guidance for all 4 engines (psychic, psychic2, async, espidf) - **Missing index.html Validation**: Automatic check for default entry point with engine-specific routing examples - Framework-specific hints: `server->defaultEndpoint` (psychic/psychic2), `server.on("/")` (async), `httpd_register_uri_handler` (espidf) - Detects index.html/index.htm in root or subdirectories - `--no-index-check` flag to bypass validation for API-only applications - Clear explanation of why index.html matters and alternative solutions - **Invalid Engine Error**: Enhanced error message with complete engine reference - Lists all 4 valid engines with descriptions and platform compatibility - Example commands and RC file format - Documentation link for further reference - **Sourcepath Not Found Error**: Detailed troubleshooting guidance - Build tool configuration hints (Vite, Webpack, Rollup) - Framework-specific build commands (Svelte, React, Vue, Angular) - Shows current directory and resolved path for debugging - Separate messages for "not found" vs "not a directory" scenarios - **max_uri_handlers Configuration Hints**: Console output after successful generation - Only shown for engines that require it (psychic, psychic2, espidf) - Calculates recommended value: `routeCount + 5` (safety margin) - Engine-specific configuration examples with code snippets - Lists runtime symptoms to watch for (404 errors, ESP_ERR_HTTPD_HANDLERS_FULL) - New `src/errorMessages.ts` module (~180 lines) with pure, testable functions: - `getMissingIndexError()` - Engine-specific index.html error messages - `getInvalidEngineError()` - Comprehensive invalid engine guidance - `getSourcepathNotFoundError()` - Build tool and framework hints - `getMaxUriHandlersHint()` - Configuration guidance for handler limits - Consistent multi-line error format with color-coded sections - 32 comprehensive unit tests in `test/unit/errorMessages.test.ts` with 100% coverage: - Tests for all 4 engines (psychic, psychic2, async, espidf) - Message content validation (error title, explanations, hints) - Edge cases (unknown engines, empty strings) - Framework-specific code snippet verification - Enhanced test suite with 156 total tests passing: - Updated `test/unit/commandLine.test.ts` with enhanced error validation - Updated `test/unit/file.test.ts` with index.html validation tests - All tests use proper TypeScript types (replaced `any` with `vi.mocked()`) ### Changed - Improved developer experience with clear, actionable error messages - Error messages now include framework-specific code examples - Console output uses color-coded sections for better readability - Validation happens early with helpful guidance before processing begins - Updated `src/commandLine.ts` to use enhanced error messages (lines 84-85, 559-567) - Updated `src/file.ts` with index.html validation (lines 117-125) - Updated `src/index.ts` to show max_uri_handlers hints (lines 150-153) ### Fixed - Linting errors: renamed `currentDir` to `currentDirectory` (unicorn/prevent-abbreviations) - Test type safety: replaced explicit `any` types with `vi.mocked()` helper ## [1.13.0] - 2025-12-04 ### Added - **NPM Package Variable Interpolation**: RC files now support automatic variable substitution from `package.json` - Syntax: `$npm_package_` (e.g., `$npm_package_version`, `$npm_package_name`) - Supported in all string fields: `version`, `define`, `sourcepath`, `outputfile`, `espmethod`, and `exclude` patterns - Nested field support: `$npm_package_repository_type` accesses `packageJson.repository.type` - Multiple variables in one field: `"$npm_package_name-v$npm_package_version"` → `"myapp-v1.2.3"` - Smart regex pattern stops at underscore + uppercase (e.g., `$npm_package_name_STATIC` → interpolates `name`, keeps `_STATIC`) - `package.json` must exist in same directory as RC file - Clear error messages when variables are used but `package.json` not found, listing affected fields - Unknown variables left unchanged (not an error) for flexibility - 5 new core functions in `src/commandLine.ts` (lines 125-220): - `findPackageJson()` - Locates package.json in RC file directory - `parsePackageJson()` - Reads and parses package.json with error handling - `getNpmPackageVariable()` - Extracts values using underscore-separated path traversal - `checkStringForNpmVariable()` - Helper for variable detection - `hasNpmVariables()` - Optimization to skip interpolation when not needed - `interpolateNpmVariables()` - Main function processing all string fields - 20+ comprehensive unit tests in `test/unit/commandLine.test.ts` (lines 604-952): - Simple field extraction (`$npm_package_version`) - Nested field extraction (`$npm_package_repository_type`) - Multiple variables in one string - Exclude array pattern interpolation - Error handling (missing package.json, invalid JSON) - Integration tests with RC file loading - Updated `.svelteesp32rc.example.json` with variable interpolation examples ### Changed - Enhanced RC file loading flow to interpolate npm variables before validation - Interpolation integrated seamlessly: defaults → RC file (with interpolation) → CLI arguments - Updated README.md with comprehensive "NPM Package Variable Interpolation" section: - Syntax explanation and examples - Nested field access documentation - Multiple variable usage examples - Requirements and error behavior - Common use cases (version sync, dynamic naming, CI/CD) - Updated CLAUDE.md with detailed implementation documentation: - Core functions and processing flow - Regex pattern design rationale - Error handling strategy - Testing coverage details - Example usage and benefits - All 118 tests passing (69 commandLine tests including 20 new npm interpolation tests) ### Use Cases - **Version Synchronization**: `"version": "v$npm_package_version"` keeps header version in sync with package.json - **Dynamic C++ Defines**: `"define": "$npm_package_name"` uses actual package name for defines - **CI/CD Integration**: Reusable RC files across different projects with variable package names - **Single Source of Truth**: Project metadata maintained only in package.json ## [1.12.1] - 2025-12-04 ### Changed - **Configuration Display in Generated Headers**: Renamed `//cmdline:` to `//config:` in all generated header files - Now displays effective configuration values regardless of source (RC file, CLI arguments, or both) - Shows all active configuration parameters: `engine`, `sourcepath`, `outputfile`, `etag`, `gzip`, `cachetime`, `espmethod`, `define`, and `exclude` patterns - Provides complete traceability of configuration used for code generation - Format: `//config: engine=psychic sourcepath=./dist outputfile=./output.h etag=true gzip=true ...` ### Fixed - Configuration comment in generated headers now displays values when using RC file (previously showed empty when using RC file without CLI arguments) ## [1.12.0] - 2025-12-04 ### Added - **Configuration File Support**: New RC file feature (`.svelteesp32rc.json`) for storing frequently-used options - Automatic search in current directory and user home directory - `--config` flag for specifying custom RC file path - All CLI options can be configured in RC file using long-form property names - CLI arguments always override RC file values (3-stage merge: defaults → RC → CLI) - **Replace mode** for exclude patterns: RC or CLI exclude completely replaces defaults - Cyan-colored console output showing which RC file was loaded - Comprehensive validation with unknown property warnings to catch typos - Example RC file (`.svelteesp32rc.example.json`) included in repository - 16 new unit tests for RC file functionality: - RC file discovery (current directory, home directory, custom path) - RC file parsing and validation (invalid JSON, invalid values, unknown properties) - CLI override behavior - Exclude pattern replace mode - Backward compatibility - Updated test coverage to 84.32% for `commandLine.ts` (up from 84.56%) - TypeScript type safety improvements: replaced `any` with `unknown` in `validateRcConfig()` ### Changed - Enhanced `commandLine.ts` with RC file loading, validation, and merging logic - Updated help text with RC file documentation and examples - Enhanced README.md with comprehensive "Configuration File" section: - Quick start guide with example RC file - Configuration reference table mapping RC properties to CLI flags - CLI override examples - Multiple environment setup guide (dev/prod configs) - Exclude pattern behavior documentation - Updated command line options table with `--config` flag - Error message for missing `--sourcepath` now mentions RC file option - All 92 tests passing with new RC file test suite ### Fixed - ESLint error: replaced `any` type with `unknown` in configuration validation ## [1.11.0] - 2025-12-03 ### Added - **File Exclusion Feature**: New `--exclude` flag for filtering files using glob patterns - Support for simple wildcards (`*.map`, `*.txt`) and directory patterns (`test/**/*.js`) - Multiple pattern formats: repeated flags (`--exclude *.map --exclude *.md`) and comma-separated (`--exclude="*.map,*.md"`) - Default exclusions for common system and development files (`.DS_Store`, `Thumbs.db`, `.git`, `.svn`, `*.swp`, `*~`, `.gitignore`, `.gitattributes`) - Cyan-colored console output showing excluded file count and list - Cross-platform path normalization for Windows compatibility - Uses `picomatch` library (transitive dependency via `tinyglobby`) for pattern matching - Unit testing infrastructure using Vitest - Comprehensive test coverage (~72%) for core modules: - `commandLine.ts` (~90%): CLI argument parsing and validation including exclude patterns - `file.ts` (100%): File operations, duplicate detection, and exclusion filtering - `cppCode.ts` (96.62%): C++ code generation and templates - `consoleColor.ts` (100%): Console utilities including new `cyanLog` function - 15 new unit tests for file exclusion feature (8 for CLI parsing, 7 for exclusion logic) - Test fixtures for validating file processing - Coverage reports with HTML output - `cyanLog()` color function for exclusion output messages ### Changed - Updated file collection pipeline to filter excluded files after pre-compressed detection - Enhanced `commandLine.ts` with exclude pattern parsing for three argument formats - Enhanced `file.ts` with `isExcluded()` function and exclusion reporting - Updated `.gitignore` to exclude `coverage/` directory - Enhanced documentation with testing sections and comprehensive file exclusion guide - Updated README.md with file exclusion section including usage examples, pattern syntax, and default exclusions - Updated CLAUDE.md with technical file exclusion implementation details and example output ## [1.10.0] - 2025-11-20 ### Changed - Replaced `ts-command-line-args` dependency with native Node.js argument parser - Reduced package dependencies from 4 to 3 runtime dependencies - Improved CLI argument parsing with custom implementation using `process.argv` ## [1.9.4] - 2025-11-18 ### Changed - Fix CVE-2025-64756 glob vulnerability ## [1.9.3] - 2025-11-01 ### Added - ETag validation support for ESP-IDF engine (`httpd_req_get_hdr_value_len` and `httpd_req_get_hdr_value_str`) - HTTP 304 Not Modified response handling for ESP-IDF engine, matching behavior of other engines ### Changed - Optimized generated C++ code for ESPAsyncWebServer engine: - Optimized If-None-Match header lookups (single call instead of `hasHeader()` + `getHeader()`) - Removed unnecessary `String()` temporary objects in ETag comparisons (use `.equals()` directly) - Optimized generated C++ code for PsychicHttpServer engines (psychic, psychic2): - Removed unnecessary `String()` temporary objects in ETag comparisons (use `.equals()` directly) ## [1.9.2] - 2025-10-18 ### Changed - Improved CLAUDE.md with detailed architecture information, processing pipeline details, and template system documentation - Enhanced README.md with comprehensive updates: - Added Node.js >= 20 and npm >= 9 requirements - Added ESP-IDF native code usage example - Improved generated code examples with complete header structure - Enhanced engine descriptions with detailed use cases - Added PsychicHttpServer `max_uri_handlers` configuration note - Improved compression output examples with percentage ratios - Better Q&A explanations for PROGMEM usage and file size - Updated command line options table with clarifications - Fixed inaccuracies in MIME type package references - Reformatted CHANGELOG.md to follow Keep a Changelog standard with proper categorization and GitHub comparison links ## [1.9.1] - 2024-09-13 ### Changed - Updated dependencies - **BREAKING**: Require Node.js >= 20 and npm >= 9 ## [1.9.0] - 2024-03-31 ### Added - Code generator support for native ESP-IDF web server (`-e espidf`) - New `src/cppCodeEspIdf.ts` module for ESP-IDF template generation - ESP-IDF demo project in `demo/esp32idf/` ## [1.8.1] - 2023-12-XX ### Fixed - HTTP header casing issues ## [1.8.0] - 2023-XX-XX ### Changed - Updated to use the new and maintained ESPAsyncWebServer from https://github.com/ESP32Async/ESPAsyncWebServer - Adapted to ESPAsyncWebServer API deprecations ## [1.7.1] - 2023-XX-XX ### Fixed - Reverted to **mime-types** npm package due to ESM compatibility issues ## [1.7.0] - 2023-XX-XX ### Added - `--cachetime` command line option to control browser caching behavior - Ability to set `max-age` cache control header value (in seconds) - Works in conjunction with ETag to replace default `no-cache` response ## [1.6.1] - 2023-XX-XX ### Changed - Force publish to npm registry ## [1.6.0] - 2023-XX-XX ### Changed - Switched to `mime-types` package for better MIME type handling - Updated MIME type for JavaScript files to standard `text/javascript` (was `application/javascript`) ## [1.5.2] - 2023-XX-XX ### Changed - Added comment in PsychicHttpServer generated code suggesting minimum `server.config.max_uri_handlers` setting (default 20) to ensure all files can be served ## [1.5.1] - 2023-XX-XX ### Fixed - Fixed error when file names contain `@` character - Enhanced character protection for file name conversion to C++ identifiers: `!&()+./@{}~-` ## [1.5.0] - 2023-XX-XX ### Added - New engine type `-e psychic2` for PsychicHttpServer V2 support - PsychicHttpServer V2 template with updated API (response passed as parameter) ### Changed - Test suite now uses GitHub repositories instead of npm packages ## [1.4.1] - 2023-XX-XX ### Added - Duplicate file detection using SHA256 hashing - Logging of files with identical content - Support for multiple linting tools (ESLint with TypeScript, Prettier, Unicorn plugins) ## [1.4.0] - 2023-XX-XX ### Added - Three-state option support for `--etag` and `--gzip` parameters: `true|false|compiler` - `compiler` mode generates both variants in .h file with C++ preprocessor directives - Allows runtime configuration via `SVELTEESP32_ENABLE_ETAG` and `SVELTEESP32_ENABLE_GZIP` defines - `--created` option to include creation timestamp in generated header - `--version` option to include version string in generated header - Comprehensive test system generating all parameter combinations (2×9 builds) - Automatic output directory creation if it doesn't exist - Pre-compressed file detection (skips `.gz`, `.br`, `.brotli` if original exists) - Colored console output for better readability - Warning messages when non-effective directives are used ### Changed - **BREAKING**: Renamed `--no-gzip` to `--gzip` (inverted logic) - Increased gzip threshold from default to 1024 bytes minimum - Reduced generated .h file size by 35-50% through optimizations - Separated demo environment - Improved Svelte demo application - Updated pipeline Node.js version ## [1.3.1] - 2023-XX-XX ### Changed - Filename C++ defines are now uppercased (e.g., `SVELTEESP32_FILE_INDEX_HTML`) ### Added - File count statistics grouped by extension (e.g., `SVELTEESP32_CSS_FILES`) ## [1.3.0] - 2023-XX-XX ### Added - C++ preprocessor defines for build-time validation: - `{PREFIX}_COUNT` - Total file count - `{PREFIX}_SIZE` - Total uncompressed size - `{PREFIX}_SIZE_GZIP` - Total gzip compressed size - `{PREFIX}_FILE_{FILENAME}` - Individual file markers - `{PREFIX}_{EXT}_FILES` - Count by file extension - Ability to use `#ifndef` and `#error` for compile-time checks ## [1.2.4] - 2023-XX-XX ### Changed - Updated dependencies ## [1.2.2] - 2023-XX-XX ### Added - Necessary C++ includes in generated headers ## [1.2.1] - 2023-XX-XX ### Fixed - Windows path handling ## [1.2.0] - 2023-XX-XX ### Added - ESP8266/ESP8285 support - PROGMEM directive usage for ESPAsyncWebServer engine ## [1.1.0] - 2023-XX-XX ### Added - ETag HTTP header support for cache validation - MD5 hash generation for ETag values - HTTP 304 Not Modified response handling - `--etag` command line option ## [1.0.0] - 2023-XX-XX ### Added - Initial release - Support for PsychicHttpServer (ESP32) - Support for ESPAsyncWebServer (ESP32/ESP8266) - Automatic gzip compression for files >1024 bytes with >15% size reduction - Binary data embedding as const arrays - MIME type detection - Handlebars-based C++ code generation - CLI interface with `-s`, `-e`, `-o` options - `index.html` automatic default route handling [3.1.2]: https://github.com/BCsabaEngine/svelteesp32/compare/v3.1.1...v3.1.2 [3.1.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v3.1.0...v3.1.1 [3.1.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v3.0.2...v3.1.0 [3.0.2]: https://github.com/BCsabaEngine/svelteesp32/compare/v3.0.1...v3.0.2 [3.0.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v3.0.0...v3.0.1 [3.0.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.4.1...v3.0.0 [2.4.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.4.0...v2.4.1 [2.4.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.3.3...v2.4.0 [2.3.3]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.3.2...v2.3.3 [2.3.2]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.3.1...v2.3.2 [2.3.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.3.0...v2.3.1 [2.3.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.2.2...v2.3.0 [2.2.2]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.2.1...v2.2.2 [2.2.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.2.0...v2.2.1 [2.2.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.1.0...v2.2.0 [2.1.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.0.1...v2.1.0 [2.0.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v2.0.0...v2.0.1 [2.0.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.16.1...v2.0.0 [1.16.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.16.0...v1.16.1 [1.16.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.15.0...v1.16.0 [1.15.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.14.0...v1.15.0 [1.14.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.13.1...v1.14.0 [1.13.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.13.0...v1.13.1 [1.13.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.12.1...v1.13.0 [1.12.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.12.0...v1.12.1 [1.12.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.11.0...v1.12.0 [1.11.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.10.0...v1.11.0 [1.10.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.9.4...v1.10.0 [1.9.4]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.9.3...v1.9.4 [1.9.3]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.9.2...v1.9.3 [1.9.2]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.9.1...v1.9.2 [1.9.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.9.0...v1.9.1 [1.9.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.8.1...v1.9.0 [1.8.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.8.0...v1.8.1 [1.8.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.7.1...v1.8.0 [1.7.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.7.0...v1.7.1 [1.7.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.6.1...v1.7.0 [1.6.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.6.0...v1.6.1 [1.6.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.5.2...v1.6.0 [1.5.2]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.5.1...v1.5.2 [1.5.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.5.0...v1.5.1 [1.5.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.4.1...v1.5.0 [1.4.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.4.0...v1.4.1 [1.4.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.3.1...v1.4.0 [1.3.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.3.0...v1.3.1 [1.3.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.2.4...v1.3.0 [1.2.4]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.2.2...v1.2.4 [1.2.2]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.2.1...v1.2.2 [1.2.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.2.0...v1.2.1 [1.2.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.0.0...v1.1.0 [1.0.0]: https://github.com/BCsabaEngine/svelteesp32/releases/tag/v1.0.0