Skip to content

Commit 80ca746

Browse files
authored
Merge pull request #6 from RudeySH/master
Minify Razor @Helper functions by implementing a build provider, resolves #5
2 parents 0e12df6 + 70505ed commit 80ca746

File tree

8 files changed

+174
-83
lines changed

8 files changed

+174
-83
lines changed

README.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The library is also [published on NuGet.org](https://www.nuget.org/packages/Razo
1919
PM> Install-Package RazorHtmlMinifier.Mvc5
2020
```
2121

22-
<sup>RazorHtmlMinifier.Mvc5 is built for .NET v4.5 with a dependency on ASP.NET MVC 5.2.3.</sup>
22+
<sup>RazorHtmlMinifier.Mvc5 is built for .NET v4.5 with a dependency on ASP.NET MVC 5.2.3 and `System.Web`.</sup>
2323

2424
### Configuration
2525

@@ -29,21 +29,36 @@ Find the **Web.config** with your Razor configuration (by default it's in `Views
2929
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
3030
```
3131

32-
In order to start minifying, replace it with (after the NuGet package is installed):
32+
In order to start minifying views and partial views, replace it with (after the NuGet package is installed):
3333

3434
```xml
3535
<host factoryType="RazorHtmlMinifier.Mvc5.MinifyingMvcWebRazorHostFactory, RazorHtmlMinifier.Mvc5, Version=1.2.0.0, Culture=neutral, PublicKeyToken=a517a17e203fcde4" />
3636
```
3737

3838
Then rebuild your solution, which should also restart the app.
3939

40+
If you're using Razor `@helper` functions placed inside the `App_Code` folder, you have to do additional configuration in order to minify those. In the root `Web.config`, you should see something like this:
41+
42+
```xml
43+
<compilation debug="true" targetFramework="4.7.2" />
44+
```
45+
46+
Your `<compilation>` element might have different attributes. Leave the current attributes as-is, and add `<buildProviders>` like so:
47+
48+
```xml
49+
<compilation debug="true" targetFramework="4.7.2">
50+
<buildProviders>
51+
<add extension=".cshtml" type="RazorHtmlMinifier.Mvc5.MinifyingRazorBuildProvider, RazorHtmlMinifier.Mvc5" />
52+
</buildProviders>
53+
</compilation>
54+
```
4055

4156
How it works
4257
------------
4358

4459
The minifier processes the code generated during Razor compilation. Because it runs in compile-time, it shouldn't add any overhead during runtime.
4560

46-
The entire source code is just a [single file](https://github.com/tompazourek/RazorHtmlMinifier.Mvc5/blob/master/src/RazorHtmlMinifier.Mvc5/MinifyingMvcWebRazorHostFactory.cs), feel free to view it.
61+
The entire source code is just [two](/src/RazorHtmlMinifier.Mvc5/MinifyingMvcWebRazorHostFactory.cs) [files](/src/RazorHtmlMinifier.Mvc5/MinifyingRazorBuildProvider.cs), feel free to view them.
4762

4863
The minification algorithm is fairly trivial. It basically:
4964

@@ -67,10 +82,10 @@ I've investigated this and it looks like **VS actually needs to have the assembl
6782

6883
If you want to add the assembly to GAC, you'll need to do the following:
6984

70-
- Open `Developer Command Prompt for VS 2017` (you'll find it in Start menu) **as an Administrator**.
85+
- Open `Developer Command Prompt for VS` (you'll find it in Start menu) **as an Administrator**.
7186
- Navigate to the folder of the NuGet package: `cd "C:\PATH_TO_YOUR_SOLUTION\packages\RazorHtmlMinifier.Mvc5.1.2.0\lib\net45"`
7287
- Install it to GAC: `gacutil /i RazorHtmlMinifier.Mvc5.dll` (it should respond `Assembly successfully added to the cache`)
73-
- Restart VS 2017 (and maybe also clear any ReSharper caches if you're using that)
88+
- Restart Visual Studio (and maybe also clear any ReSharper caches if you're using that)
7489

7590
**Then it should start working.**
7691

src/RazorHtmlMinifier.Mvc5/MinifyingMvcWebRazorHostFactory.cs

Lines changed: 63 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -40,95 +40,95 @@ public override RazorCodeGenerator DecorateCodeGenerator(RazorCodeGenerator inco
4040
return new MinifyingMvcCSharpRazorCodeGenerator(incomingCodeGenerator.ClassName, incomingCodeGenerator.RootNamespaceName, incomingCodeGenerator.SourceFileName, incomingCodeGenerator.Host);
4141
}
4242
}
43+
}
4344

44-
private class MinifyingMvcCSharpRazorCodeGenerator : CSharpRazorCodeGenerator
45+
internal class MinifyingMvcCSharpRazorCodeGenerator : CSharpRazorCodeGenerator
46+
{
47+
public MinifyingMvcCSharpRazorCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host)
48+
: base(className, rootNamespaceName, sourceFileName, host)
4549
{
46-
public MinifyingMvcCSharpRazorCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host)
47-
: base(className, rootNamespaceName, sourceFileName, host)
50+
if (host is MvcWebPageRazorHost mvcHost && !mvcHost.IsSpecialPage)
4851
{
49-
if (host is MvcWebPageRazorHost mvcHost && !mvcHost.IsSpecialPage)
50-
{
51-
// set base type dynamic by default
52-
var baseType = new CodeTypeReference($"{Context.Host.DefaultBaseClass}<dynamic>");
53-
Context.GeneratedClass.BaseTypes.Clear();
54-
Context.GeneratedClass.BaseTypes.Add(baseType);
55-
}
52+
// set base type dynamic by default
53+
var baseType = new CodeTypeReference($"{Context.Host.DefaultBaseClass}<dynamic>");
54+
Context.GeneratedClass.BaseTypes.Clear();
55+
Context.GeneratedClass.BaseTypes.Add(baseType);
5656
}
57+
}
5758

58-
public override void VisitSpan(Span currentSpan)
59+
public override void VisitSpan(Span currentSpan)
60+
{
61+
if (currentSpan?.Kind == SpanKind.Markup)
5962
{
60-
if (currentSpan?.Kind == SpanKind.Markup)
61-
{
62-
VisitMarkupSpan(currentSpan);
63-
}
64-
65-
base.VisitSpan(currentSpan);
63+
VisitMarkupSpan(currentSpan);
6664
}
6765

68-
private void VisitMarkupSpan(Span currentMarkupSpan)
69-
{
70-
var builder = new SpanBuilder(currentMarkupSpan);
71-
builder.ClearSymbols();
66+
base.VisitSpan(currentSpan);
67+
}
7268

73-
var previousMarkupSpan = currentMarkupSpan.Previous?.Kind == SpanKind.Markup ? currentMarkupSpan.Previous : null;
74-
var previousSymbol = previousMarkupSpan?.Symbols.LastOrDefault();
69+
private void VisitMarkupSpan(Span currentMarkupSpan)
70+
{
71+
var builder = new SpanBuilder(currentMarkupSpan);
72+
builder.ClearSymbols();
7573

76-
foreach (var currentSymbol in currentMarkupSpan.Symbols)
77-
{
78-
VisitSymbol(currentSymbol, previousSymbol, builder);
79-
previousSymbol = currentSymbol;
80-
}
74+
var previousMarkupSpan = currentMarkupSpan.Previous?.Kind == SpanKind.Markup ? currentMarkupSpan.Previous : null;
75+
var previousSymbol = previousMarkupSpan?.Symbols.LastOrDefault();
8176

82-
currentMarkupSpan.ReplaceWith(builder);
77+
foreach (var currentSymbol in currentMarkupSpan.Symbols)
78+
{
79+
VisitSymbol(currentSymbol, previousSymbol, builder);
80+
previousSymbol = currentSymbol;
8381
}
8482

85-
private static void VisitSymbol(ISymbol currentSymbol, ISymbol previousSymbol, SpanBuilder builder)
83+
currentMarkupSpan.ReplaceWith(builder);
84+
}
85+
86+
private static void VisitSymbol(ISymbol currentSymbol, ISymbol previousSymbol, SpanBuilder builder)
87+
{
88+
if (IsSymbolWhiteSpaceOrNewLine(currentSymbol, out var currentHtmlSymbol))
8689
{
87-
if (IsSymbolWhiteSpaceOrNewLine(currentSymbol, out var currentHtmlSymbol))
90+
if (IsSymbolWhiteSpaceOrNewLine(previousSymbol, out var _))
8891
{
89-
if (IsSymbolWhiteSpaceOrNewLine(previousSymbol, out var _))
90-
{
91-
// both current and previous symbols are whitespace/newline, we can skip current symbol
92-
return;
93-
}
94-
95-
// current symbol is whitespace/newline, previous is not, we'll replace current with the smallest
96-
var replacementSymbol = GetReplacementSymbol(currentHtmlSymbol);
97-
builder.Accept(replacementSymbol);
92+
// both current and previous symbols are whitespace/newline, we can skip current symbol
9893
return;
9994
}
10095

101-
builder.Accept(currentSymbol);
96+
// current symbol is whitespace/newline, previous is not, we'll replace current with the smallest
97+
var replacementSymbol = GetReplacementSymbol(currentHtmlSymbol);
98+
builder.Accept(replacementSymbol);
99+
return;
102100
}
103101

104-
private static bool IsSymbolWhiteSpaceOrNewLine(ISymbol symbol, out HtmlSymbol outputHtmlSymbol)
102+
builder.Accept(currentSymbol);
103+
}
104+
105+
private static bool IsSymbolWhiteSpaceOrNewLine(ISymbol symbol, out HtmlSymbol outputHtmlSymbol)
106+
{
107+
if (symbol is HtmlSymbol htmlSymbol)
105108
{
106-
if (symbol is HtmlSymbol htmlSymbol)
109+
if (htmlSymbol.Type == HtmlSymbolType.WhiteSpace || htmlSymbol.Type == HtmlSymbolType.NewLine)
107110
{
108-
if (htmlSymbol.Type == HtmlSymbolType.WhiteSpace || htmlSymbol.Type == HtmlSymbolType.NewLine)
109-
{
110-
outputHtmlSymbol = htmlSymbol;
111-
return true;
112-
}
111+
outputHtmlSymbol = htmlSymbol;
112+
return true;
113113
}
114-
115-
outputHtmlSymbol = null;
116-
return false;
117114
}
118115

119-
private static HtmlSymbol GetReplacementSymbol(HtmlSymbol htmlSymbol)
116+
outputHtmlSymbol = null;
117+
return false;
118+
}
119+
120+
private static HtmlSymbol GetReplacementSymbol(HtmlSymbol htmlSymbol)
121+
{
122+
switch (htmlSymbol.Type)
120123
{
121-
switch (htmlSymbol.Type)
122-
{
123-
case HtmlSymbolType.WhiteSpace:
124-
// any amount of whitespace is replaced with a single space
125-
return new HtmlSymbol(htmlSymbol.Start, " ", HtmlSymbolType.WhiteSpace, htmlSymbol.Errors);
126-
case HtmlSymbolType.NewLine:
127-
// newline is replaced with just \n
128-
return new HtmlSymbol(htmlSymbol.Start, "\n", HtmlSymbolType.NewLine, htmlSymbol.Errors);
129-
default:
130-
throw new ArgumentException($"Expected either {HtmlSymbolType.WhiteSpace} or {HtmlSymbolType.NewLine} symbol, {htmlSymbol.Type} given.");
131-
}
124+
case HtmlSymbolType.WhiteSpace:
125+
// any amount of whitespace is replaced with a single space
126+
return new HtmlSymbol(htmlSymbol.Start, " ", HtmlSymbolType.WhiteSpace, htmlSymbol.Errors);
127+
case HtmlSymbolType.NewLine:
128+
// newline is replaced with just \n
129+
return new HtmlSymbol(htmlSymbol.Start, "\n", HtmlSymbolType.NewLine, htmlSymbol.Errors);
130+
default:
131+
throw new ArgumentException($"Expected either {HtmlSymbolType.WhiteSpace} or {HtmlSymbolType.NewLine} symbol, {htmlSymbol.Type} given.");
132132
}
133133
}
134134
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Web.Razor.Generator;
2+
using System.Web.WebPages.Razor;
3+
4+
namespace RazorHtmlMinifier.Mvc5
5+
{
6+
public class MinifyingRazorBuildProvider : RazorBuildProvider
7+
{
8+
protected override WebPageRazorHost GetHostFromConfig()
9+
{
10+
var host = base.GetHostFromConfig();
11+
12+
if (host is WebCodeRazorHost) // this will be true when the virtual path starts with "~/App_Code"
13+
{
14+
return new MinifyingWebCodeRazorHost(host.VirtualPath, host.PhysicalPath);
15+
}
16+
17+
return host;
18+
}
19+
20+
private class MinifyingWebCodeRazorHost : WebCodeRazorHost
21+
{
22+
public MinifyingWebCodeRazorHost(string virtualPath, string physicalPath) : base(virtualPath, physicalPath)
23+
{
24+
}
25+
26+
public override RazorCodeGenerator DecorateCodeGenerator(RazorCodeGenerator incomingCodeGenerator)
27+
{
28+
if (!(incomingCodeGenerator is CSharpRazorCodeGenerator))
29+
return base.DecorateCodeGenerator(incomingCodeGenerator);
30+
31+
return new MinifyingMvcCSharpRazorCodeGenerator(incomingCodeGenerator.ClassName, incomingCodeGenerator.RootNamespaceName, incomingCodeGenerator.SourceFileName, incomingCodeGenerator.Host);
32+
}
33+
}
34+
}
35+
}

src/RazorHtmlMinifier.Mvc5/RazorHtmlMinifier.Mvc5.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@
3232
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.3" />
3333
</ItemGroup>
3434

35+
<ItemGroup>
36+
<Reference Include="System.Web" />
37+
</ItemGroup>
38+
3539
</Project>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
@helper Helper() {
2+
<div id="lipsum-helper">
3+
4+
<p>
5+
Vivamus sodales fermentum auctor. Vestibulum vulputate metus vitae tellus fringilla,
6+
quis laoreet purus vestibulum. Fusce a ex vitae libero auctor rutrum et quis nisi.
7+
Vestibulum a mi scelerisque, laoreet urna id, blandit dui. Ut eleifend ornare erat,
8+
ac rhoncus ligula elementum quis. Curabitur vulputate, lectus eu sagittis bibendum,
9+
est felis fermentum ipsum, id bibendum arcu lacus vitae ante. Quisque rutrum accumsan
10+
risus, ullamcorper dapibus est elementum ac. Morbi porta iaculis diam, ut mollis magna
11+
maximus nec. Curabitur vestibulum tortor risus, at tincidunt eros mollis at.
12+
Suspendisse pharetra leo nunc, vitae mattis urna sodales sit amet. Nam et quam molestie,
13+
dignissim enim ac, facilisis ex. Donec dapibus cursus elit ac porta. Curabitur ut lorem
14+
tempus, viverra magna non, aliquet orci. Suspendisse molestie arcu eget nibh iaculis,
15+
vitae porta est molestie. Maecenas pharetra, nunc vitae posuere porttitor, elit lectus
16+
dictum orci, a tristique dui nulla dignissim elit. Sed aliquam est vel vulputate ultrices.
17+
</p>
18+
19+
20+
<p>
21+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer quis nulla malesuada,
22+
tempor augue non, ultricies arcu. Pellentesque condimentum ligula quis arcu vestibulum,
23+
eget tincidunt eros sodales. Sed porta ullamcorper diam vel fermentum. Vivamus sit amet
24+
laoreet erat, eleifend laoreet eros. Praesent iaculis nibh in lorem porttitor mattis.
25+
Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean eleifend mauris quis
26+
commodo elementum.
27+
</p>
28+
29+
</div>
30+
}

src/RazorHtmlMinifier.Sample/RazorHtmlMinifier.Sample.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
<Folder Include="Properties\" />
114114
</ItemGroup>
115115
<ItemGroup>
116+
<Content Include="App_Code\Helpers.cshtml" />
116117
<None Include="packages.config" />
117118
</ItemGroup>
118119
<PropertyGroup>

src/RazorHtmlMinifier.Sample/Views/Home/Index.cshtml

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,33 @@
66
</head>
77
<body>
88
<div id="lipsum">
9-
9+
1010
<p>
1111
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas aliquet vulputate
1212
lectus et tincidunt. Donec non libero ligula. Ut ultrices eleifend nisl, sed faucibus
13-
arcu porta non. Sed ac blandit velit, et sagittis turpis. Aenean tristique sollicitudin diam,
14-
interdum venenatis risus viverra at. Nullam non lobortis lectus. Pellentesque sagittis, arcu
15-
vel auctor mattis, erat nisl vestibulum enim, eget fermentum lorem risus nec ligula. Aliquam
13+
arcu porta non. Sed ac blandit velit, et sagittis turpis. Aenean tristique sollicitudin diam,
14+
interdum venenatis risus viverra at. Nullam non lobortis lectus. Pellentesque sagittis, arcu
15+
vel auctor mattis, erat nisl vestibulum enim, eget fermentum lorem risus nec ligula. Aliquam
1616
aliquam ante erat, vel lobortis quam scelerisque id. In facilisis arcu non orci iaculis blandit.
1717
</p>
1818

1919

2020
<p>
2121
Vestibulum et justo eros. Morbi consectetur feugiat felis, in facilisis quam malesuada non.
2222
Maecenas euismod lectus vitae nibh blandit vehicula. Suspendisse ornare ante quis velit consequat,
23-
ac iaculis odio tristique. Nullam ligula leo, convallis eu bibendum at, tincidunt ac libero.
24-
Vivamus luctus, tellus vitae bibendum suscipit, mi nulla laoreet urna, vitae condimentum justo ex
25-
vel sapien. Quisque ante lacus, luctus nec finibus sed, sollicitudin eget magna. Nunc ultricies
26-
quam at nisl scelerisque sagittis. Nulla urna nulla, dictum eu congue non, porttitor ut turpis.
27-
Nunc congue eleifend dui, eu pretium tellus finibus at. Duis mollis nisi a gravida sodales.
28-
Sed scelerisque blandit augue vel commodo. Nam auctor odio in ante iaculis congue. Ut vel auctor nisl.
23+
ac iaculis odio tristique. Nullam ligula leo, convallis eu bibendum at, tincidunt ac libero.
24+
Vivamus luctus, tellus vitae bibendum suscipit, mi nulla laoreet urna, vitae condimentum justo ex
25+
vel sapien. Quisque ante lacus, luctus nec finibus sed, sollicitudin eget magna. Nunc ultricies
26+
quam at nisl scelerisque sagittis. Nulla urna nulla, dictum eu congue non, porttitor ut turpis.
27+
Nunc congue eleifend dui, eu pretium tellus finibus at. Duis mollis nisi a gravida sodales.
28+
Sed scelerisque blandit augue vel commodo. Nam auctor odio in ante iaculis congue. Ut vel auctor nisl.
2929
Vestibulum molestie ligula vitae orci tincidunt posuere.
3030
</p>
31-
32-
31+
32+
3333
<p>
34-
Nulla rhoncus mauris eget ligula dictum sagittis. Cras tincidunt vehicula neque, eu accumsan
35-
velit sodales nec. Nam quis sodales metus. Nulla facilisi. Pellentesque consequat feugiat tortor,
34+
Nulla rhoncus mauris eget ligula dictum sagittis. Cras tincidunt vehicula neque, eu accumsan
35+
velit sodales nec. Nam quis sodales metus. Nulla facilisi. Pellentesque consequat feugiat tortor,
3636
non eleifend neque cursus vel. Quisque porttitor nunc non nibh sodales, in cursus libero mattis.
3737
Vivamus rutrum ultricies ornare. Nunc sed varius sapien, non porttitor nunc. Orci varius natoque
3838
penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed bibendum lacus et volutpat
@@ -45,6 +45,8 @@
4545

4646
@Html.Partial("_Partial")
4747

48+
@Helpers.Helper()
49+
4850
</div>
4951
</body>
5052
</html>

src/RazorHtmlMinifier.Sample/Web.config

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
<configuration>
44
<system.web>
5-
<compilation debug="true" targetFramework="4.5" />
5+
<compilation debug="true" targetFramework="4.5">
6+
<buildProviders>
7+
<add extension=".cshtml" type="RazorHtmlMinifier.Mvc5.MinifyingRazorBuildProvider, RazorHtmlMinifier.Mvc5" />
8+
</buildProviders>
9+
</compilation>
610
<httpRuntime targetFramework="4.5" />
711
</system.web>
812
<system.webServer>

0 commit comments

Comments
 (0)