Skip to content

Commit 750dab3

Browse files
csharpfritzCopilot
andcommitted
feat(cli): add ServerCodeBlockTransform and TemplateFieldChildComponentsTransform
- ServerCodeBlockTransform (Order 510): converts <% if/else/foreach %> statement blocks to Razor @if/@foreach syntax; sanitizes raw <% %> inside @* *@ comments - TemplateFieldChildComponentsTransform (Order 620): wraps ItemStyle/HeaderStyle/ FooterStyle inside TemplateField with <ChildComponents> wrapper for BWFC compat - Both registered in Program.cs DI and TestHelpers.CreateDefaultPipeline() - 46 new tests (39 transform + 7 integration), 545 total CLI tests passing Closes Run 33 gaps #3 and #5. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6c4fc72 commit 750dab3

32 files changed

Lines changed: 3198 additions & 39 deletions

migration-toolkit/scripts/Convert-EdmxToEfCore.ps1

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
Write-Warning @"
2-
╔══════════════════════════════════════════════════════════════╗
3-
║ DEPRECATED: This script is deprecated. ║
4-
║ Use the CLI tool instead: ║
5-
║ webforms-to-blazor edmx convert --input <src> ║
6-
║ --output <out> ║
7-
║ See: docs/cli/index.md ║
8-
╚══════════════════════════════════════════════════════════════╝
9-
"@
10-
111
<#
122
.SYNOPSIS
133
Converts an Entity Framework 6 EDMX file to EF Core entity classes and DbContext.
@@ -40,6 +30,16 @@ param(
4030
[string]$Namespace
4131
)
4232

33+
Write-Warning @"
34+
╔══════════════════════════════════════════════════════════════╗
35+
║ DEPRECATED: This script is deprecated. ║
36+
║ Use the CLI tool instead: ║
37+
║ webforms-to-blazor edmx convert --input <src> ║
38+
║ --output <out> ║
39+
║ See: docs/cli/index.md ║
40+
╚══════════════════════════════════════════════════════════════╝
41+
"@
42+
4343
Set-StrictMode -Version Latest
4444
$ErrorActionPreference = 'Stop'
4545

migration-toolkit/scripts/Migrate-NugetStaticAssets.ps1

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
Write-Warning @"
2-
╔══════════════════════════════════════════════════════════════╗
3-
║ DEPRECATED: This script is deprecated. ║
4-
║ Use the CLI tool instead: ║
5-
║ webforms-to-blazor assets extract --input <src> ║
6-
║ --output <out> ║
7-
║ See: docs/cli/index.md ║
8-
╚══════════════════════════════════════════════════════════════╝
9-
"@
10-
111
<#
122
.SYNOPSIS
133
Extracts static assets (CSS, JS, fonts, images) from NuGet packages to wwwroot/lib/.
@@ -59,6 +49,16 @@ param(
5949
[switch]$ManifestOnly
6050
)
6151

52+
Write-Warning @"
53+
╔══════════════════════════════════════════════════════════════╗
54+
║ DEPRECATED: This script is deprecated. ║
55+
║ Use the CLI tool instead: ║
56+
║ webforms-to-blazor assets extract --input <src> ║
57+
║ --output <out> ║
58+
║ See: docs/cli/index.md ║
59+
╚══════════════════════════════════════════════════════════════╝
60+
"@
61+
6262
Set-StrictMode -Version Latest
6363
$ErrorActionPreference = 'Stop'
6464

migration-toolkit/scripts/bwfc-migrate.ps1

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
Write-Warning @"
2-
╔══════════════════════════════════════════════════════════════╗
3-
║ DEPRECATED: This script is deprecated. ║
4-
║ Use the CLI tool instead: ║
5-
║ webforms-to-blazor migrate --input <src> --output <out> ║
6-
║ See: docs/cli/index.md ║
7-
╚══════════════════════════════════════════════════════════════╝
8-
"@
9-
101
<#
112
.SYNOPSIS
123
Performs mechanical regex-based transforms on ASP.NET Web Forms files to produce Blazor-ready output.
@@ -88,6 +79,15 @@ param(
8879
[switch]$Prescan
8980
)
9081

