obsidiansystems / obelisk

Functional reactive web and mobile applications, with batteries included.
https://reflex-frp.org
BSD 3-Clause "New" or "Revised" License
956 stars 105 forks source link

Static site generator using obelisk #425

Closed Rizary closed 4 years ago

Rizary commented 5 years ago

Every javascript framework has its own way to build static site. They simply use npm packages, like marked or highlight.js. Since we have pandoc and also I remember seeing @dalaing work which utilize Static (or Obelisk.Generated.Static) and Pandoc, is there any plan to make it easier for user who want to build static site using obelisk?

3noch commented 4 years ago

Thanks for asking! There are no plans to do this. If you have any specific ideas please open as new issues/PRs.

anka-213 commented 3 years ago

This project seems to try be an attempt at solving this problem: https://github.com/srid/reflex-stone

cgibbard commented 3 years ago

Note that you can also just write ordinary Snap handlers for arbitrary endpoints which use reflex-dom's renderStatic to emit HTML and write it as the response. Or you can just write a program that uses a bunch of renderStatic and writeFile to emit a bunch of files which you serve up using Snap or some other way (e.g. Apache or lighttpd or whatever). Either of those options doesn't really get too much leverage out of what reflex-dom is good at though. It's pretty fair to assume that people using Obelisk are doing so in order to be able to take advantage of functional reactive programming to write their frontend.

Of course, if you have a reflex-dom application, but have no backend to speak of, you can just compile your program with ghcjs directly, and that'll result in some javascript and a small HTML shim that loads it, and again, you can serve that up however you like, so I'm again not sure what stuff Obelisk is doing that just plain reflex-dom, ghcjs, and any random webserver won't at that point. Well, it'll get you hydration rendering, so it still might be preferable from that standpoint. You're pretty much using Obelisk normally at that point though, without adding anything to the backend it provides for you.

Are there other usage patterns I'm not thinking of? Obelisk is made up of fairly modular parts, most of which are already separate, but there's a handful of libraries in there which we could split out if there was demand for it.

kmicklas commented 3 years ago

I recently converted an Obelisk site (which was previously dynamic but no longer needs to be) to be static with a simple executable and Nix derivation. I'll paste the relevant snippets here.

main :: IO ()
main = do
  for_ staticRoutes \route -> do
    html <- renderFrontendHtml mempty mempty encodeFrontendPath route frontend blank blank
    let path = makeRelative "/" $ T.unpack $ encodeFrontendPath route
    let filePath = if ".html" `T.isSuffixOf` (T.pack path)
          then path
          else path </> "index.html"
    createDirectoryIfMissing True $ takeDirectory filePath
    BS.writeFile filePath html

The exact way the .html paths are created may depend on what static hosting system you're using.

The Nix derivation just runs this binary, which is added as an Obelisk package in default.nix:

let
  obelisk = import ./. {};

in obelisk.obelisk.nixpkgs.stdenv.mkDerivation {
  name = "static-site";
  dontUnpack = true;
  buildPhase = ''
    mkdir -p $out/static
    cd $out
    ${obelisk.ghc.generate-static}/bin/generate-static
    cp -r ${obelisk.passthru.processedStatic.symlinked}/* $out/static/
  '';
  dontInstall = true;
}

If someone is interested, this could probably be abstracted to be reusable since the only way it really depends on the project details is via staticRoutes.