@@ -373,6 +373,185 @@ value. If they match, the option is excluded from the output file.
373373 configuration that includes all customized values while excluding unchanged defaults.
374374 This provides a clean view of what's been explicitly configured.
375375
376+ .. _`config_relativize_paths` :
377+
378+ needscfg_relative_path_fields
379+ -----------------------------
380+
381+ **Type: ** ``list[str | dict] ``
382+
383+ **Default: ** ``[] ``
384+
385+ Specifies which configuration fields should have their absolute paths converted to relative paths
386+ in the output file. This is particularly useful when working with build systems like Bazel that
387+ generate absolute paths, but you want the configuration file to use relative paths for portability.
388+
389+ Each entry in the list can be either:
390+
391+ 1. **String format ** (simple field pattern):
392+
393+ - ``"needs_schema_debug_path" `` - Matches the field directly
394+ - ``"needs_external_needs[*].json" `` - Supports ``* `` wildcards for array indices
395+
396+ 2. **Dict format ** (for paths embedded in strings with prefix/suffix):
397+
398+ - ``field `` (required): The field pattern to match (e.g., ``"needs_flow_configs.my_config" ``)
399+ - ``prefix `` (optional): String prefix before the path (e.g., ``"!include " ``)
400+ - ``suffix `` (optional): String suffix after the path (e.g., ``"?raw=true" ``)
401+
402+ **When a field matches a pattern: **
403+
404+ 1. Check if the value is an absolute path (``Path `` object or string)
405+ 2. Extract the path portion (removing prefix/suffix if configured)
406+ 3. Calculate a relative path from the output file location to the target path
407+ 4. Replace with relative path (preserving prefix/suffix if configured)
408+
409+ **Configuration Formats: **
410+
411+ .. code-block :: python
412+
413+ needscfg_relative_path_fields = [
414+ # Simple string format - for direct path values
415+ " needs_schema_debug_path" ,
416+
417+ # String with wildcards - for array fields
418+ " needs_external_needs[*].json_path" ,
419+
420+ # Dict with prefix - for paths embedded in strings like "!include /path/to/file"
421+ {
422+ " field" : " needs_flow_configs.plantuml_config" ,
423+ " prefix" : " !include " ,
424+ },
425+
426+ # Dict with suffix - for paths like "/path/to/file?option=value"
427+ {
428+ " field" : " needs_asset_url" ,
429+ " suffix" : " ?raw=true" ,
430+ },
431+
432+ # Dict with both prefix and suffix
433+ {
434+ " field" : " needs_custom_path" ,
435+ " prefix" : " file://" ,
436+ " suffix" : " #anchor" ,
437+ },
438+
439+ # Dict with just field (equivalent to string format)
440+ {
441+ " field" : " needs_build_json_path" ,
442+ },
443+ ]
444+
445+ **Use Cases: **
446+
447+ - Working with Bazel or similar build systems that use absolute paths
448+ - Making configuration files portable across different machines/environments
449+ - Handling PlantUML ``!include `` directives with absolute paths
450+ - Processing URL-like strings with path components
451+ - Keeping paths relative to the repository root instead of absolute system paths
452+
453+ **Example with Bazel: **
454+
455+ If you have a Bazel-generated path like:
456+
457+ .. code-block :: python
458+
459+ # In conf.py (generated by Bazel)
460+ needs_schema_debug_path = " /home/user/.cache/bazel/.../execroot/_main/bazel-out/k8-fastbuild/bin/docs.runfiles/project/schema_debug"
461+
462+ And your configuration file output is at:
463+
464+ .. code-block :: text
465+
466+ /home/user/git/project/docs/ubproject.toml
467+ /home/user/git/project/bazel-out > /home/user/.cache/bazel/.../execroot/_main/bazel-out
468+
469+ Note that Bazel creates a ``bazel-out `` symlink in the project directory
470+ (``/home/user/git/project/bazel-out ``) that points into the Bazel cache.
471+ The extension detects this symlink and uses it to create a shorter relative path.
472+
473+ With this setting:
474+
475+ .. code-block :: python
476+
477+ needscfg_relative_path_fields = [" needs_schema_debug_path" ]
478+
479+ The output will contain:
480+
481+ .. code-block :: toml
482+
483+ [needs]
484+ schema_debug_path = "../bazel-out/k8-fastbuild/bin/docs.runfiles/project/schema_debug"
485+
486+ **Example with Prefix (PlantUML !include directive): **
487+
488+ If you have a PlantUML configuration with an ``!include `` directive:
489+
490+ .. code-block :: python
491+
492+ # In conf.py
493+ needs_flow_configs = {
494+ " plantuml_theme" : " !include /home/user/project/assets/theme.puml"
495+ }
496+
497+ With this configuration:
498+
499+ .. code-block :: python
500+
501+ needscfg_relative_path_fields = [
502+ {
503+ " field" : " needs_flow_configs.plantuml_theme" ,
504+ " prefix" : " !include " ,
505+ }
506+ ]
507+
508+ The output will preserve the ``!include `` prefix with a relative path:
509+
510+ .. code-block :: toml
511+
512+ [needs.flow_configs]
513+ plantuml_theme = "!include ../assets/theme.puml"
514+
515+ **Example with Suffix (URL parameters): **
516+
517+ For paths that include URL parameters or anchors:
518+
519+ .. code-block :: python
520+
521+ # In conf.py
522+ needs_asset_url = " /home/user/project/docs/image.png?width=500"
523+
524+ needscfg_relative_path_fields = [
525+ {
526+ " field" : " needs_asset_url" ,
527+ " suffix" : " ?width=500" ,
528+ }
529+ ]
530+
531+ The output preserves the suffix:
532+
533+ .. code-block :: toml
534+
535+ [needs]
536+ asset_url = "../image.png?width=500"
537+
538+ .. warning ::.. warning ::
539+
540+ Path relativization is only applied to fields explicitly listed in the allowlist.
541+ This is a safety feature to prevent unintended path transformations. You must
542+ explicitly specify which fields should have their paths relativized.
543+
544+ .. note ::
545+
546+ - All relative paths are converted to POSIX format (forward slashes) on all platforms,
547+ including Windows. This ensures configuration files are portable across operating systems.
548+ - The extension correctly handles output file paths that don't exist yet (common for
549+ generated configuration files) by detecting file suffixes like ``.toml `` or ``.json ``.
550+ - On Unix systems, the extension attempts to find symlinks (like Bazel's ``bazel-out ``)
551+ to create shorter relative paths when possible.
552+ - If no common ancestor exists (e.g., paths on different drives on Windows), the
553+ absolute path will be returned unchanged.
554+
376555Examples
377556--------
378557
0 commit comments