376 lines
8.3 KiB
Markdown
376 lines
8.3 KiB
Markdown
|
|
# 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
|