1414from pathlib import Path
1515
1616DEVNOTES_SECTION_RE = re .compile (r"^ - section:\s+Dev Notes\s*$" )
17+ CODE_REFERENCE_SECTION_RE = re .compile (r"^ - section:\s+Code Reference\s*$" )
18+ CODE_REFERENCE_PAGE_ROOT_RE = re .compile (r"path:\s+\./([^/]+)/pages/code_reference/" )
1719NAV_PATH_RE = re .compile (r"^(\s*path:\s+)\./([^#\s]+)(.*)$" )
1820REDIRECT_VERSION_RE = re .compile (
1921 r'^\s*destination:\s+["\']/nemo/datadesigner/((?:v[0-9][^/"\']*)|older-versions)(?:/|["\'])'
4143 "fern/styles/metrics-table.css" ,
4244 "fern/styles/trajectory-viewer.css" ,
4345]
46+ CONFIG_CODE_REFERENCE_PAGES = [
47+ "analysis.mdx" ,
48+ "column_configs.mdx" ,
49+ "config_builder.mdx" ,
50+ "data_designer_config.mdx" ,
51+ "mcp.mdx" ,
52+ "models.mdx" ,
53+ "processors.mdx" ,
54+ "run_config.mdx" ,
55+ "sampler_params.mdx" ,
56+ "validator_params.mdx" ,
57+ ]
58+ CODE_REFERENCE_STRUCTURE_PAGES = [
59+ "index.mdx" ,
60+ "config/index.mdx" ,
61+ "config/seeds.mdx" ,
62+ "engine/column_generators.mdx" ,
63+ "engine/index.mdx" ,
64+ "engine/mcp.mdx" ,
65+ "engine/processors.mdx" ,
66+ "engine/seed_readers.mdx" ,
67+ "interface/data_designer.mdx" ,
68+ "interface/errors.mdx" ,
69+ "interface/index.mdx" ,
70+ "interface/results.mdx" ,
71+ ]
72+ CODE_REFERENCE_LINK_REPLACEMENTS = [
73+ ("/code-reference/topic-overviews/data-designer-config" , "/code-reference/config/data-designer-config" ),
74+ ("/code-reference/topic-overviews/column-configs" , "/code-reference/config/column-configs" ),
75+ ("/code-reference/topic-overviews/config-builder" , "/code-reference/config/config-builder" ),
76+ ("/code-reference/topic-overviews/run-config" , "/code-reference/config/run-config" ),
77+ ("/code-reference/topic-overviews/sampler-params" , "/code-reference/config/sampler-params" ),
78+ ("/code-reference/topic-overviews/validator-params" , "/code-reference/config/validator-params" ),
79+ ("/code-reference/topic-overviews/models" , "/code-reference/config/models" ),
80+ ("/code-reference/topic-overviews/mcp" , "/code-reference/config/mcp" ),
81+ ("/code-reference/topic-overviews/processors" , "/code-reference/config/processors" ),
82+ ("/code-reference/topic-overviews/analysis" , "/code-reference/config/analysis" ),
83+ ]
4484
4585
4686class PublishedBranchError (RuntimeError ):
@@ -145,6 +185,16 @@ def copy_path(source: Path, target: Path) -> None:
145185 shutil .copy2 (source , target )
146186
147187
188+ def copy_mdx_with_link_rewrites (source : Path , target : Path ) -> None :
189+ if not source .exists ():
190+ return
191+ target .parent .mkdir (parents = True , exist_ok = True )
192+ content = source .read_text ()
193+ for old , new in CODE_REFERENCE_LINK_REPLACEMENTS :
194+ content = content .replace (old , new )
195+ target .write_text (content )
196+
197+
148198def clear_published_tree (root : Path ) -> None :
149199 root .mkdir (parents = True , exist_ok = True )
150200 for path in root .iterdir ():
@@ -168,6 +218,83 @@ def merge_preserved_versions(source_versions: Path, published_versions: Path, pr
168218 copy_path (path , target )
169219
170220
221+ def extract_navigation_section (path : Path , section_re : re .Pattern [str ]) -> list [str ]:
222+ lines = path .read_text ().splitlines (keepends = True )
223+ start = next ((i for i , line in enumerate (lines ) if section_re .match (line )), - 1 )
224+ if start == - 1 :
225+ raise PublishedBranchError (f"Section not found in { path } " )
226+ end = start + 1
227+ while end < len (lines ):
228+ if lines [end ].startswith (" - " ) and lines [end ].strip ():
229+ break
230+ end += 1
231+ return lines [start :end ]
232+
233+
234+ def replace_navigation_section (path : Path , section_re : re .Pattern [str ], block : list [str ]) -> None :
235+ lines = path .read_text ().splitlines (keepends = True )
236+ start = next ((i for i , line in enumerate (lines ) if section_re .match (line )), - 1 )
237+ if start == - 1 :
238+ raise PublishedBranchError (f"Section not found in { path } " )
239+ end = start + 1
240+ while end < len (lines ):
241+ if lines [end ].startswith (" - " ) and lines [end ].strip ():
242+ break
243+ end += 1
244+ lines [start :end ] = block
245+ path .write_text ("" .join (lines ))
246+
247+
248+ def code_reference_page_root (block : list [str ]) -> str | None :
249+ for line in block :
250+ match = CODE_REFERENCE_PAGE_ROOT_RE .search (line )
251+ if match :
252+ return match .group (1 )
253+ return None
254+
255+
256+ def rewrite_code_reference_block (block : list [str ], page_root : str ) -> list [str ]:
257+ return [line .replace ("./latest/pages/code_reference/" , f"./{ page_root } /pages/code_reference/" ) for line in block ]
258+
259+
260+ def sync_code_reference_pages (source_root : Path , published_root : Path , page_root : str ) -> None :
261+ source_base = source_root / "fern" / "versions" / "latest" / "pages" / "code_reference"
262+ target_base = published_root / "fern" / "versions" / page_root / "pages" / "code_reference"
263+ if not source_base .exists () or not target_base .exists ():
264+ return
265+
266+ for rel_path in CODE_REFERENCE_STRUCTURE_PAGES :
267+ copy_mdx_with_link_rewrites (source_base / rel_path , target_base / rel_path )
268+
269+ for filename in CONFIG_CODE_REFERENCE_PAGES :
270+ flat_source = target_base / filename
271+ nested_source = target_base / "config" / filename
272+ latest_source = source_base / "config" / filename
273+ source = flat_source if flat_source .exists () else nested_source if nested_source .exists () else latest_source
274+ copy_mdx_with_link_rewrites (source , target_base / "config" / filename )
275+
276+
277+ def sync_code_reference_archive (source_root : Path , published_root : Path ) -> None :
278+ source_nav = source_root / "fern" / "versions" / "latest.yml"
279+ if not source_nav .exists ():
280+ return
281+ source_block = extract_navigation_section (source_nav , CODE_REFERENCE_SECTION_RE )
282+
283+ versions_dir = published_root / "fern" / "versions"
284+ for nav in sorted (path for path in versions_dir .glob ("*.yml" ) if path .name != "latest.yml" ):
285+ try :
286+ current_block = extract_navigation_section (nav , CODE_REFERENCE_SECTION_RE )
287+ except PublishedBranchError :
288+ continue
289+ page_root = code_reference_page_root (current_block )
290+ if page_root is None :
291+ continue
292+ sync_code_reference_pages (source_root , published_root , page_root )
293+ replace_navigation_section (
294+ nav , CODE_REFERENCE_SECTION_RE , rewrite_code_reference_block (source_block , page_root )
295+ )
296+
297+
171298def sync_source (args : argparse .Namespace ) -> int :
172299 source_root = Path (args .source_root )
173300 published_root = Path (args .published_root )
@@ -185,22 +312,14 @@ def sync_source(args: argparse.Namespace) -> int:
185312 merge_preserved_versions (
186313 source_root / "fern" / "versions" , published_root / "fern" / "versions" , preserved_versions
187314 )
315+ sync_code_reference_archive (source_root , published_root )
188316 restore_versions_block (published_root / "fern" / "docs.yml" , preserved_versions_block )
189317 validate_redirect_targets (published_root )
190318 return 0
191319
192320
193321def extract_devnotes_block (path : Path ) -> list [str ]:
194- lines = path .read_text ().splitlines (keepends = True )
195- start = next ((i for i , line in enumerate (lines ) if DEVNOTES_SECTION_RE .match (line )), - 1 )
196- if start == - 1 :
197- raise PublishedBranchError (f"Dev Notes section not found in { path } " )
198- end = start + 1
199- while end < len (lines ):
200- if lines [end ].startswith (" - " ) and lines [end ].strip ():
201- break
202- end += 1
203- return lines [start :end ]
322+ return extract_navigation_section (path , DEVNOTES_SECTION_RE )
204323
205324
206325def rewrite_devnotes_block (source_root : Path , published_root : Path , block : list [str ]) -> list [str ]:
@@ -227,17 +346,7 @@ def rewrite_devnotes_block(source_root: Path, published_root: Path, block: list[
227346
228347
229348def replace_devnotes_block (path : Path , block : list [str ]) -> None :
230- lines = path .read_text ().splitlines (keepends = True )
231- start = next ((i for i , line in enumerate (lines ) if DEVNOTES_SECTION_RE .match (line )), - 1 )
232- if start == - 1 :
233- raise PublishedBranchError (f"Dev Notes section not found in { path } " )
234- end = start + 1
235- while end < len (lines ):
236- if lines [end ].startswith (" - " ) and lines [end ].strip ():
237- break
238- end += 1
239- lines [start :end ] = block
240- path .write_text ("" .join (lines ))
349+ replace_navigation_section (path , DEVNOTES_SECTION_RE , block )
241350
242351
243352def patch_devnotes (args : argparse .Namespace ) -> int :
0 commit comments