statiqdev / Statiq.Framework

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

Unable to add SmartyPants extension via AddSetting #266

Closed kentcb closed 1 year ago

kentcb commented 1 year ago

Hi,

The following code:

    Bootstrapper
        .Factory
        .CreateWeb(argv)
        .AddSetting(Statiq.Markdown.MarkdownKeys.MarkdownExtensions, ResizeArray(seq { "Bootstrap"; "CustomContainer"; "SmartyPants" }))
        .RunAsync()

fails at runtime with:

[ERRO] Content/Process » CacheDocuments » RenderContentProcessTemplates » ExecuteIf » RenderMarkdown » [C:/Users/.../index.md => index.html] Markdown extension SmartyPants does not have a usable constructor

Indeed, it does appear as though the extension code is a little bit different in that it takes an optional Settings object, but it's unclear to me whether it should just work because it's optional and, if not, how to provide the options via the AddSetting extension.

daveaglick commented 1 year ago

That value syntax for the MarkdownExtensions looks like F#? And if so, is the C# equivalent new [] { "Bootstrap", "CustomContainer", "SmartyPants" } to make sure I'm doing the same thing?

daveaglick commented 1 year ago

I'm able to replicate this, and the problematic code is here: image

There's probably a few things that need to be done here to improve extension loading. For one, it does try to fill a non-default constructor with default arguments, but maybe that's not working in this case because the SmartyPants extension is expecting a nullable (need to research this part more).

You can already add extension instances today by modifying the Markdown template and RenderMarkdown module:

await Bootstrapper.Factory
    .CreateWeb(args)
    .ModifyTemplate(
        MediaTypes.Markdown,
        x => ((RenderMarkdown)x)
            .UseExtension<BootstrapExtension>()
            .UseExtension<CustomContainerExtension>()
            .UseExtension(new SmartyPantsExtension(new SmartyPantOptions())))
    .RunAsync();

This compiles and runs, but I still don't see the revised characters in the output, so I'll need to look more closely at this approach as well. Stay tuned...

daveaglick commented 1 year ago

Okay, I take it back about the code block above not working - .UseExtension(new SmartyPantsExtension(new SmartyPantOptions())) does appear to work as intended and enable the SmartyPant extension, I just didn't know what I was looking for.

Without: image

With: image

Does that satisfy your use case? If so I'll close out this issue and add some content to the docs about how to configure Markdig extensions that don't have a default constructor.

kentcb commented 1 year ago

Thanks @daveaglick. Yes, I specifically wanted it for smart quotes, but it certainly looks like it's working based on your screenshots.

daveaglick commented 1 year ago

Great, I'll go ahead and close this since there's already a way to add explicit Markdig extension instances, albeit undocumented. As far as documentation, I also went ahead and added a section to the documentation about how to do this too, and explained it might be needed if you see this error.