Skip to content

Commit

Permalink
Temp
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed Sep 18, 2024
1 parent dc48f27 commit 812c49b
Show file tree
Hide file tree
Showing 20 changed files with 306 additions and 26 deletions.
47 changes: 47 additions & 0 deletions Html.Net.Inline/Html.Net.Inline.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>1591</NoWarn>
<NeutralLanguage>en</NeutralLanguage>
<LangVersion>latest</LangVersion>
<RootNamespace>Mjml.Net</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.1.2" />
<PackageReference Include="AngleSharp.Css" Version="0.17.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.145">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
</ItemGroup>

<ItemGroup>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>

<None Include="icon.png" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Mjml.Net\Mjml.Net.csproj" />
</ItemGroup>

</Project>
91 changes: 91 additions & 0 deletions Html.Net.Inline/InlineProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using AngleSharp;
using AngleSharp.Dom;
using Mjml.Net;

namespace Html.Net;

public sealed class InlineProcessor : IPostProcessor
{
public static readonly InlineProcessor Instance = new InlineProcessor();

private InlineProcessor()
{
}

public string PostProcess(string html, MjmlOptions options)
{
var config = Configuration.Default.WithCss();
var context = BrowsingContext.New(config);

var document = context.OpenAsync(req => req.Content(html)).Result;

Traverse(document, a => RenameNonInline(a, document));
Traverse(document, InlineStyle);
Traverse(document, a => RestoreNonInline(a, document));

return document.ToString()!;
}

private static void Traverse(INode node, Action<IElement> action)
{
foreach (var child in node.ChildNodes.ToList())
{
Traverse(child, action);
}

if (node is IElement element)
{
action(element);
}
}

private static void InlineStyle(IElement element)
{
var currentStyle = element.ComputeCurrentStyle();

if (currentStyle.Any())
{
var css = currentStyle.ToCss();

element.SetAttribute("style", css);
}
}

private static void RenameNonInline(IElement element, IDocument document)
{
if (string.Equals(element.TagName, "style", StringComparison.OrdinalIgnoreCase) && !element.HasAttribute("inline"))
{
RenameTag(element, "non_inline_style", document);
}
}

private static void RestoreNonInline(IElement element, IDocument document)
{
if (string.Equals(element.TagName, "non_inline_style", StringComparison.OrdinalIgnoreCase))
{
RenameTag(element, "non_inline_style", document);
}

if (string.Equals(element.TagName, "style", StringComparison.OrdinalIgnoreCase) && element.HasAttribute("inline"))
{
element.Remove();
}
}

private static void RenameTag(IElement node, string tagName, IDocument document)
{
var clone = document.CreateElement(tagName);

foreach (var attribute in node.Attributes)
{
clone.SetAttribute(attribute.NamespaceUri, attribute.Name, attribute.Value);
}

var parent = node.Parent!;

clone.InnerHtml = node.InnerHtml;

parent.InsertBefore(clone, node);
parent.RemoveChild(node);
}
}
Binary file added Html.Net.Inline/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 0 additions & 7 deletions Mjml.Net.Benchmark/TemplateBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace Mjml.Net.Benchmarking;
public class TemplateBenchmarks
{
private static readonly MjmlOptions WithBeautify = new MjmlOptions { Beautify = true };
private static readonly MjmlOptions WithMinify = new MjmlOptions { Minify = true };
private readonly MjmlRenderer MjmlRenderer;

[ParamsSource(nameof(MjmlTemplates))]
Expand Down Expand Up @@ -61,10 +60,4 @@ public string Render_Template_Beautify()
{
return MjmlRenderer.Render(MjmlTemplate, WithBeautify).Html;
}

[Benchmark]
public string Render_Template_Minify()
{
return MjmlRenderer.Render(MjmlTemplate, WithMinify).Html;
}
}
6 changes: 6 additions & 0 deletions Mjml.Net.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tools", "Tools\Tools.csproj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mjml.Net.Benchmark", "Mjml.Net.Benchmark\Mjml.Net.Benchmark.csproj", "{3969326A-B528-4120-8E30-4127F66B638E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Html.Net.Inline", "Html.Net.Inline\Html.Net.Inline.csproj", "{BADECB72-90D2-44F3-AA93-27B0247C8FA9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -39,6 +41,10 @@ Global
{3969326A-B528-4120-8E30-4127F66B638E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3969326A-B528-4120-8E30-4127F66B638E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3969326A-B528-4120-8E30-4127F66B638E}.Release|Any CPU.Build.0 = Release|Any CPU
{BADECB72-90D2-44F3-AA93-27B0247C8FA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BADECB72-90D2-44F3-AA93-27B0247C8FA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BADECB72-90D2-44F3-AA93-27B0247C8FA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BADECB72-90D2-44F3-AA93-27B0247C8FA9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
2 changes: 2 additions & 0 deletions Mjml.Net/AttributeTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public static class AttributeTypes

public static readonly IType IncludeType = new EnumType(true, "mjml", "html", "css");

public static readonly IType Inline = new EnumType(true, "inline");

public static readonly IType Pixels = new NumberType(Unit.Pixels);

public static readonly IType PixelsOrAuto = new OneOfType(new EnumType(false, "auto"), Pixels);
Expand Down
1 change: 1 addition & 0 deletions Mjml.Net/BindType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum BindType
File,
FourPixelsOrPercent,
LeftRight,
Inline,
Pixels,
PixelsOrAuto,
PixelsOrEm,
Expand Down
6 changes: 4 additions & 2 deletions Mjml.Net/Components/Head/StyleComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public partial class StyleComponent : HeadComponentBase

public override string ComponentName => "mj-style";

[Bind("inline")]
[Bind("inline", BindType.Inline)]
public string? Inline;

[BindText]
Expand All @@ -19,7 +19,9 @@ public override void Render(IHtmlRenderer renderer, GlobalContext context)
// Just in case that validation is disabled.
if (Text != null)
{
var style = Style.Static(Text);
var isInline = string.Equals(Inline, "inline", StringComparison.OrdinalIgnoreCase);

var style = Style.Static(Text, isInline);

// Allow multiple styles.
context.AddGlobalData(style);
Expand Down
7 changes: 6 additions & 1 deletion Mjml.Net/Components/IncludeComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public sealed partial class IncludeComponent : Component
[Bind("path", BindType.RequiredString)]
public string Path;

[Bind("css-inline", BindType.Inline)]
public string? CssInline;

[Bind("type", typeof(TypeValidator))]
public string Type;

Expand Down Expand Up @@ -153,8 +156,10 @@ public override void Render(IHtmlRenderer renderer, GlobalContext context)
}
else if (ActualType == IncludeType.Css)
{
var isInline = string.Equals(CssInline, "inline", StringComparison.OrdinalIgnoreCase);

// Allow multiple styles and render them later.
context.AddGlobalData(Style.Static(new InnerTextOrHtml(content)));
context.AddGlobalData(Style.Static(new InnerTextOrHtml(content), isInline));
}
}

Expand Down
20 changes: 16 additions & 4 deletions Mjml.Net/Helpers/Style.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace Mjml.Net.Helpers;

public sealed record Style(Action<IHtmlRenderer, GlobalContext> Renderer) : GlobalData
public sealed record Style(Action<IHtmlRenderer, GlobalContext> Renderer, bool Inline = false) : GlobalData
{
public static Style Static(InnerTextOrHtml text)
public static Style Static(InnerTextOrHtml text, bool inline)
{
return new Style((renderer, _) => renderer.Content(text));
return new Style((renderer, _) => renderer.Content(text), inline);
}
}

Expand Down Expand Up @@ -65,11 +65,23 @@ private static void WriteMediaQueriesThunderbird(IHtmlRenderer renderer, GlobalC
}

private static void WriteStyles(IHtmlRenderer renderer, GlobalContext context)
{
WriteStyles(renderer, context, context.GlobalData.Values.OfType<Style>().Where(x => !x.Inline), null);

var inlineStyles = context.GlobalData.Values.OfType<Style>().Where(x => x.Inline).ToList();
if (inlineStyles.Count > 0)
{
WriteStyles(renderer, context, inlineStyles, "inline");
}
}

private static void WriteStyles(IHtmlRenderer renderer, GlobalContext context, IEnumerable<Style> inline, string? inlineAttr)
{
renderer.StartElement("style")
.Attr("inline", inlineAttr)
.Attr("type", "text/css");

foreach (var style in context.GlobalData.Values.OfType<Style>())
foreach (var style in inline)
{
style.Renderer(renderer, context);
}
Expand Down
6 changes: 6 additions & 0 deletions Mjml.Net/IPostProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Mjml.Net;

public interface IPostProcessor
{
string PostProcess(string html, MjmlOptions options);
}
8 changes: 7 additions & 1 deletion Mjml.Net/MjmlOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,15 @@ public sealed partial record MjmlOptions
public string Breakpoint { get; set; } = "480px";

/// <summary>
/// A list of custom styles. The default is: <c>false</c>.
/// A list of custom styles. The default is: <c>null</c>.
/// </summary>
public Style[]? Styles { get; init; }

/// <summary>
/// A list of custom post processors. The default is <c>null</c>.
/// </summary>
public IPostProcessor[] PostProcessors = [];

/// <summary>
/// True to enable media queries for OWA. The default is: <c>false</c>.
/// </summary>
Expand All @@ -47,6 +52,7 @@ public sealed partial record MjmlOptions
/// <summary>
/// True to minify the HTML. The default is: <c>false</c>.
/// </summary>
[Obsolete("No supported anymore.")]
public bool Minify { get; init; }

/// <summary>
Expand Down
12 changes: 11 additions & 1 deletion Mjml.Net/MjmlRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,17 @@ private RenderResult RenderCore(string mjml, MjmlOptions? options)
StringBuilder? buffer = null;
try
{
buffer = context.EndBuffer();
buffer = context.EndBuffer()!;

var result = buffer.ToString();

if (options.PostProcessors != null)
{
foreach (var processor in options.PostProcessors)
{
result = processor.PostProcess(result, options);
}
}

return new RenderResult(buffer!.ToString()!, context.Validate());
}
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public static void Main (string[] args) {
</mj-body>
</mjml>";

var options = new MjmlOptions {
var options = new MjmlOptions
{
Beautify = false
};

Expand Down Expand Up @@ -96,7 +97,8 @@ public static void Main (string[] args) {
</mj-body>
</mjml>";

var options = new MjmlOptions {
var options = new MjmlOptions
{
Beautify = false
};

Expand All @@ -116,7 +118,6 @@ You can also specify options to the MJML parser.
| Styles | Style[]? | [] | A list of custom styles. |
| ForceOWAQueries | bool | false | True to enable media queries for OWA. |
| Beautify | bool | true | True to beatify the HTML. Impacts performance (slower). |
| Minify | bool | false | True to minify the HTML. |
| Lax | bool | false | In lax mode some errors in the XML will be fixed. Only work when the MJML is passed in as string. Do not turn this on in production, because it can hurt performance. |
| IdGenerator | IIdGenerator | Preset | The ID generator to create random values for attributes like Ids. |
| Fonts | IReadOnlyDictionary<string, Font[]> | Preset | A list of supported default fonts. |
Expand Down
6 changes: 5 additions & 1 deletion Tests/Components/Outputs/StyleInclude.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@
.red-text div {
color: red !important;
}
</style>
</style>

<div class="red-text">
<div></div>
</div>
23 changes: 23 additions & 0 deletions Tests/Components/Outputs/StyleInline.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<style type="text/css">
@media only screen and (min-width:480px) {
}
</style>

<style media="screen and (min-width:480px)">
</style>

<style type="text/css">
</style>

<style type="text/css">
</style>

<style inline="inline" type="text/css">
.red-text div {
color: red !important;
}
</style>

<div class="red-text">
<div style="color: red !important;"></div>
</div>
Loading

0 comments on commit 812c49b

Please sign in to comment.