Skip to content

Commit 0baa36f

Browse files
committed
feat: npm variable interpolation
1 parent 3531fee commit 0baa36f

File tree

6 files changed

+682
-3
lines changed

6 files changed

+682
-3
lines changed

.svelteesp32rc.example.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
2+
"_comment": "This RC file demonstrates npm variable interpolation. Variables like $npm_package_version are replaced with values from package.json in the same directory.",
23
"engine": "psychic",
34
"sourcepath": "./demo/svelte/dist",
45
"outputfile": "./output.h",
56
"etag": "true",
67
"gzip": "true",
78
"cachetime": 0,
89
"created": false,
9-
"version": "",
10+
"version": "v$npm_package_version",
1011
"espmethod": "initSvelteStaticFiles",
11-
"define": "SVELTEESP32",
12+
"define": "$npm_package_name",
1213
"exclude": ["*.map", "*.md", "test/**/*"]
1314
}

CHANGELOG.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.13.0] - 2025-12-04
11+
12+
### Added
13+
14+
- **NPM Package Variable Interpolation**: RC files now support automatic variable substitution from `package.json`
15+
- Syntax: `$npm_package_<field_name>` (e.g., `$npm_package_version`, `$npm_package_name`)
16+
- Supported in all string fields: `version`, `define`, `sourcepath`, `outputfile`, `espmethod`, and `exclude` patterns
17+
- Nested field support: `$npm_package_repository_type` accesses `packageJson.repository.type`
18+
- Multiple variables in one field: `"$npm_package_name-v$npm_package_version"``"myapp-v1.2.3"`
19+
- Smart regex pattern stops at underscore + uppercase (e.g., `$npm_package_name_STATIC` → interpolates `name`, keeps `_STATIC`)
20+
- `package.json` must exist in same directory as RC file
21+
- Clear error messages when variables are used but `package.json` not found, listing affected fields
22+
- Unknown variables left unchanged (not an error) for flexibility
23+
- 5 new core functions in `src/commandLine.ts` (lines 125-220):
24+
- `findPackageJson()` - Locates package.json in RC file directory
25+
- `parsePackageJson()` - Reads and parses package.json with error handling
26+
- `getNpmPackageVariable()` - Extracts values using underscore-separated path traversal
27+
- `checkStringForNpmVariable()` - Helper for variable detection
28+
- `hasNpmVariables()` - Optimization to skip interpolation when not needed
29+
- `interpolateNpmVariables()` - Main function processing all string fields
30+
- 20+ comprehensive unit tests in `test/unit/commandLine.test.ts` (lines 604-952):
31+
- Simple field extraction (`$npm_package_version`)
32+
- Nested field extraction (`$npm_package_repository_type`)
33+
- Multiple variables in one string
34+
- Exclude array pattern interpolation
35+
- Error handling (missing package.json, invalid JSON)
36+
- Integration tests with RC file loading
37+
- Updated `.svelteesp32rc.example.json` with variable interpolation examples
38+
39+
### Changed
40+
41+
- Enhanced RC file loading flow to interpolate npm variables before validation
42+
- Interpolation integrated seamlessly: defaults → RC file (with interpolation) → CLI arguments
43+
- Updated README.md with comprehensive "NPM Package Variable Interpolation" section:
44+
- Syntax explanation and examples
45+
- Nested field access documentation
46+
- Multiple variable usage examples
47+
- Requirements and error behavior
48+
- Common use cases (version sync, dynamic naming, CI/CD)
49+
- Updated CLAUDE.md with detailed implementation documentation:
50+
- Core functions and processing flow
51+
- Regex pattern design rationale
52+
- Error handling strategy
53+
- Testing coverage details
54+
- Example usage and benefits
55+
- All 118 tests passing (69 commandLine tests including 20 new npm interpolation tests)
56+
57+
### Use Cases
58+
59+
- **Version Synchronization**: `"version": "v$npm_package_version"` keeps header version in sync with package.json
60+
- **Dynamic C++ Defines**: `"define": "$npm_package_name"` uses actual package name for defines
61+
- **CI/CD Integration**: Reusable RC files across different projects with variable package names
62+
- **Single Source of Truth**: Project metadata maintained only in package.json
63+
1064
## [1.12.1] - 2025-12-04
1165

1266
### Changed
@@ -320,6 +374,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
320374
- CLI interface with `-s`, `-e`, `-o` options
321375
- `index.html` automatic default route handling
322376

