svelteesp32 1.16.0 → 2.0.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 +51 -44
- package/dist/commandLine.d.ts +10 -6
- package/dist/commandLine.js +121 -149
- package/dist/cppCode.js +19 -214
- package/dist/cppCodeEspIdf.d.ts +1 -1
- package/dist/cppCodeEspIdf.js +10 -8
- package/dist/errorMessages.d.ts +1 -1
- package/dist/errorMessages.js +3 -12
- package/dist/file.d.ts +5 -1
- package/dist/file.js +9 -6
- package/dist/index.d.ts +5 -3
- package/dist/index.js +71 -53
- package/package.json +2 -4
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
[Changelog](CHANGELOG.md)
|
|
8
8
|
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="svelteesp32.png" alt="svelteesp32" />
|
|
11
|
+
</p>
|
|
12
|
+
|
|
9
13
|
---
|
|
10
14
|
|
|
11
15
|
## Why SvelteESP32?
|
|
@@ -21,7 +25,7 @@
|
|
|
21
25
|
- **Smart Caching** — Built-in SHA256 ETags deliver HTTP 304 responses, slashing bandwidth on constrained devices.
|
|
22
26
|
- **CI/CD Ready** — Simple npm package that slots into any build pipeline.
|
|
23
27
|
- **Zero Runtime Overhead** — Data served directly from flash. No filesystem reads, no RAM allocation.
|
|
24
|
-
- **
|
|
28
|
+
- **3 Web Server Engines** — PsychicHttpServer, ESPAsyncWebServer, and native ESP-IDF supported.
|
|
25
29
|
|
|
26
30
|
---
|
|
27
31
|
|
|
@@ -75,12 +79,13 @@ void setup() {
|
|
|
75
79
|
|
|
76
80
|
## What's New
|
|
77
81
|
|
|
82
|
+
- **v2.0.0** — **BREAKING**: PsychicHttpServer V2 is now the default `psychic` engine. The `psychic2` engine has been removed. Dry run mode, C++ identifier validation, improved MIME type warnings
|
|
83
|
+
- **v1.16.0** — Size budget constraints (`--maxsize`, `--maxgzipsize`)
|
|
78
84
|
- **v1.15.0** — `--basepath` for multiple frontends (e.g., `/admin`, `/app`)
|
|
79
85
|
- **v1.13.0** — npm package variable interpolation in RC files
|
|
80
86
|
- **v1.12.0** — RC file configuration support
|
|
81
87
|
- **v1.11.0** — File exclusion patterns
|
|
82
88
|
- **v1.9.0** — Native ESP-IDF engine
|
|
83
|
-
- **v1.5.0** — PsychicHttp V2 support
|
|
84
89
|
|
|
85
90
|
---
|
|
86
91
|
|
|
@@ -107,9 +112,6 @@ Choose your web server engine:
|
|
|
107
112
|
# PsychicHttpServer (recommended for ESP32)
|
|
108
113
|
npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
109
114
|
|
|
110
|
-
# PsychicHttpServer V2
|
|
111
|
-
npx svelteesp32 -e psychic2 -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
112
|
-
|
|
113
115
|
# ESPAsyncWebServer (ESP32 + ESP8266)
|
|
114
116
|
npx svelteesp32 -e async -s ./dist -o ./esp32/svelteesp32.h --etag=true
|
|
115
117
|
|
|
@@ -140,7 +142,7 @@ Watch your files get optimized in real-time:
|
|
|
140
142
|
|
|
141
143
|
### ESP32 Integration
|
|
142
144
|
|
|
143
|
-
**PsychicHttpServer (Recommended)**
|
|
145
|
+
**PsychicHttpServer V2 (Recommended)**
|
|
144
146
|
|
|
145
147
|
```c
|
|
146
148
|
#include <PsychicHttp.h>
|
|
@@ -183,14 +185,14 @@ void app_main() {
|
|
|
183
185
|
}
|
|
184
186
|
```
|
|
185
187
|
|
|
186
|
-
Working examples: [Arduino/PlatformIO](demo/esp32) | [ESP-IDF](demo/esp32idf)
|
|
188
|
+
Working examples with LED control via web interface: [Arduino/PlatformIO](demo/esp32) | [ESP-IDF](demo/esp32idf)
|
|
187
189
|
|
|
188
190
|
### What Gets Generated
|
|
189
191
|
|
|
190
192
|
The generated header file includes everything your ESP needs:
|
|
191
193
|
|
|
192
194
|
```c
|
|
193
|
-
//engine: PsychicHttpServer
|
|
195
|
+
//engine: PsychicHttpServer V2
|
|
194
196
|
//config: engine=psychic sourcepath=./dist outputfile=./output.h etag=true gzip=true cachetime=0 espmethod=initSvelteStaticFiles define=SVELTEESP32
|
|
195
197
|
//
|
|
196
198
|
#define SVELTEESP32_COUNT 5
|
|
@@ -231,23 +233,21 @@ const size_t SVELTEESP32_FILE_COUNT = sizeof(SVELTEESP32_FILES) / sizeof(SVELTEE
|
|
|
231
233
|
extern "C" void __attribute__((weak)) SVELTEESP32_onFileServed(const char* path, int statusCode) {}
|
|
232
234
|
|
|
233
235
|
void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
234
|
-
server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request) {
|
|
236
|
+
server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {
|
|
235
237
|
if (request->hasHeader("If-None-Match") &&
|
|
236
238
|
request->header("If-None-Match").equals(etag_assets_index_KwubEIf__js)) {
|
|
237
|
-
|
|
238
|
-
response304.setCode(304);
|
|
239
|
+
response->setCode(304);
|
|
239
240
|
SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 304);
|
|
240
|
-
return
|
|
241
|
+
return response->send();
|
|
241
242
|
}
|
|
242
243
|
|
|
243
|
-
|
|
244
|
-
response
|
|
245
|
-
response
|
|
246
|
-
response
|
|
247
|
-
response
|
|
248
|
-
response.setContent(datagzip_assets_index_KwubEIf__js, 12547);
|
|
244
|
+
response->setContentType("text/javascript");
|
|
245
|
+
response->addHeader("Content-Encoding", "gzip");
|
|
246
|
+
response->addHeader("Cache-Control", "no-cache");
|
|
247
|
+
response->addHeader("ETag", etag_assets_index_KwubEIf__js);
|
|
248
|
+
response->setContent(datagzip_assets_index_KwubEIf__js, 12547);
|
|
249
249
|
SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 200);
|
|
250
|
-
return response
|
|
250
|
+
return response->send();
|
|
251
251
|
});
|
|
252
252
|
|
|
253
253
|
// ... more routes
|
|
@@ -258,14 +258,13 @@ void initSvelteStaticFiles(PsychicHttpServer * server) {
|
|
|
258
258
|
|
|
259
259
|
## Supported Web Server Engines
|
|
260
260
|
|
|
261
|
-
| Engine
|
|
262
|
-
|
|
|
263
|
-
| **
|
|
264
|
-
| **
|
|
265
|
-
| **
|
|
266
|
-
| **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only |
|
|
261
|
+
| Engine | Flag | Best For | Platform |
|
|
262
|
+
| ------------------------ | ------------ | ---------------------------- | --------------- |
|
|
263
|
+
| **PsychicHttpServer V2** | `-e psychic` | Maximum performance | ESP32 only |
|
|
264
|
+
| **ESPAsyncWebServer** | `-e async` | Cross-platform compatibility | ESP32 + ESP8266 |
|
|
265
|
+
| **Native ESP-IDF** | `-e espidf` | Pure ESP-IDF projects | ESP32 only |
|
|
267
266
|
|
|
268
|
-
**Recommendation:** For ESP32-only projects, use PsychicHttpServer for the fastest, most stable experience.
|
|
267
|
+
**Recommendation:** For ESP32-only projects, use PsychicHttpServer V2 (`-e psychic`) for the fastest, most stable experience.
|
|
269
268
|
|
|
270
269
|
**Note:** For PsychicHttp, configure `server.config.max_uri_handlers` to match your file count.
|
|
271
270
|
|
|
@@ -288,7 +287,7 @@ Reduce bandwidth dramatically with HTTP 304 "Not Modified" responses. When a bro
|
|
|
288
287
|
- **Minimal overhead** — adds ~1-3% code size for significant bandwidth savings
|
|
289
288
|
- **Compiler mode** — use `--etag=compiler` and control via `-D SVELTEESP32_ENABLE_ETAG`
|
|
290
289
|
|
|
291
|
-
All
|
|
290
|
+
All three engines support full ETag validation.
|
|
292
291
|
|
|
293
292
|
### Browser Cache Control
|
|
294
293
|
|
|
@@ -402,22 +401,25 @@ Called for every response (200 = content served, 304 = cache hit).
|
|
|
402
401
|
|
|
403
402
|
## CLI Reference
|
|
404
403
|
|
|
405
|
-
| Option | Description
|
|
406
|
-
| ---------------- |
|
|
407
|
-
| `-s` | Source folder with compiled web files
|
|
408
|
-
| `-e` | Web server engine (psychic/
|
|
409
|
-
| `-o` | Output header file path
|
|
410
|
-
| `--etag` | ETag caching (true/false/compiler)
|
|
411
|
-
| `--gzip` | Gzip compression (true/false/compiler)
|
|
412
|
-
| `--exclude` | Exclude files by glob pattern
|
|
413
|
-
| `--basepath` | URL prefix for all routes
|
|
414
|
-
| `--
|
|
415
|
-
| `--
|
|
416
|
-
| `--
|
|
417
|
-
| `--
|
|
418
|
-
| `--
|
|
419
|
-
| `--
|
|
420
|
-
|
|
|
404
|
+
| Option | Description | Default |
|
|
405
|
+
| ---------------- | ------------------------------------------------ | ----------------------- |
|
|
406
|
+
| `-s` | Source folder with compiled web files | (required) |
|
|
407
|
+
| `-e` | Web server engine (psychic/async/espidf) | `psychic` |
|
|
408
|
+
| `-o` | Output header file path | `svelteesp32.h` |
|
|
409
|
+
| `--etag` | ETag caching (true/false/compiler) | `false` |
|
|
410
|
+
| `--gzip` | Gzip compression (true/false/compiler) | `true` |
|
|
411
|
+
| `--exclude` | Exclude files by glob pattern | System files |
|
|
412
|
+
| `--basepath` | URL prefix for all routes | (none) |
|
|
413
|
+
| `--maxsize` | Max total uncompressed size (e.g., `400k`, `1m`) | (none) |
|
|
414
|
+
| `--maxgzipsize` | Max total gzip size (e.g., `150k`, `500k`) | (none) |
|
|
415
|
+
| `--cachetime` | Cache-Control max-age in seconds | `0` |
|
|
416
|
+
| `--version` | Version string in header | (none) |
|
|
417
|
+
| `--define` | C++ define prefix | `SVELTEESP32` |
|
|
418
|
+
| `--espmethod` | Init function name | `initSvelteStaticFiles` |
|
|
419
|
+
| `--config` | Custom RC file path | `.svelteesp32rc.json` |
|
|
420
|
+
| `--dryrun` | Show summary without writing output | `false` |
|
|
421
|
+
| `--noindexcheck` | Skip index.html validation | `false` |
|
|
422
|
+
| `-h` | Show help | |
|
|
421
423
|
|
|
422
424
|
---
|
|
423
425
|
|
|
@@ -432,7 +434,12 @@ Store your settings in `.svelteesp32rc.json` for zero-argument builds:
|
|
|
432
434
|
"outputfile": "./esp32/svelteesp32.h",
|
|
433
435
|
"etag": "true",
|
|
434
436
|
"gzip": "true",
|
|
435
|
-
"exclude": ["*.map", "*.md"]
|
|
437
|
+
"exclude": ["*.map", "*.md"],
|
|
438
|
+
"basepath": "/ui",
|
|
439
|
+
"maxsize": "400k",
|
|
440
|
+
"maxgzipsize": "150k",
|
|
441
|
+
"noindexcheck": false,
|
|
442
|
+
"dryrun": false
|
|
436
443
|
}
|
|
437
444
|
```
|
|
438
445
|
|
package/dist/commandLine.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
interface ICopyFilesArguments {
|
|
2
|
-
engine: 'psychic' | '
|
|
2
|
+
engine: 'psychic' | 'async' | 'espidf';
|
|
3
3
|
sourcepath: string;
|
|
4
4
|
outputfile: string;
|
|
5
5
|
espmethod: string;
|
|
@@ -14,10 +14,11 @@ interface ICopyFilesArguments {
|
|
|
14
14
|
maxSize?: number;
|
|
15
15
|
maxGzipSize?: number;
|
|
16
16
|
noIndexCheck?: boolean;
|
|
17
|
+
dryRun?: boolean;
|
|
17
18
|
help?: boolean;
|
|
18
19
|
}
|
|
19
20
|
interface IRcFileConfig {
|
|
20
|
-
engine?: 'psychic' | '
|
|
21
|
+
engine?: 'psychic' | 'async' | 'espidf';
|
|
21
22
|
sourcepath?: string;
|
|
22
23
|
outputfile?: string;
|
|
23
24
|
espmethod?: string;
|
|
@@ -28,14 +29,17 @@ interface IRcFileConfig {
|
|
|
28
29
|
created?: boolean;
|
|
29
30
|
version?: string;
|
|
30
31
|
exclude?: string[];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
basepath?: string;
|
|
33
|
+
maxsize?: number | string;
|
|
34
|
+
maxgzipsize?: number | string;
|
|
35
|
+
noindexcheck?: boolean;
|
|
36
|
+
dryrun?: boolean;
|
|
34
37
|
}
|
|
38
|
+
declare function validateCppIdentifier(value: string, name: string): string;
|
|
35
39
|
declare function parseSize(value: string, name: string): number;
|
|
36
40
|
declare function getNpmPackageVariable(packageJson: Record<string, unknown>, variableName: string): string | undefined;
|
|
37
41
|
declare function hasNpmVariables(config: IRcFileConfig): boolean;
|
|
38
42
|
declare function interpolateNpmVariables(config: IRcFileConfig, rcFilePath: string): IRcFileConfig;
|
|
39
|
-
export { getNpmPackageVariable, hasNpmVariables, interpolateNpmVariables, parseSize };
|
|
43
|
+
export { getNpmPackageVariable, hasNpmVariables, interpolateNpmVariables, parseSize, validateCppIdentifier };
|
|
40
44
|
export declare function formatConfiguration(cmdLine: ICopyFilesArguments): string;
|
|
41
45
|
export declare const cmdLine: ICopyFilesArguments;
|
package/dist/commandLine.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.getNpmPackageVariable = getNpmPackageVariable;
|
|
|
8
8
|
exports.hasNpmVariables = hasNpmVariables;
|
|
9
9
|
exports.interpolateNpmVariables = interpolateNpmVariables;
|
|
10
10
|
exports.parseSize = parseSize;
|
|
11
|
+
exports.validateCppIdentifier = validateCppIdentifier;
|
|
11
12
|
exports.formatConfiguration = formatConfiguration;
|
|
12
13
|
const node_fs_1 = require("node:fs");
|
|
13
14
|
const node_os_1 = require("node:os");
|
|
@@ -23,7 +24,7 @@ Configuration:
|
|
|
23
24
|
|
|
24
25
|
Options:
|
|
25
26
|
-e, --engine <value> The engine for which the include file is created
|
|
26
|
-
(psychic|
|
|
27
|
+
(psychic|async|espidf) (default: "psychic")
|
|
27
28
|
-s, --sourcepath <path> Source dist folder contains compiled web files (required)
|
|
28
29
|
-o, --outputfile <path> Generated output file with path (default: "svelteesp32.h")
|
|
29
30
|
--etag <value> Use ETAG header for cache (true|false|compiler) (default: "false")
|
|
@@ -38,6 +39,7 @@ Options:
|
|
|
38
39
|
--basepath <path> URL prefix for all routes (e.g., "/ui") (default: "")
|
|
39
40
|
--maxsize <size> Maximum total uncompressed size (e.g., 400k, 1.5m, 409600)
|
|
40
41
|
--maxgzipsize <size> Maximum total gzip size (e.g., 150k, 1m, 153600)
|
|
42
|
+
--dryrun Show summary without writing the output file (default: false)
|
|
41
43
|
-h, --help Shows this help
|
|
42
44
|
|
|
43
45
|
RC File:
|
|
@@ -52,7 +54,11 @@ RC File:
|
|
|
52
54
|
"outputfile": "./output.h",
|
|
53
55
|
"etag": "true",
|
|
54
56
|
"gzip": "true",
|
|
55
|
-
"exclude": ["*.map", "*.md"]
|
|
57
|
+
"exclude": ["*.map", "*.md"],
|
|
58
|
+
"basepath": "/ui",
|
|
59
|
+
"maxsize": "400k",
|
|
60
|
+
"maxgzipsize": "150k",
|
|
61
|
+
"noindexcheck": false
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
CLI arguments override RC file values.
|
|
@@ -60,7 +66,7 @@ RC File:
|
|
|
60
66
|
process.exit(0);
|
|
61
67
|
}
|
|
62
68
|
function validateEngine(value) {
|
|
63
|
-
if (value === 'psychic' || value === '
|
|
69
|
+
if (value === 'psychic' || value === 'async' || value === 'espidf')
|
|
64
70
|
return value;
|
|
65
71
|
console.error((0, errorMessages_1.getInvalidEngineError)(value));
|
|
66
72
|
process.exit(1);
|
|
@@ -70,6 +76,11 @@ function validateTriState(value, name) {
|
|
|
70
76
|
return value;
|
|
71
77
|
throw new Error(`Invalid ${name}: ${value}`);
|
|
72
78
|
}
|
|
79
|
+
function validateCppIdentifier(value, name) {
|
|
80
|
+
if (!/^[A-Z_a-z]\w*$/.test(value))
|
|
81
|
+
throw new Error(`${name} must be a valid C++ identifier (letters, digits, underscores, not starting with a digit): ${value}`);
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
73
84
|
function validateBasePath(value) {
|
|
74
85
|
if (value === '')
|
|
75
86
|
return value;
|
|
@@ -170,7 +181,7 @@ function hasNpmVariables(config) {
|
|
|
170
181
|
checkStringForNpmVariable(config.espmethod) ||
|
|
171
182
|
checkStringForNpmVariable(config.define) ||
|
|
172
183
|
checkStringForNpmVariable(config.version) ||
|
|
173
|
-
checkStringForNpmVariable(config.
|
|
184
|
+
checkStringForNpmVariable(config.basepath) ||
|
|
174
185
|
(Array.isArray(config.exclude) && config.exclude.some((pattern) => checkStringForNpmVariable(pattern))) ||
|
|
175
186
|
false);
|
|
176
187
|
}
|
|
@@ -190,8 +201,8 @@ function interpolateNpmVariables(config, rcFilePath) {
|
|
|
190
201
|
affectedFields.push('espmethod');
|
|
191
202
|
if (config.define?.includes('$npm_package_'))
|
|
192
203
|
affectedFields.push('define');
|
|
193
|
-
if (config.
|
|
194
|
-
affectedFields.push('
|
|
204
|
+
if (config.basepath?.includes('$npm_package_'))
|
|
205
|
+
affectedFields.push('basepath');
|
|
195
206
|
if (config.exclude)
|
|
196
207
|
for (const [index, pattern] of config.exclude.entries())
|
|
197
208
|
if (pattern.includes('$npm_package_'))
|
|
@@ -216,8 +227,8 @@ function interpolateNpmVariables(config, rcFilePath) {
|
|
|
216
227
|
result.define = interpolateString(result.define);
|
|
217
228
|
if (result.version)
|
|
218
229
|
result.version = interpolateString(result.version);
|
|
219
|
-
if (result.
|
|
220
|
-
result.
|
|
230
|
+
if (result.basepath)
|
|
231
|
+
result.basepath = interpolateString(result.basepath);
|
|
221
232
|
if (result.exclude)
|
|
222
233
|
result.exclude = result.exclude.map((pattern) => interpolateString(pattern));
|
|
223
234
|
return result;
|
|
@@ -251,9 +262,11 @@ function validateRcConfig(config, rcPath) {
|
|
|
251
262
|
'created',
|
|
252
263
|
'version',
|
|
253
264
|
'exclude',
|
|
254
|
-
'
|
|
255
|
-
'
|
|
256
|
-
'
|
|
265
|
+
'basepath',
|
|
266
|
+
'maxsize',
|
|
267
|
+
'maxgzipsize',
|
|
268
|
+
'noindexcheck',
|
|
269
|
+
'dryrun'
|
|
257
270
|
]);
|
|
258
271
|
for (const key of Object.keys(configObject))
|
|
259
272
|
if (!validKeys.has(key))
|
|
@@ -264,9 +277,16 @@ function validateRcConfig(config, rcPath) {
|
|
|
264
277
|
configObject['etag'] = validateTriState(configObject['etag'], 'etag');
|
|
265
278
|
if (configObject['gzip'] !== undefined)
|
|
266
279
|
configObject['gzip'] = validateTriState(configObject['gzip'], 'gzip');
|
|
267
|
-
if (configObject['
|
|
268
|
-
(
|
|
269
|
-
|
|
280
|
+
if (configObject['espmethod'] !== undefined)
|
|
281
|
+
validateCppIdentifier(configObject['espmethod'], 'espmethod');
|
|
282
|
+
if (configObject['define'] !== undefined)
|
|
283
|
+
validateCppIdentifier(configObject['define'], 'define');
|
|
284
|
+
if (configObject['cachetime'] !== undefined) {
|
|
285
|
+
if (typeof configObject['cachetime'] !== 'number' || Number.isNaN(configObject['cachetime']))
|
|
286
|
+
throw new TypeError(`Invalid cachetime in RC file: ${configObject['cachetime']}`);
|
|
287
|
+
if (configObject['cachetime'] < 0)
|
|
288
|
+
throw new TypeError(`Invalid cachetime in RC file: ${configObject['cachetime']} (must be non-negative)`);
|
|
289
|
+
}
|
|
270
290
|
if (configObject['exclude'] !== undefined) {
|
|
271
291
|
if (!Array.isArray(configObject['exclude']))
|
|
272
292
|
throw new TypeError("'exclude' in RC file must be an array");
|
|
@@ -274,30 +294,34 @@ function validateRcConfig(config, rcPath) {
|
|
|
274
294
|
if (typeof pattern !== 'string')
|
|
275
295
|
throw new TypeError('All exclude patterns must be strings');
|
|
276
296
|
}
|
|
277
|
-
if (configObject['
|
|
278
|
-
const value = configObject['
|
|
297
|
+
if (configObject['maxsize'] !== undefined) {
|
|
298
|
+
const value = configObject['maxsize'];
|
|
279
299
|
if (typeof value === 'string')
|
|
280
300
|
try {
|
|
281
|
-
configObject['
|
|
301
|
+
configObject['maxsize'] = parseSize(value, 'maxsize');
|
|
282
302
|
}
|
|
283
303
|
catch {
|
|
284
|
-
throw new TypeError(`Invalid
|
|
304
|
+
throw new TypeError(`Invalid maxsize in RC file: ${value} (must be a positive number with optional k/m suffix)`);
|
|
285
305
|
}
|
|
286
306
|
else if (typeof value !== 'number' || Number.isNaN(value) || value <= 0)
|
|
287
|
-
throw new TypeError(`Invalid
|
|
307
|
+
throw new TypeError(`Invalid maxsize in RC file: ${value} (must be a positive number)`);
|
|
288
308
|
}
|
|
289
|
-
if (configObject['
|
|
290
|
-
const value = configObject['
|
|
309
|
+
if (configObject['maxgzipsize'] !== undefined) {
|
|
310
|
+
const value = configObject['maxgzipsize'];
|
|
291
311
|
if (typeof value === 'string')
|
|
292
312
|
try {
|
|
293
|
-
configObject['
|
|
313
|
+
configObject['maxgzipsize'] = parseSize(value, 'maxgzipsize');
|
|
294
314
|
}
|
|
295
315
|
catch {
|
|
296
|
-
throw new TypeError(`Invalid
|
|
316
|
+
throw new TypeError(`Invalid maxgzipsize in RC file: ${value} (must be a positive number with optional k/m suffix)`);
|
|
297
317
|
}
|
|
298
318
|
else if (typeof value !== 'number' || Number.isNaN(value) || value <= 0)
|
|
299
|
-
throw new TypeError(`Invalid
|
|
319
|
+
throw new TypeError(`Invalid maxgzipsize in RC file: ${value} (must be a positive number)`);
|
|
300
320
|
}
|
|
321
|
+
if (configObject['noindexcheck'] !== undefined && typeof configObject['noindexcheck'] !== 'boolean')
|
|
322
|
+
throw new TypeError(`Invalid noindexcheck in RC file: ${configObject['noindexcheck']} (must be boolean)`);
|
|
323
|
+
if (configObject['dryrun'] !== undefined && typeof configObject['dryrun'] !== 'boolean')
|
|
324
|
+
throw new TypeError(`Invalid dryrun in RC file: ${configObject['dryrun']} (must be boolean)`);
|
|
301
325
|
return configObject;
|
|
302
326
|
}
|
|
303
327
|
function parseArguments() {
|
|
@@ -353,15 +377,75 @@ function parseArguments() {
|
|
|
353
377
|
result.espmethod = rcConfig.espmethod;
|
|
354
378
|
if (rcConfig.define)
|
|
355
379
|
result.define = rcConfig.define;
|
|
356
|
-
if (rcConfig.
|
|
357
|
-
result.basePath = validateBasePath(rcConfig.
|
|
358
|
-
if (rcConfig.
|
|
359
|
-
result.maxSize = rcConfig.
|
|
360
|
-
if (rcConfig.
|
|
361
|
-
result.maxGzipSize = rcConfig.
|
|
380
|
+
if (rcConfig.basepath !== undefined)
|
|
381
|
+
result.basePath = validateBasePath(rcConfig.basepath);
|
|
382
|
+
if (rcConfig.maxsize !== undefined)
|
|
383
|
+
result.maxSize = rcConfig.maxsize;
|
|
384
|
+
if (rcConfig.maxgzipsize !== undefined)
|
|
385
|
+
result.maxGzipSize = rcConfig.maxgzipsize;
|
|
386
|
+
if (rcConfig.noindexcheck !== undefined)
|
|
387
|
+
result.noIndexCheck = rcConfig.noindexcheck;
|
|
388
|
+
if (rcConfig.dryrun !== undefined)
|
|
389
|
+
result.dryRun = rcConfig.dryrun;
|
|
362
390
|
if (rcConfig.exclude && rcConfig.exclude.length > 0)
|
|
363
391
|
result.exclude = [...rcConfig.exclude];
|
|
364
392
|
const cliExclude = [];
|
|
393
|
+
function applyFlag(flagName, value) {
|
|
394
|
+
switch (flagName) {
|
|
395
|
+
case 'config':
|
|
396
|
+
break;
|
|
397
|
+
case 'engine':
|
|
398
|
+
result.engine = validateEngine(value);
|
|
399
|
+
break;
|
|
400
|
+
case 'sourcepath':
|
|
401
|
+
result.sourcepath = value;
|
|
402
|
+
break;
|
|
403
|
+
case 'outputfile':
|
|
404
|
+
result.outputfile = value;
|
|
405
|
+
break;
|
|
406
|
+
case 'etag':
|
|
407
|
+
result.etag = validateTriState(value, 'etag');
|
|
408
|
+
break;
|
|
409
|
+
case 'gzip':
|
|
410
|
+
result.gzip = validateTriState(value, 'gzip');
|
|
411
|
+
break;
|
|
412
|
+
case 'version':
|
|
413
|
+
result.version = value;
|
|
414
|
+
break;
|
|
415
|
+
case 'espmethod':
|
|
416
|
+
result.espmethod = validateCppIdentifier(value, 'espmethod');
|
|
417
|
+
break;
|
|
418
|
+
case 'define':
|
|
419
|
+
result.define = validateCppIdentifier(value, 'define');
|
|
420
|
+
break;
|
|
421
|
+
case 'cachetime':
|
|
422
|
+
result.cachetime = Number.parseInt(value, 10);
|
|
423
|
+
if (Number.isNaN(result.cachetime))
|
|
424
|
+
throw new TypeError(`Invalid cachetime: ${value}`);
|
|
425
|
+
if (result.cachetime < 0)
|
|
426
|
+
throw new TypeError(`Invalid cachetime: ${value} (must be non-negative)`);
|
|
427
|
+
break;
|
|
428
|
+
case 'exclude': {
|
|
429
|
+
const patterns = value
|
|
430
|
+
.split(',')
|
|
431
|
+
.map((p) => p.trim())
|
|
432
|
+
.filter(Boolean);
|
|
433
|
+
cliExclude.push(...patterns);
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
case 'basepath':
|
|
437
|
+
result.basePath = validateBasePath(value);
|
|
438
|
+
break;
|
|
439
|
+
case 'maxsize':
|
|
440
|
+
result.maxSize = parseSize(value, '--maxsize');
|
|
441
|
+
break;
|
|
442
|
+
case 'maxgzipsize':
|
|
443
|
+
result.maxGzipSize = parseSize(value, '--maxgzipsize');
|
|
444
|
+
break;
|
|
445
|
+
default:
|
|
446
|
+
throw new Error(`Unknown flag: --${flagName}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
365
449
|
for (let index = 0; index < arguments_.length; index++) {
|
|
366
450
|
const argument = arguments_[index];
|
|
367
451
|
if (!argument)
|
|
@@ -374,59 +458,7 @@ function parseArguments() {
|
|
|
374
458
|
const value = parts.slice(1).join('=');
|
|
375
459
|
if (!flag || !value)
|
|
376
460
|
throw new Error(`Invalid argument format: ${argument}`);
|
|
377
|
-
|
|
378
|
-
switch (flagName) {
|
|
379
|
-
case 'config':
|
|
380
|
-
break;
|
|
381
|
-
case 'engine':
|
|
382
|
-
result.engine = validateEngine(value);
|
|
383
|
-
break;
|
|
384
|
-
case 'sourcepath':
|
|
385
|
-
result.sourcepath = value;
|
|
386
|
-
break;
|
|
387
|
-
case 'outputfile':
|
|
388
|
-
result.outputfile = value;
|
|
389
|
-
break;
|
|
390
|
-
case 'etag':
|
|
391
|
-
result.etag = validateTriState(value, 'etag');
|
|
392
|
-
break;
|
|
393
|
-
case 'gzip':
|
|
394
|
-
result.gzip = validateTriState(value, 'gzip');
|
|
395
|
-
break;
|
|
396
|
-
case 'version':
|
|
397
|
-
result.version = value;
|
|
398
|
-
break;
|
|
399
|
-
case 'espmethod':
|
|
400
|
-
result.espmethod = value;
|
|
401
|
-
break;
|
|
402
|
-
case 'define':
|
|
403
|
-
result.define = value;
|
|
404
|
-
break;
|
|
405
|
-
case 'cachetime':
|
|
406
|
-
result.cachetime = Number.parseInt(value, 10);
|
|
407
|
-
if (Number.isNaN(result.cachetime))
|
|
408
|
-
throw new TypeError(`Invalid cachetime: ${value}`);
|
|
409
|
-
break;
|
|
410
|
-
case 'exclude': {
|
|
411
|
-
const patterns = value
|
|
412
|
-
.split(',')
|
|
413
|
-
.map((p) => p.trim())
|
|
414
|
-
.filter(Boolean);
|
|
415
|
-
cliExclude.push(...patterns);
|
|
416
|
-
break;
|
|
417
|
-
}
|
|
418
|
-
case 'basepath':
|
|
419
|
-
result.basePath = validateBasePath(value);
|
|
420
|
-
break;
|
|
421
|
-
case 'maxsize':
|
|
422
|
-
result.maxSize = parseSize(value, '--maxsize');
|
|
423
|
-
break;
|
|
424
|
-
case 'maxgzipsize':
|
|
425
|
-
result.maxGzipSize = parseSize(value, '--maxgzipsize');
|
|
426
|
-
break;
|
|
427
|
-
default:
|
|
428
|
-
throw new Error(`Unknown flag: ${flag}`);
|
|
429
|
-
}
|
|
461
|
+
applyFlag(flag.slice(2), value);
|
|
430
462
|
continue;
|
|
431
463
|
}
|
|
432
464
|
if (argument === '--created') {
|
|
@@ -437,6 +469,10 @@ function parseArguments() {
|
|
|
437
469
|
result.noIndexCheck = true;
|
|
438
470
|
continue;
|
|
439
471
|
}
|
|
472
|
+
if (argument === '--dryrun' || argument === '--dry-run') {
|
|
473
|
+
result.dryRun = true;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
440
476
|
if (argument.startsWith('-') && !argument.startsWith('--')) {
|
|
441
477
|
const flag = argument.slice(1);
|
|
442
478
|
const nextArgument = arguments_[index + 1];
|
|
@@ -465,72 +501,8 @@ function parseArguments() {
|
|
|
465
501
|
const nextArgument = arguments_[index + 1];
|
|
466
502
|
if (!nextArgument || nextArgument.startsWith('-'))
|
|
467
503
|
throw new Error(`Missing value for flag: ${argument}`);
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
index++;
|
|
471
|
-
break;
|
|
472
|
-
case 'engine':
|
|
473
|
-
result.engine = validateEngine(nextArgument);
|
|
474
|
-
index++;
|
|
475
|
-
break;
|
|
476
|
-
case 'sourcepath':
|
|
477
|
-
result.sourcepath = nextArgument;
|
|
478
|
-
index++;
|
|
479
|
-
break;
|
|
480
|
-
case 'outputfile':
|
|
481
|
-
result.outputfile = nextArgument;
|
|
482
|
-
index++;
|
|
483
|
-
break;
|
|
484
|
-
case 'etag':
|
|
485
|
-
result.etag = validateTriState(nextArgument, 'etag');
|
|
486
|
-
index++;
|
|
487
|
-
break;
|
|
488
|
-
case 'gzip':
|
|
489
|
-
result.gzip = validateTriState(nextArgument, 'gzip');
|
|
490
|
-
index++;
|
|
491
|
-
break;
|
|
492
|
-
case 'version':
|
|
493
|
-
result.version = nextArgument;
|
|
494
|
-
index++;
|
|
495
|
-
break;
|
|
496
|
-
case 'espmethod':
|
|
497
|
-
result.espmethod = nextArgument;
|
|
498
|
-
index++;
|
|
499
|
-
break;
|
|
500
|
-
case 'define':
|
|
501
|
-
result.define = nextArgument;
|
|
502
|
-
index++;
|
|
503
|
-
break;
|
|
504
|
-
case 'cachetime':
|
|
505
|
-
result.cachetime = Number.parseInt(nextArgument, 10);
|
|
506
|
-
if (Number.isNaN(result.cachetime))
|
|
507
|
-
throw new TypeError(`Invalid cachetime: ${nextArgument}`);
|
|
508
|
-
index++;
|
|
509
|
-
break;
|
|
510
|
-
case 'exclude': {
|
|
511
|
-
const patterns = nextArgument
|
|
512
|
-
.split(',')
|
|
513
|
-
.map((p) => p.trim())
|
|
514
|
-
.filter(Boolean);
|
|
515
|
-
cliExclude.push(...patterns);
|
|
516
|
-
index++;
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
case 'basepath':
|
|
520
|
-
result.basePath = validateBasePath(nextArgument);
|
|
521
|
-
index++;
|
|
522
|
-
break;
|
|
523
|
-
case 'maxsize':
|
|
524
|
-
result.maxSize = parseSize(nextArgument, '--maxsize');
|
|
525
|
-
index++;
|
|
526
|
-
break;
|
|
527
|
-
case 'maxgzipsize':
|
|
528
|
-
result.maxGzipSize = parseSize(nextArgument, '--maxgzipsize');
|
|
529
|
-
index++;
|
|
530
|
-
break;
|
|
531
|
-
default:
|
|
532
|
-
throw new Error(`Unknown flag: --${flag}`);
|
|
533
|
-
}
|
|
504
|
+
applyFlag(flag, nextArgument);
|
|
505
|
+
index++;
|
|
534
506
|
continue;
|
|
535
507
|
}
|
|
536
508
|
throw new Error(`Unknown argument: ${argument}`);
|