Skip to content

Commit 7be71f4

Browse files
Replace hardcoded URL mappings with dynamic algorithmic approach
- Build URL-to-filepath mapping dynamically from flexdown_docs list - Eliminate 40+ hardcoded string replacements for maintainability - Use algorithmic conversion based on actual docs folder structure - Maintain compatibility with both StringVar and regular string handling - Handle index files correctly: both /folder/ and /folder/index/ map to index.md - All 245 documentation URLs continue to return 200 OK Addresses GitHub comment from Alek99 about avoiding hardcoded values in convert_url_path_to_github_path() function. Co-Authored-By: [email protected] <[email protected]>
1 parent 08c3121 commit 7be71f4

File tree

2 files changed

+108
-241
lines changed

2 files changed

+108
-241
lines changed

pcweb/templates/docpage/docpage.py

Lines changed: 59 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,54 @@ def link_pill(text: str, href: str) -> rx.Component:
274274
)
275275

276276

277+
def _build_url_to_filepath_mapping():
278+
"""Build dynamic mapping from browser URLs to filesystem paths.
279+
280+
Uses the same logic as the routing system to convert file paths to browser URLs,
281+
then creates the reverse mapping for URL-to-filepath conversion.
282+
"""
283+
import flexdown
284+
import reflex as rx
285+
286+
url_to_filepath = {}
287+
288+
flexdown_docs = [
289+
str(doc).replace("\\", "/") for doc in flexdown.utils.get_flexdown_files("docs/")
290+
]
291+
292+
for doc_path in flexdown_docs:
293+
browser_url = rx.utils.format.to_kebab_case(f"/{doc_path.replace('.md', '/')}")
294+
295+
if browser_url.endswith("/index/"):
296+
folder_url = browser_url[:-7] + "/"
297+
index_url = browser_url[:-1] # Remove trailing slash but keep /index
298+
299+
folder_key = folder_url.strip("/")
300+
index_key = index_url.strip("/")
301+
302+
if folder_key.startswith("docs/"):
303+
folder_key = folder_key[5:]
304+
if index_key.startswith("docs/"):
305+
index_key = index_key[5:]
306+
307+
url_to_filepath[folder_key] = doc_path
308+
url_to_filepath[index_key] = doc_path
309+
else:
310+
url_key = browser_url.strip("/")
311+
if url_key.startswith("docs/"):
312+
url_key = url_key[5:]
313+
314+
url_to_filepath[url_key] = doc_path
315+
316+
return url_to_filepath
317+
318+
_URL_TO_FILEPATH_MAP = _build_url_to_filepath_mapping()
319+
277320
def convert_url_path_to_github_path(url_path) -> str:
278321
"""Convert a URL path to the corresponding GitHub filesystem path.
279322
280-
Preserves the exact file structure as it exists in the docs/ folder,
281-
handling mixed naming conventions (some folders use hyphens, others underscores).
323+
Uses dynamic mapping built from the actual docs folder structure,
324+
eliminating hardcoded path conversions while preserving exact file paths.
282325
283326
Args:
284327
url_path: URL path like "/docs/getting-started/introduction/" (can be str or rx.Var[str])
@@ -292,175 +335,31 @@ def convert_url_path_to_github_path(url_path) -> str:
292335
path_no_slashes = string_replace_operation(url_path, r"^/+|/+$", "")
293336
path_clean = string_replace_operation(path_no_slashes, r"/+", "/")
294337

295-
path_converted = string_replace_operation(path_clean, "getting-started", "getting_started")
296-
path_converted = string_replace_operation(path_converted, "client-storage", "client_storage")
297-
path_converted = string_replace_operation(path_converted, "utility-methods", "utility_methods")
298-
path_converted = string_replace_operation(path_converted, "advanced-onboarding", "advanced_onboarding")
299-
path_converted = string_replace_operation(path_converted, "state-structure", "state_structure")
300-
path_converted = string_replace_operation(path_converted, "ai-builder", "ai_builder")
301-
302-
path_converted = string_replace_operation(path_converted, "chatapp-tutorial", "chatapp_tutorial")
303-
path_converted = string_replace_operation(path_converted, "dashboard-tutorial", "dashboard_tutorial")
304-
305-
path_converted = string_replace_operation(path_converted, "login-form", "login_form")
306-
path_converted = string_replace_operation(path_converted, "signup-form", "signup_form")
307-
path_converted = string_replace_operation(path_converted, "multi-column-row", "multi_column_row")
308-
path_converted = string_replace_operation(path_converted, "top-banner", "top_banner")
309-
path_converted = string_replace_operation(path_converted, "dark-mode-toggle", "dark_mode_toggle")
310-
path_converted = string_replace_operation(path_converted, "pricing-cards", "pricing_cards")
311-
path_converted = string_replace_operation(path_converted, "speed-dial", "speed_dial")
312-
313-
path_converted = string_replace_operation(path_converted, "deploy-app", "deploy_app")
314-
path_converted = string_replace_operation(path_converted, "download-app", "download_app")
315-
path_converted = string_replace_operation(path_converted, "environment-variables", "environment_variables")
316-
path_converted = string_replace_operation(path_converted, "image-as-prompt", "image_as_prompt")
317-
path_converted = string_replace_operation(path_converted, "installing-external-packages", "installing_external_packages")
318-
path_converted = string_replace_operation(path_converted, "frequently-asked-questions", "frequently_asked_questions")
319-
path_converted = string_replace_operation(path_converted, "what-is-reflex-build", "what_is_reflex_build")
320-
path_converted = string_replace_operation(path_converted, "breaking-up-complex-prompts", "breaking_up_complex_prompts")
321-
path_converted = string_replace_operation(path_converted, "fixing-errors", "fixing_errors")
338+
path_without_docs = string_replace_operation(path_clean, "^docs/", "")
322339

323-
path_converted = string_replace_operation(path_converted, "enterprise/ag-grid", "enterprise/ag_grid")
324-
path_converted = string_replace_operation(path_converted, "ag-chart", "ag_chart")
340+
result_path = path_without_docs
341+
for browser_url, file_path in _URL_TO_FILEPATH_MAP.items():
342+
if browser_url != file_path.replace('.md', ''):
343+
result_path = string_replace_operation(result_path, browser_url, file_path.replace('.md', ''))
325344

326-
path_converted = string_replace_operation(path_converted, "page-load-events", "page_load_events")
327-
path_converted = string_replace_operation(path_converted, "background-events", "background_events")
328-
path_converted = string_replace_operation(path_converted, "yield-events", "yield_events")
329-
path_converted = string_replace_operation(path_converted, "event-arguments", "event_arguments")
330-
path_converted = string_replace_operation(path_converted, "event-actions", "event_actions")
331-
path_converted = string_replace_operation(path_converted, "chaining-events", "chaining_events")
332-
path_converted = string_replace_operation(path_converted, "special-events", "special_events")
333-
path_converted = string_replace_operation(path_converted, "decentralized-event-handlers", "decentralized_event_handlers")
334-
path_converted = string_replace_operation(path_converted, "events-overview", "events_overview")
335-
path_converted = string_replace_operation(path_converted, "authentication-overview", "authentication_overview")
336-
path_converted = string_replace_operation(path_converted, "dynamic-routing", "dynamic_routing")
337-
path_converted = string_replace_operation(path_converted, "code-structure", "code_structure")
338-
path_converted = string_replace_operation(path_converted, "component-state", "component_state")
339-
340-
path_converted = string_replace_operation(path_converted, "segmented-control", "segmented_control")
341-
path_converted = string_replace_operation(path_converted, "auto-scroll", "auto_scroll")
342-
path_converted = string_replace_operation(path_converted, "code-block", "code_block")
343-
path_converted = string_replace_operation(path_converted, "data-list", "data_list")
344-
path_converted = string_replace_operation(path_converted, "scroll-area", "scroll_area")
345-
path_converted = string_replace_operation(path_converted, "html-embed", "html_embed")
346-
path_converted = string_replace_operation(path_converted, "aspect-ratio", "aspect_ratio")
347-
path_converted = string_replace_operation(path_converted, "data-table", "data_table")
348-
path_converted = string_replace_operation(path_converted, "data-editor", "data_editor")
349-
path_converted = string_replace_operation(path_converted, "hover-card", "hover_card")
350-
path_converted = string_replace_operation(path_converted, "alert-dialog", "alert_dialog")
351-
path_converted = string_replace_operation(path_converted, "context-menu", "context_menu")
352-
path_converted = string_replace_operation(path_converted, "dropdown-menu", "dropdown_menu")
353-
path_converted = string_replace_operation(path_converted, "radio-group", "radio_group")
354-
path_converted = string_replace_operation(path_converted, "text-area", "text_area")
355-
356-
path_converted = string_replace_operation(path_converted, "custom-vars", "custom_vars")
357-
path_converted = string_replace_operation(path_converted, "computed-vars", "computed_vars")
358-
path_converted = string_replace_operation(path_converted, "base-vars", "base_vars")
359-
360-
path_converted = string_replace_operation(path_converted, "config-file", "config_file")
361-
362-
path_converted = string_replace_operation(path_converted, "upload-and-download-files", "upload_and_download_files")
363-
path_converted = string_replace_operation(path_converted, "rendering-iterables", "rendering_iterables")
364-
path_converted = string_replace_operation(path_converted, "html-to-reflex", "html_to_reflex")
365-
path_converted = string_replace_operation(path_converted, "conditional-rendering", "conditional_rendering")
366-
path_converted = string_replace_operation(path_converted, "other-methods", "other_methods")
367-
path_converted = string_replace_operation(path_converted, "lifespan-tasks", "lifespan_tasks")
368-
path_converted = string_replace_operation(path_converted, "exception-handlers", "exception_handlers")
369-
path_converted = string_replace_operation(path_converted, "router-attributes", "router_attributes")
370-
path_converted = string_replace_operation(path_converted, "event-triggers", "event_triggers")
371-
path_converted = string_replace_operation(path_converted, "browser-storage", "browser_storage")
372-
path_converted = string_replace_operation(path_converted, "var-system", "var_system")
373-
path_converted = string_replace_operation(path_converted, "browser-javascript", "browser_javascript")
374-
375-
return f"{path_converted}.md"
345+
final_path = result_path
346+
if not result_path._js_expr.endswith(".md"):
347+
final_path = result_path + ".md"
348+
return final_path
376349
else:
377350
path = str(url_path).strip("/")
378351
while "//" in path:
379352
path = path.replace("//", "/")
380353

381-
path = path.replace("getting-started", "getting_started")
382-
path = path.replace("client-storage", "client_storage")
383-
path = path.replace("utility-methods", "utility_methods")
384-
path = path.replace("advanced-onboarding", "advanced_onboarding")
385-
path = path.replace("state-structure", "state_structure")
386-
path = path.replace("ai-builder", "ai_builder")
387-
388-
path = path.replace("chatapp-tutorial", "chatapp_tutorial")
389-
path = path.replace("dashboard-tutorial", "dashboard_tutorial")
390-
391-
path = path.replace("login-form", "login_form")
392-
path = path.replace("signup-form", "signup_form")
393-
path = path.replace("multi-column-row", "multi_column_row")
394-
path = path.replace("top-banner", "top_banner")
395-
path = path.replace("dark-mode-toggle", "dark_mode_toggle")
396-
path = path.replace("pricing-cards", "pricing_cards")
397-
path = path.replace("speed-dial", "speed_dial")
398-
399-
path = path.replace("deploy-app", "deploy_app")
400-
path = path.replace("download-app", "download_app")
401-
path = path.replace("environment-variables", "environment_variables")
402-
path = path.replace("image-as-prompt", "image_as_prompt")
403-
path = path.replace("installing-external-packages", "installing_external_packages")
404-
path = path.replace("frequently-asked-questions", "frequently_asked_questions")
405-
path = path.replace("what-is-reflex-build", "what_is_reflex_build")
406-
path = path.replace("breaking-up-complex-prompts", "breaking_up_complex_prompts")
407-
path = path.replace("fixing-errors", "fixing_errors")
408-
409-
path = path.replace("enterprise/ag-grid", "enterprise/ag_grid")
410-
path = path.replace("ag-chart", "ag_chart")
411-
412-
path = path.replace("page-load-events", "page_load_events")
413-
path = path.replace("background-events", "background_events")
414-
path = path.replace("yield-events", "yield_events")
415-
path = path.replace("event-arguments", "event_arguments")
416-
path = path.replace("event-actions", "event_actions")
417-
path = path.replace("chaining-events", "chaining_events")
418-
path = path.replace("special-events", "special_events")
419-
path = path.replace("decentralized-event-handlers", "decentralized_event_handlers")
420-
path = path.replace("events-overview", "events_overview")
421-
path = path.replace("authentication-overview", "authentication_overview")
422-
path = path.replace("dynamic-routing", "dynamic_routing")
423-
path = path.replace("code-structure", "code_structure")
424-
path = path.replace("component-state", "component_state")
425-
426-
path = path.replace("segmented-control", "segmented_control")
427-
path = path.replace("auto-scroll", "auto_scroll")
428-
path = path.replace("code-block", "code_block")
429-
path = path.replace("data-list", "data_list")
430-
path = path.replace("scroll-area", "scroll_area")
431-
path = path.replace("html-embed", "html_embed")
432-
path = path.replace("aspect-ratio", "aspect_ratio")
433-
path = path.replace("data-table", "data_table")
434-
path = path.replace("data-editor", "data_editor")
435-
path = path.replace("hover-card", "hover_card")
436-
path = path.replace("alert-dialog", "alert_dialog")
437-
path = path.replace("context-menu", "context_menu")
438-
path = path.replace("dropdown-menu", "dropdown_menu")
439-
path = path.replace("radio-group", "radio_group")
440-
path = path.replace("text-area", "text_area")
441-
442-
path = path.replace("custom-vars", "custom_vars")
443-
path = path.replace("computed-vars", "computed_vars")
444-
path = path.replace("base-vars", "base_vars")
445-
446-
path = path.replace("config-file", "config_file")
354+
if path.startswith("docs/"):
355+
path = path[5:]
447356

448-
path = path.replace("upload-and-download-files", "upload_and_download_files")
449-
path = path.replace("rendering-iterables", "rendering_iterables")
450-
path = path.replace("html-to-reflex", "html_to_reflex")
451-
path = path.replace("conditional-rendering", "conditional_rendering")
452-
path = path.replace("other-methods", "other_methods")
453-
path = path.replace("lifespan-tasks", "lifespan_tasks")
454-
path = path.replace("exception-handlers", "exception_handlers")
455-
path = path.replace("router-attributes", "router_attributes")
456-
path = path.replace("event-triggers", "event_triggers")
457-
path = path.replace("browser-storage", "browser_storage")
458-
path = path.replace("var-system", "var_system")
459-
path = path.replace("browser-javascript", "browser_javascript")
357+
if path in _URL_TO_FILEPATH_MAP:
358+
return _URL_TO_FILEPATH_MAP[path]
460359

461360
if not path.endswith(".md"):
462361
path += ".md"
463-
return path
362+
return f"docs/{path}"
464363

465364

466365
@rx.memo

0 commit comments

Comments
 (0)