377+
[1.13.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.12.1...v1.13.0
323378
[1.12.1]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.12.0...v1.12.1
324379
[1.12.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.11.0...v1.12.0
325380
[1.11.0]: https://github.com/BCsabaEngine/svelteesp32/compare/v1.10.0...v1.11.0

CLAUDE.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,92 @@ The generated header file includes a `//config:` comment at the top that display
310310
- Shows all configuration parameters: `engine`, `sourcepath`, `outputfile`, `etag`, `gzip`, `cachetime`, `espmethod`, `define`, and `exclude` patterns
311311
- Works consistently whether configuration comes from RC file, CLI arguments, or both
312312
- Provides complete traceability of the configuration used for code generation
313+
314+
## NPM Package Variable Interpolation
315+
316+
RC files support automatic variable interpolation from `package.json`, allowing dynamic configuration based on project metadata.
317+
318+
**Feature:** Variables like `$npm_package_version` and `$npm_package_name` in RC files are automatically replaced with values from `package.json` located in the same directory as the RC file.
319+
320+
**Supported Fields:** All string fields in RC configuration:
321+
322+
- `version` - e.g., `"v$npm_package_version"`
323+
- `define` - e.g., `"$npm_package_name_STATIC"`
324+
- `sourcepath` - e.g., `"./$npm_package_name/dist"`
325+
- `outputfile` - e.g., `"./output_$npm_package_version.h"`
326+
- `espmethod` - e.g., `"init$npm_package_name"`
327+
- `exclude` patterns - e.g., `["$npm_package_name.map"]`
328+
329+
**Variable Syntax:**
330+
331+
- Simple fields: `$npm_package_version``packageJson.version`
332+
- Nested fields: `$npm_package_repository_type``packageJson.repository.type`
333+
- Multiple variables: `"$npm_package_name-v$npm_package_version"``"myapp-v1.2.3"`
334+
335+
**Implementation** (`src/commandLine.ts` lines 125-220):
336+
337+
**Core Functions:**
338+
339+
1. **`findPackageJson(rcFilePath: string)`** - Locates package.json in the same directory as RC file
340+
2. **`parsePackageJson(packageJsonPath: string)`** - Reads and parses package.json with error handling
341+
3. **`getNpmPackageVariable(packageJson, variableName)`** - Extracts values from package.json using underscore-separated path segments (e.g., `$npm_package_repository_type` traverses `packageJson.repository.type`)
342+
4. **`checkStringForNpmVariable(value)`** - Helper to check if a string contains npm package variables
343+
5. **`hasNpmVariables(config)`** - Quick check if RC config contains any npm variables (optimization to skip interpolation when not needed)
344+
6. **`interpolateNpmVariables(config, rcFilePath)`** - Main interpolation function that processes all string fields
345+
346+
**Processing Flow:**
347+
348+
1. Load RC file JSON
349+
2. **Check for npm variables** - If present, find and parse package.json
350+
3. **Interpolate variables** - Replace `$npm_package_*` patterns using regex: `/\$npm_package_[\dA-Za-z]+(?:_[a-z][\dA-Za-z]*)*/g`
351+
4. Validate interpolated configuration
352+
5. Merge with CLI arguments
353+
354+
**Error Handling:**
355+
356+
- If variables are used but package.json not found: Throws error listing affected fields
357+
- If variable doesn't exist in package.json: Left unchanged (not an error)
358+
- If package.json is invalid JSON: Clear error message with file path
359+
360+
**Regex Pattern Design:**
361+
362+
- Matches: `$npm_package_version`, `$npm_package_repository_type`
363+
- Stops at: `_STATIC`, `_CONSTANT` (underscore followed by uppercase)
364+
- Example: `"$npm_package_name_STATIC"` → interpolates `name`, keeps `_STATIC` suffix
365+
366+
**Testing** (`test/unit/commandLine.test.ts` lines 604-952):
367+
368+
- 20+ comprehensive tests covering all edge cases
369+
- Tests for simple fields, nested fields, multiple variables, error scenarios
370+
- 100% coverage of new interpolation functions
371+
372+
**Example Usage:**
373+
374+
```json
375+
// .svelteesp32rc.json
376+
{
377+
"engine": "psychic",
378+
"version": "v$npm_package_version",
379+
"define": "$npm_package_name",
380+
"sourcepath": "./dist"
381+
}
382+
383+
// package.json
384+
{
385+
"name": "esp32-webui",
386+
"version": "2.1.0"
387+
}
388+
389+
// Result after interpolation:
390+
{
391+
"version": "v2.1.0",
392+
"define": "esp32-webui"
393+
}
394+
```
395+
396+
**Benefits:**
397+
398+
- **Version synchronization**: Header version automatically matches package version
399+
- **Dynamic naming**: C++ defines use actual package name
400+
- **CI/CD friendly**: Reusable RC files across projects
401+
- **Consistency**: Single source of truth for project metadata

README.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ In order to be able to easily update OTA, it is important - from the users' poin
1212

1313
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**.
1414

15+
> Starting with version v1.13.0, RC files support npm package variable interpolation
16+
1517
> Starting with version v1.12.0, you can use RC file for configuration
1618
1719
> 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:
540542
}
541543
```
542544

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+
543628
### Q&A
544629

545630
- **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.

0 commit comments

Comments
 (0)