Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ public string Apply(string content, FileMetadata metadata)
!content.Contains("// TODO(bwfc-general): ClientScript calls preserved"))
{
var shimComment = hasScriptManagerCall
? "\n // TODO(bwfc-general): ClientScript calls preserved — uses ClientScriptShim + ScriptManagerShim. Inject @inject ClientScriptShim ClientScript and @inject ScriptManagerShim ScriptManager if not using BaseWebFormsComponent.\n"
: "\n // TODO(bwfc-general): ClientScript calls preserved — uses ClientScriptShim. Inject @inject ClientScriptShim ClientScript if not using BaseWebFormsComponent.\n";
? "\n // TODO(bwfc-general): ClientScript calls preserved — works via WebFormsPageBase (no injection needed). ScriptManagerShim may need @inject ScriptManagerShim ScriptManager for non-page classes.\n"
: "\n // TODO(bwfc-general): ClientScript calls preserved — works via WebFormsPageBase (no injection needed).\n";
content = ClassOpenRegex.Replace(content, "$1" + shimComment, 1);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public string Apply(string content, FileMetadata metadata)
if (!content.Contains(GuidanceMarker) && ClassOpenRegex.IsMatch(content))
{
var guidanceBlock = "\n " + GuidanceMarker + "\n"
+ " // TODO(bwfc-form): Request.Form calls work via FormShim on WebFormsPageBase.\n"
+ " // TODO(bwfc-form): Request.Form calls work automatically via RequestShim on WebFormsPageBase.\n"
+ " // For interactive mode, wrap your form in <WebFormsForm OnSubmit=\"SetRequestFormData\">.\n"
+ (keys.Count > 0 ? $" // Form keys found: {string.Join(", ", keys)}\n" : "")
+ " // For non-page classes, inject RequestShim via DI.\n";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
namespace BlazorWebFormsComponents.Cli.Transforms.CodeBehind;

/// <summary>
/// Converts Response.Redirect() calls to NavigationManager.NavigateTo() and injects
/// [Inject] NavigationManager property into the class.
/// Detects Response.Redirect() calls and strips Page./this. prefixes so they compile
/// against ResponseShim on WebFormsPageBase. ResponseShim handles ~/ prefix stripping
/// and .aspx extension removal automatically — no NavigationManager injection needed.
/// </summary>
public class ResponseRedirectTransform : ICodeBehindTransform
{
Expand All @@ -32,56 +33,41 @@ public class ResponseRedirectTransform : ICodeBehindTransform
@"Response\.Redirect\(\s*([^)]+)\s*\)",
RegexOptions.Compiled);

// For injecting [Inject] NavigationManager
// Strips "Page." or "this." prefix before Response.Redirect
private static readonly Regex PageOrThisPrefixRegex = new(
@"(?:Page\.|this\.)(?=Response\.Redirect\s*\()",
RegexOptions.Compiled);

private static readonly Regex ClassOpenRegex = new(
@"((?:public|internal|private)\s+(?:partial\s+)?class\s+\w+[^{]*\{)",
RegexOptions.Compiled);

private const string GuidanceMarker = "// --- Response.Redirect Migration ---";

public string Apply(string content, FileMetadata metadata)
{
var hasRedirectConversion = false;

// Pattern 1: literal URL with endResponse bool
if (RedirectLitBoolRegex.IsMatch(content))
{
content = RedirectLitBoolRegex.Replace(content, m =>
{
var url = Regex.Replace(m.Groups[1].Value, @"^~/", "/");
return $"NavigationManager.NavigateTo(\"{url}\")";
});
hasRedirectConversion = true;
}
// Count redirect calls for guidance (using existing detection regexes)
var hasRedirect = RedirectLitBoolRegex.IsMatch(content)
|| RedirectLitRegex.IsMatch(content)
|| RedirectExprBoolRegex.IsMatch(content)
|| RedirectExprRegex.IsMatch(content);

// Pattern 2: simple literal URL
if (RedirectLitRegex.IsMatch(content))
{
content = RedirectLitRegex.Replace(content, m =>
{
var url = Regex.Replace(m.Groups[1].Value, @"^~/", "/");
return $"NavigationManager.NavigateTo(\"{url}\")";
});
hasRedirectConversion = true;
}
if (!hasRedirect) return content;

// Pattern 3: expression with endResponse bool
if (RedirectExprBoolRegex.IsMatch(content))
// Strip Page.Response.Redirect → Response.Redirect and this.Response.Redirect → Response.Redirect
if (PageOrThisPrefixRegex.IsMatch(content))
{
content = RedirectExprBoolRegex.Replace(content, "NavigationManager.NavigateTo($1) /* TODO(bwfc-navigation): Verify navigation target */");
hasRedirectConversion = true;
content = PageOrThisPrefixRegex.Replace(content, "");
}

// Pattern 4: remaining expression URLs
if (RedirectExprRegex.IsMatch(content))
// Emit guidance (idempotent)
if (!content.Contains(GuidanceMarker) && ClassOpenRegex.IsMatch(content))
{
content = RedirectExprRegex.Replace(content, "NavigationManager.NavigateTo($1) /* TODO(bwfc-navigation): Verify navigation target */");
hasRedirectConversion = true;
}
var guidanceBlock = "\n " + GuidanceMarker + "\n"
+ " // TODO(bwfc-navigation): Response.Redirect() works via ResponseShim on WebFormsPageBase. Handles ~/ and .aspx automatically.\n"
+ " // For non-page classes, inject ResponseShim via DI.\n";

// Inject [Inject] NavigationManager if conversions were made
if (hasRedirectConversion && ClassOpenRegex.IsMatch(content))
{
var injectLine = "\n [Inject] private NavigationManager NavigationManager { get; set; } // TODO(bwfc-navigation): Add @using Microsoft.AspNetCore.Components to _Imports.razor if needed\n";
content = ClassOpenRegex.Replace(content, "$1" + injectLine, 1);
content = ClassOpenRegex.Replace(content, "$1" + guidanceBlock, 1);
}

return content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public string Apply(string content, FileMetadata metadata)
}

var guidanceBlock = "\n " + GuidanceMarker + "\n"
+ " // TODO(bwfc-server): Server.* calls work via ServerShim on WebFormsPageBase.\n"
+ " // TODO(bwfc-server): Server.* calls work automatically via ServerShim on WebFormsPageBase.\n"
+ $" // Methods found: {string.Join(", ", methods)}\n"
+ " // For non-page classes, inject ServerShim via DI.\n"
+ (hasMapPath ? " // MapPath(\"~/path\") maps to IWebHostEnvironment.WebRootPath.\n" : "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public string Apply(string content, FileMetadata metadata)
}

var sessionBlock = SessionGuidanceMarker + "\n"
+ "// TODO(bwfc-session-state): SessionShim auto-wired via [Inject] — Session[\"key\"] calls compile against the shim's indexer.\n"
+ "// TODO(bwfc-session-state): Session[\"key\"] calls work automatically via SessionShim on WebFormsPageBase.\n"
+ (sessionKeys.Count > 0 ? $"// Session keys found: {string.Join(", ", sessionKeys)}\n" : "")
+ "// Options for long-term replacement:\n"
+ "// (1) ProtectedSessionStorage (Blazor Server) — persists across circuits\n"
Expand Down Expand Up @@ -91,7 +91,7 @@ public string Apply(string content, FileMetadata metadata)
}

var cacheBlock = CacheGuidanceMarker + "\n"
+ "// TODO(bwfc-session-state): CacheShim auto-wired via [Inject] — Cache[\"key\"] calls compile against the shim's indexer.\n"
+ "// TODO(bwfc-session-state): Cache[\"key\"] calls work automatically via CacheShim on WebFormsPageBase.\n"
+ (cacheKeys.Count > 0 ? $"// Cache keys found: {string.Join(", ", cacheKeys)}\n" : "")
+ "// CacheShim wraps IMemoryCache — items are per-server, not distributed.\n"
+ "// For distributed caching, consider IDistributedCache.\n\n";
Expand Down Expand Up @@ -122,27 +122,7 @@ public string Apply(string content, FileMetadata metadata)
}
}

// Inject [Inject] properties after class opening brace (idempotent)
if (ClassOpenRegex.IsMatch(content))
{
var injectLines = "";

if (hasSession && !content.Contains("[Inject] private SessionShim Session"))
{
injectLines += "\n [Inject] private SessionShim Session { get; set; }";
}

if (hasCache && !content.Contains("[Inject] private CacheShim Cache"))
{
injectLines += "\n [Inject] private CacheShim Cache { get; set; }";
}

if (!string.IsNullOrEmpty(injectLines))
{
injectLines += "\n";
content = ClassOpenRegex.Replace(content, "$1" + injectLines, 1);
}
}
// Session and Cache are provided by WebFormsPageBase — no [Inject] needed.

return content;
}
Expand Down
Loading