TypeDialog/docs/fragment-search-paths.md

376 lines
8.3 KiB
Markdown
Raw Normal View History

# Fragment Search Paths
TypeDialog supports loading form fragments from multiple directories using the `TYPEDIALOG_FRAGMENT_PATH` environment variable.
## Overview
**Fragment search paths** enable:
- ✅ Centralized fragment libraries shared across projects
- ✅ Local overrides for project-specific customizations
- ✅ Zero duplication when using standard fragments
- ✅ Automatic updates when central library is updated
## Environment Variable
### TYPEDIALOG_FRAGMENT_PATH
Colon-separated list (semicolon on Windows) of directories to search for fragment files.
**Unix/Linux/macOS:**
```bash
export TYPEDIALOG_FRAGMENT_PATH="/path/to/fragments:/another/path"
```
**Windows:**
```cmd
set TYPEDIALOG_FRAGMENT_PATH=C:\fragments;D:\other\fragments
```
## Search Order
When a fragment is referenced (e.g., `includes = ["fragments/user-fields.toml"]`), TypeDialog searches in this order:
1. **Absolute path** → Use directly if provided
2. **Form's directory** (`base_dir`) → Backward compatible behavior
3. **Directories in TYPEDIALOG_FRAGMENT_PATH** → Search left to right
4. **First match wins** → Subsequent directories are not checked
### Example
```bash
export TYPEDIALOG_FRAGMENT_PATH=".typedialog/ci/fragments:/usr/local/share/typedialog/fragments"
```
**Fragment reference:** `includes = ["user-auth.toml"]`
**Search order:**
```text
1. ./user-auth.toml (form's directory)
2. .typedialog/ci/fragments/user-auth.toml (local)
3. /usr/local/share/typedialog/fragments/user-auth.toml (system)
```
**Result:** First existing file is loaded.
## Use Cases
### Case 1: Centralized Fragment Library
Organization maintains a shared library of standard fragments.
**Setup:**
```bash
# System-wide fragments
export TYPEDIALOG_FRAGMENT_PATH="/opt/company/typedialog/fragments"
```
**Directory structure:**
```text
/opt/company/typedialog/fragments/
├── user-auth.toml
├── database-config.toml
├── api-settings.toml
└── logging.toml
```
**Form reference:**
```toml
[[item]]
type = "group"
includes = ["user-auth.toml", "database-config.toml"]
```
**Benefit:** All projects use same fragments → consistency across organization.
### Case 2: Local Overrides
Project needs custom version of standard fragment.
**Setup:**
```bash
export TYPEDIALOG_FRAGMENT_PATH=".typedialog/fragments:/opt/company/typedialog/fragments"
```
**Directory structure:**
```text
project/
├── .typedialog/
│ └── fragments/
│ └── user-auth.toml # ← Custom version
└── form.toml
```
**Form reference:**
```toml
[[item]]
type = "group"
includes = ["user-auth.toml"] # Uses local version
```
**Result:**
- `user-auth.toml` → loads from `.typedialog/fragments/` (local override)
- `database-config.toml` → loads from `/opt/company/...` (standard)
**Benefit:** Override only what you need, rest stays synchronized.
### Case 3: Multiple Fragment Sources
Combine fragments from different sources: company library, team library, local.
**Setup:**
```bash
export TYPEDIALOG_FRAGMENT_PATH=".typedialog/fragments:~/team-fragments:/opt/company/fragments"
```
**Search priority:**
1. Project-specific (`.typedialog/fragments/`)
2. Team-shared (`~/team-fragments/`)
3. Company-wide (`/opt/company/fragments/`)
**Benefit:** Flexible hierarchy for different scopes.
## Configuration
### direnv Integration
Automatically load `TYPEDIALOG_FRAGMENT_PATH` when entering project directory.
**Setup:**
```bash
# Install direnv
brew install direnv # macOS
# Or: apt install direnv # Linux
# Add to shell rc file (~/.bashrc, ~/.zshrc)
eval "$(direnv hook bash)" # or zsh, fish, etc.
# Create .envrc in project root
cat > .envrc <<EOF
export TYPEDIALOG_FRAGMENT_PATH=".typedialog/fragments:\$HOME/typedialog/fragments"
EOF
# Allow direnv to load .envrc
direnv allow
```
**Result:** `TYPEDIALOG_FRAGMENT_PATH` is automatically set when you `cd` into project.
### Shell Profile
Set globally for all projects.
**~/.bashrc or ~/.zshrc:**
```bash
export TYPEDIALOG_FRAGMENT_PATH="$HOME/.typedialog/fragments:/usr/local/share/typedialog/fragments"
```
### Per-Command
Override for single invocation.
```bash
TYPEDIALOG_FRAGMENT_PATH="/custom/path" typedialog form schema.toml
```
## Implementation Details
### Code Location
`crates/typedialog-core/src/form_parser/parser.rs`
**Functions:**
- `get_fragment_search_paths()` - Parse environment variable
- `resolve_fragment_path()` - Search directories in order
**Logic:**
```rust
fn resolve_fragment_path(path: &str, base_dir: &Path) -> PathBuf {
// 1. Absolute path → use directly
if Path::new(path).is_absolute() {
return PathBuf::from(path);
}
// 2. Try base_dir (backward compatible)
let base_path = base_dir.join(path);
if base_path.exists() {
return base_path;
}
// 3. Try each search path
for search_dir in get_fragment_search_paths() {
let candidate = search_dir.join(path);
if candidate.exists() {
return candidate;
}
}
// 4. Return base_path for error reporting
base_path
}
```
### Separator Detection
**Unix/Linux/macOS:** `:` (colon)
```rust
#[cfg(unix)]
let separator = ':';
```
**Windows:** `;` (semicolon)
```rust
#[cfg(windows)]
let separator = ';';
```
### Backward Compatibility
- ✅ If `TYPEDIALOG_FRAGMENT_PATH` not set → works as before (uses `base_dir`)
- ✅ Absolute paths → always work regardless of search paths
- ✅ Existing forms → no changes required
## Best Practices
### ✅ DO
- **Use relative paths** in form definitions (`includes = ["fragment.toml"]`)
- **Put local overrides first** in search path (`.typedialog/fragments:/system/fragments`)
- **Document custom fragments** with comments explaining why local version exists
- **Keep standard structure** when overriding (match original fragment structure)
- **Version control .envrc** if project-specific paths are important
### ❌ DON'T
- **Don't hardcode absolute paths** in forms (defeats portability)
- **Don't mix path separators** (use `:` on Unix, `;` on Windows)
- **Don't create deep hierarchies** (2-3 levels max: local, team, system)
- **Don't override without reason** (consider if change should be upstream)
## Troubleshooting
### Fragment not found
**Error:**
```text
Error: Failed to load fragment 'user-fields.toml': No such file or directory
```
**Check:**
```bash
# 1. Verify environment variable is set
echo $TYPEDIALOG_FRAGMENT_PATH
# 2. Check if file exists in any search path
ls .typedialog/fragments/user-fields.toml
ls /usr/local/share/typedialog/fragments/user-fields.toml
# 3. Check search order (add debug output)
export TYPEDIALOG_DEBUG=1 # If supported
```
### Wrong version loaded
**Symptom:** Expected local override, but system version is used.
**Check search order:**
```bash
# Verify local path is FIRST
echo $TYPEDIALOG_FRAGMENT_PATH
# Should be: .typedialog/fragments:...
# Verify local file exists
ls -la .typedialog/fragments/fragment.toml
```
### Windows path issues
**Problem:** Paths with spaces or special characters.
**Solution:** Use quotes in environment variable:
```cmd
set TYPEDIALOG_FRAGMENT_PATH="C:\Program Files\TypeDialog\fragments;D:\Custom Fragments"
```
## Examples
### Example 1: Standard Setup
**Project structure:**
```text
project/
├── form.toml
├── .envrc
└── .typedialog/
└── fragments/
└── custom-field.toml
```
**.envrc:**
```bash
export TYPEDIALOG_FRAGMENT_PATH=".typedialog/fragments:/usr/local/share/typedialog/fragments"
```
**form.toml:**
```toml
[[item]]
type = "group"
includes = [
"custom-field.toml", # Loads from .typedialog/fragments/
"standard-fields.toml" # Loads from /usr/local/share/.../
]
```
### Example 2: Multi-Environment
**Development:**
```bash
export TYPEDIALOG_FRAGMENT_PATH=".typedialog/fragments:$HOME/dev-fragments:/opt/fragments"
```
**Production:**
```bash
export TYPEDIALOG_FRAGMENT_PATH="/opt/company/fragments"
```
**Result:** Development has local overrides, production uses only approved fragments.
## See Also
- [Configuration Guide](./configuration.md) - Full TypeDialog configuration
- [Fragment System](./fragments.md) - How to write fragments
- [Environment Variables](./configuration.md#environment-variables) - All TypeDialog env vars
---
**Last Updated:** 2025-12-26
**Feature:** TYPEDIALOG_FRAGMENT_PATH
**Status:** ✅ Implemented in TypeDialog v0.1.0