Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/microsoft-fast-build/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ Each iteration pushes `("$index", JsonValue::Number(i as f64))` into `loop_vars`

For each glob pattern:
1. Find the **static prefix directory** β€” the longest directory path before any wildcard character (`*`, `?`). This avoids walking the entire filesystem.
2. Recursively walk that directory collecting all `.html` files (`walk_html_files`).
2. Recursively walk that directory collecting all `.html` files (`walk_html_files`). The walk is bounded to a maximum depth of 50 directories (`MAX_DIR_DEPTH`) and skips symlinks to prevent infinite loops from symlink cycles.
3. Normalise path separators to `/` and strip a leading `./`, then test each file path against the glob pattern.
4. For each matching file, read its content and call `parse_f_templates` to extract all `<f-template>` elements.
5. The **element name** is the `name` attribute of each `<f-template>` element (e.g. `name="my-button"`). A single file may declare multiple templates.
Expand Down
2 changes: 2 additions & 0 deletions crates/microsoft-fast-build/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ locator.add_template("my-button", "<button>{{label}}</button>");

Glob patterns support `*` (any characters within one path segment), `**` (any number of segments), and `?` (any single character).

> **Note:** `Locator::from_patterns` walks the filesystem recursively up to 50 directory levels deep and skips symlinks. Symlinked directories are never followed, guarding against cycles in the directory tree.

### HTML File Format

Template HTML files must wrap their content in an `<f-template>` element with a `name` attribute identifying the custom element, and an inner `<template>` element containing the template markup:
Expand Down
18 changes: 17 additions & 1 deletion crates/microsoft-fast-build/src/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,29 @@ fn static_prefix_dir(pattern: &str) -> String {
base
}

/// Maximum directory depth walked by `walk_html_files`. This guards against
/// accidental stack overflows on very deep directory trees and prevents infinite
/// loops caused by symlink cycles.
const MAX_DIR_DEPTH: usize = 50;

/// Recursively walk `dir` and collect all `.html` files.
fn walk_html_files(dir: &Path, result: &mut Vec<std::path::PathBuf>) -> std::io::Result<()> {
walk_html_files_inner(dir, result, 0)
}

fn walk_html_files_inner(dir: &Path, result: &mut Vec<std::path::PathBuf>, depth: usize) -> std::io::Result<()> {
if depth > MAX_DIR_DEPTH {
return Ok(());
}
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
// Skip symlinks to avoid following cycles into previously-visited dirs.
if path.is_symlink() {
continue;
}
if path.is_dir() {
walk_html_files(&path, result)?;
walk_html_files_inner(&path, result, depth + 1)?;
} else if path.extension().and_then(|s| s.to_str()) == Some("html") {
result.push(path);
}
Expand Down
Loading