Skip to content

Commit 839f983

Browse files
authored
29 file cache (#33)
* enable file caching * allow force regeneration to be configurable * rename cache file to .typecache * update caching info in readme * add more test coverage
1 parent 814189e commit 839f983

8 files changed

Lines changed: 895 additions & 9 deletions

File tree

README.md

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ A command-line tool that automatically generates TypeScript bindings from your T
1818
- 🏷️ **Serde Support**: Respects `#[serde(rename)]` and `#[serde(rename_all)]` attributes
1919
- 🎯 **Type Safety**: Keeps frontend and backend types in sync
2020
- 🛠️ **Build Integration**: Works as standalone CLI or build dependency
21+
-**Smart Caching**: Only regenerates when source files change
2122

2223
## Table of Contents
2324

@@ -29,6 +30,8 @@ A command-line tool that automatically generates TypeScript bindings from your T
2930
- [TypeScript Compatibility](#typescript-compatibility)
3031
- [API Reference](#api-reference)
3132
- [Configuration](#configuration)
33+
- [Caching](#caching)
34+
- [Usage in CI](#usage-in-ci)
3235
- [Examples](#examples)
3336
- [Contributing](#contributing)
3437

@@ -548,7 +551,8 @@ Options:
548551
-v, --validation <LIBRARY> Validation library: zod or none [default: none]
549552
--verbose Verbose output
550553
--visualize-deps Generate dependency graph
551-
-c, --config <FILE> Config file path
554+
-c, --config <FILE> Config file path
555+
-f, --force Force regeneration, ignoring cache
552556
```
553557

554558
```bash
@@ -619,11 +623,12 @@ In `tauri.conf.json`:
619623
```json
620624
{
621625
"plugins": {
622-
"tauri-typegen": {
623-
"project_path": ".",
624-
"output_path": "../src/generated",
625-
"validation_library": "zod",
626-
"verbose": true
626+
"typegen": {
627+
"projectPath": ".",
628+
"outputPath": "../src/generated",
629+
"validationLibrary": "zod",
630+
"verbose": true,
631+
"force": false
627632
}
628633
}
629634
}
@@ -692,6 +697,60 @@ export async function getFileInfo(): Promise<FileMetadata> {
692697
}
693698
```
694699

700+
## Caching
701+
702+
Tauri-typegen uses smart caching to skip regeneration when nothing has changed, improving build times.
703+
704+
### How It Works
705+
706+
A `.typecache` file is created in your output directory containing hashes of:
707+
- All discovered Tauri commands
708+
- All discovered structs and enums
709+
- Configuration settings that affect output
710+
711+
On subsequent runs, these hashes are compared. If nothing changed, generation is skipped.
712+
713+
### Force Regeneration
714+
715+
To bypass the cache and force regeneration:
716+
717+
**CLI flag (highest priority):**
718+
```bash
719+
cargo tauri-typegen generate --force
720+
# or
721+
cargo tauri-typegen generate -f
722+
```
723+
724+
**Config file (`tauri.conf.json`):**
725+
```json
726+
{
727+
"plugins": {
728+
"typegen": {
729+
"force": true
730+
}
731+
}
732+
}
733+
```
734+
735+
**Programmatic:**
736+
```rust
737+
let mut config = GenerateConfig::default();
738+
config.force = Some(true);
739+
```
740+
741+
The CLI `--force` flag always overrides the config file value.
742+
743+
### Cache File Location
744+
745+
The cache file `.typecache` is stored in your output directory (e.g., `./src/generated/.typecache`). Add it to `.gitignore`:
746+
747+
```gitignore
748+
# Tauri-typegen cache
749+
.typecache
750+
```
751+
752+
Or if your entire output directory is gitignored, the cache file is already excluded.
753+
695754
## Usage in CI
696755

697756
When running builds in CI/CD environments, you need to generate TypeScript bindings before the frontend build step.

src/bin/cargo-tauri-typegen.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use clap::Parser;
22
use std::fs;
33
use std::path::PathBuf;
44
use tauri_typegen::analysis::CommandAnalyzer;
5+
use tauri_typegen::build::GenerationCache;
56
use tauri_typegen::generators::create_generator;
67
use tauri_typegen::interface::{
78
print_dependency_visualization_info, print_usage_info, CargoCli, CargoSubcommands,
@@ -34,6 +35,7 @@ fn main() {
3435
verbose,
3536
visualize_deps,
3637
config_file,
38+
force,
3739
} => {
3840
if let Err(e) = run_generate(
3941
project_path,
@@ -42,6 +44,7 @@ fn main() {
4244
verbose,
4345
visualize_deps,
4446
config_file,
47+
force,
4548
) {
4649
eprintln!("Error: {}", e);
4750
std::process::exit(1);
@@ -81,6 +84,7 @@ fn run_generate(
8184
verbose: bool,
8285
visualize_deps: bool,
8386
config_file: Option<PathBuf>,
87+
force: bool,
8488
) -> Result<(), Box<dyn std::error::Error>> {
8589
let logger = Logger::new(verbose, false);
8690
let mut reporter = ProgressReporter::new(logger, 4);
@@ -144,6 +148,10 @@ fn run_generate(
144148
if visualize_deps {
145149
config.visualize_deps = Some(true);
146150
}
151+
// CLI --force flag overrides config
152+
if force {
153+
config.force = Some(true);
154+
}
147155

148156
reporter.complete_step(Some(&format!(
149157
"Using {} validation",
@@ -205,6 +213,35 @@ fn run_generate(
205213
return Ok(());
206214
}
207215

216+
// Check cache to see if regeneration is needed (unless force is set)
217+
let discovered_structs = analyzer.get_discovered_structs();
218+
let needs_regeneration = if config.should_force() {
219+
if config.is_verbose() {
220+
println!("🔄 Force flag set, regenerating bindings");
221+
}
222+
true
223+
} else {
224+
GenerationCache::needs_regeneration(
225+
&config.output_path,
226+
&commands,
227+
discovered_structs,
228+
&config,
229+
)
230+
.unwrap_or(true) // On error, assume regeneration is needed
231+
};
232+
233+
if !needs_regeneration {
234+
if config.is_verbose() {
235+
println!("✨ Cache hit - no changes detected, skipping generation");
236+
}
237+
println!("✅ TypeScript bindings are up to date");
238+
return Ok(());
239+
}
240+
241+
if config.is_verbose() && !config.should_force() {
242+
println!("🔄 Changes detected, regenerating bindings");
243+
}
244+
208245
// Generate bindings
209246
reporter.start_step("Generating TypeScript bindings");
210247
let validation = match config.validation_library.as_str() {
@@ -215,7 +252,7 @@ fn run_generate(
215252
let mut generator = create_generator(validation);
216253
let generated_files = generator.generate_models(
217254
&commands,
218-
analyzer.get_discovered_structs(),
255+
discovered_structs,
219256
&config.output_path,
220257
&analyzer,
221258
&config,
@@ -235,6 +272,12 @@ fn run_generate(
235272
print_dependency_visualization_info(&config.output_path);
236273
}
237274

275+
// Save cache after successful generation
276+
let cache = GenerationCache::new(&commands, discovered_structs, &config)?;
277+
if let Err(e) = cache.save(&config.output_path) {
278+
eprintln!("Warning: Failed to save generation cache: {}", e);
279+
}
280+
238281
// Print summary
239282
reporter.finish("Generation complete");
240283
print_usage_info(&config.output_path, &generated_files, commands.len());
@@ -344,7 +387,8 @@ fn run_init(
344387
Some(config.validation_library.clone()),
345388
verbose,
346389
visualize_deps,
347-
None, // No config file since we just created one
390+
None, // No config file since we just created one
391+
false, // Respect cache behavior
348392
)?;
349393

350394
logger.info("");

0 commit comments

Comments
 (0)