feat: Improve export self-containment and link handling

- Use absolute paths for path_relative calculations
- Validate links before export to populate page.links
- Copy cross-collection referenced pages for self-contained export
- Update export_link_path to generate local links for self-contained exports
- Remove page from visited map to allow re-inclusion in other contexts
This commit is contained in:
Mahmoud-Emad
2025-11-06 10:51:10 +02:00
parent ac09648a5b
commit 347ebed5ea
4 changed files with 46 additions and 32 deletions

View File

@@ -53,7 +53,10 @@ fn (mut c Collection) add_page(mut path pathlib.Path) ! {
if name in c.pages {
return error('Page ${name} already exists in collection ${c.name}')
}
relativepath := path.path_relative(c.path()!.path)!
// Use absolute paths for path_relative to work correctly
mut col_path := pathlib.get(c.path)
mut page_abs_path := pathlib.get(path.absolute())
relativepath := page_abs_path.path_relative(col_path.absolute())!
mut p_new := Page{
name: name
@@ -71,7 +74,10 @@ fn (mut c Collection) add_file(mut p pathlib.Path) ! {
if name in c.files {
return error('Page ${name} already exists in collection ${c.name}')
}
relativepath := p.path_relative(c.path()!.path)!
// Use absolute paths for path_relative to work correctly
mut col_path := pathlib.get(c.path)
mut file_abs_path := pathlib.get(p.absolute())
relativepath := file_abs_path.path_relative(col_path.absolute())!
mut file_new := File{
name: name

View File

@@ -22,8 +22,8 @@ pub fn (mut a Atlas) export(args ExportArgs) ! {
dest.empty()!
}
// Validate links before export
// a.validate_links()!
// Validate links before export to populate page.links
a.validate_links()!
for _, mut col in a.collections {
col.export(
@@ -67,6 +67,10 @@ pub fn (mut c Collection) export(args CollectionExportArgs) ! {
)!
json_file.write(meta)!
// Track cross-collection pages that need to be copied for self-contained export
mut cross_collection_pages := map[string]&Page{} // key: page.name, value: &Page
// First pass: export all pages in this collection and collect cross-collection references
for _, mut page in c.pages {
// Get content with includes processed and links transformed for export
content := page.content_with_fixed_links(
@@ -78,6 +82,19 @@ pub fn (mut c Collection) export(args CollectionExportArgs) ! {
mut dest_file := pathlib.get_file(path: '${col_dir.path}/${page.name}.md', create: true)!
dest_file.write(content)!
// Collect cross-collection page references for copying
// IMPORTANT: Use cached links from validation (before transformation) to preserve collection info
for mut link in page.links {
// Only process valid page links (not files/images) from other collections
if link.status == .found && !link.is_file_link && !link.is_local_in_collection() {
mut target_page := link.target_page() or { continue }
// Use page name as key to avoid duplicates
if target_page.name !in cross_collection_pages {
cross_collection_pages[target_page.name] = target_page
}
}
}
// Redis operations...
if args.redis {
mut context := base.context()!
@@ -86,23 +103,17 @@ pub fn (mut c Collection) export(args CollectionExportArgs) ! {
}
}
// // Export files
// if c.files.len > 0 {
// files_dir := pathlib.get_dir(
// path: '${col_dir.path}/files'
// create: true
// )!
// Second pass: copy cross-collection referenced pages to make collection self-contained
for _, mut ref_page in cross_collection_pages {
// Get the referenced page content with includes processed
ref_content := ref_page.content_with_fixed_links(
include: args.include
cross_collection: true
export_mode: true
)!
// for _, mut file in c.files {
// dest_path := '${files_dir.path}/${file.file_name()}'
// mut p2 := file.path()!
// p2.copy(dest: col_dir.path)!
// if args.redis {
// mut context := base.context()!
// mut redis := context.redis()!
// redis.hset('atlas:${c.name}', file.file_name(), file.path()!.path)!
// }
// }
// }
// Write the referenced page to this collection's directory
mut dest_file := pathlib.get_file(path: '${col_dir.path}/${ref_page.name}.md', create: true)!
dest_file.write(ref_content)!
}
}

View File

@@ -242,27 +242,20 @@ fn (mut p Page) calculate_link_path(mut link Link, args FixLinksArgs) !string {
return p.filesystem_link_path(mut link)!
}
// export_link_path calculates path for export (flat structure: collection/file.md)
// export_link_path calculates path for export (self-contained: all references are local)
fn (mut p Page) export_link_path(mut link Link) !string {
mut target_collection := ''
mut target_filename := ''
if link.is_file_link {
mut tf := link.target_file()!
target_collection = tf.collection.name
target_filename = tf.name
} else {
mut tp := link.target_page()!
target_collection = tp.collection.name
target_filename = '${tp.name}.md'
}
// Same collection: just filename, different collection: ../collection/filename
return if link.is_local_in_collection() {
target_filename
} else {
'../${target_collection}/${target_filename}'
}
// For self-contained exports, all links are local (cross-collection pages are copied)
return target_filename
}
// filesystem_link_path calculates path using actual filesystem paths

View File

@@ -128,6 +128,10 @@ fn (mut p Page) process_includes(content string, mut visited map[string]bool) !s
}
}
// Remove this page from visited map to allow it to be included again in other contexts
// This prevents false positives when a page is included multiple times (which is valid)
visited.delete(page_key)
return processed_lines.join_lines()
}