82+
Write-Warning @"
83+
╔══════════════════════════════════════════════════════════════╗
84+
║ DEPRECATED: This script is deprecated. ║
85+
║ Use the CLI tool instead: ║
86+
║ webforms-to-blazor migrate --input <src> --output <out> ║
87+
║ See: docs/cli/index.md ║
88+
╚══════════════════════════════════════════════════════════════╝
89+
"@
90+
9191
Set-StrictMode -Version Latest
9292
$ErrorActionPreference = 'Stop'
9393

migration-toolkit/scripts/bwfc-scan.ps1

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
Write-Warning @"
2-
╔══════════════════════════════════════════════════════════════╗
3-
║ DEPRECATED: This script is deprecated. ║
4-
║ Use the CLI tool instead: ║
5-
║ webforms-to-blazor scan --input <src> --output <out> ║
6-
║ See: docs/cli/index.md ║
7-
╚══════════════════════════════════════════════════════════════╝
8-
"@
9-
101
<#
112
.SYNOPSIS
123
Scans a Web Forms project for migration readiness to BlazorWebFormsComponents.
@@ -62,6 +53,15 @@ param(
6253
[string]$OutputFile
6354
)
6455

56+
Write-Warning @"
57+
╔══════════════════════════════════════════════════════════════╗
58+
║ DEPRECATED: This script is deprecated. ║
59+
║ Use the CLI tool instead: ║
60+
║ webforms-to-blazor scan --input <src> --output <out> ║
61+
║ See: docs/cli/index.md ║
62+
╚══════════════════════════════════════════════════════════════╝
63+
"@
64+
6565
Set-StrictMode -Version Latest
6666
$ErrorActionPreference = "Stop"
6767

