tlienart / Franklin.jl

(yet another) static site generator. Simple, customisable, fast, maths with KaTeX, code evaluation, optional pre-rendering, in Julia.
https://franklinjl.org
MIT License
959 stars 113 forks source link

Page variables not resetting correctly (?) #647

Open fredrikekre opened 4 years ago

fredrikekre commented 4 years ago

MWE here: https://github.com/fredrikekre/franklin-debug. The first commit is just Franklin.newsite() and the second commit is there to illustrate the issue.

If you run Franklin.serve() for that repo and go to e.g. http://localhost:8000/tag/syntax/ the title is "More goodies". I would expect (i) title not to show up at all due to this guard: https://github.com/fredrikekre/franklin-debug/blob/52fb8d05fd913ba8ec849437a7ea333abe79662c/_layout/head.html#L11 and (ii) that pagevariables should be reset between pages?

This was not the issue I tried to debug originally, but I am pretty confident the fix is the same.

fredrikekre commented 4 years ago

Maybe related to the Important note here: https://github.com/tlienart/Franklin.jl/blob/master/NEWS.md#new-stuff ? In my original usecase I define the pagevar in tag.html and expected that to be picked up. Even if that is not supported I think there is still the issue that {{ isdef title }} returns true in the repo above.

tlienart commented 4 years ago

Exactly so what's going on is that you can't guarantee which page will trigger the tag generation; and the tag generation doesn't have a "real" scope. So what you did (i.e. call {{insert head.html}} in the tag.html is mostly fine except that you need to add statements in the head.html of the form {{isnotpage /tag/*}} as tag pages cannot rely on local page variables {{fill XXX}} (so in particular the {{fill title}}). I should explain this better in the docs.

One workaround is to just define global variables (in config.md) and to use the relevant one with {{ispage /tag/tag1/}}{{fill glob_var_tag1_xxx}}{{end}}.

I haven't yet had the time to check out your example in detail but I suspect that your second comment is exactly right.

fredrikekre commented 4 years ago

What's the reason tag.html can't have it's own scope then?

fredrikekre commented 4 years ago

If that is too complicated, would it be reasonable to delete page vars such that they can't be seen from tag.html?

tlienart commented 4 years ago

What's the reason tag.html can't have it's own scope then?

it could but I wasn't clear on where you would define stuff (like where you'd put the @def xxx = yyy. Then I thought that I'd just put stuff in the config.md ... but happy to hear suggestions.

With respect to your second proposal, kind of yes (or that it would show warnings), it's a bit finnicky but might be worth it to avoid confusing. I should also make the default layout/tag.html more heavily commented to clarify what can and can't go there (at the moment).

fredrikekre commented 4 years ago

Then I thought that I'd just put stuff in the config.md ... but happy to hear suggestions.

I have a {{ define ... }} hfun that I used in tag.html such that those variables are available in {{ insert head.html }}.

tlienart commented 4 years ago

Could you paste here the define function you use?

I think you could have achieved the same thing in the config.md (you can also execute arbitrary Julia code there) and my impression is that it would be cleaner.

Possibly a compromise would be to allow global variables to be functions which get evaluated at fill time. Something like

# (in config.md)
+++
tag_title = () -> "A title $(prod(rand("abc", 2)))"
+++

And then whenever {{fill tag_title}} or {{tag_title}} is called, it would just run call the function. (Allowing this would require 1 loc).

I think this would be a potentially good compromise between what I think is good (users define tag 'scope' in config.md) and allowing a lot of flexibility in terms of functions that can depend on the tag name or whatever by allowing to pass a function. What do you think?

fredrikekre commented 4 years ago

Could you paste here the define function you use?

function hfun_define(arg)
    arg = arg[1]
    if (m = match(Franklin.ASSIGN_PAT, arg)) !== nothing
        vname, vdef = String.(m.captures[1:2])
        Franklin.set_vars!(Franklin.LOCAL_VARS, [vname => vdef, ]) # TODO: Use set_var! instead ???
    elseif (m = match(r"^([a-zA-Z_]\S*)", arg)) !== nothing
        vname = String(m.captures[1])
        Franklin.set_vars!(Franklin.LOCAL_VARS, [vname => "nothing", ]) # TODO: Use set_var! instead ???
    else
        @warn "misformed {{ define ... }} statement" arg
    end
    return ""
end
tlienart commented 4 years ago

and sorry could you show how you were using it?

fredrikekre commented 4 years ago
{{ define title="Posts" }}

for example. But for tag.html this didn't quite work (I can't read the fd_tag from the HTML: {{ define title={{ fill fd_tag }} }} which we discussed some months ago on Slack) so there I have the actual function defining it

{{ definetagtitle }}

with

function hfun_definetagtitle()
    return hfun_define(["title=\"#$(locvar("fd_tag"))\""])
end
tlienart commented 4 years ago

Right. So the way I'd have done is is:

<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/franklin.css">
<link rel="stylesheet" href="/css/basic.css">
<link rel="icon" href="/assets/favicon.png">
<!doctype html>
<html lang="en">
<head>
  {{insert base_head.html}}
  {{if hasmath}} {{insert head_katex.html }} {{end}}
  {{if hascode}} {{insert head_highlight.html }} {{end}}
  {{isdef title}} <title>{{title}}</title> {{end}}
</head>
<body>
  {{insert header.html}}
<html lang="en">
<head>
  {{insert base_head.html}}
  <title>{{fd_tag}}</title>
</head>
<body>
  {{insert header.html}}
  <div class="{{div_content}}">
  <h1>Tag: {{fd_tag}}</h1>
  {{taglist}}
  {{insert page_foot.html}}
  </div>
</body>
</html>

does that seem acceptable?

fredrikekre commented 4 years ago

Yea, I guess I have tried a little bit too hard to keep everything in head.html :)

tlienart commented 4 years ago

It's a good effort but I think I have to start wondering about how to encourage users to not wander too far off pre-defined rails to avoid people basically re-coding a static site generator using hfun 😂

Anyway I think the next steps for this issue are: