plentico / plenti

Static Site Generator with Go backend and Svelte frontend
https://plenti.co
Apache License 2.0
1.02k stars 49 forks source link

Baseurl option in config for serving site from a subdirectory #68

Closed jimafisk closed 3 years ago

jimafisk commented 4 years ago

Other SSGs like Jekyll and Hugo have a concept of baseurl which allows you serve your website from a url that has a sub directory, e.g. https://my-group.gitlab.io/my-project. I'm not sure I love the idea of calling this "baseurl" but that does seem to be the convention (11ty calls this pathprefix, Gatsby does too ). Here are some other potential names:

jimafisk commented 3 years ago

This would be helpful for spinning up demo sites for themes on https://github.com/plenti-themes using github pages.

I would love to not have to prepend every link with something like <a href="{site.baseurl}/about"> in order for this to work.

jimafisk commented 3 years ago

You could almost use the HTML5 standard <base> element to accomplish something like this. It would require devs to rig this up themselves outside of plenti, but that might even be desirable. Just add something like this to your <head>:

<base href="/mysubdir/">

Unfortunately this will only work with relative urls, not root relative urls (the difference). If you're like me and all your links are root relative, you'll need to remove the forward slash at the beginning of each of them, or just add a period . to the front, which is typically easier to accomplish.

Sounds easy enough, there are just a few small things still getting in the way:

  1. Your server rendered HTML probably works fine, but plenti adds the following scripts behind the scenes to actually hydrate the app so unless we update this it won't convert the root relative data-main to a relative link that will work with a sub directory:

    <script type='module' src='https://unpkg.com/dimport?module' data-main='/spa/ejected/main.js'></script>
    <script nomodule src='https://unpkg.com/dimport/nomodule' data-main='/spa/ejected/main.js'></script>
  2. Using the <base> element will break your local site. You could assign a dot . prefix to a variable and manually switch it to an empty string for local dev, but that's a pain. It would be nicer to figure this out automatically with something like let baseurl = location.hostname === "localhost" ? "" : "."; but the server rendered html uses v8 which doesn't have any awareness of the window.location object. It would be nice if we had a mechanism to determine if we're on a local build or not.

  3. If you have HTML in your JSON source that you're rendering in your templates via @html, it's going to be hard to inject a relative . to the front of paths. This is coming from your content source, so you could just make a policy of setting it there but it won't be flexible for the local/deployed problem presented in 2.

jimafisk commented 3 years ago

Conversation about baseurl in jekyll: https://github.com/jekyll/jekyll/issues/332

jimafisk commented 3 years ago

The best solution I've come up with is to add new local and baseurl magic props so you can do something like this in head.svelte:

<script>
  export let title, local, baseurl;
  let baseEl = local ? "/" : baseurl;
</script>
<head>
  <title>{ title }</title>
  <base href="{ baseEl }">
</head>

The local variable should set automatically when using local server for SSR and through observing the site url for DOM. The baseurl variable should be set in plenti.json so it can be adjusted when used in a theme without having to override the parent template.

This way we don't force users to prepend every url with a variable and it should work locally and on remote sites without having to set any build flags.

jimafisk commented 3 years ago

There is a new env special prop / magic variable added in v0.4.17 that should allow you to accomplish this. Check out the example in the "learner" default starter for usage details: https://github.com/plentico/plenti/blob/9dd63555b27b1d88c002dd13d026b37677867b6d/cmd/defaults/starters/learner/layouts/global/head.svelte#L11

jimafisk commented 3 years ago

There's a issue with core paths getting to root relative equivalents.

This can be seen at https://plenti-themes.github.io/bigspring/spa/ejected/main.js: import Router from '/spa/ejected/router.js';

And all exports in https://plenti-themes.github.io/bigspring/spa/ejected/layouts.js.

The HTML fallbacks will be created fine, but the client app won't work properly in a subfolder until this is addressed.

jimafisk commented 3 years ago

I killed the forced redirects for trailing slashes (https://github.com/plentico/plenti/pull/158/commits/3a8185fe98a0d7a617e54a907fb6986c0bcb6f4b) because when you're replacing with a relative url, it would get appended to the end of the current url:

Relative url replace example ![replace](https://user-images.githubusercontent.com/5913244/120054455-0ddb3400-bffe-11eb-83cf-d803f5b07a98.png)

I also figured it was better not to dictate this behavior because it prevented users from using urls that end with trailing slashes in their SPA. Now users have the choice to use trailing slashes or remove them. The caveat is when doing a hard refresh on a page, static html automatically loads with the trailing slash (e.g. mysite.com/about/ which is really mysite.com/about/index.html). This will render just fine still and if your links all do not have trailing slashes, that's how the routes will appear if they navigate once the SPA has taken over.

jimafisk commented 3 years ago

We were still getting the following errors in the console:

This is because the uri has the baseurl prepended to it, so the content lookup fails:

Debugging screenshot ![uri](https://user-images.githubusercontent.com/5913244/120119811-c4a6f380-c167-11eb-83dc-7815e3b1f565.png)

We can add some regex to main.js to remove this before the lookup:

Example regex snippet ![regex](https://user-images.githubusercontent.com/5913244/120119848-ed2eed80-c167-11eb-9aa3-dda6373216d4.png)
jimafisk commented 3 years ago

The client router is defaulting to handle404() because it's not matching any routes:

See handle404 screenshot ![content_404](https://user-images.githubusercontent.com/5913244/120127168-a6072380-c18c-11eb-9d44-38516c8ddf43.png)

This can be fixed by having the navaid router take the baseurl into account:

router.on(env.baseurl + content.path, () => {...}
See gif of fixed client router ![output](https://user-images.githubusercontent.com/5913244/120127271-f1b9cd00-c18c-11eb-89e3-ac6e8bfd1d56.gif)

For the bigspring site that works fine, we probably just want to preprocess this to account for all edge cases (e.g. content.path is relative or root relative, the baseurl has a trailing slash or not, etc).

jimafisk commented 3 years ago

I've been working out some kinks with the baseurl implementation. Things should be better in v0.4.24.