diff --git a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ClientScriptTransform.cs b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ClientScriptTransform.cs index 10a2f4d39..550edca32 100644 --- a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ClientScriptTransform.cs +++ b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ClientScriptTransform.cs @@ -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); } diff --git a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/RequestFormTransform.cs b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/RequestFormTransform.cs index 69a352133..2467ca784 100644 --- a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/RequestFormTransform.cs +++ b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/RequestFormTransform.cs @@ -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 .\n" + (keys.Count > 0 ? $" // Form keys found: {string.Join(", ", keys)}\n" : "") + " // For non-page classes, inject RequestShim via DI.\n"; diff --git a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ResponseRedirectTransform.cs b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ResponseRedirectTransform.cs index 2ce2bc954..f5bc31997 100644 --- a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ResponseRedirectTransform.cs +++ b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ResponseRedirectTransform.cs @@ -4,8 +4,9 @@ namespace BlazorWebFormsComponents.Cli.Transforms.CodeBehind; /// -/// 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. /// public class ResponseRedirectTransform : ICodeBehindTransform { @@ -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; diff --git a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ServerShimTransform.cs b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ServerShimTransform.cs index 6fe92d640..5dbd38396 100644 --- a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ServerShimTransform.cs +++ b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/ServerShimTransform.cs @@ -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" : ""); diff --git a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/SessionDetectTransform.cs b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/SessionDetectTransform.cs index af74542c3..786eece4f 100644 --- a/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/SessionDetectTransform.cs +++ b/src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/SessionDetectTransform.cs @@ -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" @@ -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"; @@ -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; }