twostraws / Ignite

A static site generator for Swift developers.
MIT License
984 stars 34 forks source link

Making a site in a subdirectory #33

Open gavineadie opened 2 weeks ago

gavineadie commented 2 weeks ago

I'd like to use Ignite to build a sub-site that doesn't live directly under the root URL but under a subdirectory of that. I've hunted around to see if that is catered for in the existing code base but not found it (which might easily be a result of my not looking hard enough).

I saw the PR "Making the buildDirectory changeable #16" and thought that might be a way to accomplish my goal by setting PublishingContext(.., buildDirectoryPath="subdir/Build") but, no. Similarly, I tried setting Site.url = https://example.com/subdir but, no there too.

Building a site and placing all its files into a subdirectory doesn't work because assets, css, etc, being referenced as, /xxx are not found because they're actually at /subdir/xxx. I'm going to dig into this more and submit a PR, if necessary, but wanted to check that I hadn't missed something obvious before setting out.

twostraws commented 2 weeks ago

Yes, right now we go back to the root every time, including when referencing things like paths in the Image element.

Perhaps if this were made a property of Site, basePath or similar, with a default value of /, that could be something that's easy to adjust anywhere. To make this less likely to break backwards compatibility, we could perhaps make the default an empty string, then elsewhere simply prepended that to the paths? So, existing code like Image("/images/whatever") would remain pointing to the root, whereas someone with an override for basePath of /subsite would automatically end up with /subsite/images/whatever.

Just some thoughts, but it seems eminently soluble.

gavineadie commented 2 weeks ago

Good! .. I'm relieved your suggested approach matches what I was considering! I read through a lot of your code last night and it seemed that making a change to a property that reaches render(..) via context would cause the least disruption. Not every element passes through render(..) at the necessary granularity .. but I'll stop surmising here and tackle some code.

twostraws commented 2 weeks ago

Thank you! Let me know if there's anything you need help with.

gavineadie commented 2 weeks ago

Thanks Paul .. A little looking over my shoulder, if you would?

For others reading along, I'm calling https://site.com [its URL has no path] a "root" site, and a site, off the root, at https://site.com/non-root is a "non root" site.

Ignite generates "root" sites and I would like to allow it to also generate "non root" sites. I see two places where the author of a new site could make the choice to construct one or the other. The partial code below shows both:

    @main
    struct IgniteWebsite {
        static func main() async {
            let site = ExploreSite()
            await site.publish(buildDirectoryPath: "non-root/Build")     <---
            . . .
        }
    }

  But the buildDirectoryPath value is arbitrary -- the name of a place to gather the site files before its contents are copied behind the server; its actual name is irrelevant.

