‹ 首页

cli-transform-authoring

@fritzandfriends · 收录于 昨天 · 上游提交 1 个月前

Create and register new CLI migration transforms for the webforms-to-blazor pipeline. Covers IMarkupTransform and ICodeBehindTransform interfaces, transform ordering, dual-registration in Program.cs and TestHelpers.cs, testing patterns, and when to use a transform vs. a semantic pattern. Use when adding a new markup or code-behind transform, debugging transform output, or understanding transform execution order.

适合你,如果正在将Web表单项目迁移到Blazor并需要编写自定义转换

/ 通过 npx 安装 校验哈希
npx oh-my-skill add fritzandfriends/blazorwebformscomponents/cli-transform-authoring
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- fritzandfriends/blazorwebformscomponents/cli-transform-authoring
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify fritzandfriends/blazorwebformscomponents/cli-transform-authoring
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
449GitHub stars
~1.3K上下文体积 · 单文件
索引托管

怎么用

技能原文 SKILL.md作者撰写 · MIT · 3154758

CLI Transform Authoring

This skill covers creating new transforms for the webforms-to-blazor CLI migration pipeline.

Architecture Overview

The migration CLI uses two transform interfaces that run in sequence during file conversion:

  1. Markup transforms (IMarkupTransform) — Convert .aspx/.ascx/.master markup to .razor syntax
  2. Code-behind transforms (ICodeBehindTransform) — Convert .aspx.cs code-behind to .razor.cs

Both interfaces share the same contract:

public interface IMarkupTransform  // (or ICodeBehindTransform)
{
    string Name { get; }
    int Order { get; }
    string Apply(string content, FileMetadata metadata);
}
  • Name — Human-readable identifier (used in logs and diagnostics)
  • Order — Execution sequence (ascending). Lower numbers run first.
  • Apply — Takes current content + file metadata, returns transformed content
Pipeline Execution Order

MigrationPipeline runs transforms in this order:

  1. Markup transforms (sorted by Order ascending) — all run on .razor content
  2. Code-behind transforms (sorted by Order ascending) — all run on .razor.cs content
  3. Semantic patterns (SemanticPatternCatalog) — page-level rewrites after all transforms
FileMetadata

Every transform receives FileMetadata which carries context about the file being processed:

  • OriginalPath — Source .aspx path
  • OutputPath — Target .razor path
  • PageDirectives — Parsed directive info (CodeBehind, Inherits, MasterPageFile)
  • MarkupContent — Can be set by code-behind transforms to modify the markup file
  • CodeBehindContent — The code-behind source
  • IsUserControl / IsMasterPage — File type flags
Creating a New Transform
Step 1: Create the Transform Class
src/BlazorWebFormsComponents.Cli/Transforms/Markup/MyNewTransform.cs
    — or —
src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/MyNewTransform.cs
using BlazorWebFormsComponents.Cli.Pipeline;

namespace BlazorWebFormsComponents.Cli.Transforms.Markup;  // or .CodeBehind

public class MyNewTransform : IMarkupTransform  // or ICodeBehindTransform
{
    public string Name => "MyNewTransform";
    public int Order => 500;  // Choose based on dependencies

    public string Apply(string content, FileMetadata metadata)
    {
        // Return content unchanged if this transform doesn't apply
        if (!content.Contains("pattern-to-match"))
            return content;

        // Apply transformation
        return content.Replace("old-pattern", "new-pattern");
    }
}
Step 2: Register in BOTH Locations

⚠️ CRITICAL: Every transform must be registered in TWO places.

src/BlazorWebFormsComponents.Cli/Program.cs (runtime DI):

services.AddSingleton<IMarkupTransform, MyNewTransform>();
// — or —
services.AddSingleton<ICodeBehindTransform, MyNewTransform>();

tests/BlazorWebFormsComponents.Cli.Tests/TestHelpers.cs (test pipeline):

