Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can we ditch Razor? #494

Closed
dsyme opened this issue Apr 24, 2019 · 28 comments
Closed

Can we ditch Razor? #494

dsyme opened this issue Apr 24, 2019 · 28 comments

Comments

@dsyme
Copy link
Contributor

dsyme commented Apr 24, 2019

Hi all,

We really need to move FSharp.Formatting to be completely based on .NET Core, because it is infecting many repositories with a dependency on Mono which is just a problem going forward for .NET-Core-centric development.

The main dependency issue seems to Razor, is that correct? Can I ask a few things about that?

  1. I understand that normally in F# doc generation there is a template.cshtml or similar. However these seem to typically use a very limited subset of Razor. Is that correct?

  2. Can we just take a feature reduction and implement our own limited templating? Is our use of typical use of templating basically string substitution?

Basically, what should we do to ditch Razor here, and what feature changes/reductions should we make to make it happen?

Some notes:

      let razor = RazorRender(layoutRoots |> Seq.toList, [], file, ?references = references)
      let props = [ "Properties", dict parameters ]
      let generated = razor.ProcessFile(props)
  • There is also RazorMetadataFormat, "This type exposes the functionality for producing documentation from dll files with associated xml files". Default templates seem to normally be used namespaces.cshtml, module.cshtml, type.cshtml. These do seem to use more serious templating, e.g. here. However surely we can find some other way of doing this in all F# code without requiring any HTML templating at all.

So, how bad is it if we just completely rearchitect how we do templating, and drop Razor completely. It just seems wrong for such a core F# technology to have this dependency.

@dsyme
Copy link
Contributor Author

dsyme commented Apr 24, 2019

The use of RazorEngine is another complication. It looks like a basically dead technology but sits between us and Razor, making it hard to see what's what.

@dsyme
Copy link
Contributor Author

dsyme commented Apr 24, 2019

My starting proposal is

  1. RazorLiterate.ProcessDirectory and friends only do the current property substitution through *.cshtml, there is no Razor support

  2. RazorMetadataFormat.Generate hardwires precisely the generation implied by the default, built-in namespaces.cshtml and friends - no custom templates are initially supported, and we implement the expansion by replacing the *.cshtml by bits of F# code that do exactly the same thing.

  3. At some later point RazorMetadataFormat.Generate can accept some kind of callback that does template formatting (but there is no built-in dependency on any particular formatter)

My general point is that I think it's essential we simplify the dependency chain here - and also simplify the spec of FSharp.Formatting so it doesn't include arbitrary Razor templates as input. The use of Razor by FSharp.Formatting is causing a Mono dependency in almost every single "fsprojects" project. We need a doc generation story that works on .NET Core alone.

@matthid
Copy link
Member

matthid commented Apr 24, 2019

I understand that normally in F# doc generation there is a template.cshtml or similar . However these seem to typically use a very limited subset of Razor. Is that correct?

I guess typical use-case is to not modify the template at all. However I have done very hard modifications. One public example is https://fake.build which is obviously generated by FSF as well (https://github.com/fsharp/FAKE/tree/release/next/help)

Can we just take a feature reduction and implement our own limited templating? Is our use of typical use of templating basically string substitution?

There are already some related discussions around this:

Honestly, I don't think using only string substitution will make a lot of fun.

So, how bad is it if we just completely rearchitect how we do templating, and drop Razor completely. It just seems wrong for such a core F# technology to have this dependency.

I think it's more a matter of packaging. For historic reasons we only have the FSharp.Formatting package. But in reality a lot of .dlls already work together and could be used on their own (for example the markdown parser). So I think splitting into several packages might be an easy first step to see what actually depends on what.

The use of RazorEngine is another complication. It looks like a basically dead technology but sits between us and Razor, making it hard to see what's what.

It's really just a thin layer with an easy API. It implements a bunch of interfaces you need to implement to use razor. It also handles such things as reusing/caching/cleanup and template resolution. I'd say nothing fundamental you couldn't rebuild from scratch. In fact using official razor packages is probably the way forward if they now have an API we can use (which I believe they have now).

RazorLiterate.ProcessDirectory and friends only do the current property substitution through *.cshtml, there is no Razor support

If it isn't razor it shouldn't be called razor. We should have a new function.