AND

    struct ExploreSite: Site {
        var name = "Exploring .."
        var url = URL("https://site.com/non-root")     <---
        . . .

  This is an explicit declaration of the website's URL and I favor it. It also allows the default value of buildDirectoryPath to be used so the site files are gathered is the same place regardless of the rootiness of the site.

The site (and its url property, if any) is carried in the PublishingContext and available to all the render(..) functions where it is needed to modify the emitted HTML appropriately.

I've read though much of the Ignite code and the site.url is little used but I want to give the opportunity to Paul, and others, to adjust my thinking. I'm particularly nervous about messing up the <HEAD> metadata and/or negative effects on the recent RSS feed PR.

twostraws commented 2 weeks ago

URLs are indeed a complex beast. I think the main concerns for me are:

  1. Specifying a path in a Markdown file stored in Content.
  2. The sitemap and RSS feeds.
  3. Images, scripts, etc.
  4. Canonical URLs.

Right now Ignite uses absolute URLs everywhere. For example, images are Image("/images/whatever"), and if you look at IgniteSamples you'll see that the swift-enterprise-edition.md article file uses path: /path/to/enterprise-edition to ensure that paths work there too.

If we adopt your suggestion of using the path component of the site URL, and effectively prepend that to any URL we work with, my instinct is that it would preserve back-compatibility completely while also allowing the extra functionality to work. So, that seems like a great idea!

However, this would also be a great example of where writing some tests would be helpful, not least to give you peace of mind that your code isn't breaking anything. For example, if you write tests that show the current behavior works, then write tests to show that your a site URL with /subsite fails with the current code (because the current code ignores the subdirectory), you can then implement your code to make /subsite be honored and your new tests should pass – along with your old tests, of course 🙂

How does that sound?

gavineadie commented 2 weeks ago

👍🏻

gavineadie commented 1 week ago

I've got subsite almost all working and it was easier than I thought! A couple of things are, maybe, not correct and I wanted to check your four concerns above and a couple of mine. I'll press on during your night (I'm -0500) and may resolve some of my questions before you wake, or you may answer some before I wake!

My informal test was to 'subsite' IgniteSamples -- with one exception (assets in *.md Contents) everything rendered the same, though I may have missed something arcane.


What is the purpose of path? I'm going to guess that it's a URL which returns to you to the original article when you click on the title in an RSS browser.


RSS feeds work (checked using NetNewsWire). The sitemap.xml file contains records of the form:

<url><loc>https://www.yoursite.com/subsite/content-examples</loc><priority>0.9</priority></url>


These work correctly.


The site page <head> contains:

<link href="https://www.yoursite.com/subsite/" rel="canonical">


fischej commented 1 week ago

A interested lurker's comment, if I may (fully acknowledging in advance I may be well out of my depth here): What if we just drop the leading "/" character from all hrefs and srcs (both user and Ignite created)? If we do that, the site becomes completely relative to whatever subdirectory it's installed in, no?

twostraws commented 1 week ago

<< What is the purpose of path? I'm going to guess that it's a URL which returns to you to the original article when you click on the title in an RSS browser. >>

It's used to locate that article in ContentPreview, in ContentPage, in the sitemap, and more. It should be the full path (minus whatever override you're adding) to the document.

<< There is no tags page rendered >>

You need to add a tags page to your site to get this rendering.

<< I believe the elements impacted by subsite are Image, Link, Script and Body and I've added unit tests for the changes made therein. >>

MetaLink?

<< @fischej: What if we just drop the leading "/" character from all hrefs and srcs (both user and Ignite created)? If we do that, the site becomes completely relative to whatever subdirectory it's installed in, no? >>

I think we need a way to be able to distinguish between "relative to the location of the current document" and "relative to the location of the root of my site."

gavineadie commented 1 week ago

<< There is no tags page rendered >>

You need to add a tags page to your site to get this rendering.

Does IgniteSamples have a tags page? I see the files present under /tags, and clicking on a "tag capsule" takes you to them, but they're empty between the navbar and footer

twostraws commented 1 week ago

It has one here: https://github.com/twostraws/IgniteSamples/blob/main/Sources/Pages/TagPage.swift

gavineadie commented 1 week ago

Thanks, Paul, I saw that. My question isn't about the Swift code; it's why is https://ignitesamples.hackingwithswift.com/tags/ lacking any content between its navbar and footer? I expected a summary of tags, or some such; am I not understanding? I'm sorry to pester but I want to check everything before submitting the PR.

Addition edit: tracing the code -- a breakpoint on the body(..) function on TagPage.swift isn't executed by ignitesamples

gavineadie commented 1 week ago

I've chased the change I see in the behavior of Tag pages to this commit (which doesn't seem to relate to Tag page rendering):

Added examples for fontWeight().

Commit: 212e4647768e62ac90517a4f604b4276a9ac4999 [212e464]
Parents: [d341130c15](rev://d341130c15c2c54f9098ae716182596e535446c6)
Author: Paul Hudson <paul.hudson@gmail.com>
Date: April 27, 2024 at 5:35:33 AM EDT

"d341130" generates tag pages that contain tag info but "212e464" doesn't. To assure me that I'm not chasing a phantom of my own creation, I'd love someone to click on https://ignitesamples.hackingwithswift.com/tags/swift/ and show me they don't see this (wrong) result:

Screenshot 2024-05-05 at 1 53 10 AM

gavineadie commented 1 week ago

for reference "d341130" generates:

Screenshot 2024-05-05 at 2 27 18 AM

twostraws commented 1 week ago

Ah! I see the problem, and have resolved it. Hopefully that puts you back on track – thank you! 🙌