svelteesp32 2.3.3 → 2.4.1
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 +92 -27
- package/dist/commandLine.d.ts +8 -4
- package/dist/commandLine.js +54 -10
- package/dist/index.d.ts +8 -1
- package/dist/index.js +69 -1
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -88,6 +88,7 @@ void setup() {
|
|
|
88
88
|
|
|
89
89
|
## What's New
|
|
90
90
|
|
|
91
|
+
- **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
|
|
91
92
|
- **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)
|
|
92
93
|
- **v2.2.0** — SPA routing catch-all (`--spa`) for client-side routers on all four engines
|
|
93
94
|
- **v2.1.0** — New Arduino WebServer engine (`-e webserver`), dependency updates
|
|
@@ -241,10 +242,10 @@ The generated header file includes everything your ESP needs:
|
|
|
241
242
|
#include <PsychicHttp.h>
|
|
242
243
|
#include <PsychicHttpsServer.h>
|
|
243
244
|
|
|
244
|
-
const uint8_t datagzip_assets_index_KwubEIf__js[12547] = {0x1f, 0x8b, 0x8, 0x0, ...
|
|
245
|
-
const uint8_t datagzip_assets_index_Soe6cpLA_css[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ...
|
|
246
|
-
const char
|
|
247
|
-
const char
|
|
245
|
+
static const uint8_t datagzip_assets_index_KwubEIf__js[12547] = {0x1f, 0x8b, 0x8, 0x0, ...
|
|
246
|
+
static const uint8_t datagzip_assets_index_Soe6cpLA_css[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ...
|
|
247
|
+
static const char etag_assets_index_KwubEIf__js[] = "387b88e345cc56ef9091...";
|
|
248
|
+
static const char etag_assets_index_Soe6cpLA_css[] = "d4f23bc45ef67890ab12...";
|
|
248
249
|
|
|
249
250
|
// File manifest for runtime introspection
|
|
250
251
|
struct SVELTEESP32_FileInfo {
|
|
@@ -424,6 +425,63 @@ What gets generated per engine:
|
|
|
424
425
|
|
|
425
426
|
**Note:** `--spa` requires `index.html` or `index.htm` in the source directory — a warning is printed if it is missing.
|
|
426
427
|
|
|
428
|
+
### Analyze Mode (CI Size Budget Checks)
|
|
429
|
+
|
|
430
|
+
Use `--analyze` in CI to validate firmware size budgets without producing any output file:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
npx svelteesp32 -e psychic -s ./dist --maxsize=400k --maxgzipsize=150k --analyze
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Sample output:
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
index.html Original Gzip
|
|
440
|
+
──────────────────────────────────── ──────── ────────
|
|
441
|
+
assets/index-KwubEIf-.js 37.9kB 12.3kB
|
|
442
|
+
assets/index-Soe6cpLA.css 31.7kB 5.2kB
|
|
443
|
+
favicon.png 32.5kB 32.5kB [no gzip]
|
|
444
|
+
index.html 0.5kB 0.3kB
|
|
445
|
+
────────────────────────────────────────────────────────
|
|
446
|
+
Total 102.6kB 50.3kB
|
|
447
|
+
Budget (maxsize) 400.0kB - ✓ PASS
|
|
448
|
+
Budget (maxgzipsize) - 150.0kB ✓ PASS
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Exits with code **1** if any budget is exceeded — CI fails automatically. Mutually exclusive with `--dryrun`.
|
|
452
|
+
|
|
453
|
+
### JSON Manifest
|
|
454
|
+
|
|
455
|
+
Add `--manifest` to write a companion `.manifest.json` file alongside the header (same directory, same base name):
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --manifest
|
|
459
|
+
# also writes ./esp32/svelteesp32.manifest.json
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
The manifest records build metadata and per-file details for tooling and dashboards:
|
|
463
|
+
|
|
464
|
+
```json
|
|
465
|
+
{
|
|
466
|
+
"generated": "2026-04-26T12:00:00.000Z",
|
|
467
|
+
"engine": "psychic",
|
|
468
|
+
"etag": "false",
|
|
469
|
+
"gzip": "true",
|
|
470
|
+
"filecount": 4,
|
|
471
|
+
"size": 104960,
|
|
472
|
+
"gzipSize": 51507,
|
|
473
|
+
"files": [
|
|
474
|
+
{
|
|
475
|
+
"path": "/assets/index-KwubEIf-.js",
|
|
476
|
+
"mime": "text/javascript",
|
|
477
|
+
"size": 38850,
|
|
478
|
+
"gzipSize": 12547,
|
|
479
|
+
"isGzip": true
|
|
480
|
+
}
|
|
481
|
+
]
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
427
485
|
### C++ Build-Time Validation
|
|
428
486
|
|
|
429
487
|
Catch configuration issues at compile time with generated defines:
|
|
@@ -473,28 +531,31 @@ Called for every response (200 = content served, 304 = cache hit).
|
|
|
473
531
|
|
|
474
532
|
## CLI Reference
|
|
475
533
|
|
|
476
|
-
| Option | Description
|
|
477
|
-
| -------------------- |
|
|
478
|
-
| `-s` | Source folder with compiled web files
|
|
479
|
-
| `-e` | Web server engine (psychic/async/espidf/webserver)
|
|
480
|
-
| `-o` | Output header file path
|
|
481
|
-
| `--etag` | ETag caching (true/false/compiler)
|
|
482
|
-
| `--gzip` | Gzip compression (true/false/compiler)
|
|
483
|
-
| `--
|
|
484
|
-
| `--
|
|
485
|
-
| `--
|
|
486
|
-
| `--
|
|
487
|
-
| `--
|
|
488
|
-
| `--cachetime
|
|
489
|
-
| `--cachetime-
|
|
490
|
-
| `--
|
|
491
|
-
| `--
|
|
492
|
-
| `--
|
|
493
|
-
| `--
|
|
494
|
-
| `--
|
|
495
|
-
| `--
|
|
496
|
-
| `--
|
|
497
|
-
|
|
|
534
|
+
| Option | Description | Default |
|
|
535
|
+
| -------------------- | -------------------------------------------------------------------------------------- | ----------------------- |
|
|
536
|
+
| `-s` | Source folder with compiled web files | (required) |
|
|
537
|
+
| `-e` | Web server engine (psychic/async/espidf/webserver) | `psychic` |
|
|
538
|
+
| `-o` | Output header file path | `svelteesp32.h` |
|
|
539
|
+
| `--etag` | ETag caching (true/false/compiler) | `false` |
|
|
540
|
+
| `--gzip` | Gzip compression (true/false/compiler) | `true` |
|
|
541
|
+
| `--created` | Include creation timestamp in header | `false` |
|
|
542
|
+
| `--exclude` | Exclude files by glob pattern | (none) |
|
|
543
|
+
| `--basepath` | URL prefix for all routes | (none) |
|
|
544
|
+
| `--maxsize` | Max total uncompressed size (e.g., `400k`, `1m`) | (none) |
|
|
545
|
+
| `--maxgzipsize` | Max total gzip size (e.g., `150k`, `500k`) | (none) |
|
|
546
|
+
| `--cachetime` | Cache-Control max-age in seconds (all files) | `0` |
|
|
547
|
+
| `--cachetime-html` | max-age for HTML files (overrides `--cachetime`) | (unset) |
|
|
548
|
+
| `--cachetime-assets` | max-age for non-HTML files (overrides `--cachetime`) | (unset) |
|
|
549
|
+
| `--version` | Version string in header | (none) |
|
|
550
|
+
| `--define` | C++ define prefix | `SVELTEESP32` |
|
|
551
|
+
| `--espmethod` | Init function name | `initSvelteStaticFiles` |
|
|
552
|
+
| `--config` | Custom RC file path | `.svelteesp32rc.json` |
|
|
553
|
+
| `--dryrun` | Show route table + summary without writing output | `false` |
|
|
554
|
+
| `--analyze` | Print per-file size table and budget status, no output written; exits 1 if over budget | `false` |
|
|
555
|
+
| `--manifest` | Write companion `.manifest.json` alongside the header | `false` |
|
|
556
|
+
| `--spa` | Serve index.html for unmatched routes (SPA routing) | `false` |
|
|
557
|
+
| `--noindexcheck` | Skip index.html validation | `false` |
|
|
558
|
+
| `-h` | Show help | |
|
|
498
559
|
|
|
499
560
|
---
|
|
500
561
|
|
|
@@ -518,10 +579,14 @@ Store your settings in `.svelteesp32rc.json` for zero-argument builds:
|
|
|
518
579
|
"cachetimeassets": 31536000,
|
|
519
580
|
"noindexcheck": false,
|
|
520
581
|
"dryrun": false,
|
|
521
|
-
"
|
|
582
|
+
"analyze": false,
|
|
583
|
+
"spa": false,
|
|
584
|
+
"manifest": false
|
|
522
585
|
}
|
|
523
586
|
```
|
|
524
587
|
|
|
588
|
+
Boolean fields (`noindexcheck`, `dryrun`, `analyze`, `spa`, `manifest`, `created`) accept native JSON booleans (`true`/`false`) or their string equivalents (`"true"`/`"false"`), matching the existing behaviour of `etag` and `gzip`.
|
|
589
|
+
|
|
525
590
|
Then just run:
|
|
526
591
|
|
|
527
592
|
```bash
|
package/dist/commandLine.d.ts
CHANGED
|
@@ -17,7 +17,9 @@ interface ICopyFilesArguments {
|
|
|
17
17
|
maxGzipSize?: number;
|
|
18
18
|
noIndexCheck?: boolean;
|
|
19
19
|
dryRun?: boolean;
|
|
20
|
+
analyze?: boolean;
|
|
20
21
|
spa?: boolean;
|
|
22
|
+
manifest?: boolean;
|
|
21
23
|
help?: boolean;
|
|
22
24
|
}
|
|
23
25
|
interface IRcFileConfig {
|
|
@@ -31,15 +33,17 @@ interface IRcFileConfig {
|
|
|
31
33
|
cachetime?: number;
|
|
32
34
|
cachetimehtml?: number;
|
|
33
35
|
cachetimeassets?: number;
|
|
34
|
-
created?: boolean;
|
|
36
|
+
created?: boolean | 'true' | 'false';
|
|
35
37
|
version?: string;
|
|
36
38
|
exclude?: string[];
|
|
37
39
|
basepath?: string;
|
|
38
40
|
maxsize?: number | string;
|
|
39
41
|
maxgzipsize?: number | string;
|
|
40
|
-
noindexcheck?: boolean;
|
|
41
|
-
dryrun?: boolean;
|
|
42
|
-
|
|
42
|
+
noindexcheck?: boolean | 'true' | 'false';
|
|
43
|
+
dryrun?: boolean | 'true' | 'false';
|
|
44
|
+
analyze?: boolean | 'true' | 'false';
|
|
45
|
+
spa?: boolean | 'true' | 'false';
|
|
46
|
+
manifest?: boolean | 'true' | 'false';
|
|
43
47
|
}
|
|
44
48
|
declare function validateCppIdentifier(value: string, name: string): string;
|
|
45
49
|
declare function parseSize(value: string, name: string): number;
|
package/dist/commandLine.js
CHANGED
|
@@ -25,7 +25,7 @@ Configuration:
|
|
|
25
25
|
Options:
|
|
26
26
|
-e, --engine <value> The engine for which the include file is created
|
|
27
27
|
(psychic|async|espidf|webserver) (default: "psychic")
|
|
28
|
-
-s, --sourcepath <path> Source dist folder
|
|
28
|
+
-s, --sourcepath <path> Source dist folder with compiled web files (required)
|
|
29
29
|
-o, --outputfile <path> Generated output file with path (default: "svelteesp32.h")
|
|
30
30
|
--etag <value> Use ETAG header for cache (true|false|compiler) (default: "false")
|
|
31
31
|
--gzip <value> Compress content with gzip (true|false|compiler) (default: "true")
|
|
@@ -42,8 +42,10 @@ Options:
|
|
|
42
42
|
--maxsize <size> Maximum total uncompressed size (e.g., 400k, 1.5m, 409600)
|
|
43
43
|
--maxgzipsize <size> Maximum total gzip size (e.g., 150k, 1m, 153600)
|
|
44
44
|
--dryrun Show summary without writing the output file (default: false)
|
|
45
|
+
--analyze Print per-file size table and budget status without writing (default: false)
|
|
45
46
|
--spa Serve index.html for unmatched routes (SPA routing) (default: false)
|
|
46
|
-
|
|
47
|
+
--manifest Write companion JSON manifest file alongside the header (default: false)
|
|
48
|
+
-h, --help Show this help
|
|
47
49
|
|
|
48
50
|
RC File:
|
|
49
51
|
The tool searches for .svelteesp32rc.json in:
|
|
@@ -287,7 +289,9 @@ function validateRcConfig(config, rcPath) {
|
|
|
287
289
|
'maxgzipsize',
|
|
288
290
|
'noindexcheck',
|
|
289
291
|
'dryrun',
|
|
290
|
-
'
|
|
292
|
+
'analyze',
|
|
293
|
+
'spa',
|
|
294
|
+
'manifest'
|
|
291
295
|
]);
|
|
292
296
|
for (const key of Object.keys(configObject))
|
|
293
297
|
if (!validKeys.has(key))
|
|
@@ -329,12 +333,36 @@ function validateRcConfig(config, rcPath) {
|
|
|
329
333
|
}
|
|
330
334
|
validateSizeOption(configObject, 'maxsize');
|
|
331
335
|
validateSizeOption(configObject, 'maxgzipsize');
|
|
332
|
-
if (configObject['
|
|
336
|
+
if (configObject['created'] !== undefined &&
|
|
337
|
+
typeof configObject['created'] !== 'boolean' &&
|
|
338
|
+
configObject['created'] !== 'true' &&
|
|
339
|
+
configObject['created'] !== 'false')
|
|
340
|
+
throw new TypeError(`Invalid created in RC file: ${configObject['created']} (must be boolean)`);
|
|
341
|
+
if (configObject['noindexcheck'] !== undefined &&
|
|
342
|
+
typeof configObject['noindexcheck'] !== 'boolean' &&
|
|
343
|
+
configObject['noindexcheck'] !== 'true' &&
|
|
344
|
+
configObject['noindexcheck'] !== 'false')
|
|
333
345
|
throw new TypeError(`Invalid noindexcheck in RC file: ${configObject['noindexcheck']} (must be boolean)`);
|
|
334
|
-
if (configObject['dryrun'] !== undefined &&
|
|
346
|
+
if (configObject['dryrun'] !== undefined &&
|
|
347
|
+
typeof configObject['dryrun'] !== 'boolean' &&
|
|
348
|
+
configObject['dryrun'] !== 'true' &&
|
|
349
|
+
configObject['dryrun'] !== 'false')
|
|
335
350
|
throw new TypeError(`Invalid dryrun in RC file: ${configObject['dryrun']} (must be boolean)`);
|
|
336
|
-
if (configObject['
|
|
351
|
+
if (configObject['analyze'] !== undefined &&
|
|
352
|
+
typeof configObject['analyze'] !== 'boolean' &&
|
|
353
|
+
configObject['analyze'] !== 'true' &&
|
|
354
|
+
configObject['analyze'] !== 'false')
|
|
355
|
+
throw new TypeError(`Invalid analyze in RC file: ${configObject['analyze']} (must be boolean)`);
|
|
356
|
+
if (configObject['spa'] !== undefined &&
|
|
357
|
+
typeof configObject['spa'] !== 'boolean' &&
|
|
358
|
+
configObject['spa'] !== 'true' &&
|
|
359
|
+
configObject['spa'] !== 'false')
|
|
337
360
|
throw new TypeError(`Invalid spa in RC file: ${configObject['spa']} (must be boolean)`);
|
|
361
|
+
if (configObject['manifest'] !== undefined &&
|
|
362
|
+
typeof configObject['manifest'] !== 'boolean' &&
|
|
363
|
+
configObject['manifest'] !== 'true' &&
|
|
364
|
+
configObject['manifest'] !== 'false')
|
|
365
|
+
throw new TypeError(`Invalid manifest in RC file: ${configObject['manifest']} (must be boolean)`);
|
|
338
366
|
if (configObject['outputfile'] !== undefined && node_path_1.default.isAbsolute(configObject['outputfile']))
|
|
339
367
|
throw new Error(`'outputfile' in RC file must be a relative path (use --output CLI flag for absolute paths): ${configObject['outputfile']}`);
|
|
340
368
|
return configObject;
|
|
@@ -392,7 +420,7 @@ function parseArguments() {
|
|
|
392
420
|
if (rcConfig.cachetimeassets !== undefined)
|
|
393
421
|
result.cachetimeAssets = rcConfig.cachetimeassets;
|
|
394
422
|
if (rcConfig.created !== undefined)
|
|
395
|
-
result.created = rcConfig.created;
|
|
423
|
+
result.created = rcConfig.created === true || rcConfig.created === 'true';
|
|
396
424
|
if (rcConfig.version)
|
|
397
425
|
result.version = rcConfig.version;
|
|
398
426
|
if (rcConfig.espmethod)
|
|
@@ -406,11 +434,15 @@ function parseArguments() {
|
|
|
406
434
|
if (rcConfig.maxgzipsize !== undefined)
|
|
407
435
|
result.maxGzipSize = rcConfig.maxgzipsize;
|
|
408
436
|
if (rcConfig.noindexcheck !== undefined)
|
|
409
|
-
result.noIndexCheck = rcConfig.noindexcheck;
|
|
437
|
+
result.noIndexCheck = rcConfig.noindexcheck === true || rcConfig.noindexcheck === 'true';
|
|
410
438
|
if (rcConfig.dryrun !== undefined)
|
|
411
|
-
result.dryRun = rcConfig.dryrun;
|
|
439
|
+
result.dryRun = rcConfig.dryrun === true || rcConfig.dryrun === 'true';
|
|
440
|
+
if (rcConfig.analyze !== undefined)
|
|
441
|
+
result.analyze = rcConfig.analyze === true || rcConfig.analyze === 'true';
|
|
412
442
|
if (rcConfig.spa !== undefined)
|
|
413
|
-
result.spa = rcConfig.spa;
|
|
443
|
+
result.spa = rcConfig.spa === true || rcConfig.spa === 'true';
|
|
444
|
+
if (rcConfig.manifest !== undefined)
|
|
445
|
+
result.manifest = rcConfig.manifest === true || rcConfig.manifest === 'true';
|
|
414
446
|
if (rcConfig.exclude && rcConfig.exclude.length > 0)
|
|
415
447
|
result.exclude = [...rcConfig.exclude];
|
|
416
448
|
const cliExclude = [];
|
|
@@ -513,10 +545,18 @@ function parseArguments() {
|
|
|
513
545
|
result.dryRun = true;
|
|
514
546
|
continue;
|
|
515
547
|
}
|
|
548
|
+
if (argument === '--analyze') {
|
|
549
|
+
result.analyze = true;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
516
552
|
if (argument === '--spa') {
|
|
517
553
|
result.spa = true;
|
|
518
554
|
continue;
|
|
519
555
|
}
|
|
556
|
+
if (argument === '--manifest') {
|
|
557
|
+
result.manifest = true;
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
520
560
|
if (argument.startsWith('-') && !argument.startsWith('--')) {
|
|
521
561
|
const flag = argument.slice(1);
|
|
522
562
|
const nextArgument = arguments_[index + 1];
|
|
@@ -557,6 +597,8 @@ function parseArguments() {
|
|
|
557
597
|
console.error('Error: --sourcepath is required (can be specified in RC file or CLI)');
|
|
558
598
|
showHelp();
|
|
559
599
|
}
|
|
600
|
+
if (result.dryRun && result.analyze)
|
|
601
|
+
throw new Error('--analyze and --dryrun are mutually exclusive. Use --analyze for CI budget checks or --dryrun for a developer route preview.');
|
|
560
602
|
return result;
|
|
561
603
|
}
|
|
562
604
|
function formatConfiguration(cmdLine) {
|
|
@@ -588,6 +630,8 @@ function formatConfiguration(cmdLine) {
|
|
|
588
630
|
parts.push(`maxGzipSize=${cmdLine.maxGzipSize}`);
|
|
589
631
|
if (cmdLine.spa)
|
|
590
632
|
parts.push(`spa=${cmdLine.spa}`);
|
|
633
|
+
if (cmdLine.analyze)
|
|
634
|
+
parts.push(`analyze=${cmdLine.analyze}`);
|
|
591
635
|
if (cmdLine.exclude.length > 0)
|
|
592
636
|
parts.push(`exclude=[${cmdLine.exclude.join(', ')}]`);
|
|
593
637
|
return parts.join(' ').replace(/[\n\r]/g, ' ');
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { CppCodeSource, CppCodeSources, ExtensionGroups } from './cppCode';
|
|
2
|
+
type ProcessingSummary = {
|
|
3
|
+
filecount: number;
|
|
4
|
+
size: number;
|
|
5
|
+
gzipsize: number;
|
|
6
|
+
};
|
|
2
7
|
declare const shouldUseGzip: (originalSize: number, compressedSize: number) => boolean;
|
|
3
8
|
declare const calculateCompressionRatio: (originalSize: number, compressedSize: number) => number;
|
|
4
9
|
declare const formatCompressionLog: (filename: string, padding: string, originalSize: number, compressedSize: number, useGzip: boolean) => string;
|
|
5
10
|
declare const formatSize: (bytes: number) => string;
|
|
11
|
+
declare const formatSizePrecise: (bytes: number) => string;
|
|
6
12
|
declare const createSourceEntry: (filename: string, dataname: string, content: Buffer, contentGzip: Buffer, mimeType: string, sha256: string, isGzip: boolean) => CppCodeSource;
|
|
7
13
|
declare const updateExtensionGroup: (filesByExtension: ExtensionGroups, extension: string) => void;
|
|
8
14
|
declare const formatDryRunRoutes: (sources: CppCodeSources, engine: "psychic" | "async" | "espidf" | "webserver", basePath: string, spa: boolean) => string;
|
|
15
|
+
declare const formatAnalyzeTable: (sources: CppCodeSources, summary: ProcessingSummary, maxSize: number | undefined, maxGzipSize: number | undefined) => string;
|
|
9
16
|
export declare function main(): void;
|
|
10
|
-
export { calculateCompressionRatio, createSourceEntry, formatCompressionLog, formatDryRunRoutes, formatSize, shouldUseGzip, updateExtensionGroup };
|
|
17
|
+
export { calculateCompressionRatio, createSourceEntry, formatAnalyzeTable, formatCompressionLog, formatDryRunRoutes, formatSize, formatSizePrecise, shouldUseGzip, updateExtensionGroup };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ 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.updateExtensionGroup = exports.shouldUseGzip = exports.formatSize = exports.formatDryRunRoutes = exports.formatCompressionLog = exports.createSourceEntry = exports.calculateCompressionRatio = void 0;
|
|
6
|
+
exports.updateExtensionGroup = exports.shouldUseGzip = exports.formatSizePrecise = exports.formatSize = exports.formatDryRunRoutes = exports.formatCompressionLog = exports.formatAnalyzeTable = exports.createSourceEntry = exports.calculateCompressionRatio = void 0;
|
|
7
7
|
exports.main = main;
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -35,6 +35,12 @@ const formatSize = (bytes) => {
|
|
|
35
35
|
return `${Math.round(bytes / 1024)}kB`;
|
|
36
36
|
};
|
|
37
37
|
exports.formatSize = formatSize;
|
|
38
|
+
const formatSizePrecise = (bytes) => {
|
|
39
|
+
if (bytes < 1024)
|
|
40
|
+
return `${bytes}B`;
|
|
41
|
+
return `${(bytes / 1024).toFixed(1)}kB`;
|
|
42
|
+
};
|
|
43
|
+
exports.formatSizePrecise = formatSizePrecise;
|
|
38
44
|
const createSourceEntry = (filename, dataname, content, contentGzip, mimeType, sha256, isGzip) => ({
|
|
39
45
|
filename,
|
|
40
46
|
dataname,
|
|
@@ -94,6 +100,39 @@ const formatDryRunRoutes = (sources, engine, basePath, spa) => {
|
|
|
94
100
|
.join('\n');
|
|
95
101
|
};
|
|
96
102
|
exports.formatDryRunRoutes = formatDryRunRoutes;
|
|
103
|
+
const formatAnalyzeTable = (sources, summary, maxSize, maxGzipSize) => {
|
|
104
|
+
const rows = sources.map((s) => ({
|
|
105
|
+
file: s.filename,
|
|
106
|
+
orig: formatSizePrecise(s.content.length),
|
|
107
|
+
gzip: formatSizePrecise(s.isGzip ? s.contentGzip.length : s.content.length),
|
|
108
|
+
tag: s.isGzip ? '' : '[no gzip]'
|
|
109
|
+
}));
|
|
110
|
+
const fileWidth = Math.max(4, ...rows.map((r) => r.file.length), 'Total'.length);
|
|
111
|
+
const origWidth = Math.max(8, ...rows.map((r) => r.orig.length), formatSizePrecise(summary.size).length);
|
|
112
|
+
const gzipWidth = Math.max(8, ...rows.map((r) => r.gzip.length), formatSizePrecise(summary.gzipsize).length);
|
|
113
|
+
const separator = `${'─'.repeat(fileWidth)} ${'─'.repeat(origWidth)} ${'─'.repeat(gzipWidth)}`;
|
|
114
|
+
const header = `${'File'.padEnd(fileWidth)} ${'Original'.padEnd(origWidth)} ${'Gzip'.padEnd(gzipWidth)}`;
|
|
115
|
+
const dataRows = rows.map((r) => {
|
|
116
|
+
const tagPart = r.tag ? ` ${r.tag}` : '';
|
|
117
|
+
return `${r.file.padEnd(fileWidth)} ${r.orig.padEnd(origWidth)} ${r.gzip.padEnd(gzipWidth)}${tagPart}`.trimEnd();
|
|
118
|
+
});
|
|
119
|
+
const totalOrig = formatSizePrecise(summary.size);
|
|
120
|
+
const totalGzip = formatSizePrecise(summary.gzipsize);
|
|
121
|
+
const totalRow = `${'Total'.padEnd(fileWidth)} ${totalOrig.padEnd(origWidth)} ${totalGzip.padEnd(gzipWidth)}`;
|
|
122
|
+
const lines = [header, separator, ...dataRows, separator, totalRow];
|
|
123
|
+
if (maxSize !== undefined) {
|
|
124
|
+
const pass = summary.size <= maxSize;
|
|
125
|
+
const budgetRow = `${'Budget (maxsize)'.padEnd(fileWidth)} ${formatSizePrecise(maxSize).padEnd(origWidth)} ${'-'.padEnd(gzipWidth)} ${pass ? '✓ PASS' : '✗ FAIL'}`;
|
|
126
|
+
lines.push(budgetRow);
|
|
127
|
+
}
|
|
128
|
+
if (maxGzipSize !== undefined) {
|
|
129
|
+
const pass = summary.gzipsize <= maxGzipSize;
|
|
130
|
+
const budgetRow = `${'Budget (maxgzipsize)'.padEnd(fileWidth)} ${'-'.padEnd(origWidth)} ${formatSizePrecise(maxGzipSize).padEnd(gzipWidth)} ${pass ? '✓ PASS' : '✗ FAIL'}`;
|
|
131
|
+
lines.push(budgetRow);
|
|
132
|
+
}
|
|
133
|
+
return lines.join('\n');
|
|
134
|
+
};
|
|
135
|
+
exports.formatAnalyzeTable = formatAnalyzeTable;
|
|
97
136
|
function main() {
|
|
98
137
|
const summary = {
|
|
99
138
|
filecount: 0,
|
|
@@ -138,6 +177,14 @@ function main() {
|
|
|
138
177
|
}
|
|
139
178
|
console.log('');
|
|
140
179
|
filesByExtension.sort((left, right) => left.extension.localeCompare(right.extension));
|
|
180
|
+
if (commandLine_1.cmdLine.analyze) {
|
|
181
|
+
console.log(formatAnalyzeTable(sources, summary, commandLine_1.cmdLine.maxSize, commandLine_1.cmdLine.maxGzipSize));
|
|
182
|
+
const overBudget = (commandLine_1.cmdLine.maxSize !== undefined && summary.size > commandLine_1.cmdLine.maxSize) ||
|
|
183
|
+
(commandLine_1.cmdLine.maxGzipSize !== undefined && summary.gzipsize > commandLine_1.cmdLine.maxGzipSize);
|
|
184
|
+
if (overBudget)
|
|
185
|
+
process.exit(1);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
141
188
|
if (commandLine_1.cmdLine.maxSize !== undefined && summary.size > commandLine_1.cmdLine.maxSize) {
|
|
142
189
|
console.error((0, errorMessages_1.getSizeBudgetExceededError)('size', commandLine_1.cmdLine.maxSize, summary.size));
|
|
143
190
|
process.exit(1);
|
|
@@ -161,6 +208,27 @@ function main() {
|
|
|
161
208
|
(0, node_fs_1.writeFileSync)(commandLine_1.cmdLine.outputfile, cppFile, { flush: true, encoding: 'utf8' });
|
|
162
209
|
console.log(`${summary.filecount} files, ${formatSize(summary.size)} original size, ${formatSize(summary.gzipsize)} gzip size`);
|
|
163
210
|
console.log(`${commandLine_1.cmdLine.outputfile} ${formatSize(cppFile.length)} size`);
|
|
211
|
+
if (commandLine_1.cmdLine.manifest) {
|
|
212
|
+
const manifestPath = node_path_1.default.join(node_path_1.default.dirname(commandLine_1.cmdLine.outputfile), node_path_1.default.basename(commandLine_1.cmdLine.outputfile, node_path_1.default.extname(commandLine_1.cmdLine.outputfile)) + '.manifest.json');
|
|
213
|
+
const manifest = {
|
|
214
|
+
generated: new Date().toISOString(),
|
|
215
|
+
engine: commandLine_1.cmdLine.engine,
|
|
216
|
+
etag: commandLine_1.cmdLine.etag,
|
|
217
|
+
gzip: commandLine_1.cmdLine.gzip,
|
|
218
|
+
filecount: summary.filecount,
|
|
219
|
+
size: summary.size,
|
|
220
|
+
gzipSize: summary.gzipsize,
|
|
221
|
+
files: sources.map((s) => ({
|
|
222
|
+
path: s.filename,
|
|
223
|
+
mime: s.mime,
|
|
224
|
+
size: s.content.length,
|
|
225
|
+
gzipSize: s.isGzip ? s.contentGzip.length : s.content.length,
|
|
226
|
+
isGzip: s.isGzip
|
|
227
|
+
}))
|
|
228
|
+
};
|
|
229
|
+
(0, node_fs_1.writeFileSync)(manifestPath, JSON.stringify(manifest, undefined, 2), { encoding: 'utf8' });
|
|
230
|
+
console.log(`${manifestPath} manifest written`);
|
|
231
|
+
}
|
|
164
232
|
if (commandLine_1.cmdLine.engine === 'psychic' || commandLine_1.cmdLine.engine === 'espidf')
|
|
165
233
|
console.log('\n' + (0, errorMessages_1.getMaxUriHandlersHint)(commandLine_1.cmdLine.engine, sources.length, commandLine_1.cmdLine.espmethod));
|
|
166
234
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteesp32",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "Convert Svelte (or any frontend) JS application to serve it from ESP32 webserver (PsychicHttp)",
|
|
5
5
|
"author": "BCsabaEngine",
|
|
6
6
|
"license": "ISC",
|
|
@@ -62,27 +62,27 @@
|
|
|
62
62
|
"@eslint/eslintrc": "^3.3.5",
|
|
63
63
|
"@eslint/js": "^10.0.1",
|
|
64
64
|
"@types/mime-types": "^3.0.1",
|
|
65
|
-
"@types/node": "^25.
|
|
65
|
+
"@types/node": "^25.6.0",
|
|
66
66
|
"@types/picomatch": "^4.0.3",
|
|
67
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
68
|
-
"@typescript-eslint/parser": "^8.
|
|
69
|
-
"@vitest/coverage-v8": "^4.1.
|
|
70
|
-
"eslint": "^10.2.
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^8.59.0",
|
|
68
|
+
"@typescript-eslint/parser": "^8.59.0",
|
|
69
|
+
"@vitest/coverage-v8": "^4.1.5",
|
|
70
|
+
"eslint": "^10.2.1",
|
|
71
71
|
"eslint-config-prettier": "^10.1.8",
|
|
72
72
|
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
73
73
|
"eslint-plugin-unicorn": "^64.0.0",
|
|
74
|
-
"memfs": "^4.57.
|
|
74
|
+
"memfs": "^4.57.2",
|
|
75
75
|
"nodemon": "^3.1.14",
|
|
76
|
-
"prettier": "^3.8.
|
|
76
|
+
"prettier": "^3.8.3",
|
|
77
77
|
"ts-node": "^10.9.2",
|
|
78
78
|
"tsx": "^4.21.0",
|
|
79
|
-
"typescript": "^6.0.
|
|
80
|
-
"vitest": "^4.1.
|
|
79
|
+
"typescript": "^6.0.3",
|
|
80
|
+
"vitest": "^4.1.5"
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
83
|
"handlebars": "^4.7.9",
|
|
84
84
|
"mime-types": "^3.0.2",
|
|
85
85
|
"picomatch": "^4.0.4",
|
|
86
|
-
"tinyglobby": "^0.2.
|
|
86
|
+
"tinyglobby": "^0.2.16"
|
|
87
87
|
}
|
|
88
88
|
}
|