Behind the Code

Technical articles, tips and other observations

Why Publish?

I finally decided it was time to rework my website, and this time do it with Publish by John Sundell. In this post, I'll explore why I came to that decision.

The Options

In the past I had always leaned toward a more static design for my personal website. In fact, the previous version of this website was done with Rapid Weaver by Realmac Software which uses local editing via a desktop application and static HTML generation. I was initially leaning in that direction again for the redesign.

One other option I had considered was an obvious option for a blog based site, and that was Wordpress. My hosting provider even makes it simple to install Wordpress with a single click. I did do a test install and realized that wasn't the experience I wanted. All the templates, the plugins, so many layout choices, and being such a popular piece of software makes it a high priority target for attacks. This wasn't the simple streamlined workflow experience I was wanting.

My Workflow

I wanted to reorient the direction of my website to more longer form technical articles. I needed a solution that put the focus on the technical writing and meshed well with my existing workflow.

I take a lot of notes in my daily job, kind of a technical work journal as well as some research work for longer tasks. There are a lot of tools out there for notetakers, but I have always preferred plain text files. I have been burned in the past, by relying on a piece of software that used a custom file format or data base. Then at some point that piece of software was retired or abandoned. I was left trying to copy and paste my notes back into existence.

These days I prefer using text files in the Markdown format. The files are still plain text, but with simple markup styling that is widely supported an all platforms, and is easy to convert to HTML for web publishing as well as PDF for sending my research notes to colleagues.

The software I am currently using Is iA Writer on both macOS and iOS. Its ability to sync via iCloud and Dropbox has been extremely useful in picking up whichever device I have handy and being able to jot down a few notes.

Modifications to Publish

One of the interesting aspects of Publish is that it allows you to specify both custom plugins and custom publishing steps. While plugins and publishing steps can both be used to perform similar tasks, plugins are geared more toward modifying pieces of content, and publishing steps are used to either pre-process the input or post-process the output during the publish phase.

Splash Plugin

try RCookNet().publish(withTheme: .rcooknet,
                       additionalSteps: [],
                       plugins: [.splash(withClassPrefix: ""), .imgPathMarkup])

For my website, I am currently using two plugins for my Publish flow. The first is the Splash plugin, also by Publish author John Sundell. Splash is a custom syntax highlighter for code blocks, and makes your code output look like the above code snippet.

Image Path Modifier Plugin

extension Plugin {
    static var imgPathMarkup: Self {
        Plugin(name: "Img Path Markup") { context in
            context.markdownParser.addModifier(.imgPathModifier())
        }
    }
}

extension Modifier {
    static func imgPathModifier() -> Self {
        return Modifier(target: .images) { html, markdown -> String in
            guard !markdown.contains("http") else { return html }
            let output = html.replacingOccurrences(
                of: "src=\"", 
                with: "src=\"/images/articles/")
            return output
        }
    }
}

The other plugin I use, as you can see from the code snippet, is a custom plugin I wrote called "Img Path Markup". There are two types of images I use for this website, one that is uploaded to my image server, and the other that is saved alongside the markdown text file for the article. If the image is on the remote image server, it will have a fully qualified HTTP domain and path specified.

However if the image is simply in the same folder as the article text, it needs to have its path altered prior to publication. While writing the article, the image will be referenced from the same folder as the article. However when copied into the Publish project, the image needs to be put into a "Resources" path within the project. Thus, when publishing the article, we need to prefix the image path with that output folder path so the HTML page can properly load the image. My custom plugin does exactly that for an image reference that is not fully domain and path qualified.

Status == Done Publishing Step

extension PublishingStep where Site == RCookNet {
    static func requireStatusDone() -> Self {
        .removeAllItems(in: .articles, 
                        matching: (\.metadata.status == "done").inverse())
    }
}

While I am not currently using it, I did write a custom publish step as well. This publish step looks at the metadata for the article and will only publish the article if the "status" key has a value of "done". This would let you have articles in your content folder that are still in progress and they would not be published to the website yet. However as I said, I am not current using this custom publishing step as I am writing my articles outside of the Xcode Publish project. I am still making tweaks to my final writing workflow, but at least have this bit of customization as an additional option.

Theming

Publish comes with a built-in theme called Foundation. Making your own custom theme is fairly straightforward. There are several functions to override that define various sections of the webpage. Within those functions, you have an HTML builder that lets you assemble the desired HTML blocks.

Here is an example of one of the functions you can override. In this case, the function for defining the structure of a content page.

func makePageHTML(for page: Page,
                  context: PublishingContext<Site>) throws -> HTML {
    HTML(
        .lang(context.site.language),
        .head(for: page, on: context.site),
        .body(
            .header(for: context, selectedSection: nil),
            .wrapper(.contentBody(page.body)),
            .footer(for: context.site)
        )
    )
}

And of course with any webpage theming comes the CSS. I haven't done much CSS tweaking in probably 15 years, back when I was still doing a bit of web development on the side. To me, CSS is a piece of the site that always seems to never quite be done. I always see just one more bit I want to change. With Publish, you just furnish a CSS files as a resource and it bundles it up with your website.

The End Result

So far I'm pretty happy with the outcome. I'm no web designer, but I was able to accomplish the look I was after ... a site design geared more towards longer form articles, easy to read and without any distractions or dancing hamster GIFs.

I will be sure to post an update in the future relating my experiences on the workflow via Publish.