svelteesp32 1.10.0 → 1.11.0
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 +111 -0
- package/dist/commandLine.d.ts +1 -0
- package/dist/commandLine.js +35 -5
- package/dist/consoleColor.d.ts +1 -0
- package/dist/consoleColor.js +3 -1
- package/dist/file.js +36 -11
- package/dist/index.js +3 -6
- package/package.json +14 -7
package/README.md
CHANGED
|
@@ -37,6 +37,51 @@ This npm package provides a solution for **inserting any JS client application i
|
|
|
37
37
|
- Node.js >= 20
|
|
38
38
|
- npm >= 9
|
|
39
39
|
|
|
40
|
+
### Development
|
|
41
|
+
|
|
42
|
+
#### Testing
|
|
43
|
+
|
|
44
|
+
The project includes comprehensive unit tests using Vitest:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Run tests once
|
|
48
|
+
npm run test
|
|
49
|
+
|
|
50
|
+
# Run tests in watch mode (for development)
|
|
51
|
+
npm run test:watch
|
|
52
|
+
|
|
53
|
+
# Generate coverage report
|
|
54
|
+
npm run test:coverage
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Test Coverage:** ~68% overall with focus on core functionality:
|
|
58
|
+
|
|
59
|
+
- `commandLine.ts`: 84.56% - CLI argument parsing and validation
|
|
60
|
+
- `file.ts`: 100% - File operations and duplicate detection
|
|
61
|
+
- `cppCode.ts`: 96.62% - C++ code generation and templates
|
|
62
|
+
- `consoleColor.ts`: 100% - Console output utilities
|
|
63
|
+
|
|
64
|
+
Coverage reports are generated in the `coverage/` directory and can be viewed by opening `coverage/index.html` in a browser.
|
|
65
|
+
|
|
66
|
+
#### Code Quality
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Check formatting
|
|
70
|
+
npm run format:check
|
|
71
|
+
|
|
72
|
+
# Fix formatting
|
|
73
|
+
npm run format:fix
|
|
74
|
+
|
|
75
|
+
# Check linting
|
|
76
|
+
npm run lint:check
|
|
77
|
+
|
|
78
|
+
# Fix linting issues
|
|
79
|
+
npm run lint:fix
|
|
80
|
+
|
|
81
|
+
# Fix all formatting and linting issues
|
|
82
|
+
npm run fix
|
|
83
|
+
```
|
|
84
|
+
|
|
40
85
|
### Usage
|
|
41
86
|
|
|
42
87
|
**Install package** as devDependency (it is practical if the package is part of the project so that you always receive updates)
|
|
@@ -258,6 +303,71 @@ At the same time, it can be an advantage that the content is cached by the brows
|
|
|
258
303
|
|
|
259
304
|
Typically, the entry point for web applications is the **index.htm or index.html** file. This does not need to be listed in the browser's address bar because web servers know that this file should be served by default. Svelteesp32 also does this: if there is an index.htm or index.html file, it sets it as the main file to be served. So using `http://esp_xxx.local` or just entering the `http://x.y.w.z/` IP address will serve this main file.
|
|
260
305
|
|
|
306
|
+
### File Exclusion
|
|
307
|
+
|
|
308
|
+
The `--exclude` option allows you to exclude files from being embedded in the ESP32 firmware using glob patterns. This is useful for excluding source maps, documentation, and test files that shouldn't be part of the deployed application.
|
|
309
|
+
|
|
310
|
+
#### Basic Usage
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# Exclude source maps
|
|
314
|
+
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map"
|
|
315
|
+
|
|
316
|
+
# Exclude documentation files
|
|
317
|
+
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.md"
|
|
318
|
+
|
|
319
|
+
# Exclude multiple file types (comma-separated)
|
|
320
|
+
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map,*.md,*.txt"
|
|
321
|
+
|
|
322
|
+
# Exclude using multiple flags
|
|
323
|
+
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map" --exclude="*.md"
|
|
324
|
+
|
|
325
|
+
# Exclude entire directories
|
|
326
|
+
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="test/**/*"
|
|
327
|
+
|
|
328
|
+
# Combine multiple approaches
|
|
329
|
+
npx svelteesp32 -e psychic -s ./dist -o ./output.h --exclude="*.map,*.md" --exclude="docs/**/*"
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Pattern Syntax
|
|
333
|
+
|
|
334
|
+
The exclude patterns use standard glob syntax:
|
|
335
|
+
|
|
336
|
+
- `*.map` - Match all files ending with `.map`
|
|
337
|
+
- `**/*.test.js` - Match all `.test.js` files in any directory
|
|
338
|
+
- `test/**/*` - Match all files in the `test` directory and subdirectories
|
|
339
|
+
- `.DS_Store` - Match specific filename
|
|
340
|
+
|
|
341
|
+
#### Default Exclusions
|
|
342
|
+
|
|
343
|
+
By default, the following system and development files are automatically excluded:
|
|
344
|
+
|
|
345
|
+
- `.DS_Store` (macOS system file)
|
|
346
|
+
- `Thumbs.db` (Windows thumbnail cache)
|
|
347
|
+
- `.git` (Git directory)
|
|
348
|
+
- `.svn` (SVN directory)
|
|
349
|
+
- `*.swp` (Vim swap files)
|
|
350
|
+
- `*~` (Backup files)
|
|
351
|
+
- `.gitignore` (Git ignore file)
|
|
352
|
+
- `.gitattributes` (Git attributes file)
|
|
353
|
+
|
|
354
|
+
Custom exclude patterns are added to these defaults.
|
|
355
|
+
|
|
356
|
+
#### Exclusion Output
|
|
357
|
+
|
|
358
|
+
When files are excluded, you'll see a summary in the build output:
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
Excluded 5 file(s):
|
|
362
|
+
- assets/index.js.map
|
|
363
|
+
- assets/vendor.js.map
|
|
364
|
+
- README.md
|
|
365
|
+
- docs/guide.md
|
|
366
|
+
- test/unit.test.js
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
This helps you verify that the correct files are being excluded from your build.
|
|
370
|
+
|
|
261
371
|
### C++ defines
|
|
262
372
|
|
|
263
373
|
To make it easy to integrate into a larger c++ project, we have made a couple of variables available as c++ defines.
|
|
@@ -307,6 +417,7 @@ You can use the following c++ directives at the project level if you want to con
|
|
|
307
417
|
| `-s` | **Source dist folder contains compiled web files** | (required) |
|
|
308
418
|
| `-e` | The engine for which the include file is created (psychic/psychic2/async/espidf) | psychic |
|
|
309
419
|
| `-o` | Generated output file with path | `svelteesp32.h` |
|
|
420
|
+
| `--exclude` | Exclude files matching glob pattern (repeatable or comma-separated) | Default system files |
|
|
310
421
|
| `--etag` | Use ETag header for cache (true/false/compiler) | false |
|
|
311
422
|
| `--cachetime` | Override no-cache response with a max-age=\<cachetime\> response (seconds) | 0 |
|
|
312
423
|
| `--gzip` | Compress content with gzip (true/false/compiler) | true |
|
package/dist/commandLine.d.ts
CHANGED
package/dist/commandLine.js
CHANGED
|
@@ -18,6 +18,8 @@ Options:
|
|
|
18
18
|
--espmethod <name> Name of generated method (default: "initSvelteStaticFiles")
|
|
19
19
|
--define <prefix> Prefix of c++ defines (default: "SVELTEESP32")
|
|
20
20
|
--cachetime <seconds> max-age cache time in seconds (default: 0)
|
|
21
|
+
--exclude <pattern> Exclude files matching glob pattern (repeatable or comma-separated)
|
|
22
|
+
Examples: --exclude="*.map" --exclude="test/**/*.ts"
|
|
21
23
|
-h, --help Shows this help
|
|
22
24
|
`);
|
|
23
25
|
process.exit(0);
|
|
@@ -32,6 +34,16 @@ function validateTriState(value, name) {
|
|
|
32
34
|
return value;
|
|
33
35
|
throw new Error(`Invalid ${name}: ${value}`);
|
|
34
36
|
}
|
|
37
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
38
|
+
'.DS_Store',
|
|
39
|
+
'Thumbs.db',
|
|
40
|
+
'.git',
|
|
41
|
+
'.svn',
|
|
42
|
+
'*.swp',
|
|
43
|
+
'*~',
|
|
44
|
+
'.gitignore',
|
|
45
|
+
'.gitattributes'
|
|
46
|
+
];
|
|
35
47
|
function parseArguments() {
|
|
36
48
|
const arguments_ = process.argv.slice(2);
|
|
37
49
|
const result = {
|
|
@@ -43,7 +55,8 @@ function parseArguments() {
|
|
|
43
55
|
version: '',
|
|
44
56
|
espmethod: 'initSvelteStaticFiles',
|
|
45
57
|
define: 'SVELTEESP32',
|
|
46
|
-
cachetime: 0
|
|
58
|
+
cachetime: 0,
|
|
59
|
+
exclude: [...DEFAULT_EXCLUDE_PATTERNS]
|
|
47
60
|
};
|
|
48
61
|
for (let index = 0; index < arguments_.length; index++) {
|
|
49
62
|
const argument = arguments_[index];
|
|
@@ -85,10 +98,18 @@ function parseArguments() {
|
|
|
85
98
|
break;
|
|
86
99
|
case 'cachetime':
|
|
87
100
|
result.cachetime = Number.parseInt(value, 10);
|
|
88
|
-
if (Number.isNaN(result.cachetime))
|
|
101
|
+
if (Number.isNaN(result.cachetime))
|
|
89
102
|
throw new TypeError(`Invalid cachetime: ${value}`);
|
|
90
|
-
}
|
|
91
103
|
break;
|
|
104
|
+
case 'exclude': {
|
|
105
|
+
const patterns = value
|
|
106
|
+
.split(',')
|
|
107
|
+
.map((p) => p.trim())
|
|
108
|
+
.filter(Boolean);
|
|
109
|
+
result.exclude = result.exclude || [...DEFAULT_EXCLUDE_PATTERNS];
|
|
110
|
+
result.exclude.push(...patterns);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
92
113
|
default:
|
|
93
114
|
throw new Error(`Unknown flag: ${flag}`);
|
|
94
115
|
}
|
|
@@ -161,11 +182,20 @@ function parseArguments() {
|
|
|
161
182
|
break;
|
|
162
183
|
case 'cachetime':
|
|
163
184
|
result.cachetime = Number.parseInt(nextArgument, 10);
|
|
164
|
-
if (Number.isNaN(result.cachetime))
|
|
185
|
+
if (Number.isNaN(result.cachetime))
|
|
165
186
|
throw new TypeError(`Invalid cachetime: ${nextArgument}`);
|
|
166
|
-
}
|
|
167
187
|
index++;
|
|
168
188
|
break;
|
|
189
|
+
case 'exclude': {
|
|
190
|
+
const patterns = nextArgument
|
|
191
|
+
.split(',')
|
|
192
|
+
.map((p) => p.trim())
|
|
193
|
+
.filter(Boolean);
|
|
194
|
+
result.exclude = result.exclude || [...DEFAULT_EXCLUDE_PATTERNS];
|
|
195
|
+
result.exclude.push(...patterns);
|
|
196
|
+
index++;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
169
199
|
default:
|
|
170
200
|
throw new Error(`Unknown flag: --${flag}`);
|
|
171
201
|
}
|
package/dist/consoleColor.d.ts
CHANGED
package/dist/consoleColor.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.redLog = exports.yellowLog = exports.greenLog = void 0;
|
|
3
|
+
exports.cyanLog = exports.redLog = exports.yellowLog = exports.greenLog = void 0;
|
|
4
4
|
const greenLog = (s) => `\u001B[32m${s}\u001B[0m`;
|
|
5
5
|
exports.greenLog = greenLog;
|
|
6
6
|
const yellowLog = (s) => `\u001B[33m${s}\u001B[0m`;
|
|
7
7
|
exports.yellowLog = yellowLog;
|
|
8
8
|
const redLog = (s) => `\u001B[31m${s}\u001B[0m`;
|
|
9
9
|
exports.redLog = redLog;
|
|
10
|
+
const cyanLog = (s) => `\u001B[36m${s}\u001B[0m`;
|
|
11
|
+
exports.cyanLog = cyanLog;
|
package/dist/file.js
CHANGED
|
@@ -7,6 +7,7 @@ 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"));
|
|
10
11
|
const tinyglobby_1 = require("tinyglobby");
|
|
11
12
|
const commandLine_1 = require("./commandLine");
|
|
12
13
|
const consoleColor_1 = require("./consoleColor");
|
|
@@ -15,19 +16,15 @@ const findSimilarFiles = (files) => {
|
|
|
15
16
|
for (const [filename, content] of files.entries()) {
|
|
16
17
|
const hash = (0, node_crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
17
18
|
const existingFiles = contentComparer.get(hash);
|
|
18
|
-
if (existingFiles)
|
|
19
|
+
if (existingFiles)
|
|
19
20
|
existingFiles.push(filename);
|
|
20
|
-
|
|
21
|
-
else {
|
|
21
|
+
else
|
|
22
22
|
contentComparer.set(hash, [filename]);
|
|
23
|
-
}
|
|
24
23
|
}
|
|
25
24
|
const result = [];
|
|
26
|
-
for (const filenames of contentComparer.values())
|
|
27
|
-
if (filenames.length > 1)
|
|
25
|
+
for (const filenames of contentComparer.values())
|
|
26
|
+
if (filenames.length > 1)
|
|
28
27
|
result.push(filenames);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
28
|
return result;
|
|
32
29
|
};
|
|
33
30
|
const shouldSkipFile = (filename, allFilenames) => {
|
|
@@ -42,18 +39,46 @@ const shouldSkipFile = (filename, allFilenames) => {
|
|
|
42
39
|
}
|
|
43
40
|
return false;
|
|
44
41
|
};
|
|
42
|
+
const isExcluded = (filename, excludePatterns) => {
|
|
43
|
+
if (excludePatterns.length === 0)
|
|
44
|
+
return false;
|
|
45
|
+
const normalizedFilename = filename.replace(/\\/g, '/');
|
|
46
|
+
const isMatch = (0, picomatch_1.default)(excludePatterns, {
|
|
47
|
+
dot: true,
|
|
48
|
+
noglobstar: false,
|
|
49
|
+
matchBase: false
|
|
50
|
+
});
|
|
51
|
+
return isMatch(normalizedFilename);
|
|
52
|
+
};
|
|
45
53
|
const getFiles = () => {
|
|
46
54
|
const allFilenames = (0, tinyglobby_1.globSync)('**/*', { cwd: commandLine_1.cmdLine.sourcepath, onlyFiles: true, dot: false });
|
|
47
|
-
const
|
|
55
|
+
const withoutCompressed = allFilenames.filter((filename) => !shouldSkipFile(filename, allFilenames));
|
|
56
|
+
const excludePatterns = commandLine_1.cmdLine.exclude || [];
|
|
57
|
+
const excludedFiles = [];
|
|
58
|
+
const filenames = withoutCompressed.filter((filename) => {
|
|
59
|
+
if (isExcluded(filename, excludePatterns)) {
|
|
60
|
+
excludedFiles.push(filename);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
});
|
|
65
|
+
if (excludedFiles.length > 0) {
|
|
66
|
+
console.log((0, consoleColor_1.cyanLog)(`\n Excluded ${excludedFiles.length} file(s):`));
|
|
67
|
+
const displayLimit = 10;
|
|
68
|
+
for (const file of excludedFiles.slice(0, displayLimit))
|
|
69
|
+
console.log((0, consoleColor_1.cyanLog)(` - ${file}`));
|
|
70
|
+
if (excludedFiles.length > displayLimit)
|
|
71
|
+
console.log((0, consoleColor_1.cyanLog)(` ... and ${excludedFiles.length - displayLimit} more`));
|
|
72
|
+
console.log();
|
|
73
|
+
}
|
|
48
74
|
const result = new Map();
|
|
49
75
|
for (const filename of filenames) {
|
|
50
76
|
const filePath = node_path_1.default.join(commandLine_1.cmdLine.sourcepath, filename);
|
|
51
77
|
result.set(filename, (0, node_fs_1.readFileSync)(filePath, { flag: 'r' }));
|
|
52
78
|
}
|
|
53
79
|
const duplicates = findSimilarFiles(result);
|
|
54
|
-
for (const sameFiles of duplicates)
|
|
80
|
+
for (const sameFiles of duplicates)
|
|
55
81
|
console.log((0, consoleColor_1.yellowLog)(` ${sameFiles.join(', ')} files look like identical`));
|
|
56
|
-
}
|
|
57
82
|
return result;
|
|
58
83
|
};
|
|
59
84
|
exports.getFiles = getFiles;
|
package/dist/index.js
CHANGED
|
@@ -26,9 +26,8 @@ const calculateCompressionRatio = (originalSize, compressedSize) => Math.round((
|
|
|
26
26
|
const formatCompressionLog = (filename, padding, originalSize, compressedSize, useGzip) => {
|
|
27
27
|
const ratio = calculateCompressionRatio(originalSize, compressedSize);
|
|
28
28
|
const sizeInfo = `(${originalSize} -> ${compressedSize} = ${ratio}%)`;
|
|
29
|
-
if (useGzip)
|
|
29
|
+
if (useGzip)
|
|
30
30
|
return (0, consoleColor_1.greenLog)(` [${filename}] ${padding} ✓ gzip used ${sizeInfo}`);
|
|
31
|
-
}
|
|
32
31
|
const tooSmall = originalSize <= GZIP_MIN_SIZE ? '(too small) ' : '';
|
|
33
32
|
return (0, consoleColor_1.yellowLog)(` [${filename}] ${padding} x gzip unused ${tooSmall}${sizeInfo}`);
|
|
34
33
|
};
|
|
@@ -44,12 +43,10 @@ const createSourceEntry = (filename, dataname, content, contentGzip, mimeType, m
|
|
|
44
43
|
});
|
|
45
44
|
const updateExtensionGroup = (extension) => {
|
|
46
45
|
const group = filesByExtension.find((fe) => fe.extension === extension);
|
|
47
|
-
if (group)
|
|
46
|
+
if (group)
|
|
48
47
|
group.count += 1;
|
|
49
|
-
|
|
50
|
-
else {
|
|
48
|
+
else
|
|
51
49
|
filesByExtension.push({ extension, count: 1 });
|
|
52
|
-
}
|
|
53
50
|
};
|
|
54
51
|
console.log('Collecting source files');
|
|
55
52
|
const files = (0, file_1.getFiles)();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteesp32",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
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",
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
"dev:async": "nodemon src/index.ts -- -e async -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32.h --etag=true --gzip=true --cachetime=86400 --version=v$npm_package_version",
|
|
32
32
|
"dev:psychic": "nodemon src/index.ts -- -e psychic -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32.h --etag=false --gzip=false --version=v$npm_package_version",
|
|
33
33
|
"dev:psychic2": "nodemon src/index.ts -- -e psychic2 -s ./demo/svelte/dist -o ./demo/esp32/include/svelteesp32.h --etag=false --gzip=false --version=v$npm_package_version",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:watch": "vitest",
|
|
36
|
+
"test:coverage": "vitest run --coverage",
|
|
34
37
|
"test:esp32": "./package.script && ~/.platformio/penv/bin/pio run -d ./demo/esp32",
|
|
35
38
|
"test:esp32idf": "./package.script && ~/.platformio/penv/bin/pio run -d ./demo/esp32idf",
|
|
36
39
|
"test:all": "node --run test:esp32 && node --run test:esp32idf",
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
"lint:check": "eslint .",
|
|
42
45
|
"lint:fix": "eslint --fix .",
|
|
43
46
|
"fix": "node --run format:fix && node --run lint:fix && node --run format:fix",
|
|
44
|
-
"all": "node --run fix && node --run build",
|
|
47
|
+
"all": "node --run fix && node --run build && node --run test",
|
|
45
48
|
"npm:reinstall": "rm -rf ./node_modules && rm -f ./package-lock.json && npm i && npm i"
|
|
46
49
|
},
|
|
47
50
|
"keywords": [
|
|
@@ -60,21 +63,25 @@
|
|
|
60
63
|
"@types/mime-types": "^3.0.1",
|
|
61
64
|
"@types/node": "^24.10.1",
|
|
62
65
|
"@types/picomatch": "^4.0.2",
|
|
63
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
64
|
-
"@typescript-eslint/parser": "^8.
|
|
66
|
+
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
|
67
|
+
"@typescript-eslint/parser": "^8.48.1",
|
|
68
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
65
69
|
"eslint": "^9.39.1",
|
|
66
70
|
"eslint-config-prettier": "^10.1.8",
|
|
67
71
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
68
72
|
"eslint-plugin-unicorn": "^62.0.0",
|
|
73
|
+
"memfs": "^4.51.1",
|
|
69
74
|
"nodemon": "^3.1.11",
|
|
70
|
-
"prettier": "^3.
|
|
75
|
+
"prettier": "^3.7.4",
|
|
71
76
|
"ts-node": "^10.9.2",
|
|
72
|
-
"tsx": "^4.
|
|
73
|
-
"typescript": "^5.9.3"
|
|
77
|
+
"tsx": "^4.21.0",
|
|
78
|
+
"typescript": "^5.9.3",
|
|
79
|
+
"vitest": "^4.0.15"
|
|
74
80
|
},
|
|
75
81
|
"dependencies": {
|
|
76
82
|
"handlebars": "^4.7.8",
|
|
77
83
|
"mime-types": "^3.0.2",
|
|
84
|
+
"picomatch": "^4.0.3",
|
|
78
85
|
"tinyglobby": "^0.2.15"
|
|
79
86
|
}
|
|
80
87
|
}
|