Minifying Wyam Output - Part 2
Update 2019-01-31:
Wyam 2.1.2 and 2.1.3 include an update implementing IList<IfCondition>
on If
modules. This makes adding MinifyHtml to the TagIndex and BlogArchive Pipelines much easier than before:
Pipelines["TagIndex"].GetFirst<If>()[0].InsertBeforeFirst<WriteFiles>
(
new MinifyHtml()
);
Pipelines["BlogArchive"].GetFirst<If>()[0].InsertBeforeFirst<WriteFiles>
(
new MinifyHtml()
);
Original Article:
While the pipelines in Part 1 made it easy to insert a minification module before WriteFiles was executed, both TagIndex and BlogArchive make this much more difficult. This is due to the fact that they both execute conditionally, and therefore use an If module.
If
modules do not allow access to the child modules they contain, so barring reflection, it isn't possible to manipulate the modules that the If
executes. Instead, the If
module included in the recipe must be replaced outright with a customized version.
To do so, first start with the modules the TagIndex pipeline creates initially and add the minify module:
new If
(
ctx => ctx.Documents[Blog.Tags].Any(),
new ReadFiles("_Tags.cshtml"),
new FrontMatter(new Wyam.Yaml.Yaml()),
new Wyam.Razor.Razor()
.IgnorePrefix(null)
.WithLayout("/_Layout.cshtml"),
// Minify the HTML produced before writing
new MinifyHtml(),
new WriteFiles((doc, ctx) => "tags/index.html")
).WithoutUnmatchedDocuments()
Then use the new If
to replace TagIndex's default:
Pipelines["TagIndex"].Replace
(
0,
new If
(
ctx => ctx.Documents[Blog.Tags].Any(),
new ReadFiles("_Tags.cshtml"),
new FrontMatter(new Wyam.Yaml.Yaml()),
new Wyam.Razor.Razor()
.IgnorePrefix(null)
.WithLayout("/_Layout.cshtml"),
// Minify the HTML produced before writing
new MinifyHtml(),
new WriteFiles((doc, ctx) => "tags/index.html")
).WithoutUnmatchedDocuments()
);
Now the TagIndex pipeline is producing a minified page. Lastly, the BlogArchive pipeline needs to be minified. However, getting the default modules won't be as simple as it was with TagIndex. The Archive pipeline is generated based off of quite a few different settings. Instead, recreate the pipeline the same way the Blog recipe and save it to a variable:
var blogArchive = new Wyam.Web.Pipelines.Archive
(
"BlogArchive",
new Wyam.Web.Pipelines.ArchiveSettings
{
Pipelines = new string[] { "BlogPosts" },
TemplateFile = ctx => "_Archive.cshtml",
Layout = "/_Layout.cshtml",
PageSize = ctx => ctx.Get(BlogKeys.ArchivePageSize, int.MaxValue),
Title = (doc, ctx) => "Archive",
RelativePath = (doc, ctx) =>
$"{ctx.DirectoryPath(BlogKeys.PostsPath, ".").FullPath}"
}
);
With the fresh, unwrapped pipeline, insert the minify module before WriteFiles:
blogArchive.InsertBefore
(
"WriteFiles",
"MinifyArchive",
new MinifyHtml()
);
Finally, the old modules can be replaced in the same way that TagIndex's modules were replaced. Since BlogArchive is a pipeline, just create a module array to use with the new If
:
Pipelines["BlogArchive"].Replace
(
0,
new If
(
ctx => ctx.Bool(BlogKeys.GenerateArchive),
blogArchive.ToArray()
)
);
At last, it's all minified. Unfortunately, the method used in part 2 is rather brittle, since any changes to the Blog recipe will now have to be manually accounted for. Hopefully, the If
module will provide access to it's children in the future.
In total, minifying Part 1 shaved off 14% of the size of the HTML + CSS. However, ociaw.com is intentionally simple and minimalistic, so bigger gains can be realized if a heavier website is minified. There's still room for improvement, however. SVG files, the RSS/Atom feeds, and the sitemap can all be minified as well.