var markupTransforms = new List<IMarkupTransform>
{
    // ... existing transforms ...
    new MyNewTransform(),
};
// — or for code-behind —
var codeBehindTransforms = new List<ICodeBehindTransform>
{
    // ... existing transforms ...
    new MyNewTransform(),
};
Step 3: Write Tests

Create a test file at tests/BlazorWebFormsComponents.Cli.Tests/MyNewTransformTests.cs:

using BlazorWebFormsComponents.Cli.Pipeline;
using BlazorWebFormsComponents.Cli.Transforms.Markup;
using Xunit;

namespace BlazorWebFormsComponents.Cli.Tests;

public class MyNewTransformTests
{
    private readonly MyNewTransform _transform = new();

    [Fact]
    public void Apply_WithMatchingPattern_TransformsCorrectly()
    {
        var input = "<asp:CustomControl runat=\"server\" />";
        var metadata = new FileMetadata { OriginalPath = "Test.aspx" };

        var result = _transform.Apply(input, metadata);

        Assert.Contains("expected-output", result);
        Assert.DoesNotContain("asp:", result);
    }

    [Fact]
    public void Apply_WithoutMatchingPattern_ReturnsUnchanged()
    {
        var input = "<div>No Web Forms here</div>";
        var metadata = new FileMetadata { OriginalPath = "Test.aspx" };

        var result = _transform.Apply(input, metadata);

        Assert.Equal(input, result);
    }
}
Step 4: Run Tests
dotnet test tests\BlazorWebFormsComponents.Cli.Tests --nologo
Transform Ordering Guidelines

| Order Range | Category | Examples | |-------------|----------|----------| | 0–99 | Directive stripping | PageDirectiveTransform, RegisterDirectiveTransform | | 100–199 | Structural transforms | MasterPageTransform, ContentWrapperTransform | | 200–299 | Prefix/attribute removal | AspPrefixTransform, AttributeStripTransform | | 300–399 | Expression/binding rewrites | ExpressionTransform, DataBindingAttributeTransform | | 400–499 | Template/column processing | TemplateFieldChildComponentsTransform, GridViewColumnItemTypeTransform | | 500–599 | Event/form wiring | EventWiringTransform, FormWrapperTransform | | 600–699 | URL/reference cleanup | UrlReferenceTransform, ScriptManagerStripTransform | | 700+ | Post-processing | FormAntiforgeryPostProcessor, ValidatorGenericTypeTransform |

Key ordering dependencies:

  • AspPrefixTransform must run before AttributeStripTransform (strip asp: before removing runat)
  • TemplateContextTransform must run before TemplateFieldChildComponentsTransform
  • SelectMethodTransform must run before EventWiringTransform
Transform vs. Semantic Pattern

| Use a Transform when... | Use a Semantic Pattern when... | |--------------------------|-------------------------------| | Converting a specific Web Forms tag/attribute | Rewriting page-level structure | | Change is mechanical (regex/string) | Change requires understanding page intent | | Applies to ALL files universally | Applies only to specific page types (query, detail, action) | | No context about surrounding markup needed | Needs to understand the full page layout | | Example: stripping runat="server" | Example: converting a search form + GridView into a query page |

Current Transform Inventory
  • 24 markup transforms in src/BlazorWebFormsComponents.Cli/Transforms/Markup/
  • 5 directive transforms in src/BlazorWebFormsComponents.Cli/Transforms/Directives/
  • 27 code-behind transforms in src/BlazorWebFormsComponents.Cli/Transforms/CodeBehind/
Checklist
  • [ ] Transform class implements IMarkupTransform or ICodeBehindTransform
  • [ ] Order is set appropriately (check ordering dependencies)
  • [ ] Registered in Program.cs DI container
  • [ ] Registered in TestHelpers.cs test pipeline
  • [ ] Unit tests cover: matching input, non-matching input, edge cases
  • [ ] dotnet test tests\BlazorWebFormsComponents.Cli.Tests passes
  • [ ] Benchmark regression check: run migration against current focus app
按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。