Rougamo.Fody 2.0.0-priview-1680984436

This is a prerelease version of Rougamo.Fody.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Rougamo.Fody --version 2.0.0-priview-1680984436                
NuGet\Install-Package Rougamo.Fody -Version 2.0.0-priview-1680984436                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rougamo.Fody" Version="2.0.0-priview-1680984436" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Rougamo.Fody --version 2.0.0-priview-1680984436                
#r "nuget: Rougamo.Fody, 2.0.0-priview-1680984436"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Rougamo.Fody as a Cake Addin
#addin nuget:?package=Rougamo.Fody&version=2.0.0-priview-1680984436&prerelease

// Install Rougamo.Fody as a Cake Tool
#tool nuget:?package=Rougamo.Fody&version=2.0.0-priview-1680984436&prerelease                

Rougamo - 肉夹馍

Translated by google

中文 | English

Rougamo, a Chinese snack, perhaps somewhat similar to a hamburger. Wrapping important logic code to provide AOP is like wrapping delicious stuffing in buns.

Quick start

// 1. Install-Package Rougamo.Fody
// 2. Define the class to inherit MoAttribute, and define the code that needs to be woven
public class LoggingAttribute : MoAttribute
{
    public override void OnEntry(MethodContext context)
    {
        Log.Info("Before the method execution");
    }

    public override void OnException(MethodContext context)
    {
        Log.Error("When an exception occurs when the method is executed", context.Exception);
    }

    public override void OnSuccess(MethodContext context)
    {
        Log.Info("After the method executes successfully");
    }

    public override void OnExit(MethodContext context)
    {
        Log.Info("After the method is executed, whether it succeeds or fails");
    }
}

// 3. Apply Attribute
public class Service
{
    [Logging]
    public static int Sync(Model model)
    {
        // ...
    }

    [Logging]
    public async Task<Data> Async(int id)
    {
        // ...
    }
}

Batch apply based on method accessibility

In the quick start, we introduced how to weave the code into the specified method, but in actual use, it may be very cumbersome and intrusive to add this attribute to each method or many methods in a huge project, so MoAttribute is designed to be applied to methods, classes, assemblies, and modules, while setting accessibility attributes for added flexibility.

// 1. While inheriting MoAttribute, override the Flags attribute, and default to InstancePublic (public instance method) when not overridden.
public class LoggingAttribute : MoAttribute
{
    // Changing to all public methods is valid, whether it is an instance method or a static method.
    public override AccessFlags Flags => AccessFlags.Public;

    // Method override omitted.
}

// 2. Apply
// 2.1. apply to class
[Logging]
public class Service
{
    // Logging whill be apply.
    public static void M1() { }

    // Logging whill be apply.
    public void M2() { }

    // protected access level does not apply Logging code weaving.
    protected void M3() { }
}
// 2.2. Applied to an assembly, all public methods of the assembly will be statically woven into the application.
[assembly: Logging]

Weaving by implementing an empty interface(IRougamo<>)

If marking each method via Attribute feels too cumbersome or intrusive, and batch weaving via method accessibility is too general and not custom enough, weaving via an empty interface will provide intrusiveness and convenient way.

// 1. Define the code that needs to be woven.(Because LoggingMo is used as a generic parameter of the IRougamo interface, you can simply implement the IMo interface here, and of course you can inherit MoAttribute like LoggingAttribute in Quick Start)
public class LoggingMo : IMo
{
    public override AccessFlags Flags => AccessFlags.All;

    public override void OnEntry(MethodContext context)
    {
        // Information including input parameters, class instances, method descriptions, etc. can be obtained from the context object.
        Log.Info("Before the method execution");
    }

    public override void OnException(MethodContext context)
    {
        Log.Error("When an exception occurs when the method is executed", context.Exception);
    }

    public override void OnExit(MethodContext context)
    {
        Log.Info("After the method is executed, whether it succeeds or fails");
    }

    public override void OnSuccess(MethodContext context)
    {
        Log.Info("After the method executes successfully");
    }
}

// 2. Define an empty interface. The ILoggingRougamo interface defined in this step can be skipped, and IRougamo<LoggingMo> can also be implemented directly in the next step.
public interface ILoggingRougamo : IRougamo<LoggingMo>
{
}

// 3. Apply an empty interface. If you are used to defining a parent interface/parent class for the same type/domain class when programming, you only need the parent interface/parent class to implement the interface.
public interface IRepository<TModel, TId> : ILoggingRougamo
{
    // ...
}

