statiqdev / Statiq.Framework

A flexible and extensible static content generation framework for .NET.
https://statiq.dev/framework
MIT License
425 stars 74 forks source link

Add comment style front matter for Razor #231

Closed daveaglick closed 2 years ago

daveaglick commented 2 years ago

See https://github.com/statiqdev/Discussions/discussions/132

Note that the syntax should clearly indicate front matter over other top-of-file block comments. I.e.

@*---
Front: matter
---*@

As part of this change, since we're establishing a pattern, the script front matter comment support should be changed to require a similar dash style delimiter.

daveaglick commented 2 years ago

To that end I wonder if it's possible to generalize this and look for --- at the end of the first line of a file and at the start of a following line (with the reverse of the first line extra characters?).

daveaglick commented 2 years ago

So it turns out changing the module to accept two different styles of delimiters ("normal" and the proposed comment style) is going to be tricky. It would also be nice to support multiple delimiter sets in a single module so as more kinds of delimiters are supported we don't have to continue parsing the file into a string array each time. Being able to pass in front matter delimiter data as a single string would be nice too so we can add a setting to describe additional front matter delimiters.

I think the answer to all these is to support RegEx for specifying how to find front matter. That leaves a question of what to do about existing code. Should the current way of specifying also be accepted (and maybe converted to a RegEx)?

daveaglick commented 2 years ago

This is now done and will go out with the next Statiq Web release, which will allow syntax like what's in the description of this issue for defining front matter in Razor files (in addition to HTML comment syntax like <!--- as well).

In Statiq Framework front matter is extracted by the ExtractFrontMatter module which now accepts regular expressions. When using the ExtractFrontMatter module in Statiq Framework without predefined front matter handling like Statiq Web provides, the following regular expressing can be used for Razor block comment front matter: \A(?:^\r*@\*-+[^\S\n]*$\r?\n)(.*?)(?:^\r*-+\*@[^\S\n]*$\r?\n).

ror3d commented 2 years ago

I'm trying to use this feature and I'm not sure it's working properly for me.

I have my project configured to use Statiq.Web 1.0.0-beta.47, so I think that should have the code for this to work already.

In the head of base.cshtml I have

@if(Document.GetBool("HighlightCode", false))
{
<link rel="stylesheet" href="highlight/styles/vs2015.min.css">
<script src="highlight/highlight.min.js" onload="hljs.highlightAll();"></script>
}

And then in post.cshtml I start the file with

@*---
HighlightCode: true
---*@

But that doesn't seem to include the contents of the @if. Am I doing something wrong?

Edit: looking more into the original discussion, this might be because I'm trying to use it in a template, whereas the original discussion was talking specifically about final pages containing the frontmatter.

Edit2: Okay, I sorted it out with the ViewData dictionary. It becomes a bit more complex than I hoped but it will have to do

@if((ViewData.ContainsKey("HighlightCode") && (bool)ViewData["HighlightCode"]) || Document.GetBool("HighlightCode", false))
daveaglick commented 2 years ago

What's the relationship between base.cshtml and post.cshtml? If base.html is a Razor layout, then the document metadata should still be available through the Document page property just like the actual page.

ror3d commented 2 years ago

Both are layouts.

A post will have Layout: @"_layouts/post.cshtml", whereas in the post.cshtml file I have

@{
    Layout = @"_layouts/base.cshtml";
}
daveaglick commented 2 years ago

Interesting - as far as I can tell, that should still work. Here's a little example I worked up...

input/_OuterLayout.cshtml

<html>
<head>
</head>
<body> 
<div>Outer layout color: @Document.GetString("Color")</div>
@RenderBody()
</body>
</html>

input/_InnerLayout.cshtml:

@{
    Layout = @"/_OuterLayout.cshtml";
}
<div>Inner layout color: @Document.GetString("Color")</div>
@RenderBody()

input/Test.cshtml:

@*---
Color: Blue
---*@
@{
    Layout = @"/_InnerLayout.cshtml";
}
<div>Test color: @Document.GetString("Color")</div>

Output:

<html>
<head>
</head>
<body> 
<div>Outer layout color: Blue</div>
<div>Inner layout color: Blue</div>
<div>Test color: Blue</div>
</body>
</html>

Have I correctly replicated your scenario, or did I miss something?

ror3d commented 2 years ago

Almost, but I was trying to set the frontmatter from the template, which I think is what's not supported in this manner.

input/_OuterLayout.cshtml

<body> 
<div>Outer layout color: @Document.GetString("Color")</div>
@RenderBody()
</body>

input/_InnerLayout.cshtml:

@*---
Color: Blue
---*@
@{
    Layout = @"/_OuterLayout.cshtml";
}
<div>Inner layout color: @Document.GetString("Color")</div>
@RenderBody()

input/Test.cshtml:

@{
    Layout = @"/_InnerLayout.cshtml";
}
<div>Test color: @Document.GetString("Color")</div>

Expected output:

<body> 
<div>Outer layout color: Blue</div>
<div>Inner layout color: Blue</div>
<div>Test color: Blue</div>
</body>
daveaglick commented 2 years ago

I was trying to set the frontmatter from the template, which I think is what's not supported in this manner

Ah, yes - you're exactly right. The layout files don't get processed in the same way so they can't set front matter (there's not even a Statiq document for them, they become "part" of the contained page during rendering).

Glad you figured out an approach that will (mostly) work though!