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.