Exception handling and modifying return values(v1.1.0)

In the OnException method, you can call the HandledException method of MethodContext to indicate that the exception has been handled and set the return value. In the OnEntry and OnSuccess methods, you can modify the actual method by calling the ReplaceReturnValue method of MethodContext The return value of ReturnValue, ExceptionHandled and other attributes should not be used to modify the return value and handle exceptions directly. HandledException and ReplaceReturnValue contain some other logic, which may be updated in the future. Also note that Iterator/AsyncIterator does not have this functionality.

public class TestAttribute : MoAttribute
{
    public override void OnException(MethodContext context)
    {
        // Handle exceptions and set the return value to newReturnValue. If the method has no return value (void), simply pass in null
        context.HandledException(this, newReturnValue);
    }

    public override void OnSuccess(MethodContext context)
    {
        // Modify method return value
        context.ReplaceReturnValue(this, newReturnValue);
    }
}

rewrite method arguments(v1.3.0)

In OnEntry, you can modify the arguments of the method by modifying the elements of MethodContext.Arguments. In order to be compatible with the situation where MethodContext.Arguments may be used to store some temporary values in the old version (although the possibility is very small), so you also need to set MethodContext.RewriteArguments to true to confirm the rewrite arguments.

public class DefaultValueAttribute : MoAttribute
{
    public override void OnEntry(MethodContext context)
    {
        context.RewriteArguments = true;

        var parameters = context.Method.GetParameters();
        for (var i = 0; i < parameters.Length; i++)
        {
            if (parameters[i].ParameterType == typeof(string) && context.Arguments[i] == null)
            {
                context.Arguments[i] = string.Empty;
            }
        }
    }
}

public class Test
{
    // When the value is null, an empty string will be returned
    [DefaultValue]
    public string EmptyIfNull(string value) => value;
}

Unified processing logic for async/non-async methods(v1.2.0)

