svelteesp32 1.9.4 → 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 CHANGED
@@ -12,6 +12,8 @@ In order to be able to easily update OTA, it is important - from the users' poin
12
12
 
13
13
  This npm package provides a solution for **inserting any JS client application into the ESP web server** (PsychicHttp and also ESPAsyncWebServer (https://github.com/ESP32Async/ESPAsyncWebServer) and ESP-IDF available, PsychicHttp is the default). For this, JS, html, css, font, assets, etc. files must be converted to binary byte array. Npm mode is easy to use and easy to **integrate into your CI/CD pipeline**.
14
14
 
15
+ > Starting with version v1.10.0, we reduced npm dependencies
16
+
15
17
  > Starting with version v1.9.0, code generator for esp-idf is available
16
18
 
17
19
  > Starting with version v1.8.0, use the new and maintained ESPAsyncWebserver available at https://github.com/ESP32Async/ESPAsyncWebServer
@@ -35,6 +37,51 @@ This npm package provides a solution for **inserting any JS client application i
35
37
  - Node.js >= 20
36
38
  - npm >= 9
37
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
+
38
85
  ### Usage
39
86
 
40
87
  **Install package** as devDependency (it is practical if the package is part of the project so that you always receive updates)
@@ -256,6 +303,71 @@ At the same time, it can be an advantage that the content is cached by the brows
256
303
 
257
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.
258
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
+
259
371
  ### C++ defines
260
372
 
261
373
  To make it easy to integrate into a larger c++ project, we have made a couple of variables available as c++ defines.
@@ -305,6 +417,7 @@ You can use the following c++ directives at the project level if you want to con
305
417
  | `-s` | **Source dist folder contains compiled web files** | (required) |
306
418
  | `-e` | The engine for which the include file is created (psychic/psychic2/async/espidf) | psychic |
307
419
  | `-o` | Generated output file with path | `svelteesp32.h` |
420
+ | `--exclude` | Exclude files matching glob pattern (repeatable or comma-separated) | Default system files |
308
421
  | `--etag` | Use ETag header for cache (true/false/compiler) | false |
309
422
  | `--cachetime` | Override no-cache response with a max-age=\<cachetime\> response (seconds) | 0 |
310
423
  | `--gzip` | Compress content with gzip (true/false/compiler) | true |
@@ -9,6 +9,7 @@ interface ICopyFilesArguments {
9
9
  cachetime: number;
10
10
  created: boolean;
11
11
  version: string;
12
+ exclude: string[];
12
13
  help?: boolean;
13
14
  }
14
15
  export declare const cmdLine: ICopyFilesArguments;
@@ -2,91 +2,214 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.cmdLine = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
- const ts_command_line_args_1 = require("ts-command-line-args");
6
- exports.cmdLine = (0, ts_command_line_args_1.parse)({
7
- engine: {
8
- type: (value) => {
9
- if (value === 'psychic')
10
- return 'psychic';
11
- if (value === 'psychic2')
12
- return 'psychic2';
13
- if (value === 'async')
14
- return 'async';
15
- if (value === 'espidf')
16
- return 'espidf';
17
- throw new Error(`Invalid engine: ${value}`);
18
- },
19
- alias: 'e',
20
- description: 'The engine for which the include file is created (psychic|psychic2|async)',
21
- defaultValue: 'psychic'
22
- },
23
- sourcepath: {
24
- type: String,
25
- alias: 's',
26
- description: 'Source dist folder contains compiled web files'
27
- },
28
- outputfile: {
29
- type: String,
30
- alias: 'o',
31
- description: 'Generated output file with path',
32
- defaultValue: 'svelteesp32.h'
33
- },
34
- etag: {
35
- type: (value) => {
36
- if (value === 'true')
37
- return 'true';
38
- if (value === 'false')
39
- return 'false';
40
- if (value === 'compiler')
41
- return 'compiler';
42
- throw new Error(`Invalid etag: ${value}`);
43
- },
44
- description: 'Use ETAG header for cache',
45
- defaultValue: 'false'
46
- },
47
- gzip: {
48
- type: (value) => {
49
- if (value === 'true')
50
- return 'true';
51
- if (value === 'false')
52
- return 'false';
53
- if (value === 'compiler')
54
- return 'compiler';
55
- throw new Error(`Invalid etag: ${value}`);
56
- },
57
- description: 'Compress content with gzip',
58
- defaultValue: 'true'
59
- },
60
- created: {
61
- type: Boolean,
62
- description: 'Include creation time in the output file',
63
- defaultValue: false
64
- },
65
- version: {
66
- type: String,
67
- description: 'Include version info in the output file',
68
- defaultValue: ''
69
- },
70
- espmethod: {
71
- type: String,
72
- description: 'Name of generated method',
73
- defaultValue: 'initSvelteStaticFiles'
74
- },
75
- define: {
76
- type: String,
77
- description: 'Prefix of c++ defines',
78
- defaultValue: 'SVELTEESP32'
79
- },
80
- cachetime: {
81
- type: Number,
82
- description: 'max-age cache time in seconds',
83
- defaultValue: 0
84
- },
85
- help: { type: Boolean, optional: true, alias: 'h', description: 'Shows this help' }
86
- }, {
87
- helpArg: 'help',
88
- headerContentSections: [{ header: 'svelteesp32', content: 'Svelte JS to ESP32 converter' }]
89
- });
5
+ function showHelp() {
6
+ console.log(`
7
+ svelteesp32 - Svelte JS to ESP32 converter
8
+
9
+ Options:
10
+ -e, --engine <value> The engine for which the include file is created
11
+ (psychic|psychic2|async|espidf) (default: "psychic")
12
+ -s, --sourcepath <path> Source dist folder contains compiled web files (required)
13
+ -o, --outputfile <path> Generated output file with path (default: "svelteesp32.h")
14
+ --etag <value> Use ETAG header for cache (true|false|compiler) (default: "false")
15
+ --gzip <value> Compress content with gzip (true|false|compiler) (default: "true")
16
+ --created Include creation time in the output file (default: false)
17
+ --version <value> Include version info in the output file (default: "")
18
+ --espmethod <name> Name of generated method (default: "initSvelteStaticFiles")
19
+ --define <prefix> Prefix of c++ defines (default: "SVELTEESP32")
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"
23
+ -h, --help Shows this help
24
+ `);
25
+ process.exit(0);
26
+ }
27
+ function validateEngine(value) {
28
+ if (value === 'psychic' || value === 'psychic2' || value === 'async' || value === 'espidf')
29
+ return value;
30
+ throw new Error(`Invalid engine: ${value}`);
31
+ }
32
+ function validateTriState(value, name) {
33
+ if (value === 'true' || value === 'false' || value === 'compiler')
34
+ return value;
35
+ throw new Error(`Invalid ${name}: ${value}`);
36
+ }
37
+ const DEFAULT_EXCLUDE_PATTERNS = [
38
+ '.DS_Store',
39
+ 'Thumbs.db',
40
+ '.git',
41
+ '.svn',
42
+ '*.swp',
43
+ '*~',
44
+ '.gitignore',
45
+ '.gitattributes'
46
+ ];
47
+ function parseArguments() {
48
+ const arguments_ = process.argv.slice(2);
49
+ const result = {
50
+ engine: 'psychic',
51
+ outputfile: 'svelteesp32.h',
52
+ etag: 'false',
53
+ gzip: 'true',
54
+ created: false,
55
+ version: '',
56
+ espmethod: 'initSvelteStaticFiles',
57
+ define: 'SVELTEESP32',
58
+ cachetime: 0,
59
+ exclude: [...DEFAULT_EXCLUDE_PATTERNS]
60
+ };
61
+ for (let index = 0; index < arguments_.length; index++) {
62
+ const argument = arguments_[index];
63
+ if (!argument)
64
+ continue;
65
+ if (argument === '--help' || argument === '-h')
66
+ showHelp();
67
+ if (argument.startsWith('--') && argument.includes('=')) {
68
+ const parts = argument.split('=');
69
+ const flag = parts[0];
70
+ const value = parts.slice(1).join('=');
71
+ if (!flag || !value)
72
+ throw new Error(`Invalid argument format: ${argument}`);
73
+ const flagName = flag.slice(2);
74
+ switch (flagName) {
75
+ case 'engine':
76
+ result.engine = validateEngine(value);
77
+ break;
78
+ case 'sourcepath':
79
+ result.sourcepath = value;
80
+ break;
81
+ case 'outputfile':
82
+ result.outputfile = value;
83
+ break;
84
+ case 'etag':
85
+ result.etag = validateTriState(value, 'etag');
86
+ break;
87
+ case 'gzip':
88
+ result.gzip = validateTriState(value, 'gzip');
89
+ break;
90
+ case 'version':
91
+ result.version = value;
92
+ break;
93
+ case 'espmethod':
94
+ result.espmethod = value;
95
+ break;
96
+ case 'define':
97
+ result.define = value;
98
+ break;
99
+ case 'cachetime':
100
+ result.cachetime = Number.parseInt(value, 10);
101
+ if (Number.isNaN(result.cachetime))
102
+ throw new TypeError(`Invalid cachetime: ${value}`);
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
+ }
113
+ default:
114
+ throw new Error(`Unknown flag: ${flag}`);
115
+ }
116
+ continue;
117
+ }
118
+ if (argument === '--created') {
119
+ result.created = true;
120
+ continue;
121
+ }
122
+ if (argument.startsWith('-') && !argument.startsWith('--')) {
123
+ const flag = argument.slice(1);
124
+ const nextArgument = arguments_[index + 1];
125
+ if (!nextArgument || nextArgument.startsWith('-'))
126
+ throw new Error(`Missing value for flag: ${argument}`);
127
+ switch (flag) {
128
+ case 'e':
129
+ result.engine = validateEngine(nextArgument);
130
+ index++;
131
+ break;
132
+ case 's':
133
+ result.sourcepath = nextArgument;
134
+ index++;
135
+ break;
136
+ case 'o':
137
+ result.outputfile = nextArgument;
138
+ index++;
139
+ break;
140
+ default:
141
+ throw new Error(`Unknown flag: ${argument}`);
142
+ }
143
+ continue;
144
+ }
145
+ if (argument.startsWith('--')) {
146
+ const flag = argument.slice(2);
147
+ const nextArgument = arguments_[index + 1];
148
+ if (!nextArgument || nextArgument.startsWith('-'))
149
+ throw new Error(`Missing value for flag: ${argument}`);
150
+ switch (flag) {
151
+ case 'engine':
152
+ result.engine = validateEngine(nextArgument);
153
+ index++;
154
+ break;
155
+ case 'sourcepath':
156
+ result.sourcepath = nextArgument;
157
+ index++;
158
+ break;
159
+ case 'outputfile':
160
+ result.outputfile = nextArgument;
161
+ index++;
162
+ break;
163
+ case 'etag':
164
+ result.etag = validateTriState(nextArgument, 'etag');
165
+ index++;
166
+ break;
167
+ case 'gzip':
168
+ result.gzip = validateTriState(nextArgument, 'gzip');
169
+ index++;
170
+ break;
171
+ case 'version':
172
+ result.version = nextArgument;
173
+ index++;
174
+ break;
175
+ case 'espmethod':
176
+ result.espmethod = nextArgument;
177
+ index++;
178
+ break;
179
+ case 'define':
180
+ result.define = nextArgument;
181
+ index++;
182
+ break;
183
+ case 'cachetime':
184
+ result.cachetime = Number.parseInt(nextArgument, 10);
185
+ if (Number.isNaN(result.cachetime))
186
+ throw new TypeError(`Invalid cachetime: ${nextArgument}`);
187
+ index++;
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
+ }
199
+ default:
200
+ throw new Error(`Unknown flag: --${flag}`);
201
+ }
202
+ continue;
203
+ }
204
+ throw new Error(`Unknown argument: ${argument}`);
205
+ }
206
+ if (!result.sourcepath) {
207
+ console.error('Error: --sourcepath is required');
208
+ showHelp();
209
+ }
210
+ return result;
211
+ }
212
+ exports.cmdLine = parseArguments();
90
213
  if (!(0, node_fs_1.existsSync)(exports.cmdLine.sourcepath) || !(0, node_fs_1.statSync)(exports.cmdLine.sourcepath).isDirectory()) {
91
214
  console.error(`Directory ${exports.cmdLine.sourcepath} not exists or not a directory`);
92
215
  process.exit(1);
@@ -1,3 +1,4 @@
1
1
  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
+ export declare const cyanLog: (s: string) => string;
@@ -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,7 +7,8 @@ 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 glob_1 = require("glob");
10
+ const picomatch_1 = __importDefault(require("picomatch"));
11
+ const tinyglobby_1 = require("tinyglobby");
11
12
  const commandLine_1 = require("./commandLine");
12
13
  const consoleColor_1 = require("./consoleColor");
13
14
  const findSimilarFiles = (files) => {
@@ -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
- const allFilenames = (0, glob_1.globSync)('**/*', { cwd: commandLine_1.cmdLine.sourcepath, nodir: true });
47
- const filenames = allFilenames.filter((filename) => !shouldSkipFile(filename, allFilenames));
54
+ const allFilenames = (0, tinyglobby_1.globSync)('**/*', { cwd: commandLine_1.cmdLine.sourcepath, onlyFiles: true, dot: false });
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.9.4",
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": [
@@ -59,22 +62,26 @@
59
62
  "devDependencies": {
60
63
  "@types/mime-types": "^3.0.1",
61
64
  "@types/node": "^24.10.1",
62
- "@typescript-eslint/eslint-plugin": "^8.47.0",
63
- "@typescript-eslint/parser": "^8.47.0",
65
+ "@types/picomatch": "^4.0.2",
66
+ "@typescript-eslint/eslint-plugin": "^8.48.1",
67
+ "@typescript-eslint/parser": "^8.48.1",
68
+ "@vitest/coverage-v8": "^4.0.15",
64
69
  "eslint": "^9.39.1",
65
70
  "eslint-config-prettier": "^10.1.8",
66
71
  "eslint-plugin-simple-import-sort": "^12.1.1",
67
72
  "eslint-plugin-unicorn": "^62.0.0",
73
+ "memfs": "^4.51.1",
68
74
  "nodemon": "^3.1.11",
69
- "prettier": "^3.6.2",
75
+ "prettier": "^3.7.4",
70
76
  "ts-node": "^10.9.2",
71
- "tsx": "^4.20.6",
72
- "typescript": "^5.9.3"
77
+ "tsx": "^4.21.0",
78
+ "typescript": "^5.9.3",
79
+ "vitest": "^4.0.15"
73
80
  },
74
81
  "dependencies": {
75
- "glob": "^12.0.0",
76
82
  "handlebars": "^4.7.8",
77
- "mime-types": "^3.0.1",
78
- "ts-command-line-args": "^2.5.1"
83
+ "mime-types": "^3.0.2",
84
+ "picomatch": "^4.0.3",
85
+ "tinyglobby": "^0.2.15"
79
86
  }
80
87
  }