svelteesp32 1.12.1 → 1.13.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.13.0, RC files support npm package variable interpolation
16
+
15
17
  > Starting with version v1.12.0, you can use RC file for configuration
16
18
 
17
19
  > Starting with version v1.11.0, you can exclude files by pattern
@@ -540,6 +542,89 @@ To keep defaults, explicitly list them in your RC file:
540
542
  }
541
543
  ```
542
544
 
545
+ #### NPM Package Variable Interpolation
546
+
547
+ RC files support automatic variable interpolation from your `package.json`. This allows you to reference package.json fields in your RC configuration using npm-style variable syntax.
548
+
549
+ **Syntax:** `$npm_package_<field_name>`
550
+
551
+ **Supported in:** All string fields (`version`, `define`, `sourcepath`, `outputfile`, `espmethod`, `exclude` patterns)
552
+
553
+ **Example:**
554
+
555
+ ```json
556
+ // .svelteesp32rc.json
557
+ {
558
+ "engine": "psychic",
559
+ "version": "v$npm_package_version",
560
+ "define": "$npm_package_name",
561
+ "sourcepath": "./dist",
562
+ "outputfile": "./output.h"
563
+ }
564
+ ```
565
+
566
+ With `package.json` containing:
567
+
568
+ ```json
569
+ {
570
+ "name": "my-esp32-app",
571
+ "version": "2.1.0"
572
+ }
573
+ ```
574
+
575
+ The variables are automatically interpolated to:
576
+
577
+ ```json
578
+ {
579
+ "version": "v2.1.0",
580
+ "define": "my_esp32_app"
581
+ }
582
+ ```
583
+
584
+ **Nested Fields:**
585
+
586
+ You can access nested package.json fields using underscores:
587
+
588
+ ```json
589
+ // package.json
590
+ {
591
+ "name": "myapp",
592
+ "repository": {
593
+ "type": "git",
594
+ "url": "https://github.com/user/repo.git"
595
+ }
596
+ }
597
+
598
+ // .svelteesp32rc.json
599
+ {
600
+ "version": "$npm_package_repository_type"
601
+ }
602
+ // Results in: "version": "git"
603
+ ```
604
+
605
+ **Multiple Variables:**
606
+
607
+ Combine multiple variables in a single field:
608
+
609
+ ```json
610
+ {
611
+ "version": "$npm_package_name-v$npm_package_version-release"
612
+ }
613
+ // Results in: "my-esp32-app-v2.1.0-release"
614
+ ```
615
+
616
+ **Requirements:**
617
+
618
+ - `package.json` must exist in the same directory as the RC file
619
+ - If variables are used but `package.json` is not found, an error is thrown with details about which fields contain variables
620
+ - Unknown variables are left unchanged (e.g., `$npm_package_nonexistent` stays as-is)
621
+
622
+ **Use Cases:**
623
+
624
+ - **Version Synchronization:** Keep header version in sync with npm package version
625
+ - **Dynamic Naming:** Use package name for C++ defines automatically
626
+ - **CI/CD Integration:** Reusable RC files across projects with different package names
627
+
543
628
  ### Q&A
544
629
 
545
630
  - **How big a frontend application can be placed?** If you compress the content with gzip, even a 3-4Mb assets directory can be placed. This is a serious enough amount to serve a complete application.
@@ -12,6 +12,22 @@ interface ICopyFilesArguments {
12
12
  exclude: string[];
13
13
  help?: boolean;
14
14
  }
15
+ interface IRcFileConfig {
16
+ engine?: 'psychic' | 'psychic2' | 'async' | 'espidf';
17
+ sourcepath?: string;
18
+ outputfile?: string;
19
+ espmethod?: string;
20
+ define?: string;
21
+ gzip?: 'true' | 'false' | 'compiler';
22
+ etag?: 'true' | 'false' | 'compiler';
23
+ cachetime?: number;
24
+ created?: boolean;
25
+ version?: string;
26
+ exclude?: string[];
27
+ }
28
+ declare function getNpmPackageVariable(packageJson: Record<string, unknown>, variableName: string): string | undefined;
29
+ declare function hasNpmVariables(config: IRcFileConfig): boolean;
30
+ declare function interpolateNpmVariables(config: IRcFileConfig, rcFilePath: string): IRcFileConfig;
31
+ export { getNpmPackageVariable, hasNpmVariables, interpolateNpmVariables };
15
32
  export declare function formatConfiguration(cmdLine: ICopyFilesArguments): string;
16
33
  export declare const cmdLine: ICopyFilesArguments;
17
- export {};
@@ -4,6 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.cmdLine = void 0;
7
+ exports.getNpmPackageVariable = getNpmPackageVariable;
8
+ exports.hasNpmVariables = hasNpmVariables;
9
+ exports.interpolateNpmVariables = interpolateNpmVariables;
7
10
  exports.formatConfiguration = formatConfiguration;
8
11
  const node_fs_1 = require("node:fs");
9
12
  const node_os_1 = require("node:os");
@@ -90,11 +93,100 @@ function findRcFile(customConfigPath) {
90
93
  }
91
94
  return undefined;
92
95
  }
96
+ function findPackageJson(rcFilePath) {
97
+ const rcDirectory = node_path_1.default.dirname(rcFilePath);
98
+ const packageJsonPath = node_path_1.default.join(rcDirectory, 'package.json');
99
+ return (0, node_fs_1.existsSync)(packageJsonPath) ? packageJsonPath : undefined;
100
+ }
101
+ function parsePackageJson(packageJsonPath) {
102
+ try {
103
+ const content = (0, node_fs_1.readFileSync)(packageJsonPath, 'utf8');
104
+ return JSON.parse(content);
105
+ }
106
+ catch (error) {
107
+ throw new Error(`Failed to parse package.json at ${packageJsonPath}: ${error.message}`);
108
+ }
109
+ }
110
+ function getNpmPackageVariable(packageJson, variableName) {
111
+ const prefix = '$npm_package_';
112
+ if (!variableName.startsWith(prefix))
113
+ return undefined;
114
+ const pathString = variableName.slice(prefix.length);
115
+ const pathSegments = pathString.split('_');
116
+ let current = packageJson;
117
+ for (const segment of pathSegments) {
118
+ if (current === null || current === undefined)
119
+ return undefined;
120
+ if (typeof current !== 'object')
121
+ return undefined;
122
+ current = current[segment];
123
+ }
124
+ if (current === null || current === undefined)
125
+ return undefined;
126
+ return String(current);
127
+ }
128
+ function checkStringForNpmVariable(value) {
129
+ return value?.includes('$npm_package_') ?? false;
130
+ }
131
+ function hasNpmVariables(config) {
132
+ return (checkStringForNpmVariable(config.sourcepath) ||
133
+ checkStringForNpmVariable(config.outputfile) ||
134
+ checkStringForNpmVariable(config.espmethod) ||
135
+ checkStringForNpmVariable(config.define) ||
136
+ checkStringForNpmVariable(config.version) ||
137
+ (Array.isArray(config.exclude) && config.exclude.some((pattern) => checkStringForNpmVariable(pattern))) ||
138
+ false);
139
+ }
140
+ function interpolateNpmVariables(config, rcFilePath) {
141
+ if (!hasNpmVariables(config))
142
+ return config;
143
+ const packageJsonPath = findPackageJson(rcFilePath);
144
+ if (!packageJsonPath) {
145
+ const affectedFields = [];
146
+ if (config.sourcepath?.includes('$npm_package_'))
147
+ affectedFields.push('sourcepath');
148
+ if (config.outputfile?.includes('$npm_package_'))
149
+ affectedFields.push('outputfile');
150
+ if (config.version?.includes('$npm_package_'))
151
+ affectedFields.push('version');
152
+ if (config.espmethod?.includes('$npm_package_'))
153
+ affectedFields.push('espmethod');
154
+ if (config.define?.includes('$npm_package_'))
155
+ affectedFields.push('define');
156
+ if (config.exclude)
157
+ for (const [index, pattern] of config.exclude.entries())
158
+ if (pattern.includes('$npm_package_'))
159
+ affectedFields.push(`exclude[${index}]`);
160
+ throw new Error(`RC file uses npm package variables but package.json not found in ${node_path_1.default.dirname(rcFilePath)}\n` +
161
+ `Variables found in fields: ${affectedFields.join(', ')}\n` +
162
+ `Please ensure package.json exists in the same directory as your RC file.`);
163
+ }
164
+ const packageJson = parsePackageJson(packageJsonPath);
165
+ const interpolateString = (value) => {
166
+ const regex = /\$npm_package_[\dA-Za-z]+(?:_[a-z][\dA-Za-z]*)*/g;
167
+ return value.replace(regex, (match) => getNpmPackageVariable(packageJson, match) ?? match);
168
+ };
169
+ const result = { ...config };
170
+ if (result.sourcepath)
171
+ result.sourcepath = interpolateString(result.sourcepath);
172
+ if (result.outputfile)
173
+ result.outputfile = interpolateString(result.outputfile);
174
+ if (result.espmethod)
175
+ result.espmethod = interpolateString(result.espmethod);
176
+ if (result.define)
177
+ result.define = interpolateString(result.define);
178
+ if (result.version)
179
+ result.version = interpolateString(result.version);
180
+ if (result.exclude)
181
+ result.exclude = result.exclude.map((pattern) => interpolateString(pattern));
182
+ return result;
183
+ }
93
184
  function loadRcFile(rcPath) {
94
185
  try {
95
186
  const content = (0, node_fs_1.readFileSync)(rcPath, 'utf8');
96
187
  const config = JSON.parse(content);
97
- return validateRcConfig(config, rcPath);
188
+ const interpolatedConfig = interpolateNpmVariables(config, rcPath);
189
+ return validateRcConfig(interpolatedConfig, rcPath);
98
190
  }
99
191
  catch (error) {
100
192
  if (error instanceof SyntaxError)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelteesp32",
3
- "version": "1.12.1",
3
+ "version": "1.13.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",