Skip to content

Commit 4d6c13a

Browse files
feat(webui-press): normalize duplicate folder/filename paths as index
Treat markdown files where the filename matches the parent folder name as index pages, avoiding redundant URL paths. Example: - Before: components/webui-button/webui-button.md → /webui-button/webui-button/ - After: components/webui-button/webui-button.md → /webui-button/ This enables cleaner component documentation URLs while preserving support for additional pages (e.g., webui-button/usage.md → /webui-button/usage). Changes: - Add normalize_path_as_index() helper to strip duplicate filenames - Update build_page_registry() to normalize paths before URL generation - Add comprehensive test coverage (6 test cases) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 53a71db commit 4d6c13a

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

crates/webui-press/src/content.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,29 @@ fn collect_sidebar_links(items: &[SidebarItem]) -> Vec<&str> {
156156
///
157157
/// Discovery is filesystem-driven: any markdown file under content_dir becomes
158158
/// a page. The sidebar/nav config controls navigation, not discovery.
159+
/// Strip redundant filename if it matches the parent folder name.
160+
/// E.g., "my-button/my-button" → "my-button"
161+
/// "my-button/usage" → "my-button/usage" (unchanged)
162+
fn normalize_path_as_index(path: &str) -> &str {
163+
// Find the last slash to split parent/filename
164+
let Some(slash_pos) = path.rfind('/') else {
165+
return path;
166+
};
167+
168+
let parent = &path[..slash_pos];
169+
let filename = &path[slash_pos + 1..];
170+
171+
// Get the parent folder name (last segment before filename)
172+
let folder_name = parent.rsplit('/').next().unwrap_or("");
173+
174+
// If folder name matches filename, treat as index and strip filename
175+
if folder_name == filename {
176+
parent
177+
} else {
178+
path
179+
}
180+
}
181+
159182
fn build_page_registry(content_dir: &Path, base_path: &str) -> Vec<(String, std::path::PathBuf)> {
160183
let mut pages = Vec::new();
161184
let mut stack: Vec<std::path::PathBuf> = vec![content_dir.to_path_buf()];
@@ -188,7 +211,14 @@ fn build_page_registry(content_dir: &Path, base_path: &str) -> Vec<(String, std:
188211
};
189212
let rel_str = rel.to_string_lossy().replace('\\', "/");
190213
let trimmed = rel_str.trim_end_matches(".md");
214+
215+
// Strip /index suffix
191216
let trimmed = trimmed.strip_suffix("/index").unwrap_or(trimmed);
217+
218+
// Strip redundant filename if it matches parent folder
219+
// e.g., "webui-button/webui-button" → "webui-button"
220+
let trimmed = normalize_path_as_index(trimmed);
221+
192222
// Root index.md => "/"
193223
let url_path = if trimmed.is_empty() || trimmed == "index" {
194224
if base_path.is_empty() {
@@ -743,6 +773,56 @@ mod tests {
743773
assert!(out.ends_with("é-page"), "got {out}");
744774
}
745775

776+
// --- normalize_path_as_index -----------------------------------------
777+
778+
#[test]
779+
fn normalize_path_as_index_strips_duplicate_folder_name() {
780+
assert_eq!(
781+
normalize_path_as_index("webui-button/webui-button"),
782+
"webui-button"
783+
);
784+
}
785+
786+
#[test]
787+
fn normalize_path_as_index_keeps_different_filename() {
788+
assert_eq!(
789+
normalize_path_as_index("webui-button/usage"),
790+
"webui-button/usage"
791+
);
792+
}
793+
794+
#[test]
795+
fn normalize_path_as_index_nested_folders() {
796+
assert_eq!(
797+
normalize_path_as_index("components/webui-button/webui-button"),
798+
"components/webui-button"
799+
);
800+
}
801+
802+
#[test]
803+
fn normalize_path_as_index_no_slash_returns_unchanged() {
804+
assert_eq!(
805+
normalize_path_as_index("webui-button"),
806+
"webui-button"
807+
);
808+
}
809+
810+
#[test]
811+
fn normalize_path_as_index_deep_nesting_with_match() {
812+
assert_eq!(
813+
normalize_path_as_index("a/b/c/webui-card/webui-card"),
814+
"a/b/c/webui-card"
815+
);
816+
}
817+
818+
#[test]
819+
fn normalize_path_as_index_deep_nesting_without_match() {
820+
assert_eq!(
821+
normalize_path_as_index("a/b/c/webui-card/examples"),
822+
"a/b/c/webui-card/examples"
823+
);
824+
}
825+
746826
// --- parse_frontmatter -----------------------------------------------
747827

748828
#[test]

0 commit comments

Comments
 (0)