Before version 1.2.0, for methods with similar method signatures Task<int> M(), when using async syntax, the type of the return value obtained through MethodContext.ReturnValue is int, while in The type of the return value obtained when the async syntax is not used is Task<int>. This is not a bug, but the initial setting. Just as when we use the sync syntax, the return value of the code is int, but without the When using the async syntax, the return value of the code is like Task<int>. But when you first came into contact with the project, it may be difficult to notice this setting and in the process of using it, you also hope that there is no method using async syntax to execute OnSuccess/OnExit after the Task is executed, so 1.2.0 introduced ExMoAttribute , Use the processing logic of the previous async syntax method for the Task/ValueTask` return value method with and without async syntax.

Note that ExMoAttribute and MoAttribute have the following differences:

  • ExMoAttribute overridable method named ExOnEntry/ExOnException/ExOnSuccess/ExOnExit
  • The return value of ExMoAttribute is obtained through MethodContext.ExReturnValue, and the value obtained through MethodContext.ReturnValue will be the value of Task/ValueTask
  • Whether the return value of ExMoAttribute is replaced/set, it is obtained through MethodContext.ExReturnValueReplaced, and the value obtained through MethodContext.ReturnValueReplaced is generally true (because it is replaced with the Task returned by ContinueWith)
  • The return value type of ExMoAttribute is obtained through MethodContext.ExReturnType. What is the difference between ReturnType/RealReturnType/ExReturnType can be seen in the description of the respective attribute document or the type document description of ExMoAttribute
[Fact]
public async Task Test()
{
    Assert.Equal(1, Sync());
    Assert.Equal(-1, SyncFailed1());
    Assert.Throws<InvalidOperationException>(() => SyncFailed3());

    Assert.Equal(1, await NonAsync());
    Assert.Equal(-1, await NonAsyncFailed1());
    Assert.Equal(-1, await NonAsyncFailed2());
    await Assert.ThrowsAsync<InvalidOperationException>(() => NonAsyncFailed3());
    await Assert.ThrowsAsync<InvalidOperationException>(() => NonAsyncFailed4());

    Assert.Equal(1, await Async());
    Assert.Equal(-1, await AsyncFailed1());
    Assert.Equal(-1, await AsyncFailed2());
    await Assert.ThrowsAsync<InvalidOperationException>(() => AsyncFailed3());
    await Assert.ThrowsAsync<InvalidOperationException>(() => AsyncFailed4());
}

[FixedInt]
static int Sync()
{
    return int.MaxValue;
}

[FixedInt]
static int SyncFailed1()
{
    throw new NotImplementedException();
}

[FixedInt]
static int SyncFailed3()
{
    throw new InvalidOperationException();
}

[FixedInt]
static Task<int> NonAsync()
{
    return Task.FromResult(int.MinValue);
}

[FixedInt]
static Task<int> NonAsyncFailed1()
{
    throw new NotImplementedException();
}

[FixedInt]
static Task<int> NonAsyncFailed2()
{
    return Task.Run(int () => throw new NotImplementedException());
}

[FixedInt]
static Task<int> NonAsyncFailed3()
{
    throw new InvalidOperationException();
}

[FixedInt]
static Task<int> NonAsyncFailed4()
{
    return Task.Run(int () => throw new InvalidOperationException());
}

[FixedInt]
static async Task<int> Async()
{
    await Task.Yield();
    return int.MaxValue / 2;
}

[FixedInt]
static async Task<int> AsyncFailed1()
{
    throw new NotImplementedException();
}

[FixedInt]
static async Task<int> AsyncFailed2()
{
    await Task.Yield();
    throw new NotImplementedException();
}

[FixedInt]
static async Task<int> AsyncFailed3()
{
    throw new InvalidOperationException();
}

[FixedInt]
static async Task<int> AsyncFailed4()
{
    await Task.Yield();
    throw new InvalidOperationException();
}

class FixedIntAttribute : ExMoAttribute
{
    protected override void ExOnException(MethodContext context)
    {
        if (context.Exception is NotImplementedException)
        {
            context.HandledException(this, -1);
        }
    }

    protected override void ExOnSuccess(MethodContext context)
    {
        context.ReplaceReturnValue(this, 1);
    }
}

Retry(v1.4.0)

Starting from version 1.4.0, methods can be re-executed when a specified exception occurs or the return value is unexpected. Set the value of MethodContext.RetryCount in OnException or OnSuccess, and the method will be re-execute after OnException and OnSuccess are executed if the value of MethodContext.RetryCount is greater than 0.

internal class RetryAttribute : MoAttribute
{
    public override void OnEntry(MethodContext context)
    {
        context.RetryCount = 3;
    }

    public override void OnException(MethodContext context)
    {
        context.RetryCount--;
    }

    public override void OnSuccess(MethodContext context)
    {
        context.RetryCount--;
    }
}

// After apply the RetryAttribute,Test method will be re-execute three times
[Retry]
public void Test()
{
    throw new Exception();
}

For the scenario of retrying exception handling, I created the Rougamo.Retry project independently. If you only want to retry for some kind of exception, you can directly use Rougamo. Retry

Note the following when using the retry feature:

  • When handling exceptions through MethodContext.HandledException() or modifying the return value through MethodContext.ReplaceReturnValue(), it will directly set MethodContext.RetryCount to 0, because manually handling exceptions and modifying the return value means that you have decided the final result of the method, so there is no need to retry
  • OnEntry and OnExit of MoAttribute will only be executed once, and will not be executed multiple times due to retries.
  • Try not to use the retry feature in ExMoAttribute unless you really know the actual processing logic. Consider the following codes, ExMoAttribute cannot re-execute the entire external method after the internal error of Task.
    public Task Test()
    {
      DoSomething();
    
      return Task.Run(() => DoOtherThings());
    }
    

Ignore weaving(IgnoreMoAttribute)

In the quick start, we introduced how to apply in batches. Since the rules of batch references only limit the accessibility of methods, there may be some methods that meet the rules and do not want to apply weaving. At this time, you can use IgnoreMoAttribute to specify method/ class, then that method/class (all methods) will ignore weaving. If IgnoreMoAttribute is applied to an assembly or module, all weaving will be ignored for that assembly/module. Additionally, it is possible to specify ignored weaving types via MoTypes when applying the IgnoreMoAttribute.

// The current assembly ignores all weaving
[assembly: IgnoreMo]
// The current assembly ignores weaving of TheMoAttribute
[assembly: IgnoreMo(MoTypes = new[] { typeof(TheMoAttribute))]

// The current class ignores all weaving
[IgnoreMo]
class Class1
{
    // ...
}

// The current class ignores weaving of TheMoAttribute
[IgnoreMo(MoTypes = new[] { typeof(TheMoAttribute))]
class Class2
{
    // ...
}

Attribute proxy weaving(MoProxyAttribute)

If you have used some third-party components to mark some methods with Attribute, and now you want to perform aop operations on these marked methods, but do not want to manually add the Attribute mark of rougamo one by one, you can step by proxy. Complete aop weaving. Another example is that your project now has a lot of obsolete methods marked with ObsoleteAttribute. You want to output the call stack log when the expired method is called, to check which entries are using these expired methods. You can also do this in this way.

public class ObsoleteProxyMoAttribute : MoAttribute
{
    public override void OnEntry(MethodContext context)
    {
        Log.Warning("expired method was called." + Environment.StackTrace);
    }
}

[module: MoProxy(typeof(ObsoleteAttribute), typeof(ObsoleteProxyMoAttribute))]

public class Cls
{
    [Obsolete]
    private int GetId()
    {
        // This method weaves the application into the code
        return 123;
    }
}

Weave Mutex

Single type mutex(IRougamo<,>)

Since we have two weaving methods, Attribute tag and interface implementation, it may be applied at the same time, and if the content of the two weaving is the same, there will be repeated weaving. In order to avoid this as much as possible In this case, when the interface is defined, mutually exclusive types can be defined, that is, only one can take effect at the same time, and which one takes effect is determined according to Priority (#Priority).

public class Mo1Attribute : MoAttribute
{
    // ...
}
public class Mo2Attribute : MoAttribute
{
    // ...
}
public class Mo3Attribute : MoAttribute
{
    // ...
}

public class Test : IRougamo<Mo1Attribute, Mo2Attribute>
{
    [Mo2]
    public void M1()
    {
        // Mo2Attribute is applied to the method, the priority is higher than the Mo1Attribute implemented by the interface, and the Mo2Attribute will be applied
    }

    [Mo3]
    public void M2()
    {
        // Mo1Attribute and Mo3Attribute are not mutually exclusive, both will be applied
    }
}

Multitype Mutual Exclusion(IRepulsionsRougamo<,>)

IRougamo<,> can only be mutually exclusive with one type, IRepulsionsRougamo<,> can be mutually exclusive with multiple types.

public class Mo1Attribute : MoAttribute
{
}
public class Mo2Attribute : MoAttribute
{
}
public class Mo3Attribute : MoAttribute
{
}
public class Mo4Attribute : MoAttribute
{
}
public class Mo5Attribute : MoAttribute
{
}

public class TestRepulsion : MoRepulsion
{
    public override Type[] Repulsions => new[] { typeof(Mo2Attribute), typeof(Mo3Attribute) };
}

[assembly: Mo2]
[assembly: Mo5]

public class Class2 : IRepulsionsRougamo<Mo1Attribute, TestRepulsion>
{
    [Mo3]
    public void M1()
    {
        // Mo1 is mutually exclusive with Mo2 and Mo3, but since Mo3 has a higher priority than Mo1, when Mo1 does not take effect, all mutually exclusive types will take effect.
        // So eventually Mo2Attribute, Mo3Attribute, Mo5Attribute will be applied.
        Console.WriteLine("m1");
    }

    [Mo4]
    public void M2()
    {
        // Mo1 is mutually exclusive with Mo2 and Mo3, but since Mo1 has a higher priority than Mo2, Mo2 will not take effect
        // Eventually Mo1Attribute, Mo4Attribute, Mo5Attribute will be applied
        Console.WriteLine("m2");
    }
}

<font color=red>Through the above example, you may notice that this multi-type mutual exclusion is not mutual exclusion between multiple types, but the mutual exclusion of the first generic type and the type defined by the second generic type, and the second generic type is mutually exclusive. They are not mutually exclusive. Just like the above example, when Mo1Attribute does not take effect, the mutually exclusive Mo2Attribute and Mo3Attribute will take effect. It needs to be understood here that the reason for defining mutual exclusion is the possible repeated application of Attribute and empty interface implementation, not to exclude all weaving repetitions. At the same time, it is not recommended to use multiple mutual exclusion definitions, which is prone to logical confusion. It is recommended to carefully consider a set of unified rules before application weaving, rather than random definitions, and then try to use multiple mutual exclusions to solve problems.</font>

Priority

  1. IgnoreMoAttribute
  2. Method MoAttribute
  3. Method MoProxyAttribute
  4. Type MoAttribute
  5. Type MoProxyAttribute
  6. Type IRougamo<>, IRougamo<,>, IRepulsionsRougamo<,>
  7. Assembly & Module MoAttribute

Switch

Rougamo is developed by individuals. Due to limited capabilities, the research on IL is not so thorough, and with the development of .NET, some new types, new semantics and even new IL instructions will continue to appear. Therefore, there may exist Some bugs, and the bugs at the IL level may not be able to quickly locate the problem and fix it, so here is a switch to avoid code weaving without removing the Rougamo reference. Therefore, it is recommended that you use Rougamo for code weaving When the woven code does not affect the business, such as logs and APM. If you want to use static weaving components that are stable and can be quickly supported when you encounter problems, it is recommended to use PostSharp

Rougamo is developed on the basis of fody. After referencing Rougamo, the first compilation will generate a FodyWeavers.xml file. The default content is as follows

<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Rougamo />
</Weavers>

When you want to disable Rougamo, you need to add the attribute enabled to the Rougamo node of the configuration file and set the value to false

<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Rougamo enabled="false" />
</Weavers>

Record yield return IEnumerable/IAsyncEnumerable return value

We know that using yield return syntax sugar + IEnumerable return value method, after calling the method, the code of the method is not actually executed, the actual execution of the code is when you access the elements in the IEnumerable object When, for example, you go to foreach this object or call ToList/ToArray, and the returned elements are not stored in an array/linked list (the specific principle is not explained here), so by default there is no The method directly obtains the collection of all elements returned by yield return IEnumerable.

But there may be some scenarios with strict code monitoring that need to record all return values, so in the implementation, I created an array to save all the returned elements, but since this array is created additionally, it will take up additional memory space, and at the same time It is not clear how big the set of elements returned by this IEnumerable is, so in order to avoid excessive memory consumption, the return value of yield return IEnumerable will not be recorded by default. The Rougamo node of FodyWeavers.xmladds attribute configurationenumerable-returns="true"`.

