svelteesp32 3.0.0 → 3.0.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
@@ -88,7 +88,7 @@ void setup() {
88
88
 
89
89
  ## What's New
90
90
 
91
- - **v3.0.0** — **Vite plugin** (`import { svelteESP32 } from 'svelteesp32/vite'`) generates the header automatically after every build; `npx svelteesp32 init` interactive RC file wizard; Node.js >= 22 required
91
+ - **v3.0.0** — **Vite plugin** (`import { svelteESP32 } from 'svelteesp32/vite'`) generates the header automatically after every build — call with no argument for RC file mode or pass an options object for plugin-options mode; `npx svelteesp32 init` interactive RC file wizard; Node.js >= 22 required
92
92
  - **v2.4.0** — `--analyze` for CI size budget checks (per-file table, exits 1 on over-budget); `--manifest` to write a companion JSON manifest alongside the header
93
93
  - **v2.3.0** — `--cachetime-html` and `--cachetime-assets` for per-type cache control (e.g. `no-cache` for HTML, 1-year for content-hashed JS/CSS)
94
94
  - **v2.2.0** — SPA routing catch-all (`--spa`) for client-side routers on all four engines
@@ -132,13 +132,27 @@ It asks for engine, source path, output path, and ETag preference, writes the RC
132
132
 
133
133
  For Vite-based projects (SvelteKit, React, Vue, Vanilla) you can skip the manual CLI step entirely — the plugin hooks into the build pipeline and regenerates the C++ header automatically after every `vite build`.
134
134
 
135
- **`vite.config.ts`**
135
+ The plugin has two exclusive modes — pick one:
136
+
137
+ **RC file mode** — call with no argument (or a string path to a custom RC file). All settings come from `.svelteesp32rc.json`; `outputfile` in the RC file is required.
136
138
 
137
139
  ```ts
138
140
  import { svelteKit } from '@sveltejs/kit/vite';
139
141
  import { svelteESP32 } from 'svelteesp32/vite';
140
142
  import { defineConfig } from 'vite';
141
143
 
144
+ export default defineConfig({
145
+ plugins: [
146
+ svelteKit(),
147
+ svelteESP32() // auto-discover .svelteesp32rc.json
148
+ // svelteESP32('/path/to/custom.rc.json') // or specify path explicitly
149
+ ]
150
+ });
151
+ ```
152
+
153
+ **Plugin options mode** — call with an options object. The RC file is completely ignored; `output` is required.
154
+
155
+ ```ts
142
156
  export default defineConfig({
143
157
  plugins: [
144
158
  svelteKit(),
@@ -147,27 +161,27 @@ export default defineConfig({
147
161
  engine: 'psychic',
148
162
  etag: 'always',
149
163
  gzip: 'always',
150
- cachetimeHtml: 0,
151
- cachetimeAssets: 31536000
164
+ cachetimehtml: 0,
165
+ cachetimeassets: 31536000
152
166
  })
153
167
  ]
154
168
  });
155
169
  ```
156
170
 
157
- The `output` path is required; all other options mirror the CLI flags and have the same defaults. `sourcepath` defaults to Vite's `build.outDir` when omitted.
171
+ `sourcepath` defaults to Vite's `build.outDir` in both modes.
158
172
 
159
173
  **Plugin options**
160
174
 
161
175
  | Option | Type | Default | Description |
162
176
  | ----------------- | ------------------------------------------- | ------------------------- | -------------------------------------------------- |
163
- | `output` | `string` | (required) | Output `.h` file path |
177
+ | `output` | `string` | RC `outputfile` | Output `.h` file path |
164
178
  | `sourcepath` | `string` | Vite's `build.outDir` | Source directory (compiled web files) |
165
179
  | `engine` | `'psychic'\|'async'\|'espidf'\|'webserver'` | `'psychic'` | Target web server engine |
166
180
  | `etag` | `'always'\|'never'\|'compiler'` | `'never'` | ETag generation mode |
167
181
  | `gzip` | `'always'\|'never'\|'compiler'` | `'always'` | Gzip compression mode |
168
182
  | `cachetime` | `number` | `0` | `Cache-Control: max-age` in seconds (all files) |
169
- | `cachetimeHtml` | `number` | (unset) | max-age for HTML files (overrides `cachetime`) |
170
- | `cachetimeAssets` | `number` | (unset) | max-age for non-HTML files (overrides `cachetime`) |
183
+ | `cachetimehtml` | `number` | (unset) | max-age for HTML files (overrides `cachetime`) |
184
+ | `cachetimeassets` | `number` | (unset) | max-age for non-HTML files (overrides `cachetime`) |
171
185
  | `exclude` | `string[]` | `[]` | Glob patterns to exclude |
172
186
  | `basepath` | `string` | (none) | URL prefix for all routes |
173
187
  | `espmethod` | `string` | `'initSvelteStaticFiles'` | Generated init function name |
@@ -176,9 +190,9 @@ The `output` path is required; all other options mirror the CLI flags and have t
176
190
  | `created` | `boolean` | `false` | Include creation timestamp |
177
191
  | `spa` | `boolean` | `false` | Serve `index.html` for unmatched routes |
178
192
  | `manifest` | `boolean` | `false` | Write companion `.manifest.json` |
179
- | `noIndexCheck` | `boolean` | `false` | Skip `index.html` validation |
180
- | `maxSize` | `number` | (none) | Max total uncompressed size in bytes |
181
- | `maxGzipSize` | `number` | (none) | Max total gzip size in bytes |
193
+ | `noindexcheck` | `boolean` | `false` | Skip `index.html` validation |
194
+ | `maxsize` | `number` | (none) | Max total uncompressed size in bytes |
195
+ | `maxgzipsize` | `number` | (none) | Max total gzip size in bytes |
182
196
 
183
197
  ### Generate Header File (CLI)
184
198
 
@@ -21,8 +21,9 @@ export interface ICopyFilesArguments {
21
21
  spa?: boolean;
22
22
  manifest?: boolean;
23
23
  help?: boolean;
24
+ configSource: 'cli' | 'rcfile' | 'vite';
24
25
  }
25
- interface IRcFileConfig {
26
+ export interface IRcFileConfig {
26
27
  engine?: 'psychic' | 'async' | 'espidf' | 'webserver';
27
28
  sourcepath?: string;
28
29
  outputfile?: string;
@@ -46,11 +47,12 @@ interface IRcFileConfig {
46
47
  manifest?: boolean | 'true' | 'false';
47
48
  }
48
49
  declare function validateCppIdentifier(value: string, name: string): string;
50
+ export declare function validateBasePath(value: string): string;
49
51
  declare function parseSize(value: string, name: string): number;
50
52
  declare function getNpmPackageVariable(packageJson: Record<string, unknown>, variableName: string): string | undefined;
51
53
  declare function hasNpmVariables(config: IRcFileConfig): boolean;
52
54
  declare function interpolateNpmVariables(config: IRcFileConfig, rcFilePath: string): IRcFileConfig;
55
+ export declare function loadRcFileConfig(configPath?: string): Partial<IRcFileConfig>;
56
+ export declare function parseArguments(): ICopyFilesArguments;
53
57
  export { getNpmPackageVariable, hasNpmVariables, interpolateNpmVariables, parseSize, validateCppIdentifier };
54
58
  export declare function formatConfiguration(cmdLine: ICopyFilesArguments): string;
55
- export declare const cmdLine: ICopyFilesArguments;
56
- //# sourceMappingURL=commandLine.d.ts.map
@@ -3,7 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.cmdLine = void 0;
6
+ exports.validateBasePath = validateBasePath;
7
+ exports.loadRcFileConfig = loadRcFileConfig;
8
+ exports.parseArguments = parseArguments;
7
9
  exports.getNpmPackageVariable = getNpmPackageVariable;
8
10
  exports.hasNpmVariables = hasNpmVariables;
9
11
  exports.interpolateNpmVariables = interpolateNpmVariables;
@@ -255,6 +257,12 @@ function loadRcFile(rcPath) {
255
257
  throw error;
256
258
  }
257
259
  }
260
+ function loadRcFileConfig(configPath) {
261
+ const rcPath = findRcFile(configPath);
262
+ if (!rcPath)
263
+ return {};
264
+ return loadRcFile(rcPath);
265
+ }
258
266
  function validateSizeOption(configObject, key) {
259
267
  const value = configObject[key];
260
268
  if (value === undefined)
@@ -404,7 +412,8 @@ function parseArguments() {
404
412
  define: 'SVELTEESP32',
405
413
  cachetime: 0,
406
414
  exclude: [],
407
- basePath: ''
415
+ basePath: '',
416
+ configSource: 'cli'
408
417
  };
409
418
  if (rcConfig.engine)
410
419
  result.engine = rcConfig.engine;
@@ -525,8 +534,10 @@ function parseArguments() {
525
534
  const argument = arguments_[index];
526
535
  if (!argument)
527
536
  continue;
528
- if (argument === '--help' || argument === '-h')
537
+ if (argument === '--help' || argument === '-h') {
529
538
  showHelp();
539
+ return result;
540
+ }
530
541
  if (argument.startsWith('--') && argument.includes('=')) {
531
542
  const parts = argument.split('=');
532
543
  const flag = parts[0];
@@ -605,10 +616,12 @@ function parseArguments() {
605
616
  return result;
606
617
  }
607
618
  function formatConfiguration(cmdLine) {
619
+ const relativeOutput = node_path_1.default.relative(process.cwd(), cmdLine.outputfile);
608
620
  const parts = [
621
+ `source=${cmdLine.configSource}`,
609
622
  `engine=${cmdLine.engine}`,
610
623
  `sourcepath=${cmdLine.sourcepath}`,
611
- `outputfile=${cmdLine.outputfile}`,
624
+ `outputfile=${relativeOutput}`,
612
625
  `etag=${cmdLine.etag}`,
613
626
  `gzip=${cmdLine.gzip}`,
614
627
  `cachetime=${cmdLine.cachetime}`
@@ -639,13 +652,3 @@ function formatConfiguration(cmdLine) {
639
652
  parts.push(`exclude=[${cmdLine.exclude.join(', ')}]`);
640
653
  return parts.join(' ').replace(/[\n\r]/g, ' ');
641
654
  }
642
- exports.cmdLine = parseArguments();
643
- if (!(0, node_fs_1.existsSync)(exports.cmdLine.sourcepath)) {
644
- console.error((0, errorMessages_1.getSourcepathNotFoundError)(exports.cmdLine.sourcepath, 'not_found'));
645
- process.exit(1);
646
- }
647
- if (!(0, node_fs_1.statSync)(exports.cmdLine.sourcepath).isDirectory()) {
648
- console.error((0, errorMessages_1.getSourcepathNotFoundError)(exports.cmdLine.sourcepath, 'not_directory'));
649
- process.exit(1);
650
- }
651
- console.log(`[SvelteESP32] Generate code for ${exports.cmdLine.engine} engine`);
@@ -2,4 +2,3 @@ export declare const greenLog: (s: string) => string;
2
2
  export declare const yellowLog: (s: string) => string;
3
3
  export declare const redLog: (s: string) => string;
4
4
  export declare const cyanLog: (s: string) => string;
5
- //# sourceMappingURL=consoleColor.d.ts.map
package/dist/cppCode.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ICopyFilesArguments } from './commandLine';
1
2
  export type CppCodeSource = {
2
3
  filename: string;
3
4
  dataname: string;
@@ -14,5 +15,4 @@ export type ExtensionGroup = {
14
15
  count: number;
15
16
  };
16
17
  export type ExtensionGroups = ExtensionGroup[];
17
- export declare const getCppCode: (sources: CppCodeSources, filesByExtension: ExtensionGroups) => string;
18
- //# sourceMappingURL=cppCode.d.ts.map
18
+ export declare const getCppCode: (sources: CppCodeSources, filesByExtension: ExtensionGroups, options: ICopyFilesArguments) => string;
package/dist/cppCode.js CHANGED
@@ -1010,17 +1010,17 @@ const createHandlebarsHelpers = () => {
1010
1010
  }
1011
1011
  };
1012
1012
  };
1013
- const getCppCode = (sources, filesByExtension) => {
1014
- const template = (0, handlebars_1.compile)(getTemplate(commandLine_1.cmdLine.engine));
1013
+ const getCppCode = (sources, filesByExtension, options) => {
1014
+ const template = (0, handlebars_1.compile)(getTemplate(options.engine));
1015
1015
  const transformedSources = sources.map((s) => {
1016
1016
  const effectiveCacheTime = s.mime === 'text/html'
1017
- ? (commandLine_1.cmdLine.cachetimeHtml ?? commandLine_1.cmdLine.cachetime)
1018
- : (commandLine_1.cmdLine.cachetimeAssets ?? commandLine_1.cmdLine.cachetime);
1019
- return transformSourceToTemplateData(s, commandLine_1.cmdLine.etag, effectiveCacheTime);
1017
+ ? (options.cachetimeHtml ?? options.cachetime)
1018
+ : (options.cachetimeAssets ?? options.cachetime);
1019
+ return transformSourceToTemplateData(s, options.etag, effectiveCacheTime);
1020
1020
  });
1021
- const spaSource = commandLine_1.cmdLine.spa ? transformedSources.find((s) => s.isDefault) : undefined;
1021
+ const spaSource = options.spa ? transformedSources.find((s) => s.isDefault) : undefined;
1022
1022
  const templateData = {
1023
- config: (0, commandLine_1.formatConfiguration)(commandLine_1.cmdLine),
1023
+ config: (0, commandLine_1.formatConfiguration)(options),
1024
1024
  now: (() => {
1025
1025
  const d = new Date();
1026
1026
  return `${d.toLocaleDateString()} ${d.toLocaleTimeString()}`;
@@ -1030,16 +1030,16 @@ const getCppCode = (sources, filesByExtension) => {
1030
1030
  fileGzipSize: sources.reduce((previous, current) => previous + current.contentGzip.length, 0).toString(),
1031
1031
  sources: transformedSources,
1032
1032
  filesByExtension,
1033
- etag: commandLine_1.cmdLine.etag,
1034
- gzip: commandLine_1.cmdLine.gzip,
1035
- created: commandLine_1.cmdLine.created,
1036
- version: commandLine_1.cmdLine.version,
1037
- methodName: commandLine_1.cmdLine.espmethod,
1038
- definePrefix: commandLine_1.cmdLine.define,
1039
- basePath: commandLine_1.cmdLine.basePath,
1040
- spa: !!commandLine_1.cmdLine.spa,
1033
+ etag: options.etag,
1034
+ gzip: options.gzip,
1035
+ created: options.created,
1036
+ version: options.version,
1037
+ methodName: options.espmethod,
1038
+ definePrefix: options.define,
1039
+ basePath: options.basePath,
1040
+ spa: !!options.spa,
1041
1041
  spaSource,
1042
- isPsychic: commandLine_1.cmdLine.engine === 'psychic',
1042
+ isPsychic: options.engine === 'psychic',
1043
1043
  maxUriHandlers: (sources.length + 5).toString()
1044
1044
  };
1045
1045
  const rawCode = template(templateData, { helpers: createHandlebarsHelpers() });
@@ -1,2 +1 @@
1
1
  export declare const espidfTemplate = "\n//engine: espidf\n//config: {{{config}}}\n{{#if created }}\n//created: {{now}}\n{{/if}}\n//\n\n{{#switch etag}}\n{{#case \"always\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n#warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched ON\n#endif\n{{/case}}\n{{#case \"never\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n#warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched OFF\n#endif\n{{/case}}\n{{/switch}}\n\n{{#switch gzip}}\n{{#case \"always\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n#warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched ON\n#endif\n{{/case}}\n{{#case \"never\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n#warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched OFF\n#endif\n{{/case}}\n{{/switch}}\n\n//\n{{#if version }}\n#define {{definePrefix}}_VERSION \"{{version}}\"\n{{/if}}\n#define {{definePrefix}}_COUNT {{fileCount}}\n#define {{definePrefix}}_SIZE {{fileSize}}\n#define {{definePrefix}}_SIZE_GZIP {{fileGzipSize}}\n\n//\n{{#each sources}}\n#define {{../definePrefix}}_FILE_{{this.datanameUpperCase}}\n{{/each}}\n\n//\n{{#each filesByExtension}}\n#define {{../definePrefix}}_{{this.extension}}_FILES {{this.count}}\n{{/each}}\n\n#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>\n#include <esp_err.h>\n#include <esp_http_server.h>\n\n//\n{{#switch gzip}}\n{{#case \"always\"}}\n {{#each sources}}\nstatic const unsigned char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };\n {{/each}}\n{{/case}}\n{{#case \"never\"}}\n {{#each sources}}\nstatic const unsigned char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };\n {{/each}}\n{{/case}}\n{{#case \"compiler\"}}\n#ifdef {{definePrefix}}_ENABLE_GZIP\n {{#each sources}}\nstatic const unsigned char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} };\n {{/each}}\n#else\n {{#each sources}}\nstatic const unsigned char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} };\n {{/each}}\n#endif \n{{/case}}\n{{/switch}}\n\n//\n{{#switch etag}}\n{{#case \"always\"}}\n {{#each sources}}\nstatic const char etag_{{this.dataname}}[] = \"{{this.sha256}}\";\n {{/each}}\n{{/case}}\n{{#case \"never\"}}\n{{/case}}\n{{#case \"compiler\"}}\n#ifdef {{definePrefix}}_ENABLE_ETAG\n {{#each sources}}\nstatic const char etag_{{this.dataname}}[] = \"{{this.sha256}}\";\n {{/each}}\n#endif\n{{/case}}\n{{/switch}}\n\n// File manifest struct (C-compatible typedef)\ntypedef struct {\n const char* path;\n uint32_t size;\n uint32_t gzipSize;\n const char* etag;\n const char* contentType;\n} {{definePrefix}}_FileInfo;\n\n// File manifest array\nstatic const {{definePrefix}}_FileInfo {{definePrefix}}_FILES[] = {\n{{#each sources}}\n { \"{{../basePath}}/{{this.filename}}\", {{this.length}}, {{this.gzipSizeForManifest}}, {{this.etagForManifest}}, \"{{this.mime}}\" },\n{{/each}}\n};\nstatic const size_t {{definePrefix}}_FILE_COUNT = sizeof({{definePrefix}}_FILES) / sizeof({{definePrefix}}_FILES[0]);\n\n// File served hook - override with your own implementation\n__attribute__((weak)) void {{definePrefix}}_onFileServed(const char* path, int statusCode) {}\n\n{{#each sources}}\n\nstatic esp_err_t file_handler_{{this.datanameUpperCase}} (httpd_req_t *req)\n{\n{{#switch ../etag}}\n{{#case \"always\"}}\n size_t hdr_len = httpd_req_get_hdr_value_len(req, \"If-None-Match\");\n if (hdr_len > 0) {\n char* hdr_value = malloc(hdr_len + 1);\n if (hdr_value == NULL) { httpd_resp_send_500(req); return ESP_FAIL; }\n if (httpd_req_get_hdr_value_str(req, \"If-None-Match\", hdr_value, hdr_len + 1) == ESP_OK) {\n if (strcmp(hdr_value, etag_{{this.dataname}}) == 0) {\n free(hdr_value);\n httpd_resp_set_status(req, \"304 Not Modified\");\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 304);\n httpd_resp_send(req, NULL, 0);\n return ESP_OK;\n }\n }\n free(hdr_value);\n }\n{{/case}}\n{{#case \"compiler\"}}\n #ifdef {{../definePrefix}}_ENABLE_ETAG\n size_t hdr_len = httpd_req_get_hdr_value_len(req, \"If-None-Match\");\n if (hdr_len > 0) {\n char* hdr_value = malloc(hdr_len + 1);\n if (hdr_value == NULL) { httpd_resp_send_500(req); return ESP_FAIL; }\n if (httpd_req_get_hdr_value_str(req, \"If-None-Match\", hdr_value, hdr_len + 1) == ESP_OK) {\n if (strcmp(hdr_value, etag_{{this.dataname}}) == 0) {\n free(hdr_value);\n httpd_resp_set_status(req, \"304 Not Modified\");\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 304);\n httpd_resp_send(req, NULL, 0);\n return ESP_OK;\n }\n }\n free(hdr_value);\n }\n #endif\n{{/case}}\n{{/switch}}\n httpd_resp_set_type(req, \"{{this.mime}}\");\n{{#switch ../gzip}}\n{{#case \"always\"}}\n{{#if this.isGzip}}\n httpd_resp_set_hdr(req, \"Content-Encoding\", \"gzip\");\n{{/if}}\n{{/case}}\n{{#case \"compiler\"}}\n {{#if this.isGzip}}\n #ifdef {{../definePrefix}}_ENABLE_GZIP\n httpd_resp_set_hdr(req, \"Content-Encoding\", \"gzip\");\n #endif \n {{/if}}\n{{/case}}\n{{/switch}}\n\n{{#switch ../etag}}\n{{#case \"always\"}}\n{{#this.cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"max-age={{value}}\");\n{{/this.cacheTime}}\n{{^this.cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"no-cache\");\n{{/this.cacheTime}}\n httpd_resp_set_hdr(req, \"ETag\", etag_{{this.dataname}});\n{{/case}}\n{{#case \"compiler\"}}\n #ifdef {{../definePrefix}}_ENABLE_ETAG\n{{#this.cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"max-age={{value}}\");\n{{/this.cacheTime}}\n{{^this.cacheTime}}\n httpd_resp_set_hdr(req, \"Cache-Control\", \"no-cache\");\n{{/this.cacheTime}}\n httpd_resp_set_hdr(req, \"ETag\", etag_{{this.dataname}});\n #endif \n{{/case}}\n{{/switch}}\n\n{{#switch ../gzip}}\n{{#case \"always\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n httpd_resp_send(req, (const char *)datagzip_{{this.dataname}}, {{this.lengthGzip}});\n{{/case}}\n{{#case \"never\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n httpd_resp_send(req, (const char *)data_{{this.dataname}}, {{this.length}});\n{{/case}}\n{{#case \"compiler\"}}\n {{../definePrefix}}_onFileServed(\"{{../basePath}}/{{this.filename}}\", 200);\n #ifdef {{../definePrefix}}_ENABLE_GZIP\n httpd_resp_send(req, (const char *)datagzip_{{this.dataname}}, {{this.lengthGzip}});\n #else\n httpd_resp_send(req, (const char *)data_{{this.dataname}}, {{this.length}});\n #endif\n{{/case}}\n{{/switch}}\n return ESP_OK;\n}\n\n{{#if this.isDefault}}\nstatic const httpd_uri_t route_def_{{this.datanameUpperCase}} = {\n .uri = \"{{#if ../basePath}}{{../basePath}}{{else}}/{{/if}}\",\n .method = HTTP_GET,\n .handler = file_handler_{{this.datanameUpperCase}},\n};\n{{/if}}\n\nstatic const httpd_uri_t route_{{this.datanameUpperCase}} = {\n .uri = \"{{../basePath}}/{{this.filename}}\",\n .method = HTTP_GET,\n .handler = file_handler_{{this.datanameUpperCase}},\n};\n\n{{/each}}\n\n{{#if spa}}\n{{#with spaSource}}\nstatic esp_err_t spa_handler_{{this.datanameUpperCase}}(httpd_req_t *req, httpd_err_code_t err) {\n{{#if ../basePath}}\n const char* prefix = \"{{../basePath}}/\";\n if (strncmp(req->uri, prefix, strlen(prefix)) != 0 && strcmp(req->uri, \"{{../basePath}}\") != 0) {\n httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, \"Not found\");\n return ESP_FAIL;\n }\n{{/if}}\n return file_handler_{{this.datanameUpperCase}}(req);\n}\n{{/with}}\n{{/if}}\n\n\nstatic inline void {{methodName}}(httpd_handle_t server) {\n{{#each sources}}\n{{#if this.isDefault}}\n httpd_register_uri_handler(server, &route_def_{{this.datanameUpperCase}});\n{{/if}}\n httpd_register_uri_handler(server, &route_{{this.datanameUpperCase}});\n{{/each}}\n{{#if spa}}\n{{#with spaSource}}\n httpd_register_err_handler(server, HTTPD_404_NOT_FOUND, spa_handler_{{this.datanameUpperCase}});\n{{/with}}\n{{/if}}\n\n}";
2
- //# sourceMappingURL=cppCodeEspIdf.d.ts.map
@@ -3,4 +3,3 @@ export declare function getInvalidEngineError(attempted: string): string;
3
3
  export declare function getSourcepathNotFoundError(sourcepath: string, reason: 'not_found' | 'not_directory'): string;
4
4
  export declare function getSizeBudgetExceededError(type: 'size' | 'gzipSize', limit: number, actual: number): string;
5
5
  export declare function getMaxUriHandlersHint(engine: string, routeCount: number, espmethod?: string): string;
6
- //# sourceMappingURL=errorMessages.d.ts.map
package/dist/file.d.ts CHANGED
@@ -4,4 +4,3 @@ export type FileData = {
4
4
  hash: string;
5
5
  };
6
6
  export declare const getFiles: (options: Pick<ICopyFilesArguments, "sourcepath" | "exclude" | "noIndexCheck" | "engine">) => Map<string, FileData>;
7
- //# sourceMappingURL=file.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export declare function main(): void;
2
2
  export { calculateCompressionRatio, createSourceEntry, formatAnalyzeTable, formatChangeSummary, formatCompressionLog, formatDryRunRoutes, formatSize, formatSizePrecise, type PreviousManifestFile, shouldUseGzip, updateExtensionGroup } from './pipeline';
3
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -2,11 +2,23 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.updateExtensionGroup = exports.shouldUseGzip = exports.formatSizePrecise = exports.formatSize = exports.formatDryRunRoutes = exports.formatCompressionLog = exports.formatChangeSummary = exports.formatAnalyzeTable = exports.createSourceEntry = exports.calculateCompressionRatio = void 0;
4
4
  exports.main = main;
5
+ const node_fs_1 = require("node:fs");
5
6
  const commandLine_1 = require("./commandLine");
7
+ const errorMessages_1 = require("./errorMessages");
6
8
  const pipeline_1 = require("./pipeline");
7
9
  function main() {
10
+ const cmdLine = (0, commandLine_1.parseArguments)();
11
+ if (!(0, node_fs_1.existsSync)(cmdLine.sourcepath)) {
12
+ console.error((0, errorMessages_1.getSourcepathNotFoundError)(cmdLine.sourcepath, 'not_found'));
13
+ process.exit(1);
14
+ }
15
+ if (!(0, node_fs_1.statSync)(cmdLine.sourcepath).isDirectory()) {
16
+ console.error((0, errorMessages_1.getSourcepathNotFoundError)(cmdLine.sourcepath, 'not_directory'));
17
+ process.exit(1);
18
+ }
19
+ console.log(`[SvelteESP32] Generate code for ${cmdLine.engine} engine`);
8
20
  try {
9
- (0, pipeline_1.runPipeline)(commandLine_1.cmdLine);
21
+ (0, pipeline_1.runPipeline)(cmdLine);
10
22
  }
11
23
  catch (error) {
12
24
  if (!(error instanceof pipeline_1.OverBudgetError)) {
@@ -1,2 +1 @@
1
1
  export declare function runInit(): Promise<void>;
2
- //# sourceMappingURL=initCommand.d.ts.map
@@ -26,4 +26,3 @@ export declare class OverBudgetError extends Error {
26
26
  declare const formatChangeSummary: (sources: CppCodeSources, previousFiles: PreviousManifestFile[]) => string;
27
27
  export declare function runPipeline(options: ICopyFilesArguments): void;
28
28
  export { calculateCompressionRatio, createSourceEntry, formatAnalyzeTable, formatChangeSummary, formatCompressionLog, formatDryRunRoutes, formatSize, formatSizePrecise, shouldUseGzip, updateExtensionGroup };
29
- //# sourceMappingURL=pipeline.d.ts.map
package/dist/pipeline.js CHANGED
@@ -181,6 +181,8 @@ function runPipeline(options) {
181
181
  };
182
182
  const sources = [];
183
183
  const filesByExtension = [];
184
+ const sourceLabels = { cli: 'command line', rcfile: 'RC file', vite: 'vite plugin' };
185
+ console.log(`Config source: ${sourceLabels[options.configSource]}`);
184
186
  console.log('Collecting source files');
185
187
  const files = (0, file_1.getFiles)(options);
186
188
  if (files.size === 0)
@@ -237,7 +239,7 @@ function runPipeline(options) {
237
239
  console.log(formatDryRunRoutes(sources, options.engine, options.basePath, options.spa ?? false));
238
240
  return;
239
241
  }
240
- const cppFile = (0, cppCode_1.getCppCode)(sources, filesByExtension);
242
+ const cppFile = (0, cppCode_1.getCppCode)(sources, filesByExtension, options);
241
243
  (0, node_fs_1.mkdirSync)(node_path_1.default.normalize(node_path_1.default.dirname(options.outputfile)), { recursive: true });
242
244
  (0, node_fs_1.writeFileSync)(options.outputfile, cppFile, { flush: true, encoding: 'utf8' });
243
245
  console.log(`${summary.filecount} files, ${formatSize(summary.size)} original size, ${formatSize(summary.gzipsize)} gzip size`);
@@ -9,14 +9,14 @@ interface VitePlugin {
9
9
  closeBundle: () => void;
10
10
  }
11
11
  export interface SvelteESP32PluginOptions {
12
- output: string;
12
+ output?: string;
13
13
  sourcepath?: string;
14
14
  engine?: 'psychic' | 'async' | 'espidf' | 'webserver';
15
15
  etag?: 'always' | 'never' | 'compiler';
16
16
  gzip?: 'always' | 'never' | 'compiler';
17
17
  cachetime?: number;
18
- cachetimeHtml?: number;
19
- cachetimeAssets?: number;
18
+ cachetimehtml?: number;
19
+ cachetimeassets?: number;
20
20
  exclude?: string[];
21
21
  basepath?: string;
22
22
  espmethod?: string;
@@ -25,10 +25,9 @@ export interface SvelteESP32PluginOptions {
25
25
  created?: boolean;
26
26
  spa?: boolean;
27
27
  manifest?: boolean;
28
- noIndexCheck?: boolean;
29
- maxSize?: number;
30
- maxGzipSize?: number;
28
+ noindexcheck?: boolean;
29
+ maxsize?: number;
30
+ maxgzipsize?: number;
31
31
  }
32
- export declare function svelteESP32(options: SvelteESP32PluginOptions): VitePlugin;
32
+ export declare function svelteESP32(optionsOrRcPath?: SvelteESP32PluginOptions | string): VitePlugin;
33
33
  export {};
34
- //# sourceMappingURL=vitePlugin.d.ts.map
@@ -5,8 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.svelteESP32 = svelteESP32;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
+ const commandLine_1 = require("./commandLine");
8
9
  const pipeline_1 = require("./pipeline");
9
- function svelteESP32(options) {
10
+ function coerceBool(value) {
11
+ if (value === undefined)
12
+ return undefined;
13
+ return value === true || value === 'true';
14
+ }
15
+ function svelteESP32(optionsOrRcPath) {
10
16
  let outDirectory = 'dist';
11
17
  return {
12
18
  name: 'svelteesp32',
@@ -14,42 +20,72 @@ function svelteESP32(options) {
14
20
  outDirectory = config.build.outDir;
15
21
  },
16
22
  closeBundle() {
17
- const sourcepath = options.sourcepath ?? outDirectory;
18
- const outputfile = node_path_1.default.resolve(options.output);
19
- if (options.basepath !== undefined) {
20
- const bp = options.basepath;
21
- if (bp !== '' && !bp.startsWith('/'))
22
- throw new Error(`basePath must start with /: ${bp}`);
23
- if (bp.endsWith('/'))
24
- throw new Error(`basePath must not end with /: ${bp}`);
25
- if (bp.includes('//'))
26
- throw new Error(`basePath must not contain //: ${bp}`);
27
- if (bp.includes('"'))
28
- throw new Error(`basePath must not contain double quotes: ${bp}`);
29
- if (bp.includes('\\'))
30
- throw new Error(`basePath must not contain backslashes: ${bp}`);
23
+ let options_;
24
+ if (optionsOrRcPath === undefined || typeof optionsOrRcPath === 'string') {
25
+ const rcPath = optionsOrRcPath;
26
+ const rcConfig = (0, commandLine_1.loadRcFileConfig)(rcPath);
27
+ const rawOutput = rcConfig.outputfile;
28
+ if (!rawOutput)
29
+ throw new Error('output is required — specify outputfile in the RC file (.svelteesp32rc.json)');
30
+ const outputfile = node_path_1.default.resolve(rawOutput);
31
+ const sourcepath = rcConfig.sourcepath ?? outDirectory;
32
+ const rawBasepath = rcConfig.basepath ?? '';
33
+ const basePath = (0, commandLine_1.validateBasePath)(rawBasepath);
34
+ options_ = {
35
+ configSource: 'rcfile',
36
+ engine: rcConfig.engine ?? 'psychic',
37
+ sourcepath,
38
+ outputfile,
39
+ etag: rcConfig.etag ?? 'never',
40
+ gzip: rcConfig.gzip ?? 'always',
41
+ cachetime: rcConfig.cachetime ?? 0,
42
+ cachetimeHtml: rcConfig.cachetimehtml,
43
+ cachetimeAssets: rcConfig.cachetimeassets,
44
+ created: coerceBool(rcConfig.created) ?? false,
45
+ version: rcConfig.version ?? '',
46
+ espmethod: rcConfig.espmethod ?? 'initSvelteStaticFiles',
47
+ define: rcConfig.define ?? 'SVELTEESP32',
48
+ exclude: rcConfig.exclude ?? [],
49
+ basePath,
50
+ noIndexCheck: coerceBool(rcConfig.noindexcheck),
51
+ spa: coerceBool(rcConfig.spa),
52
+ manifest: coerceBool(rcConfig.manifest),
53
+ maxSize: rcConfig.maxsize,
54
+ maxGzipSize: rcConfig.maxgzipsize
55
+ };
56
+ }
57
+ else {
58
+ const options = optionsOrRcPath;
59
+ const rawOutput = options.output;
60
+ if (!rawOutput)
61
+ throw new Error('output is required — specify it as a plugin option or use svelteESP32() for RC file mode');
62
+ const outputfile = node_path_1.default.resolve(rawOutput);
63
+ const sourcepath = options.sourcepath ?? outDirectory;
64
+ const rawBasepath = options.basepath ?? '';
65
+ const basePath = (0, commandLine_1.validateBasePath)(rawBasepath);
66
+ options_ = {
67
+ configSource: 'vite',
68
+ engine: options.engine ?? 'psychic',
69
+ sourcepath,
70
+ outputfile,
71
+ etag: options.etag ?? 'never',
72
+ gzip: options.gzip ?? 'always',
73
+ cachetime: options.cachetime ?? 0,
74
+ cachetimeHtml: options.cachetimehtml,
75
+ cachetimeAssets: options.cachetimeassets,
76
+ created: options.created ?? false,
77
+ version: options.version ?? '',
78
+ espmethod: options.espmethod ?? 'initSvelteStaticFiles',
79
+ define: options.define ?? 'SVELTEESP32',
80
+ exclude: options.exclude ?? [],
81
+ basePath,
82
+ noIndexCheck: options.noindexcheck,
83
+ spa: options.spa,
84
+ manifest: options.manifest,
85
+ maxSize: options.maxsize,
86
+ maxGzipSize: options.maxgzipsize
87
+ };
31
88
  }
32
- const options_ = {
33
- engine: options.engine ?? 'psychic',
34
- sourcepath,
35
- outputfile,
36
- etag: options.etag ?? 'never',
37
- gzip: options.gzip ?? 'always',
38
- cachetime: options.cachetime ?? 0,
39
- cachetimeHtml: options.cachetimeHtml,
40
- cachetimeAssets: options.cachetimeAssets,
41
- created: options.created ?? false,
42
- version: options.version ?? '',
43
- espmethod: options.espmethod ?? 'initSvelteStaticFiles',
44
- define: options.define ?? 'SVELTEESP32',
45
- exclude: options.exclude ?? [],
46
- basePath: options.basepath ?? '',
47
- noIndexCheck: options.noIndexCheck,
48
- spa: options.spa,
49
- manifest: options.manifest,
50
- maxSize: options.maxSize,
51
- maxGzipSize: options.maxGzipSize
52
- };
53
89
  (0, pipeline_1.runPipeline)(options_);
54
90
  }
55
91
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelteesp32",
3
- "version": "3.0.0",
3
+ "version": "3.0.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",
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "./vite": {
13
13
  "types": "./dist/vitePlugin.d.ts",
14
+ "require": "./dist/vitePlugin.js",
14
15
  "default": "./dist/vitePlugin.js"
15
16
  }
16
17
  },
@@ -73,8 +74,8 @@
73
74
  "@types/mime-types": "^3.0.1",
74
75
  "@types/node": "^25.6.0",
75
76
  "@types/picomatch": "^4.0.3",
76
- "@typescript-eslint/eslint-plugin": "^8.59.1",
77
- "@typescript-eslint/parser": "^8.59.1",
77
+ "@typescript-eslint/eslint-plugin": "^8.59.2",
78
+ "@typescript-eslint/parser": "^8.59.2",
78
79
  "@vitest/coverage-v8": "^4.1.5",
79
80
  "eslint": "^10.3.0",
80
81
  "eslint-config-prettier": "^10.1.8",