svelteesp32 3.0.1 → 3.1.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.
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.genPsychicCpp = void 0;
4
+ const cppCode_1 = require("./cppCode");
5
+ const genPsychicHandlerBody = (d, source, path) => {
6
+ const lines = [];
7
+ const etagCheck = (0, cppCode_1.sw)(d.etag, {
8
+ always: [
9
+ ` if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_${source.dataname})) {`,
10
+ ` response->setCode(304);`,
11
+ ` ${d.definePrefix}_onFileServed("${path}", 304);`,
12
+ ` return response->send();`,
13
+ ` }`
14
+ ].join('\n'),
15
+ compiler: [
16
+ ` #ifdef ${d.definePrefix}_ENABLE_ETAG`,
17
+ ` if (request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag_${source.dataname})) {`,
18
+ ` response->setCode(304);`,
19
+ ` ${d.definePrefix}_onFileServed("${path}", 304);`,
20
+ ` return response->send();`,
21
+ ` }`,
22
+ ` #endif`
23
+ ].join('\n')
24
+ });
25
+ if (etagCheck)
26
+ lines.push(etagCheck);
27
+ lines.push(` response->setContentType("${source.mime}");`);
28
+ const gzipEncoding = (0, cppCode_1.sw)(d.gzip, {
29
+ always: source.isGzip ? ` response->addHeader("Content-Encoding", "gzip");` : '',
30
+ compiler: source.isGzip
31
+ ? [
32
+ ` #ifdef ${d.definePrefix}_ENABLE_GZIP`,
33
+ ` response->addHeader("Content-Encoding", "gzip");`,
34
+ ` #endif`
35
+ ].join('\n')
36
+ : ''
37
+ });
38
+ if (gzipEncoding)
39
+ lines.push(gzipEncoding);
40
+ const cacheHeaders = (0, cppCode_1.sw)(d.etag, {
41
+ always: [
42
+ ` response->addHeader("Cache-Control", "${(0, cppCode_1.cacheCtrl)(source)}");`,
43
+ ` response->addHeader("ETag", etag_${source.dataname});`
44
+ ].join('\n'),
45
+ compiler: [
46
+ ` #ifdef ${d.definePrefix}_ENABLE_ETAG`,
47
+ ` response->addHeader("Cache-Control", "${(0, cppCode_1.cacheCtrl)(source)}");`,
48
+ ` response->addHeader("ETag", etag_${source.dataname});`,
49
+ ` #endif`
50
+ ].join('\n')
51
+ });
52
+ if (cacheHeaders)
53
+ lines.push(cacheHeaders);
54
+ lines.push((0, cppCode_1.sw)(d.gzip, {
55
+ always: ` response->setContent(datagzip_${source.dataname}, ${source.lengthGzip});`,
56
+ never: ` response->setContent(data_${source.dataname}, ${source.length});`,
57
+ compiler: [
58
+ ` #ifdef ${d.definePrefix}_ENABLE_GZIP`,
59
+ ` response->setContent(datagzip_${source.dataname}, ${source.lengthGzip});`,
60
+ ` #else`,
61
+ ` response->setContent(data_${source.dataname}, ${source.length});`,
62
+ ` #endif`
63
+ ].join('\n')
64
+ }), ` ${d.definePrefix}_onFileServed("${path}", 200);`, ` return response->send();`);
65
+ return lines.join('\n');
66
+ };
67
+ const genPsychicCpp = (d) => {
68
+ const lines = [
69
+ `//engine: PsychicHttpServer`,
70
+ `//config: ${d.config}`,
71
+ ...(d.created ? [`//created: ${d.now}`] : []),
72
+ '//',
73
+ (0, cppCode_1.genCommonHeader)(d),
74
+ '//',
75
+ '#include <Arduino.h>',
76
+ '#include <PsychicHttp.h>',
77
+ '#include <PsychicHttpsServer.h>',
78
+ '//',
79
+ (0, cppCode_1.genDataArrays)(d, false),
80
+ '//',
81
+ (0, cppCode_1.genEtagArrays)(d),
82
+ '//',
83
+ (0, cppCode_1.genManifest)(d),
84
+ '//',
85
+ (0, cppCode_1.genHook)(d),
86
+ '//',
87
+ '// Http Handlers',
88
+ `void ${d.methodName}(PsychicHttpServer * server) {`
89
+ ];
90
+ for (const source of d.sources) {
91
+ const path = `${d.basePath}/${source.filename}`;
92
+ const serverPrefix = source.isDefault && !d.basePath ? 'server->defaultEndpoint = ' : '';
93
+ lines.push('//', `// ${source.filename}`, ` ${serverPrefix}server->on("${path}", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {`, genPsychicHandlerBody(d, source, path), ` });`);
94
+ if (source.isDefault && d.basePath)
95
+ lines.push('//', `// ${source.filename} (base path route)`, ` server->on("${d.basePath}", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {`, genPsychicHandlerBody(d, source, d.basePath), ` });`);
96
+ }
97
+ if (d.spa && d.spaSource && d.basePath) {
98
+ const source = d.spaSource;
99
+ const path = `${d.basePath}/${source.filename}`;
100
+ lines.push('//', `// SPA catch-all: unmatched routes serve ${source.filename}`, ` server->on("${d.basePath}/*", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {`, genPsychicHandlerBody(d, source, path), ` });`);
101
+ }
102
+ lines.push('}');
103
+ return lines.join('\n');
104
+ };
105
+ exports.genPsychicCpp = genPsychicCpp;
@@ -0,0 +1,2 @@
1
+ import type { TemplateData } from './cppCode';
2
+ export declare const genWebserverCpp: (d: TemplateData) => string;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.genWebserverCpp = void 0;
4
+ const cppCode_1 = require("./cppCode");
5
+ const genWebserverHandlerBody = (d, source, path) => {
6
+ const lines = [];
7
+ const etagCheck = (0, cppCode_1.sw)(d.etag, {
8
+ always: [
9
+ ` if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_${source.dataname})) {`,
10
+ ` server->send(304);`,
11
+ ` ${d.definePrefix}_onFileServed("${path}", 304);`,
12
+ ` return;`,
13
+ ` }`
14
+ ].join('\n'),
15
+ compiler: [
16
+ ` #ifdef ${d.definePrefix}_ENABLE_ETAG`,
17
+ ` if (server->hasHeader("If-None-Match") && server->header("If-None-Match").equals(etag_${source.dataname})) {`,
18
+ ` server->send(304);`,
19
+ ` ${d.definePrefix}_onFileServed("${path}", 304);`,
20
+ ` return;`,
21
+ ` }`,
22
+ ` #endif`
23
+ ].join('\n')
24
+ });
25
+ if (etagCheck)
26
+ lines.push(etagCheck);
27
+ const cacheHeaders = (0, cppCode_1.sw)(d.etag, {
28
+ always: [
29
+ ` server->sendHeader("Cache-Control", "${(0, cppCode_1.cacheCtrl)(source)}");`,
30
+ ` server->sendHeader("ETag", etag_${source.dataname});`
31
+ ].join('\n'),
32
+ compiler: [
33
+ ` #ifdef ${d.definePrefix}_ENABLE_ETAG`,
34
+ ` server->sendHeader("Cache-Control", "${(0, cppCode_1.cacheCtrl)(source)}");`,
35
+ ` server->sendHeader("ETag", etag_${source.dataname});`,
36
+ ` #endif`
37
+ ].join('\n')
38
+ });
39
+ if (cacheHeaders)
40
+ lines.push(cacheHeaders);
41
+ lines.push((0, cppCode_1.sw)(d.gzip, {
42
+ always: [
43
+ ...(source.isGzip ? [` server->sendHeader("Content-Encoding", "gzip");`] : []),
44
+ ` server->setContentLength(${source.lengthGzip});`,
45
+ ` server->send(200, "${source.mime}", "");`,
46
+ ` ${d.definePrefix}_sendChunked(server, datagzip_${source.dataname}, ${source.lengthGzip});`
47
+ ].join('\n'),
48
+ never: [
49
+ ` server->setContentLength(${source.length});`,
50
+ ` server->send(200, "${source.mime}", "");`,
51
+ ` ${d.definePrefix}_sendChunked(server, data_${source.dataname}, ${source.length});`
52
+ ].join('\n'),
53
+ compiler: [
54
+ ` #ifdef ${d.definePrefix}_ENABLE_GZIP`,
55
+ ...(source.isGzip ? [` server->sendHeader("Content-Encoding", "gzip");`] : []),
56
+ ` server->setContentLength(${source.lengthGzip});`,
57
+ ` server->send(200, "${source.mime}", "");`,
58
+ ` ${d.definePrefix}_sendChunked(server, datagzip_${source.dataname}, ${source.lengthGzip});`,
59
+ ` #else`,
60
+ ` server->setContentLength(${source.length});`,
61
+ ` server->send(200, "${source.mime}", "");`,
62
+ ` ${d.definePrefix}_sendChunked(server, data_${source.dataname}, ${source.length});`,
63
+ ` #endif`
64
+ ].join('\n')
65
+ }), ` ${d.definePrefix}_onFileServed("${path}", 200);`);
66
+ return lines.join('\n');
67
+ };
68
+ const genWebserverCpp = (d) => {
69
+ const lines = [
70
+ `//engine: Arduino WebServer`,
71
+ `//config: ${d.config}`,
72
+ ...(d.created ? [`//created: ${d.now}`] : []),
73
+ '//',
74
+ (0, cppCode_1.genCommonHeader)(d),
75
+ '//',
76
+ '#include <Arduino.h>',
77
+ '#include <WebServer.h>',
78
+ '//',
79
+ (0, cppCode_1.genDataArrays)(d, true),
80
+ '//',
81
+ (0, cppCode_1.genEtagArrays)(d),
82
+ '//',
83
+ (0, cppCode_1.genManifest)(d),
84
+ '//',
85
+ (0, cppCode_1.genHook)(d),
86
+ '//',
87
+ `// Chunked send helper for PROGMEM data`,
88
+ `static inline void ${d.definePrefix}_sendChunked(WebServer * server, const uint8_t * data, size_t len) {`,
89
+ ` const size_t chunkSize = 4096;`,
90
+ ` for (size_t offset = 0; offset < len; offset += chunkSize) {`,
91
+ ` size_t remaining = len - offset;`,
92
+ ` size_t toSend = remaining < chunkSize ? remaining : chunkSize;`,
93
+ ` server->sendContent_P((const char *)(data + offset), toSend);`,
94
+ ` }`,
95
+ `}`,
96
+ '//',
97
+ '// Http Handlers',
98
+ `void ${d.methodName}(WebServer * server) {`
99
+ ];
100
+ for (const source of d.sources) {
101
+ const path = `${d.basePath}/${source.filename}`;
102
+ const defaultPath = d.basePath || '/';
103
+ lines.push('//', `// ${source.filename}`, ` server->on("${path}", HTTP_GET, [server]() {`, genWebserverHandlerBody(d, source, path), ` });`);
104
+ if (source.isDefault)
105
+ lines.push(` server->on("${defaultPath}", HTTP_GET, [server]() {`, genWebserverHandlerBody(d, source, defaultPath), ` });`);
106
+ }
107
+ if (d.spa && d.spaSource) {
108
+ const source = d.spaSource;
109
+ const path = `${d.basePath}/${source.filename}`;
110
+ lines.push('//', `// SPA catch-all: unmatched routes serve ${source.filename}`, ` server->onNotFound([server]() {`, ` if (server->method() != HTTP_GET) { server->send(404, "text/plain", "Not found"); return; }`);
111
+ if (d.basePath)
112
+ lines.push(` if (!server->uri().startsWith("${d.basePath}/") && server->uri() != "${d.basePath}") { server->send(404, "text/plain", "Not found"); return; }`);
113
+ lines.push(genWebserverHandlerBody(d, source, path), ` });`);
114
+ }
115
+ lines.push('}');
116
+ return lines.join('\n');
117
+ };
118
+ exports.genWebserverCpp = genWebserverCpp;
package/dist/index.js CHANGED
@@ -40,3 +40,5 @@ Object.defineProperty(exports, "formatSize", { enumerable: true, get: function (
40
40
  Object.defineProperty(exports, "formatSizePrecise", { enumerable: true, get: function () { return pipeline_2.formatSizePrecise; } });
41
41
  Object.defineProperty(exports, "shouldUseGzip", { enumerable: true, get: function () { return pipeline_2.shouldUseGzip; } });
42
42
  Object.defineProperty(exports, "updateExtensionGroup", { enumerable: true, get: function () { return pipeline_2.updateExtensionGroup; } });
43
+ if (require.main === module)
44
+ main();
package/dist/pipeline.js CHANGED
@@ -181,6 +181,8 @@ function runPipeline(options) {
181
181
  };
182
182
  const sources = [];
183
183
  const filesByExtension = [];
184
+ const sourceLabels = { cli: 'command line', rcfile: 'RC file', vite: 'vite plugin' };
185
+ console.log(`Config source: ${sourceLabels[options.configSource]}`);
184
186
  console.log('Collecting source files');
185
187
  const files = (0, file_1.getFiles)(options);
186
188
  if (files.size === 0)
@@ -28,7 +28,6 @@ export interface SvelteESP32PluginOptions {
28
28
  noindexcheck?: boolean;
29
29
  maxsize?: number;
30
30
  maxgzipsize?: number;
31
- config?: string;
32
31
  }
33
- export declare function svelteESP32(options: SvelteESP32PluginOptions): VitePlugin;
32
+ export declare function svelteESP32(optionsOrRcPath?: SvelteESP32PluginOptions | string): VitePlugin;
34
33
  export {};
@@ -12,7 +12,7 @@ function coerceBool(value) {
12
12
  return undefined;
13
13
  return value === true || value === 'true';
14
14
  }
15
- function svelteESP32(options) {
15
+ function svelteESP32(optionsOrRcPath) {
16
16
  let outDirectory = 'dist';
17
17
  return {
18
18
  name: 'svelteesp32',
@@ -20,35 +20,72 @@ function svelteESP32(options) {
20
20
  outDirectory = config.build.outDir;
21
21
  },
22
22
  closeBundle() {
23
- const rcConfig = (0, commandLine_1.loadRcFileConfig)(options.config);
24
- const rawOutput = options.output ?? rcConfig.outputfile;
25
- if (!rawOutput)
26
- throw new Error('output is required — specify it as a plugin option or in the RC file (outputfile)');
27
- const outputfile = node_path_1.default.resolve(rawOutput);
28
- const sourcepath = options.sourcepath ?? rcConfig.sourcepath ?? outDirectory;
29
- const rawBasepath = options.basepath ?? rcConfig.basepath ?? '';
30
- const basePath = (0, commandLine_1.validateBasePath)(rawBasepath);
31
- const options_ = {
32
- engine: options.engine ?? rcConfig.engine ?? 'psychic',
33
- sourcepath,
34
- outputfile,
35
- etag: options.etag ?? rcConfig.etag ?? 'never',
36
- gzip: options.gzip ?? rcConfig.gzip ?? 'always',
37
- cachetime: options.cachetime ?? rcConfig.cachetime ?? 0,
38
- cachetimeHtml: options.cachetimehtml ?? rcConfig.cachetimehtml,
39
- cachetimeAssets: options.cachetimeassets ?? rcConfig.cachetimeassets,
40
- created: options.created ?? coerceBool(rcConfig.created) ?? false,
41
- version: options.version ?? rcConfig.version ?? '',
42
- espmethod: options.espmethod ?? rcConfig.espmethod ?? 'initSvelteStaticFiles',
43
- define: options.define ?? rcConfig.define ?? 'SVELTEESP32',
44
- exclude: options.exclude ?? rcConfig.exclude ?? [],
45
- basePath,
46
- noIndexCheck: options.noindexcheck ?? coerceBool(rcConfig.noindexcheck),
47
- spa: options.spa ?? coerceBool(rcConfig.spa),
48
- manifest: options.manifest ?? coerceBool(rcConfig.manifest),
49
- maxSize: options.maxsize ?? rcConfig.maxsize,
50
- maxGzipSize: options.maxgzipsize ?? rcConfig.maxgzipsize
51
- };
23
+ let options_;
24
+ if (optionsOrRcPath === undefined || typeof optionsOrRcPath === 'string') {
25
+ const rcPath = optionsOrRcPath;
26
+ const rcConfig = (0, commandLine_1.loadRcFileConfig)(rcPath);
27
+ const rawOutput = rcConfig.outputfile;
28
+ if (!rawOutput)
29
+ throw new Error('output is required specify outputfile in the RC file (.svelteesp32rc.json)');
30
+ const outputfile = node_path_1.default.resolve(rawOutput);
31
+ const sourcepath = rcConfig.sourcepath ?? outDirectory;
32
+ const rawBasepath = rcConfig.basepath ?? '';
33
+ const basePath = (0, commandLine_1.validateBasePath)(rawBasepath);
34
+ options_ = {
35
+ configSource: 'rcfile',
36
+ engine: rcConfig.engine ?? 'psychic',
37
+ sourcepath,
38
+ outputfile,
39
+ etag: rcConfig.etag ?? 'never',
40
+ gzip: rcConfig.gzip ?? 'always',
41
+ cachetime: rcConfig.cachetime ?? 0,
42
+ cachetimeHtml: rcConfig.cachetimehtml,
43
+ cachetimeAssets: rcConfig.cachetimeassets,
44
+ created: coerceBool(rcConfig.created) ?? false,
45
+ version: rcConfig.version ?? '',
46
+ espmethod: rcConfig.espmethod ?? 'initSvelteStaticFiles',
47
+ define: rcConfig.define ?? 'SVELTEESP32',
48
+ exclude: rcConfig.exclude ?? [],
49
+ basePath,
50
+ noIndexCheck: coerceBool(rcConfig.noindexcheck),
51
+ spa: coerceBool(rcConfig.spa),
52
+ manifest: coerceBool(rcConfig.manifest),
53
+ maxSize: rcConfig.maxsize,
54
+ maxGzipSize: rcConfig.maxgzipsize
55
+ };
56
+ }
57
+ else {
58
+ const options = optionsOrRcPath;
59
+ const rawOutput = options.output;
60
+ if (!rawOutput)
61
+ throw new Error('output is required — specify it as a plugin option or use svelteESP32() for RC file mode');
62
+ const outputfile = node_path_1.default.resolve(rawOutput);
63
+ const sourcepath = options.sourcepath ?? outDirectory;
64
+ const rawBasepath = options.basepath ?? '';
65
+ const basePath = (0, commandLine_1.validateBasePath)(rawBasepath);
66
+ options_ = {
67
+ configSource: 'vite',
68
+ engine: options.engine ?? 'psychic',
69
+ sourcepath,
70
+ outputfile,
71
+ etag: options.etag ?? 'never',
72
+ gzip: options.gzip ?? 'always',
73
+ cachetime: options.cachetime ?? 0,
74
+ cachetimeHtml: options.cachetimehtml,
75
+ cachetimeAssets: options.cachetimeassets,
76
+ created: options.created ?? false,
77
+ version: options.version ?? '',
78
+ espmethod: options.espmethod ?? 'initSvelteStaticFiles',
79
+ define: options.define ?? 'SVELTEESP32',
80
+ exclude: options.exclude ?? [],
81
+ basePath,
82
+ noIndexCheck: options.noindexcheck,
83
+ spa: options.spa,
84
+ manifest: options.manifest,
85
+ maxSize: options.maxsize,
86
+ maxGzipSize: options.maxgzipsize
87
+ };
88
+ }
52
89
  (0, pipeline_1.runPipeline)(options_);
53
90
  }
54
91
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelteesp32",
3
- "version": "3.0.1",
3
+ "version": "3.1.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",
@@ -72,11 +72,11 @@
72
72
  "devDependencies": {
73
73
  "@eslint/js": "^10.0.1",
74
74
  "@types/mime-types": "^3.0.1",
75
- "@types/node": "^25.6.0",
75
+ "@types/node": "^25.6.2",
76
76
  "@types/picomatch": "^4.0.3",
77
- "@typescript-eslint/eslint-plugin": "^8.59.1",
78
- "@typescript-eslint/parser": "^8.59.1",
79
- "@vitest/coverage-v8": "^4.1.5",
77
+ "@typescript-eslint/eslint-plugin": "^8.59.3",
78
+ "@typescript-eslint/parser": "^8.59.3",
79
+ "@vitest/coverage-v8": "^4.1.6",
80
80
  "eslint": "^10.3.0",
81
81
  "eslint-config-prettier": "^10.1.8",
82
82
  "eslint-plugin-simple-import-sort": "^13.0.0",
@@ -85,9 +85,10 @@
85
85
  "memfs": "^4.57.2",
86
86
  "nodemon": "^3.1.14",
87
87
  "prettier": "^3.8.3",
88
+ "ts-node": "^10.9.2",
88
89
  "tsx": "^4.21.0",
89
90
  "typescript": "^6.0.3",
90
- "vitest": "^4.1.5"
91
+ "vitest": "^4.1.6"
91
92
  },
92
93
  "peerDependencies": {
93
94
  "vite": ">=5"
@@ -98,7 +99,6 @@
98
99
  }
99
100
  },
100
101
  "dependencies": {
101
- "handlebars": "^4.7.9",
102
102
  "mime-types": "^3.0.2",
103
103
  "picomatch": "^4.0.4",
104
104
  "tinyglobby": "^0.2.16"