What you might not realize is that Razor is already separated into its own project (https://github.com/fsprojects/FSharp.Formatting/tree/master/src/FSharp.Formatting.Razor)
and all other projects don't have that dependency afaik (https://github.com/fsprojects/FSharp.Formatting/tree/master/src)

@matthid
Copy link
Member

matthid commented Apr 24, 2019

What you might not realize is that Razor is already separated into its own project (https://github.com/fsprojects/FSharp.Formatting/tree/master/src/FSharp.Formatting.Razor )
and all other projects don't have that dependency afaik (https://github.com/fsprojects/FSharp.Formatting/tree/master/src )

Or put into different words: We are already ready code-wise. All we need is someone implementing a new templating engine and prove it is useful ;)
I don't consider string replacements 'useful' in that case we should suggest people to just use https://dotnet.github.io/docfx/ or maybe integrate there?

@thinkbeforecoding
Copy link
Contributor

About splitting packages, the Fsharp.Literate package already contains the dlls without the templating and the tool. It can be used with other template engines like fable.react server side static rendering for advanced templating scenario

@matthid
Copy link
Member

matthid commented Apr 24, 2019

@thinkbeforecoding Yes indeed. Though I think the Literate part is not exactly the most interesting part (but still important in the hole picture). I'd argue that you can quickly build something similar using FCS and some other markdown library.

@dsyme
Copy link
Contributor Author

dsyme commented Apr 24, 2019

Splitting alone (and using the DLLs like Literate as they are) doesn't help me. What I'm after an end-to-end doc generation story for "fsprojects" projects which is purely based on .NET Core.

And that basically means the default functionality of ProcessDirectory and MetadataFormat.Generate, preferably driven by a really-easy-to-use command line tool.

I suppose we could add the equivalent of MetadataFormat.Generate to FSharp.Formatting.Literate - just the "simple" versions which does the default job without Razor.

My baseline is really that "ProjectScaffold" projects (and thus typical fsprojects projects) be completely free of any Mono-based tooling. That would mean the FSharp.Formatting command line tool and the "FSFormatting" module in FAKE are both free of Razor. I don't mind if the Razor support continues in some other project as some optional thing (e.g. a FAKE module FSFormatting.Razor). But FSharp.Formatting itself (and its command-line tool and FAKE module) should really be free of Razor - otherwise we infect every project with Mono dependencies.

Note I also don't want FSharp.Formatting to be interesting I just want it to work without Mono :)

@matthid
Copy link
Member

matthid commented Apr 24, 2019

I don't disagree, but we need a replacement and someone to do it :)
Don't get me wrong I'm not against simplifying stuff but as docs generation is basically a tree-like structure I don't think string replacement will work as long as you want/need any kind of customization.

@thinkbeforecoding
Copy link
Contributor

@dsyme yes.. What I meant is that no need for advanced templating in the CLI tool.
Basic, straight forward, easy to use and non customizable is enough for the fsproject cli tool use case. Like string replacement.
For most advanced case, use fsharp.formating and a specific templating engine.

@smoothdeveloper
Copy link

smoothdeveloper commented Apr 25, 2019

Can we consider if https://github.com/dotnet-websharper/ui/blob/master/docs/UINext-Templates.md is a good base for what could replace "Razor"?

I've used it a bit for simple html responses, and it worked for the deed and, it is less flexible than a full fledged language in text templating DSL (such as asp/t4/razor) but it could fit the use intended for FSharp.Formatting / netstandard support.

@matthid
Copy link
Member

matthid commented Apr 25, 2019

What I meant is that no need for advanced templating in the CLI tool.
Basic, straight forward, easy to use and non customizable is enough for the fsproject cli tool use case. Like string replacement.

I would only believe that if I see it used for some real thing. We definitely would need a working POC. Obviously, for me it would be important that we can still generate the FAKE docs.

But that's basically the minimum bar for any replacement.

Another option is to have some new command line tool (different package). In that scenario we can easily start from scratch. It might split the ecosystem and I still doubt it will be 'usable' with string replacement only, but it could be a starting point.

@TheAngryByrd
Copy link

https://github.com/TheAngryByrd/MiniScaffold/pull/110/files has all of the FSharp.Formatting razor templates converted to Fable.React DSL style components. This is using pure .net core as well, the only thing needed for mono in MiniScaffold is if you want to build net45+ on osx/linux.

@wallymathieu
Copy link
Member

If you look at Razor, it's essentially a language that consists of a mix of c# and HTML syntax. Is there a need for such a full blown templating solution? Could a mustache inspired templating solution work (since it's a simpler syntax)? There seems a mustache like templating called Fue and then there is mustache implementation in c# Stubble.

Otherwise, what @TheAngryByrd has done looks like it could be a good default solution.

@smoothdeveloper
Copy link

If we are going to remove current usage of razor and templates, I'm actually considering Spark view engine:

https://github.com/SparkViewEngine/spark

  • statically typed / compile time checking
  • works with plain CLR types, I expect it to work well with F#
  • is well documented & over a decade of usage in Monorail & ASPNETMVC
  • easy to translate from html to spark markup (templates are html file + extra tags that are put under a namespace)
  • advanced and no-surprise / well behaved master layout / templates system

You can currently embed C# & VB.NET code and this could probably be extended to F# under some aspects, but I don't think this is needed.

My experience using the library has been positive.

documentation:

@realvictorprm
Copy link

Cool discussion.

I'll look into what Gauthier suggested, it sounds promising :)

@TheAngryByrd
Copy link

TheAngryByrd commented Apr 30, 2019

I'd heavily vote against Spark.

  • It's still only full .net framework, so mono is involved which is the major point of this issue
  • its documentation and examples are pretty weak for more complex scenarios
  • it hasn't received updates in ~4 years
  • it "plays" with F# but as well as writing C# interacting with F# types in a file all without C# tooling support like intellisense.

My experience has been negative and we just led a giant charge to get off of Spark at my current job.

@baronfel
Copy link
Collaborator

baronfel commented Apr 30, 2019

I would echo @TheAngryByrd's points against Spark, as someone who was a part of that giant change to get off of Spark.

The primary problem was one of composition. There aren't good examples or docs about how to make partials or components that take parameters to enable reuse. One of the best side-effects of switching to Giraffe View Engine (or any html dsl really) has been the easy ability to parameterize components for reuse and consistency. It's miles better than text-based templating, especially because those template systems often have second-class support for F# in the first place.

@wallymathieu
Copy link
Member

GiraffeViewEngine looks simple enough. It looks similar to what @TheAngryByrd proposed, if I understand the code correctly?

@baronfel
Copy link
Collaborator

@wallymathieu correct, the two are very close. The major differences seem to be names for html attributes/tags and that the react server-side render also embeds some react header data (see https://github.com/fable-compiler/fable-react/blob/master/src/Fable.ReactServer.fs#L761 for details).

@TheAngryByrd
Copy link

It would be good to use Giraffe HotReload with FAKE so you can get the same nice edit/update experience. @baronfel do you know what it would take to work with FAKE?

@wallymathieu
Copy link
Member

What do you think about such a solution @dsyme?

@baronfel
Copy link
Collaborator

That's kind of an interesting question @TheAngryByrd. In the work done on the MiniScaffold MR it was just file watchers that would rerun the templates, but you had to re-run FAKE to get new versions of the templates. Was that a large hurdle? Because the other options require a bit more work. For the live-reload case especially you have to map the path that you're going to to the LiterateDocument for that path and then match which template you need to render it against.

@thinkbeforecoding
Copy link
Contributor

thinkbeforecoding commented Apr 30, 2019 via email

@smoothdeveloper
Copy link

There aren't good examples or docs about how to make partials or components that take parameters to enable reuse.

Spark + Monorail worked great in that context, I had views for "components" that could take arbitrary parameters as attributes.

<vc:mycomponent param1="someObject.Something" param2="..." />

This wasn't available in ASP.NET MVC but I don't think it was due to Spark architecture itself.

I didn't check that it runs on netstandard, so for now, this option for templating is not even applicable, sorry for that.

I'd be happy with any DSL really, I'm looking forward to use FSharp.Formatting once a solution emerges. If the solution does hot reloading and static type checks, then it is amazing 🙂.

@TheAngryByrd
Copy link

TheAngryByrd commented May 1, 2019

In the work done on the MiniScaffold MR it was just file watchers that would rerun the templates, but you had to re-run FAKE to get new versions of the templates. Was that a large hurdle?

I don't think I ever got around to re-running a separate instance of FAKE to "hot reload" the F# templates. We only hot reload the static assets and if the doc comments are changed in a dll. Could try it out though

@Krzysztof-Cieslak
Copy link
Member

As a side note - in Waypoint I've added API reference generation using FSharp.Formatting as a library (without Razor part) and Fornax as the generator.

Fornax loader - https://github.com/ionide/Waypoint/blob/master/Content/docs/loaders/apirefloader.fsx

API ref generator - https://github.com/ionide/Waypoint/blob/master/Content/docs/generators/apiref.fsx

@dsyme
Copy link
Contributor Author

dsyme commented Jun 30, 2020

My impression is the .NET Core Razro thing is now unblocked so there is no specific reason to try to ditch Razor. Other documentation generators are emerging which might, but FSharp.Formatting should now keep it for stability.

@dsyme dsyme closed this as completed Jun 30, 2020
@eiriktsarpalis
Copy link
Member

Worth pointing out that we're using an OSS netstandard port of the Razor components that depends on aspnetcore2.x. I suspect it will become a problem again eventually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants