-
-
Notifications
You must be signed in to change notification settings - Fork 266
Apply Brouter improvements (#12559) #12560
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
msynk
wants to merge
21
commits into
bitfoundation:develop
Choose a base branch
from
msynk:12559-brouter-improvements
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
5371612
apply Brouter improvements #12559
msynk 2bf38b5
apply type renames
msynk 62d8c17
make guards preventive
msynk ce28f77
add route discovery and prerender support
msynk 5f2aa76
add focus management
msynk eca2b61
improve scroll handling
msynk 9e73832
improve BrouterLink
msynk 243df1e
fix param
msynk f7a2e90
fix comment
msynk 209bd24
fix method
msynk 977f1ce
improve matching performance
msynk 920cc0a
improve route uniqueness check
msynk b599fb8
rename incorrect ones back
msynk 06eb3f4
rename types back
msynk a5252e2
fix global static constraint registry
msynk 791955d
add pending UI feature
msynk 70fd776
add navigation type to context
msynk c484772
resolve review comments
msynk 7a28e64
resolve review comments II
msynk 448ed58
apply further improvements
msynk 1038fe2
resolve review comments III
msynk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Text.Json; | ||
|
|
||
| namespace Bit.Brouter; | ||
|
|
||
| /// <summary> | ||
| /// The serialized form of a single loader result carried across the SSR/prerender -> interactive | ||
| /// boundary. The concrete runtime type name is stored alongside the JSON so the value can be | ||
| /// rehydrated into the exact type the loader produced (rather than a raw <see cref="JsonElement"/>), | ||
| /// which is what components consuming the cascading <c>RouteData</c> expect. | ||
| /// </summary> | ||
| internal sealed class PersistedLoaderState | ||
| { | ||
| /// <summary>Assembly-qualified name of the loaded value's runtime type. Null when the loader returned null.</summary> | ||
| public string? TypeName { get; set; } | ||
|
|
||
| /// <summary>The loaded value serialized as JSON. Null when the loader returned null.</summary> | ||
| public string? Json { get; set; } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Bridges route <see cref="Broute.Loader"/> results across the prerender -> interactive transition so a | ||
| /// loader that ran on the server isn't re-run (double-fetched) when the component becomes interactive. | ||
| /// Serialization is reflection/JSON based, hence trim/AOT-unsafe for arbitrary types; this is only reached | ||
| /// when the consumer opts in via <see cref="BrouterOptions.PersistLoaderState"/> and takes responsibility | ||
| /// for keeping their loader data types serializable and preserved. | ||
| /// </summary> | ||
| internal static class BroutePrerenderState | ||
| { | ||
| // Web defaults mirror the conventions Blazor itself uses for persisted component state and for | ||
| // JSON over the wire, so a single symmetric options instance is used for both directions. | ||
| private static readonly JsonSerializerOptions _options = new(JsonSerializerDefaults.Web); | ||
|
|
||
| /// <summary> | ||
| /// Builds the persistence key for a loader in the matched chain. It is derived purely from the URL | ||
| /// (path + query) and the node's position in the matched chain, both of which are identical on the | ||
| /// prerender and interactive passes for the same navigation, so keys line up across the boundary. | ||
| /// </summary> | ||
| internal static string MakeKey(string path, string query, int chainIndex) => | ||
| $"Bit.Brouter|{path}|{query}|{chainIndex}"; | ||
|
|
||
| /// <summary>Captures a loader result into its persistable form.</summary> | ||
| [RequiresUnreferencedCode("Serializes an arbitrary loader result via System.Text.Json reflection.")] | ||
| [RequiresDynamicCode("Serializes an arbitrary loader result via System.Text.Json reflection.")] | ||
| internal static PersistedLoaderState Capture(object? value) | ||
| { | ||
| if (value is null) return new PersistedLoaderState { TypeName = null, Json = null }; | ||
|
|
||
| var type = value.GetType(); | ||
| return new PersistedLoaderState | ||
| { | ||
| TypeName = type.AssemblyQualifiedName, | ||
| Json = JsonSerializer.Serialize(value, type, _options), | ||
| }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Rehydrates a previously-captured loader result. Returns <c>true</c> when a value (possibly null) | ||
| /// was restored and the loader should be skipped; <c>false</c> when restoration wasn't possible | ||
| /// (unknown type, malformed JSON) and the loader should run normally. | ||
| /// </summary> | ||
| [RequiresUnreferencedCode("Deserializes a loader result into its runtime type via System.Text.Json reflection.")] | ||
| [RequiresDynamicCode("Deserializes a loader result into its runtime type via System.Text.Json reflection.")] | ||
| internal static bool TryRestore(PersistedLoaderState? state, out object? value) | ||
| { | ||
| value = null; | ||
| if (state is null) return false; | ||
|
|
||
| // A persisted null result is still a decision the loader made: honor it and skip re-running. | ||
| if (string.IsNullOrEmpty(state.TypeName) || state.Json is null) return true; | ||
|
|
||
| try | ||
| { | ||
| // Type.GetType(throwOnError: false) suppresses TypeLoadException (returns null) but can still | ||
| // throw for a stale/unloadable persisted name - a malformed assembly-qualified string | ||
| // (ArgumentException), or a referenced assembly that can't be loaded here | ||
| // (FileLoadException / FileNotFoundException / BadImageFormatException). Treat all of those, | ||
| // like a missing type or malformed JSON, as "can't restore" and fall back to running the loader. | ||
| var type = Type.GetType(state.TypeName, throwOnError: false); | ||
| if (type is null) return false; // type not available here; fall back to running the loader | ||
|
|
||
| value = JsonSerializer.Deserialize(state.Json, type, _options); | ||
| return true; | ||
| } | ||
| catch (Exception ex) when (ex is JsonException | ||
| or ArgumentException | ||
| or System.IO.FileLoadException | ||
| or System.IO.FileNotFoundException | ||
| or BadImageFormatException | ||
| or TypeLoadException) | ||
| { | ||
| value = null; | ||
| return false; | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.