<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Rougamo enumerable-returns="true" />
</Weavers>
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (49)

Showing the top 5 NuGet packages that depend on Rougamo.Fody:

Package Downloads
HandeSoft.Core

Package Description

HandeSoft.Web.Core

Package Description

BotSharp.Abstraction

Package Description

Ray.Infrastructure

This client library is a infrastructure that including extensions and helpers etc.

HZY.Framework.Core

HZY Framework 核心 1、ScheduledAttribute 定时任务特性标记 2、IServerMetricMonitoringService 服务器指标监控 CPU、内存、硬盘、运行时长 3、HZY.Framework.DynamicApiController 动态Api控制器 4、HZY.Framework.DependencyInjection 依赖注入

GitHub repositories (6)

Showing the top 5 popular GitHub repositories that depend on Rougamo.Fody:

Repository Stars
RayWangQvQ/BiliBiliToolPro
B 站(bilibili)自动任务工具,支持docker、青龙、k8s等多种部署方式。敏感肌也能用。
dotnetcore/FreeSql
🦄 .NET aot orm, C# orm, VB.NET orm, Mysql orm, Postgresql orm, SqlServer orm, Oracle orm, Sqlite orm, Firebird orm, 达梦 orm, 人大金仓 orm, 神通 orm, 翰高 orm, 南大通用 orm, 虚谷 orm, 国产 orm, Clickhouse orm, DuckDB orm, TDengine orm, QuestDB orm, MsAccess orm.
SciSharp/BotSharp
AI Multi-Agent Framework in .NET
ThingsGateway/ThingsGateway
ThingsGateway is a cross platform high-performance edge acquisition gateway based on Net8, providing underlying PLC communication libraries, communication debugging software, and more.
2881099/FreeSql.AdminLTE
这是一个 .NETCore MVC 中间件,基于 AdminLTE 前端框架动态产生 FreeSql 实体的增删查改界面。
Version Downloads Last updated
5.0.0 2,816 12/8/2024
5.0.0-preview-1733564400 75 12/7/2024
5.0.0-preview-1733289406 83 12/4/2024
4.0.4 8,025 9/29/2024
4.0.4-preview-1727349912 103 9/26/2024
4.0.3 1,313 9/16/2024
4.0.3-preview-1726120802 107 9/12/2024
4.0.3-preview-1725957423 113 9/10/2024
4.0.2 568 9/9/2024
4.0.2-preview-1725956948 106 9/10/2024
4.0.2-preview-1725875652 103 9/9/2024
4.0.2-preview-1725466232 108 9/4/2024
4.0.1 2,679 9/2/2024
4.0.1-preview-1725141430 101 8/31/2024
4.0.0 7,172 8/10/2024
4.0.0-priview-1723306347 118 8/10/2024
4.0.0-priview-1722831925 97 8/5/2024
3.1.0 1,543 7/16/2024
3.0.2 468 7/8/2024
3.0.2-priview-1720363148 118 7/7/2024
3.0.2-priview-1720251661 113 7/6/2024
3.0.1 201 7/4/2024
3.0.1-priview-1720089186 120 7/4/2024
3.0.1-priview-1720085112 97 7/4/2024
3.0.0 3,942 5/4/2024
3.0.0-priview-1714754497 86 5/3/2024
3.0.0-priview-1714407561 140 4/29/2024
2.3.1 8,399 4/23/2024
2.3.1-priview-1713854631 124 4/23/2024
2.3.1-priview-1713791514 110 4/22/2024
2.3.0 3,937 3/10/2024
2.3.0-priview-1709894403 123 3/8/2024
2.2.0 3,121 1/20/2024
2.2.0-priview-1705656978 117 1/19/2024
2.2.0-priview-1705571301 111 1/18/2024
2.2.0-priview-1705566213 121 1/18/2024
2.2.0-priview-1702899195 188 12/18/2023
2.1.1 4,678 12/14/2023
2.1.1-priview-1702545048 148 12/14/2023
2.1.1-priview-1702542781 146 12/14/2023
2.0.1 1,281 11/16/2023
2.0.0 3,057 10/8/2023
2.0.0-priview-1696783135 143 10/8/2023
2.0.0-priview-1696592398 136 10/6/2023
2.0.0-priview-1695658688 160 9/25/2023
2.0.0-priview-1695465141 153 9/23/2023
2.0.0-priview-1680984436 225 4/8/2023
2.0.0-priview-1680981587 187 4/8/2023
1.4.1 12,091 3/12/2023
1.4.1-priview-1678603084 194 3/12/2023
1.4.1-priview-1678557697 195 3/11/2023
1.4.1-priview-1678557463 191 3/11/2023
1.4.0 2,828 3/1/2023
1.4.0-beta 368 2/27/2023
1.4.0-alpha 238 2/25/2023
1.3.4 60,872 2/17/2023
1.3.3 987 1/17/2023
1.3.2 19,050 12/20/2022
1.3.1 347 12/20/2022
1.3.1-beta 182 12/14/2022
1.3.0 1,288 12/8/2022 1.3.0 is deprecated because it has critical bugs.
1.2.3 353 1/17/2023
1.2.2 326 12/20/2022
1.2.2-beta 172 12/14/2022
1.2.1 704 11/29/2022
1.2.1-beta 169 11/29/2022
1.2.0 2,078 9/14/2022 1.2.0 is deprecated.
1.2.0-beta 191 9/12/2022
1.2.0-alpha2 179 9/12/2022
1.2.0-alpha1 184 8/31/2022
1.2.0-alpha 177 8/30/2022
1.1.4 387 11/29/2022
1.1.4-alpha 193 12/25/2022
1.1.3 540 9/11/2022 1.1.3 is deprecated because it has critical bugs.
1.1.2 1,672 8/22/2022
1.1.2-beta 184 8/22/2022
1.1.1 2,964 8/8/2022
1.1.1-beta 194 8/1/2022
1.1.0 638 7/28/2022
1.1.0-beta 209 7/15/2022
1.1.0-alpha4 192 6/24/2022
1.1.0-alpha3 179 6/24/2022
1.1.0-alpha2 178 6/23/2022
1.1.0-alpha1 180 6/22/2022
1.1.0-alpha 194 5/22/2022
1.0.3 723 5/6/2022
1.0.3-beta 199 4/26/2022
1.0.2 727 12/23/2021
1.0.1 8,102 11/23/2021
1.0.1-beta 4,923 11/23/2021

## todo
- [x] ��Ҫ֯���Mo����X��ʱ��ʹ�����鱣��
- [x] ֧������
- [x] ֧�ְ���֯��
- [x] �շ���֯���Ż�����ֱ��ɾ���շ���֯��Ķ����߼������ϣ���շ���֯�뾫��Ĵ��룬ͨ������֯��ķ�ʽʵ�֡�
- [x] ֧�ָ����ķ���ɨ��
- [ ] ����CLI�����CICD����������������
- [x] Attribute����ֵ��ȡ֧���ڹ��췽�������Խ��г�ʼ���ķ�ʽ�����ַ�ʽ�ǵ�������Set������ֵ��
- [x] �Ż�nop anchors