TypeDialog/docs/fragment-search-paths.md
2026-01-11 22:35:49 +00:00

8.4 KiB

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:

export TYPEDIALOG_FRAGMENT_PATH="/path/to/fragments:/another/path"
```text

**Windows:**

```cmd
set TYPEDIALOG_FRAGMENT_PATH=C:\fragments;D:\other\fragments
```text

## 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"
```text

**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)
```text

**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"
```text

**Directory structure:**

```text
/opt/company/typedialog/fragments/
├── user-auth.toml
├── database-config.toml
├── api-settings.toml
└── logging.toml
```text

**Form reference:**

```toml
[[item]]
type = "group"
includes = ["user-auth.toml", "database-config.toml"]
```text

**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"
```text

**Directory structure:**

```text
project/
├── .typedialog/
│   └── fragments/
│       └── user-auth.toml  # ← Custom version
└── form.toml
```text

**Form reference:**

```toml
[[item]]
type = "group"
includes = ["user-auth.toml"]  # Uses local version
```text

**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"
```text

**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
```text

**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"
```text

### Per-Command

Override for single invocation.

```bash
TYPEDIALOG_FRAGMENT_PATH="/custom/path" typedialog form schema.toml
```text

## 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
}
```text

### Separator Detection

**Unix/Linux/macOS:** `:` (colon)

```rust
#[cfg(unix)]
let separator = ':';
```text

**Windows:** `;` (semicolon)

```rust
#[cfg(windows)]
let separator = ';';
```text

### 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
```text

**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
```text

### 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
```text

### 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"
```text

## Examples

### Example 1: Standard Setup

**Project structure:**

```text
project/
├── form.toml
├── .envrc
└── .typedialog/
    └── fragments/
        └── custom-field.toml
```text

**.envrc:**

```bash
export TYPEDIALOG_FRAGMENT_PATH=".typedialog/fragments:/usr/local/share/typedialog/fragments"
```text

**form.toml:**

```toml
[[item]]
type = "group"
includes = [
    "custom-field.toml",     # Loads from .typedialog/fragments/
    "standard-fields.toml"   # Loads from /usr/local/share/.../
]
```text

### Example 2: Multi-Environment

**Development:**

```bash
export TYPEDIALOG_FRAGMENT_PATH=".typedialog/fragments:$HOME/dev-fragments:/opt/fragments"
```text

**Production:**

```bash
export TYPEDIALOG_FRAGMENT_PATH="/opt/company/fragments"
```text

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