Publish is a static web builder for Swift developers. It uses Swift to build the entire site and supports themes, plug-ins, and a host of other customization options. As a Swift developer, the process and experience of creating a site via Publish is similar to that of developing any other application.

The opening

Developer John Sundell

Publish developer John Sundell has been publishing high-quality articles, podcasts, and videos about Swift for years. Most of his work is published on Swift by Sundell, which he runs independently. He developed Publish to create and manage his own sites.

In the process of Publish, he also opened a number of other basic libraries such as Ink (efficient Markdown parser), Plot (creating DSL for HTML, XML, and RSS), Sweep (efficient string scanning library), Codextended (enhanced Codable), and more. Together, they not only build powerful Publish, but they are also excellent open source libraries in their respective areas.

Why do I use Publish

I used Hexo when I restored my personal blog a year ago. Hexo has a great audience in China, with plenty of great tutorials online, and lots of developers contributing their own themes and plugins. Although Hexo was quite satisfying to me, it was difficult to customize or modify Hexo further because my main language was Swift and I was very unfamiliar with JavaScript.

As a developer (even an amateur), I always want more control over my project, so Publish, developed entirely by Swift, is my first choice.

As THE process of rebuilding Swift notepad with Publish progressed, I felt I had made the right choice. Publish allows me to create a site with the same mindset and logic that I use to develop a normal app, and to make all the customization and changes I want efficiently.

The reason for writing this article

Publish has a 3.1k rating on Github at the time of writing. However, there is not much introduction to Publish on the network, especially the lack of information and communication about topic customization and plug-in development. Searches for related plug-ins and topics on Github are also limited in number.

This is partly due to the short Publish time, low usage, and small Swift community, but I think it is also exacerbated by the fact that unlike other static web site generators, developers can do a lot of things in Publish with very short code. This fragmented code is not easy to share and not easy to search; In addition, due to the deep functional binding between the topic and the website in Publish, the utilization of the topic shared separately is also low.

But this very quality of Publish is also part of its appeal.

With this in mind, I will complete a brief introduction to Publish in three articles (introduction, theme development, plug-in development), and hope that Swift developers or fans in China can learn more about and use this excellent tool.

To get you started quickly, I’ve put the code for my Swift Notepad site (including themes, custom plugins, etc.) on Github for you to learn about Publish faster.

Quick Start

How to Install Publish

As with a number of other static web site generators, Publish provides a CLI. You can quickly create templates, update content, publish remotely, and more from the command line. Publish is currently available on Mac and Linux, but due to its low operating system dependency, it is expected to be available later on Windows.

Install via BREW on Mac

$brew install publish
Copy the code

Source code installation

$git clone https://github.com/JohnSundell/Publish.git
$cd Publish
$make
Copy the code

Create your first project

Let’s create a new Blog project

$mkdir myblog
$cd myblog
$publish new
Copy the code

Publish will create the project template we need in the myBlog directory. Its basic composition is roughly as follows:

| - myblog | | - Content | | - posts | | - first - post. Md | | - index. The md | | - index. The md | | - Resources | | - Sources. | | - Myblog | | - main. SwiftCopy the code
  • Content

    Put in markDown files for articles, pages, and so on that you want to publish on your site.

  • Resources

    Some resources required for the project theme, such as CSS, images, etc., are currently empty. After you make your first release, you can see that it contains the default FoundationTheme styles.css file.

  • Source

    Describes the code for the website. Main.swift defines the basic properties of the website, creating workflow and so on.

Compile and run

Swift is a compiled language, so every time your site’s code is modified, it needs to be compiled and run natively to generate the content, all with one command.

Let’s let Publish do the above and launch the built-in Web Server so we can browse the newly created project.

$publish run
Copy the code

On your first run, Publish will automatically pick up the additional libraries you need from Github. Please wait a few minutes.

$publish run
............
Publishing Myblog (6 steps)
[1/6] Copy 'Resources' files
[2/6] Add Markdown files from 'Content'Folder [3/6] Sort items [4/6] Generate HTML [5/6] Generate RSS feed [6/6] Generate site map ✅ Successfully published Myblog 🌍 Starting Web server at http://localhost:8000 Press ENTER to stop the server andexit
Copy the code

Now you can access your new site with your browser at http://localhost:8000.

The entire content of the site is generated and placed in the Output directory. You just need to upload the contents to your server, or through a simple configuration like:

.unwrap(.gitHub("fatbobman/fatbobman.github.io", useSSH: true), PublishingStep.deploy)
Copy the code

