From e69b689540d093c79d690c2bca04ea9c32ea414c Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Thu, 18 Sep 2025 01:50:57 +0200 Subject: [PATCH] Add JavaScript-based redirects to supplement Read the Docs redirects Read the Docs redirects don't reliably apply when there is a large amount of redirects defined. While this JavaScript-based solution is not as seamless, it has no limit on how many redirects can be defined. --- _static/js/custom.js | 32 ++++++++++++++++++++- {_tools/redirects => _static}/redirects.csv | 28 +++++++++--------- _tools/redirects/README.md | 21 +++++++++----- _tools/redirects/create_redirects.py | 2 +- 4 files changed, 60 insertions(+), 23 deletions(-) rename {_tools/redirects => _static}/redirects.csv (97%) diff --git a/_static/js/custom.js b/_static/js/custom.js index d028dd0f329..860deb940ad 100644 --- a/_static/js/custom.js +++ b/_static/js/custom.js @@ -1,4 +1,3 @@ - // Handle page scroll and adjust sidebar accordingly. // Each page has two scrolls: the main scroll, which is moving the content of the page; @@ -298,6 +297,37 @@ const registerGiscus = function () { }; $(document).ready(() => { + const httpResponseStatus = window.performance.getEntries()[0].responseStatus; + if (httpResponseStatus === 404) { + // Check for redirects if on a currently invalid page. + // This is done in JavaScript, as we exceed Read the Docs' limit for the amount of redirects configurable. + // When testing this feature on a local web server, replace the URL below with just `/_static/redirects.csv`. + fetch("/en/latest/_static/redirects.csv") + .then(response => response.text()) + .then(csvText => { + const lines = csvText.trim().split('\n'); + for (const line of lines) { + if (!line.trim()) { + continue; + } + const [from, to] = line.split(',').map(s => s.trim()); + if (from && to) { + if (window.location.pathname.endsWith(from)) { + if (to.startsWith('https://')) { + window.location.replace(to); + } else { + const newUrl = window.location.href.replace(window.location.pathname, to); + window.location.replace(newUrl); + } + } + } + } + }) + .catch(err => { + console.error("Couldn't fetch redirects list:", err); + }); + } + // Remove the search match highlights from the page, and adjust the URL in the // navigation history. const url = new URL(location.href); diff --git a/_tools/redirects/redirects.csv b/_static/redirects.csv similarity index 97% rename from _tools/redirects/redirects.csv rename to _static/redirects.csv index 14f19a965c9..664ab8ce08b 100644 --- a/_tools/redirects/redirects.csv +++ b/_static/redirects.csv @@ -431,20 +431,20 @@ source,destination /contributing/workflow/testing_pull_requests.html,https://contributing.godotengine.org/en/latest/organization/pull_requests/testing.html /contributing/development/core_and_modules/2d_coordinate_systems.html,/engine_details/architecture/2d_coordinate_systems.html /contributing/development/core_and_modules/common_engine_methods_and_macros.html,/engine_details/architecture/common_engine_methods_and_macros.html -/contributing/development/core_and_modules/core_types.html,//architecture/core_types.html -/contributing/development/core_and_modules/custom_audiostreams.html,//architecture/custom_audiostreams.html -/contributing/development/core_and_modules/custom_godot_servers.html,//architecture/custom_godot_servers.html -/contributing/development/core_and_modules/custom_modules_in_cpp.html,//architecture/custom_modules_in_cpp.html -/contributing/development/core_and_modules/custom_platform_ports.html,//architecture/custom_platform_ports.html -/contributing/development/core_and_modules/custom_resource_format_loaders.html,//architecture/custom_resource_format_loaders.html -/contributing/development/core_and_modules/godot_architecture_diagram.html,//architecture/godot_architecture_diagram.html -/contributing/development/core_and_modules/index.html,//architecture/index.html -/contributing/development/core_and_modules/inheritance_class_tree.html,//architecture/inheritance_class_tree.html -/contributing/development/core_and_modules/internal_rendering_architecture.html,//architecture/internal_rendering_architecture.html -/contributing/development/core_and_modules/object_class.html,//architecture/object_class.html -/contributing/development/core_and_modules/scripting_development.html,//architecture/scripting_development.html -/contributing/development/core_and_modules/unit_testing.html,//architecture/unit_testing.html -/contributing/development/core_and_modules/variant_class.html,//architecture/variant_class.html +/contributing/development/core_and_modules/core_types.html,/engine_details/architecture/core_types.html +/contributing/development/core_and_modules/custom_audiostreams.html,/engine_details/architecture/custom_audiostreams.html +/contributing/development/core_and_modules/custom_godot_servers.html,/engine_details/architecture/custom_godot_servers.html +/contributing/development/core_and_modules/custom_modules_in_cpp.html,/engine_details/architecture/custom_modules_in_cpp.html +/contributing/development/core_and_modules/custom_platform_ports.html,/engine_details/architecture/custom_platform_ports.html +/contributing/development/core_and_modules/custom_resource_format_loaders.html,/engine_details/architecture/custom_resource_format_loaders.html +/contributing/development/core_and_modules/godot_architecture_diagram.html,/engine_details/architecture/godot_architecture_diagram.html +/contributing/development/core_and_modules/index.html,/engine_details/architecture/index.html +/contributing/development/core_and_modules/inheritance_class_tree.html,/engine_details/architecture/inheritance_class_tree.html +/contributing/development/core_and_modules/internal_rendering_architecture.html,/engine_details/architecture/internal_rendering_architecture.html +/contributing/development/core_and_modules/object_class.html,/engine_details/architecture/object_class.html +/contributing/development/core_and_modules/scripting_development.html,/engine_details/architecture/scripting_development.html +/contributing/development/core_and_modules/unit_testing.html,/engine_details/architecture/unit_testing.html +/contributing/development/core_and_modules/variant_class.html,/engine_details/architecture/variant_class.html /contributing/documentation/class_reference_primer.html,engine_details/class_reference/index.html /contributing/development/compiling/compiling_for_android.html,/engine_details/development/compiling/compiling_for_android.html, /contributing/development/compiling/compiling_for_ios.html,/engine_details/development/compiling/compiling_for_ios.html diff --git a/_tools/redirects/README.md b/_tools/redirects/README.md index af31b2f2a3f..c991b956426 100644 --- a/_tools/redirects/README.md +++ b/_tools/redirects/README.md @@ -21,19 +21,26 @@ To interact with the Read the Docs API, a valid API key must be set as ## Usage -Let's say we recently renamed some files in the Git branch `3.4` (compared to the `stable` branch), and now we want to create redirects for these. -For this, we would (after setting up the API token and requirements, see Setup above): +Let's say we recently renamed some files in the Git branch `3.4` (compared to the `stable` branch), +and now we want to create redirects for these. For this, we would (after setting up the API token +and requirements, see Setup above): -> python convert_git_renames_to_csv.py stable 3.4 +``` +python convert_git_renames_to_csv.py stable 3.4 +``` This should output a list of the redirects to create. Let's append these to the redirects file: -> python convert_git_renames_to_csv.py stable 3.4 >> redirects.csv +``` +python convert_git_renames_to_csv.py stable 3.4 >> ../../_tools/redirects.csv +``` -After this, redirects for renamed files should have been appended to `redirects.csv`. You may want to double-check that! -Now let's submit these to ReadTheDocs and create redirects there: +After this, redirects for renamed files should have been appended to `../../_tools/redirects.csv`. +You may want to double-check that! Now let's submit these to ReadTheDocs and create redirects there: -> python create_redirects.py +``` +python create_redirects.py +``` And that should be it! diff --git a/_tools/redirects/create_redirects.py b/_tools/redirects/create_redirects.py index baef253c4e6..67c5fd9563b 100644 --- a/_tools/redirects/create_redirects.py +++ b/_tools/redirects/create_redirects.py @@ -50,7 +50,7 @@ def parse_command_line_args(): "-f", "--file", metavar="file", - default="redirects.csv", + default="../../_static/redirects.csv", type=str, help="Path to a CSV file used to keep a list of redirects, containing two columns: source and destination.", )