svelteesp32 3.1.0 → 3.1.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 +8 -8
- package/dist/commandLine.js +8 -8
- package/dist/consoleColor.js +4 -4
- package/dist/file.js +20 -21
- package/dist/pipeline.js +38 -5
- package/package.json +12 -15
package/README.md
CHANGED
|
@@ -88,7 +88,7 @@ void setup() {
|
|
|
88
88
|
|
|
89
89
|
## What's New
|
|
90
90
|
|
|
91
|
-
- **v3.1.0** — Removed `handlebars`
|
|
91
|
+
- **v3.1.0** — Removed `handlebars`, `picomatch`, and `mime-types` dependencies; C++ generation is now pure TypeScript with a built-in MIME type map and direct `tinyglobby` exclude handling. `--cachetime-html` → `--cachetimehtml`, `--cachetime-assets` → `--cachetimeassets` (CLI now matches RC file keys); `--dry-run` alias removed — use `--dryrun`
|
|
92
92
|
- **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
|
|
93
93
|
- **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
|
|
94
94
|
- **v2.3.0** — `--cachetimehtml` and `--cachetimeassets` for per-type cache control (e.g. `no-cache` for HTML, 1-year for content-hashed JS/CSS)
|
|
@@ -370,12 +370,12 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
|
370
370
|
|
|
371
371
|
## Supported Web Server Engines
|
|
372
372
|
|
|
373
|
-
| Engine | Flag | Best For | Platform |
|
|
374
|
-
| ------------------------ | -------------- | ---------------------------- | --------------- |
|
|
375
|
-
| **PsychicHttpServer V2** | `-e psychic` | Maximum performance | ESP32 only |
|
|
376
|
-
| **ESPAsyncWebServer** | `-e async` | Cross-platform compatibility | ESP32 + ESP8266 |
|
|
377
|
-
| **Arduino WebServer** | `-e webserver` | No dependencies, simplicity | ESP32 only |
|
|
378
|
-
| **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only |
|
|
373
|
+
| Engine | Flag | Best For | Platform | Library |
|
|
374
|
+
| ------------------------ | -------------- | ---------------------------- | --------------- | ---------------------------------------------------------------------------- |
|
|
375
|
+
| **PsychicHttpServer V2** | `-e psychic` | Maximum performance | ESP32 only | [PsychicHttp 2.2.0, 3.1.1](https://github.com/hoeken/PsychicHttp) |
|
|
376
|
+
| **ESPAsyncWebServer** | `-e async` | Cross-platform compatibility | ESP32 + ESP8266 | [ESPAsyncWebServer v3.11.1](https://github.com/ESP32Async/ESPAsyncWebServer) |
|
|
377
|
+
| **Arduino WebServer** | `-e webserver` | No dependencies, simplicity | ESP32 only | (built-in) |
|
|
378
|
+
| **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only | (built-in) |
|
|
379
379
|
|
|
380
380
|
**Recommendation:** For ESP32-only projects, use PsychicHttpServer V2 (`-e psychic`) for the fastest, most stable experience.
|
|
381
381
|
|
|
@@ -745,7 +745,7 @@ platform = espressif32
|
|
|
745
745
|
board = esp32dev
|
|
746
746
|
framework = arduino
|
|
747
747
|
lib_deps =
|
|
748
|
-
|
|
748
|
+
https://github.com/ESP32Async/ESPAsyncWebServer#v3.11.1
|
|
749
749
|
extra_scripts = pre:scripts/build_frontend.py
|
|
750
750
|
```
|
|
751
751
|
|
package/dist/commandLine.js
CHANGED
|
@@ -77,13 +77,13 @@ RC File:
|
|
|
77
77
|
process.exit(0);
|
|
78
78
|
}
|
|
79
79
|
function validateEngine(value) {
|
|
80
|
-
if (
|
|
80
|
+
if (['psychic', 'async', 'espidf', 'webserver'].includes(value))
|
|
81
81
|
return value;
|
|
82
82
|
console.error((0, errorMessages_1.getInvalidEngineError)(value));
|
|
83
83
|
process.exit(1);
|
|
84
84
|
}
|
|
85
85
|
function validateTriState(value, name) {
|
|
86
|
-
if (
|
|
86
|
+
if (['always', 'never', 'compiler'].includes(value))
|
|
87
87
|
return value;
|
|
88
88
|
throw new Error(`Invalid ${name}: ${value}`);
|
|
89
89
|
}
|
|
@@ -117,7 +117,7 @@ function parseSize(value, name) {
|
|
|
117
117
|
const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*([KMkm])?$/);
|
|
118
118
|
if (!match || !match[1])
|
|
119
119
|
throw new Error(`${name} must be a positive number with optional k/K (×1024) or m/M (×1024²) suffix: ${value}`);
|
|
120
|
-
const numericPart = Number
|
|
120
|
+
const numericPart = Number(match[1]);
|
|
121
121
|
const suffix = match[2]?.toLowerCase();
|
|
122
122
|
let bytes;
|
|
123
123
|
if (suffix === 'k')
|
|
@@ -385,7 +385,7 @@ function parseArguments() {
|
|
|
385
385
|
const argument = arguments_[index];
|
|
386
386
|
if (!argument)
|
|
387
387
|
continue;
|
|
388
|
-
if (argument === '--config' && arguments_[index + 1]) {
|
|
388
|
+
if (argument === '--config' && arguments_[index + 1] !== undefined) {
|
|
389
389
|
customConfigPath = arguments_[index + 1];
|
|
390
390
|
break;
|
|
391
391
|
}
|
|
@@ -395,12 +395,12 @@ function parseArguments() {
|
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
397
|
const rcPath = findRcFile(customConfigPath);
|
|
398
|
-
const rcConfig = rcPath ? loadRcFile(rcPath) : {};
|
|
399
398
|
if (rcPath) {
|
|
400
399
|
console.log((0, consoleColor_1.cyanLog)(`[SvelteESP32] Using config from: ${rcPath}`));
|
|
401
400
|
if (!customConfigPath && node_path_1.default.dirname(rcPath) === process.cwd())
|
|
402
401
|
console.warn((0, consoleColor_1.yellowLog)(`Warning: Loading config from current directory — verify this file is trusted before proceeding.`));
|
|
403
402
|
}
|
|
403
|
+
const rcConfig = rcPath ? loadRcFile(rcPath) : {};
|
|
404
404
|
const result = {
|
|
405
405
|
engine: 'psychic',
|
|
406
406
|
outputfile: 'svelteesp32.h',
|
|
@@ -487,14 +487,14 @@ function parseArguments() {
|
|
|
487
487
|
result.define = validateCppIdentifier(value, 'define');
|
|
488
488
|
break;
|
|
489
489
|
case 'cachetime':
|
|
490
|
-
result.cachetime =
|
|
490
|
+
result.cachetime = Math.trunc(Number(value));
|
|
491
491
|
if (Number.isNaN(result.cachetime))
|
|
492
492
|
throw new TypeError(`Invalid cachetime: ${value}`);
|
|
493
493
|
if (result.cachetime < 0)
|
|
494
494
|
throw new TypeError(`Invalid cachetime: ${value} (must be non-negative)`);
|
|
495
495
|
break;
|
|
496
496
|
case 'cachetimehtml': {
|
|
497
|
-
result.cachetimeHtml =
|
|
497
|
+
result.cachetimeHtml = Math.trunc(Number(value));
|
|
498
498
|
if (Number.isNaN(result.cachetimeHtml))
|
|
499
499
|
throw new TypeError(`Invalid cachetimehtml: ${value}`);
|
|
500
500
|
if (result.cachetimeHtml < 0)
|
|
@@ -502,7 +502,7 @@ function parseArguments() {
|
|
|
502
502
|
break;
|
|
503
503
|
}
|
|
504
504
|
case 'cachetimeassets': {
|
|
505
|
-
result.cachetimeAssets =
|
|
505
|
+
result.cachetimeAssets = Math.trunc(Number(value));
|
|
506
506
|
if (Number.isNaN(result.cachetimeAssets))
|
|
507
507
|
throw new TypeError(`Invalid cachetimeassets: ${value}`);
|
|
508
508
|
if (result.cachetimeAssets < 0)
|
package/dist/consoleColor.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.cyanLog = exports.redLog = exports.yellowLog = exports.greenLog = void 0;
|
|
4
|
-
const greenLog = (s) => `\
|
|
4
|
+
const greenLog = (s) => `\u{1B}[32m${s}\u{1B}[0m`;
|
|
5
5
|
exports.greenLog = greenLog;
|
|
6
|
-
const yellowLog = (s) => `\
|
|
6
|
+
const yellowLog = (s) => `\u{1B}[33m${s}\u{1B}[0m`;
|
|
7
7
|
exports.yellowLog = yellowLog;
|
|
8
|
-
const redLog = (s) => `\
|
|
8
|
+
const redLog = (s) => `\u{1B}[31m${s}\u{1B}[0m`;
|
|
9
9
|
exports.redLog = redLog;
|
|
10
|
-
const cyanLog = (s) => `\
|
|
10
|
+
const cyanLog = (s) => `\u{1B}[36m${s}\u{1B}[0m`;
|
|
11
11
|
exports.cyanLog = cyanLog;
|
package/dist/file.js
CHANGED
|
@@ -7,13 +7,12 @@ exports.getFiles = void 0;
|
|
|
7
7
|
const node_crypto_1 = require("node:crypto");
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
-
const picomatch_1 = __importDefault(require("picomatch"));
|
|
11
10
|
const tinyglobby_1 = require("tinyglobby");
|
|
12
11
|
const consoleColor_1 = require("./consoleColor");
|
|
13
12
|
const errorMessages_1 = require("./errorMessages");
|
|
14
13
|
const findSimilarFiles = (files) => {
|
|
15
14
|
const contentComparer = new Map();
|
|
16
|
-
for (const [filename, fileData] of files
|
|
15
|
+
for (const [filename, fileData] of files) {
|
|
17
16
|
const existingFiles = contentComparer.get(fileData.hash);
|
|
18
17
|
if (existingFiles)
|
|
19
18
|
existingFiles.push(filename);
|
|
@@ -38,17 +37,6 @@ const shouldSkipFile = (filename, allFilenames) => {
|
|
|
38
37
|
}
|
|
39
38
|
return false;
|
|
40
39
|
};
|
|
41
|
-
const isExcluded = (filename, excludePatterns) => {
|
|
42
|
-
if (excludePatterns.length === 0)
|
|
43
|
-
return false;
|
|
44
|
-
const normalizedFilename = filename.replace(/\\/g, '/');
|
|
45
|
-
const isMatch = (0, picomatch_1.default)(excludePatterns, {
|
|
46
|
-
dot: true,
|
|
47
|
-
noglobstar: false,
|
|
48
|
-
matchBase: false
|
|
49
|
-
});
|
|
50
|
-
return isMatch(normalizedFilename);
|
|
51
|
-
};
|
|
52
40
|
const getFiles = (options) => {
|
|
53
41
|
const allFilenames = (0, tinyglobby_1.globSync)('**/*', {
|
|
54
42
|
cwd: options.sourcepath,
|
|
@@ -59,13 +47,22 @@ const getFiles = (options) => {
|
|
|
59
47
|
const withoutCompressed = allFilenames.filter((filename) => !shouldSkipFile(filename, allFilenames));
|
|
60
48
|
const excludePatterns = options.exclude;
|
|
61
49
|
const excludedFiles = [];
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
50
|
+
let filenames = withoutCompressed;
|
|
51
|
+
if (excludePatterns.length > 0) {
|
|
52
|
+
const allowedSet = new Set((0, tinyglobby_1.globSync)('**/*', {
|
|
53
|
+
cwd: options.sourcepath,
|
|
54
|
+
onlyFiles: true,
|
|
55
|
+
dot: false,
|
|
56
|
+
followSymbolicLinks: false,
|
|
57
|
+
ignore: excludePatterns
|
|
58
|
+
}));
|
|
59
|
+
filenames = [];
|
|
60
|
+
for (const filename of withoutCompressed)
|
|
61
|
+
if (allowedSet.has(filename))
|
|
62
|
+
filenames.push(filename);
|
|
63
|
+
else
|
|
64
|
+
excludedFiles.push(filename);
|
|
65
|
+
}
|
|
69
66
|
if (excludedFiles.length > 0) {
|
|
70
67
|
console.log(`\nExcluded ${excludedFiles.length} file(s):`);
|
|
71
68
|
const displayLimit = 10;
|
|
@@ -91,7 +88,9 @@ const getFiles = (options) => {
|
|
|
91
88
|
const duplicates = findSimilarFiles(result);
|
|
92
89
|
for (const sameFiles of duplicates)
|
|
93
90
|
console.log((0, consoleColor_1.yellowLog)(` ${sameFiles.join(', ')} files appear identical`));
|
|
94
|
-
const hasIndex =
|
|
91
|
+
const hasIndex = result
|
|
92
|
+
.keys()
|
|
93
|
+
.some((f) => f === 'index.html' || f === 'index.htm' || f.endsWith('/index.html') || f.endsWith('/index.htm'));
|
|
95
94
|
if (!hasIndex && !options.noIndexCheck)
|
|
96
95
|
throw new Error((0, errorMessages_1.getMissingIndexError)(options.engine));
|
|
97
96
|
return result;
|
package/dist/pipeline.js
CHANGED
|
@@ -8,11 +8,43 @@ exports.runPipeline = runPipeline;
|
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const node_zlib_1 = require("node:zlib");
|
|
11
|
-
const mime_types_1 = require("mime-types");
|
|
12
11
|
const consoleColor_1 = require("./consoleColor");
|
|
13
12
|
const cppCode_1 = require("./cppCode");
|
|
14
13
|
const errorMessages_1 = require("./errorMessages");
|
|
15
14
|
const file_1 = require("./file");
|
|
15
|
+
const MIME_TYPES = {
|
|
16
|
+
html: 'text/html',
|
|
17
|
+
htm: 'text/html',
|
|
18
|
+
css: 'text/css',
|
|
19
|
+
js: 'text/javascript',
|
|
20
|
+
mjs: 'text/javascript',
|
|
21
|
+
json: 'application/json',
|
|
22
|
+
png: 'image/png',
|
|
23
|
+
jpg: 'image/jpeg',
|
|
24
|
+
jpeg: 'image/jpeg',
|
|
25
|
+
gif: 'image/gif',
|
|
26
|
+
svg: 'image/svg+xml',
|
|
27
|
+
ico: 'image/x-icon',
|
|
28
|
+
webp: 'image/webp',
|
|
29
|
+
woff: 'font/woff',
|
|
30
|
+
woff2: 'font/woff2',
|
|
31
|
+
ttf: 'font/ttf',
|
|
32
|
+
otf: 'font/otf',
|
|
33
|
+
eot: 'application/vnd.ms-fontobject',
|
|
34
|
+
mp3: 'audio/mpeg',
|
|
35
|
+
wav: 'audio/wav',
|
|
36
|
+
mp4: 'video/mp4',
|
|
37
|
+
webm: 'video/webm',
|
|
38
|
+
ogg: 'audio/ogg',
|
|
39
|
+
txt: 'text/plain',
|
|
40
|
+
xml: 'application/xml',
|
|
41
|
+
pdf: 'application/pdf',
|
|
42
|
+
map: 'application/json'
|
|
43
|
+
};
|
|
44
|
+
const mimeLookup = (filename) => {
|
|
45
|
+
const extension = node_path_1.default.extname(filename).slice(1).toLowerCase();
|
|
46
|
+
return MIME_TYPES[extension] ?? false;
|
|
47
|
+
};
|
|
16
48
|
const GZIP_MIN_SIZE = 1024;
|
|
17
49
|
const GZIP_MIN_REDUCTION_RATIO = 0.85;
|
|
18
50
|
const shouldUseGzip = (originalSize, compressedSize) => originalSize > GZIP_MIN_SIZE && compressedSize < originalSize * GZIP_MIN_REDUCTION_RATIO;
|
|
@@ -187,14 +219,14 @@ function runPipeline(options) {
|
|
|
187
219
|
const files = (0, file_1.getFiles)(options);
|
|
188
220
|
if (files.size === 0)
|
|
189
221
|
throw new Error(`Directory ${options.sourcepath} is empty`);
|
|
190
|
-
if (options.spa &&
|
|
222
|
+
if (options.spa && files.keys().every((f) => !(f === 'index.html' || f === 'index.htm')))
|
|
191
223
|
console.warn((0, consoleColor_1.yellowLog)('[SvelteESP32] Warning: --spa is set but no index.html/index.htm found; catch-all will not be generated.'));
|
|
192
224
|
console.log();
|
|
193
225
|
console.log('Translation to header file');
|
|
194
|
-
const longestFilename =
|
|
226
|
+
const longestFilename = files.keys().reduce((p, c) => Math.max(c.length, p), 0);
|
|
195
227
|
for (const [originalFilename, fileData] of files) {
|
|
196
228
|
const { content, hash: sha256 } = fileData;
|
|
197
|
-
const rawMime = (
|
|
229
|
+
const rawMime = mimeLookup(originalFilename);
|
|
198
230
|
if (!rawMime)
|
|
199
231
|
console.log((0, consoleColor_1.yellowLog)(` [${originalFilename}] unknown MIME type for extension '${node_path_1.default.extname(originalFilename)}', using text/plain`));
|
|
200
232
|
const mimeType = rawMime || 'text/plain';
|
|
@@ -253,8 +285,9 @@ function runPipeline(options) {
|
|
|
253
285
|
}
|
|
254
286
|
catch {
|
|
255
287
|
}
|
|
288
|
+
const now = new Date();
|
|
256
289
|
const manifest = {
|
|
257
|
-
generated:
|
|
290
|
+
generated: now.toISOString(),
|
|
258
291
|
engine: options.engine,
|
|
259
292
|
etag: options.etag,
|
|
260
293
|
gzip: options.gzip,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteesp32",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.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",
|
|
@@ -71,24 +71,23 @@
|
|
|
71
71
|
],
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@eslint/js": "^10.0.1",
|
|
74
|
-
"@types/
|
|
75
|
-
"@types/node": "^25.6.2",
|
|
74
|
+
"@types/node": "^25.9.3",
|
|
76
75
|
"@types/picomatch": "^4.0.3",
|
|
77
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
78
|
-
"@typescript-eslint/parser": "^8.
|
|
79
|
-
"@vitest/coverage-v8": "^4.1.
|
|
80
|
-
"eslint": "^10.
|
|
76
|
+
"@typescript-eslint/eslint-plugin": "^8.61.1",
|
|
77
|
+
"@typescript-eslint/parser": "^8.61.1",
|
|
78
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
79
|
+
"eslint": "^10.5.0",
|
|
81
80
|
"eslint-config-prettier": "^10.1.8",
|
|
82
81
|
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
83
|
-
"eslint-plugin-unicorn": "^
|
|
82
|
+
"eslint-plugin-unicorn": "^66.0.0",
|
|
84
83
|
"globals": "^17.6.0",
|
|
85
|
-
"memfs": "^4.57.
|
|
84
|
+
"memfs": "^4.57.7",
|
|
86
85
|
"nodemon": "^3.1.14",
|
|
87
|
-
"prettier": "^3.8.
|
|
86
|
+
"prettier": "^3.8.4",
|
|
88
87
|
"ts-node": "^10.9.2",
|
|
89
|
-
"tsx": "^4.
|
|
88
|
+
"tsx": "^4.22.4",
|
|
90
89
|
"typescript": "^6.0.3",
|
|
91
|
-
"vitest": "^4.1.
|
|
90
|
+
"vitest": "^4.1.9"
|
|
92
91
|
},
|
|
93
92
|
"peerDependencies": {
|
|
94
93
|
"vite": ">=5"
|
|
@@ -99,8 +98,6 @@
|
|
|
99
98
|
}
|
|
100
99
|
},
|
|
101
100
|
"dependencies": {
|
|
102
|
-
"
|
|
103
|
-
"picomatch": "^4.0.4",
|
|
104
|
-
"tinyglobby": "^0.2.16"
|
|
101
|
+
"tinyglobby": "^0.2.17"
|
|
105
102
|
}
|
|
106
103
|
}
|