Then use the

$publish deploy
Copy the code

You can publish the content to your Github. IO (more on the configuration).

At this point you add the following file to content-posts: second-post.md

-- Date: 2021-01-29 19:58 Description: My second articletags: publish,swift ---
# Hello Wolrd
Copy the code

Publish Run again to see the new post on the page.

Use the template I provided to get started quickly

First make sure you have the Publish CLI installed

$ git clone https://github.com/fatbobman/PublishThemeForFatbobmanBlog.git
$ cd PublishThemeForFatbobmanBlog
$ publish run
Copy the code

More about Publish

This section introduces several concepts in Publish that will be important for understanding topic customization and feature extension later.

Site

When you use Publish to create a project, Publish automatically generates a Swift Package. The site is generated and deployed using the native and type-safe Swift code.

The following code is the contents of the main.swift (the package entry file) generated using Publish New MyBlog.

The definition of / / Site
struct Myblog: Website {
    enum SectionID: String.WebsiteSectionID {
        // Add the Section you want
        case posts
    }

    struct ItemMetadata: WebsiteItemMetadata {
        // Add any site-specific metadata you want to use here
    }

    // Some configuration of your website xn'xi
    var url = URL(string: "https://your-website-url.com")!
    var name = "Myblog"
    var description = "A description of Myblog"
    var language: Language { .english }
    var imagePath: Path? { nil}}// Properties in a Site can be accessed from locations such as themes or plug-ins

// Execute entry. You are using the default theme and using the Publish preset build, export, and Publish process.
// Define the workflow. See Step for more details
try Myblog().publish(withTheme: .foundation)
Copy the code

A Site defines not only the basic configuration information for a Site project, but also the workflow of the Site from generation to publication.

Section

Each Section will generate a subdirectory under Output. In main.swift, sections are defined by enumeration. You can use a Section as a container for a set of items, or you can just point to a Page (not an Item’s own Page). When you want to use a Section to manage a set of posts, you can simply create a subdirectory in the Content directory with the same name as the Section. For details, see Posts and Project under Content in the example.

The definition of Section

enum SectionID: String.WebsiteSectionID {
     case posts // By default, the new project will only have this, which corresponds to the Content-posts directory
     case links // You can add them by yourself. You can just put the articles belonging to this section into the corresponding directory
     case about
}
Copy the code

In Swift Notepad, each Section also corresponds to an option in the upper navigation area. Sections can be used in many ways, more on that in the topic customization Section.

Item

Save the article in Content– corresponding to the Sectioin directory. Each Item corresponds to a Section. No special setting is required. It belongs to the Section in which it is stored. If the Section does not need to be used as a file container, you can simply create an MD file with the same name as the Section in the Content. In the sample template I provided, “About” takes this form.

Page

An article that does not belong to any Section. Page does not appear in a Section’s item list, and usually does not appear in the index list. Create a Page by adding files to a directory under Content that does not belong to any Section. Note the relationship between the creation path and the access path of the Page.

| content -- 404
|             | -- index.md
Copy the code

You can visit http://localhost:8000/404/index/ to view

Page gives us a way to build free pages. For example, you can use it to create articles that do not need to be shown in the list, or to create 404😅 as demonstrated by the sample topic.

Content

This refers specifically to the content attribute in Item and Page. As a content set, its scope includes text (such as title and description), owning tags, converted HTML code, audio, video, and other metadata. The metadata needs to be noted at the head of the Markdonw article.

A Section also has Content, which corresponds to the index.md that you create (if necessary) in the Section’s corresponding Content subdirectory.

There’s another Content you’ll encounter in the future in your code, namely a PublishingContext. It contains all the information for the entire project (sites, Sections, Items, Tags, etc.) and can be used to modify or configure the Site by passing an instance of it to a Step or Plugin.

Metadata

The metadata of the Markdown file, identified in the header of the article (Markdown) file. Divided into two categories, one is Publish preset. The other is customizable through ItemMetadata in the Site.

---
date: 2021-01-29 19:58
description: A description of my first post.
tags: first, article
author: fat 
image: /images/first-post.png
---
Copy the code

The preset

  • Title Text title

    If not set, Publish will look directly for the first top-level Head# in the body of the article as the title

  • Description:

  • Date Date when the article was created

    If not, use the file’s modificationDate directly

  • Tags Article tags, each article can be set to multiple tags, for the organization of the article one more dimension

  • For example, this can be used to display the topic image of an article in the item list (defined in the topic).

  • Audio data

  • Video Video data

    The definition of audio and video is too complex, so you can define it yourself if you really need to.

