@@ -184,8 +184,21 @@ This file should be transitively included in `main.html`
184184 config. included_files = collect_included_files ( & config, processor. as_ref ( ) )
185185 . expect ( "Failed to collect include files" ) ;
186186
187- let markdown_files = collect_markdown_files ( & input_dir) ;
188- generate_search_index ( & config, & markdown_files)
187+ let all_markdown_files = collect_markdown_files ( & input_dir) ;
188+
189+ // Filter out included files - only standalone files should be in search index
190+ let searchable_files: Vec < _ > = all_markdown_files
191+ . iter ( )
192+ . filter ( |file| {
193+ file
194+ . strip_prefix ( & input_dir)
195+ . ok ( )
196+ . map_or ( true , |rel| !config. included_files . contains_key ( rel) )
197+ } )
198+ . cloned ( )
199+ . collect ( ) ;
200+
201+ generate_search_index ( & config, & searchable_files)
189202 . expect ( "Failed to generate search index" ) ;
190203
191204 // Verify that search data is generated correctly
@@ -194,27 +207,179 @@ This file should be transitively included in `main.html`
194207 . expect ( "Failed to open search-data.json" ) ;
195208 let search_data: Vec < SearchDocument > =
196209 serde_json:: from_reader ( index_file) . expect ( "Failed to read index data" ) ;
197- let included_doc = search_data
210+
211+ // Only the main file should appear in search index
212+ // The included files' content is already in main.html, so they're searchable
213+ // through the main document
214+ let main_doc = search_data
198215 . iter ( )
199- . find ( |doc| doc. title == "Included file" )
200- . expect ( "included file not found in search-data.json" ) ;
216+ . find ( |doc| doc. title == "Main file" )
217+ . expect ( "main file not found in search-data.json" ) ;
218+
219+ assert_eq ! ( main_doc. path, "main.html" ) ;
220+
221+ // Included files should NOT have separate search entries
222+ assert ! (
223+ search_data. iter( ) . all( |doc| doc. title != "Included file" ) ,
224+ "Included files should not have separate search entries"
225+ ) ;
226+ assert ! (
227+ search_data
228+ . iter( )
229+ . all( |doc| doc. title != "Section without an anchor ID" ) ,
230+ "Included files should not have separate search entries"
231+ ) ;
232+ assert ! (
233+ search_data
234+ . iter( )
235+ . all( |doc| doc. title != "Transitively included file" ) ,
236+ "Included files should not have separate search entries"
237+ ) ;
238+ }
239+
240+ #[ test]
241+ fn test_nested_directory_include_search_paths ( ) {
242+ // This test replicates a real-world scenario where:
243+ // - index.md includes installation/modules.md
244+ // - installation/modules.md includes installation/modules/nixos.md
245+ // The search index should NOT create entries for included files as
246+ // standalone pages (e.g., installation/modules/nixos.html), but should
247+ // index their content under the root document with anchors.
248+
249+ let temp_dir = TempDir :: new ( ) . expect ( "Failed to create temp dir" ) ;
250+ let input_dir = temp_dir. path ( ) . join ( "input" ) ;
251+ let installation_dir = input_dir. join ( "installation" ) ;
252+ let modules_dir = installation_dir. join ( "modules" ) ;
253+ let output_dir = temp_dir. path ( ) . join ( "output" ) ;
254+
255+ create_dir_all ( & modules_dir) . expect ( "failed to create modules dir" ) ;
256+ create_dir_all ( & output_dir) . expect ( "failed to create output dir" ) ;
257+
258+ // Root document that includes a file from a subdirectory
259+ let index_content = "# Documentation Index
260+
261+ Welcome to the documentation.
262+
263+ ```{=include=}
264+ installation/modules.md
265+ ```
266+ " ;
267+ fs:: write ( input_dir. join ( "index.md" ) , index_content)
268+ . expect ( "Failed to write index.md" ) ;
269+
270+ // Intermediate file that includes deeper nested files
271+ let modules_content = "# Module Installation {#ch-module-installation}
272+
273+ The below chapters describe module installation.
274+
275+ ```{=include=}
276+ modules/nixos.md
277+ modules/home-manager.md
278+ ```
279+ " ;
280+ fs:: write ( installation_dir. join ( "modules.md" ) , modules_content)
281+ . expect ( "Failed to write installation/modules.md" ) ;
282+
283+ // Deeply nested included files
284+ let nixos_content = "# NixOS Module {#ch-nixos-module}
285+
286+ This describes the NixOS module installation.
287+ " ;
288+ fs:: write ( modules_dir. join ( "nixos.md" ) , nixos_content)
289+ . expect ( "Failed to write installation/modules/nixos.md" ) ;
290+
291+ let hm_content = "# Home Manager Module {#ch-home-manager-module}
201292
202- assert_eq ! ( included_doc. path, "main.html#included-file-heading" ) ;
293+ This describes the Home Manager module installation.
294+ " ;
295+ fs:: write ( modules_dir. join ( "home-manager.md" ) , hm_content)
296+ . expect ( "Failed to write installation/modules/home-manager.md" ) ;
297+
298+ let mut config = Config {
299+ input_dir : Some ( input_dir. clone ( ) ) ,
300+ output_dir : output_dir. clone ( ) ,
301+ module_options : None ,
302+ title : "Test Documentation" . to_string ( ) ,
303+ generate_search : true ,
304+ ..Default :: default ( )
305+ } ;
203306
204- let no_id_doc = search_data
307+ let processor = Some ( create_processor ( & config, None ) ) ;
308+ config. included_files = collect_included_files ( & config, processor. as_ref ( ) )
309+ . expect ( "Failed to collect include files" ) ;
310+
311+ let all_markdown_files = collect_markdown_files ( & input_dir) ;
312+
313+ // Process markdown files to generate HTML
314+ ndg:: utils:: process_markdown_files ( & config, processor. as_ref ( ) )
315+ . expect ( "Failed to process markdown files" ) ;
316+
317+ // Filter out included files - only standalone files should be in search index
318+ let searchable_files: Vec < _ > = all_markdown_files
205319 . iter ( )
206- . find ( |doc| doc. title == "Section without an anchor ID" )
207- . expect ( "section_no_id file not found in search-data.json" ) ;
320+ . filter ( |file| {
321+ file
322+ . strip_prefix ( & input_dir)
323+ . ok ( )
324+ . map_or ( true , |rel| !config. included_files . contains_key ( rel) )
325+ } )
326+ . cloned ( )
327+ . collect ( ) ;
328+
329+ // Generate search index with only standalone files
330+ generate_search_index ( & config, & searchable_files)
331+ . expect ( "Failed to generate search index" ) ;
208332
209- assert_eq ! ( no_id_doc. path, "main.html#section-without-an-anchor-id" ) ;
333+ // Verify that search data is generated correctly
334+ let index_file =
335+ File :: open ( output_dir. join ( "assets" ) . join ( "search-data.json" ) )
336+ . expect ( "Failed to open search-data.json" ) ;
337+ let search_data: Vec < SearchDocument > =
338+ serde_json:: from_reader ( index_file) . expect ( "Failed to read index data" ) ;
210339
211- let transitive_inc_doc = search_data
340+ // The index document should be in search results
341+ let index_doc = search_data
212342 . iter ( )
213- . find ( |doc| doc. title == "Transitively included file" )
214- . expect ( "transitively included file not found in search-data.json" ) ;
343+ . find ( |doc| doc. title == "Documentation Index" ) ;
344+ assert ! (
345+ index_doc. is_some( ) ,
346+ "Index document should be in search results"
347+ ) ;
348+ assert_eq ! ( index_doc. unwrap( ) . path, "index.html" ) ;
349+
350+ // Included files should NOT appear as separate search entries
351+ // Their content is already in index.html
352+ assert ! (
353+ search_data
354+ . iter( )
355+ . all( |doc| doc. title != "Module Installation" ) ,
356+ "Included files should not have separate search entries"
357+ ) ;
358+ assert ! (
359+ search_data. iter( ) . all( |doc| doc. title != "NixOS Module" ) ,
360+ "Included files should not have separate search entries"
361+ ) ;
362+ assert ! (
363+ search_data
364+ . iter( )
365+ . all( |doc| doc. title != "Home Manager Module" ) ,
366+ "Included files should not have separate search entries"
367+ ) ;
215368
216- assert_eq ! (
217- transitive_inc_doc. path,
218- "main.html#transitively-included-file"
369+ // Verify that the included files are NOT created as standalone HTML files
370+ assert ! (
371+ !output_dir. join( "installation/modules.html" ) . exists( ) ,
372+ "installation/modules.html should not be created (file is included)"
373+ ) ;
374+ assert ! (
375+ !output_dir. join( "installation/modules/nixos.html" ) . exists( ) ,
376+ "installation/modules/nixos.html should not be created (file is included)"
377+ ) ;
378+ assert ! (
379+ !output_dir
380+ . join( "installation/modules/home-manager.html" )
381+ . exists( ) ,
382+ "installation/modules/home-manager.html should not be created (file is \
383+ included)"
219384 ) ;
220385}
0 commit comments