src/BlazorWebFormsComponents.Cli/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ private static ServiceProvider BuildServiceProvider()
4949
services.AddSingleton<IMarkupTransform, ContentWrapperTransform>();
5050
services.AddSingleton<IMarkupTransform, FormWrapperTransform>();
5151
services.AddSingleton<IMarkupTransform, ExpressionTransform>();
52+
services.AddSingleton<IMarkupTransform, ServerCodeBlockTransform>();
5253
services.AddSingleton<IMarkupTransform, LoginViewTransform>();
5354
services.AddSingleton<IMarkupTransform, SelectMethodTransform>();
5455
services.AddSingleton<IMarkupTransform, AjaxToolkitPrefixTransform>();
5556
services.AddSingleton<IMarkupTransform, AspPrefixTransform>();
5657
services.AddSingleton<IMarkupTransform, ValidatorGenericTypeTransform>();
58+
services.AddSingleton<IMarkupTransform, TemplateFieldChildComponentsTransform>();
5759
services.AddSingleton<IMarkupTransform, AttributeStripTransform>();
5860
services.AddSingleton<IMarkupTransform, EventWiringTransform>();
5961
services.AddSingleton<IMarkupTransform, ComponentRefMarkupTransform>();
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System.Text.RegularExpressions;
2+
using BlazorWebFormsComponents.Cli.Pipeline;
3+
4+
namespace BlazorWebFormsComponents.Cli.Transforms.Markup;
5+
6+
/// <summary>
7+
/// Converts ASP.NET Web Forms server-side code blocks to Razor control-flow syntax.
8+
/// Handles if/else/foreach/for/while statement blocks and bare code blocks.
9+
/// Also sanitizes any residual &lt;% %&gt; delimiters found inside @* *@ Razor comments
10+
/// (which ExpressionTransform produced from &lt;%-- --%&gt; comments).
11+
/// Must run after ExpressionTransform (500) and before AspPrefixTransform (610).
12+
/// </summary>
13+
public class ServerCodeBlockTransform : IMarkupTransform
14+
{
15+
public string Name => "ServerCodeBlock";
16+
public int Order => 510;
17+
18+
// Match @* ... *@ Razor comments so we can sanitize inner <% %> remnants
19+
private static readonly Regex RazorCommentRegex = new(
20+
@"(?s)@\*(.*?)\*@",
21+
RegexOptions.Compiled);
22+
23+
// Match <% ... %> statement blocks — excludes <%#, <%:, <%=, <%--
24+
private static readonly Regex StatementBlockRegex = new(
25+
@"(?s)<%(?![#:=\-])(.*?)%>",
26+
RegexOptions.Compiled);
27+
28+
public string Apply(string content, FileMetadata metadata)
29+
{
30+
// Step 1: neutralize <% %> inside @* *@ Razor comments so Razor doesn't choke.
31+
// ExpressionTransform already converted <%-- --%> to @* *@, but the inner
32+
// <% %> fragments survived as raw text.
33+
content = RazorCommentRegex.Replace(content, m =>
34+
{
35+
var inner = m.Groups[1].Value
36+
.Replace("<%", "[%")
37+
.Replace("%>", "%]");
38+
return $"@*{inner}*@";
39+
});
40+
41+
// Step 2: convert remaining statement blocks to Razor control-flow.
42+
content = StatementBlockRegex.Replace(content, m =>
43+
TransformStatementBlock(m.Groups[1].Value));
44+
45+
return content;
46+
}
47+
48+
private static string TransformStatementBlock(string rawInner)
49+
{
50+
// Collapse all whitespace (including newlines from multi-line blocks) to single spaces.
51+
var normalized = Regex.Replace(rawInner.Trim(), @"\s+", " ").Trim();
52+
53+
// <% } %> — closing brace only
54+
if (Regex.IsMatch(normalized, @"^\}\s*$"))
55+
return "}";
56+
57+
// <% } else if (condition) { %>
58+
var elseIfMatch = Regex.Match(normalized,
59+
@"^\}\s*else\s+if\s*(\(.*\))\s*\{?\s*$", RegexOptions.IgnoreCase);
60+
if (elseIfMatch.Success)
61+
return "}\nelse if " + elseIfMatch.Groups[1].Value + "\n{";
62+
63+
// <% } else { %>
64+
if (Regex.IsMatch(normalized, @"^\}\s*else\s*\{?\s*$", RegexOptions.IgnoreCase))
65+
return "}\nelse\n{";
66+
67+
// <% foreach (...) { %>
68+
var foreachCond = TryExtractCondition(normalized, "foreach");
69+
if (foreachCond != null)
70+
return "@foreach " + foreachCond + "\n{";
71+
72+
// <% for (...) { %>
73+
var forCond = TryExtractCondition(normalized, "for");
74+
if (forCond != null)
75+
return "@for " + forCond + "\n{";
76+
77+
// <% while (...) { %>
78+
var whileCond = TryExtractCondition(normalized, "while");
79+
if (whileCond != null)
80+
return "@while " + whileCond + "\n{";
81+
82+
// <% if (...) { %> — checked after foreach/for/while to avoid false matches
83+
var ifCond = TryExtractCondition(normalized, "if");
84+
if (ifCond != null)
85+
return "@if " + ifCond + "\n{";
86+
87+
// Bare statement block: <% code; %>
88+
return "@{ " + normalized + " }";
89+
}
90+
91+
/// <summary>
92+
/// Tries to extract the parenthesised condition after a control-flow keyword.
93+
/// Returns the condition string (including outer parens) or null if no match.
94+
/// </summary>
95+
private static string? TryExtractCondition(string normalized, string keyword)
96+
{
97+
if (!normalized.StartsWith(keyword, StringComparison.OrdinalIgnoreCase))
98+
return null;
99+
100+
// Ensure the keyword is not a prefix of a longer identifier
101+
if (normalized.Length > keyword.Length)
102+
{
103+
var next = normalized[keyword.Length];
104+
if (char.IsLetterOrDigit(next) || next == '_')
105+
return null;
106+
}
107+
108+
var rest = normalized.Substring(keyword.Length).TrimStart();
109+
110+
// Condition must start with '('
111+
if (!rest.StartsWith("("))
112+
return null;
113+
114+
// Strip trailing ' {' or '{' when the opening brace was on the same line
115+
rest = rest.TrimEnd();
116+
if (rest.EndsWith("{"))
117+
rest = rest[..^1].TrimEnd();
118+
119+
return rest.Trim();
120+
}
121+
}

0 commit comments

Comments
 (0)