The custom

struct ItemMetadata: WebsiteItemMetadata {
    // Add any site-specific metadata that you want to use here.
    var author:String
}
Copy the code

If the preset metadata is not sufficient for your needs, you can customize it in ItemMetadata.

Differences between the two types of metadata

Default metadata exists as a property in Publish.

for item in content.allItems(sortedBy: \.date){
         print(item.title)
 }
Copy the code

You can obtain user-defined metadata in the following ways

let author = (item.metadata as! Myblog.ItemMetadata).author
Copy the code

It’s easier to use in a theme

.text(item.metadata.author)
Copy the code

The default metadata Item in Publish is not required. However, custom metadata must be added to the MarkDown document. Index. md, Page can have no metadata (whether custom or not)

Theme

Publish uses Plot as its description engine for HTML topics, allowing developers to define pages in a very Swift way. If you have used dSL-type development, you will feel very familiar with it. For example, the following code defines the layout rendering of the Section List

func makeSectionHTML(
        for section: Section<Site>,
        context: PublishingContext<Site>) throws -> HTML {
        HTML(
            .lang(context.site.language), // Web language
            .head(for: section, on: context.site), // Header files, metadata, CSS, etc
            .body(
                .header(for: context, selectedSection: section.id), // The upper section of the site, which in this example contains the Logo, and the navigation bar
                .wrapper(
                    .itemList(for: section.items, on: context.site) // The list of articles
                ),
                .footer(for: context.site) // The bottom copyright area))}Copy the code

The layout details defined in the Theme still need to be set further in the CSS.

Headers, wrappers, etc. in the above code are all called nodes in Plot. In addition to using a large number of nodes preset in Publish, we can use our own nodes.

More on themes will be covered in Publish Blog (2).

Step

Publish uses workflow (Pipeline) to define the operation process of each link clearly. Reading from files, Markdown parsing, HTML generation, RSS export, and so on. In Main. swift generated by Publish New, even though only one statement is used

try Myblog().publish(withTheme: .foundation)
Copy the code

But behind it is the following sequence of steps:

using: [
      .group(plugins.map(PublishingStep.installPlugin)),
      .optional(.copyResources()),
      .addMarkdownFiles(),
      .sortItems(by: \.date, order: .descending),
      .group(additionalSteps),
      .generateHTML(withTheme: theme, indentation: indentation),
      .unwrap(rssFeedConfig) { config in
      .generateRSSFeed(
               including: rssFeedSections,
                      config: config
                    )
          },
       .generateSiteMap(indentedBy: indentation),
       .unwrap(deploymentMethod, PublishingStep.deploy)
         ]
Copy the code

In most cases, we will explicitly indicate each step. Each Step is called a Step in Publish. Publish already presets a number of steps for developers to use. We can also inject our own steps into the workflow where appropriate to achieve more functionality.

Each Step is passed a PublishContent instance that can be used to change various elements in the site (including files, folders, items, pages, and so on). For the difference between PublishContent and Content, see above.

Plugin

.installPlugin(.highlightJS()), // Syntax highlighting
Copy the code

The above code installs the plug-in highlightJS in the Publish workflow with a Step called installPlugin.

The development of a Plugin is very similar to a Step in that you take a PublishContent instance and do the work through it.

For example, you can use Step to do something that has side effects; Use plugins to do injection work like Modifier (markDown’s custom resolver).

For custom code, both do each other’s work from a functional standpoint. Therefore, it is a matter of personal preference whether to use a Step or Plugin when creating a functional extension.

How to customize the Step and Plugin will be explained in Publish creating a blog (3).

Who is Publish for

Compared with the current mainstream static website generator, Publish has some shortcomings, such as low community activity, short development time, small Swift users and so on. The current Publish is suitable for friends who meet the following conditions:

  • A developer who uses the Swift language or a fan of Swift
  • Lack of Javascripte experience, or like Javascripte free style
  • The pursuit of efficient, concise web presentation
  • Hope to be able to complete the various links of the website and gradually through their own hands to achieve various functions
  • Early adopters

Next

I’ll look at Theme development in Publish (2), and how to extend Publish’s capabilities in a variety of ways in Publish (3), a plug-in development.

If you’re already interested, start your Github. IO site on Github and Publish your own blog.

My personal blog, Swift Notepad, has more on Swift, SwiftUI, and CoreData.