Publish

repository·master·Indexed 26 days ago

https://github.com/johnsundell/publish

A static site generator for Swift developers that allows websites, including structure, metadata, and themes, to be defined as type-safe Swift packages. It features a customizable publishing pipeline using PublishingStep, support for custom HTML themes via the Plot engine, and a CLI tool for site generation and deployment.

Tokens
5.5K
Snippets
22
Records
29
Agent score
86%

What's inside Publish

  1. Create the Disqus JavaScript embed file

    master

    To enable Disqus comments, create a file named disqus.js in your Resources folder. This file will dynamically load the Disqus embed script. Ensure you replace REPLACE-WITH-SHORTNAME in the URL with your actual Disqus shortname.

    (function() {
        var t = document,
            e = t.createElement("script");
        e.src = "https://REPLACE-WITH-SHORTNAME.disqus.com/embed.js", e.setAttribute("data-timestamp", +new Date), (t.head || t.body).appendChild(e)
    })();
  2. Add Swift syntax highlighting using Splash

    master

    To add Swift syntax highlighting to Markdown code blocks in your Publish website, use the SplashPublishPlugin. This plugin automatically highlights all code blocks except those explicitly marked with no-highlight.

    1. Install the SplashPublishPlugin package to your Swift package.
    2. Add the plugin to your publishing pipeline before your Markdown files are processed using .installPlugin(.splash(withClassPrefix: "")).
    3. Provide a CSS stylesheet to your website that defines styles for the classes assigned by Splash to ensure the highlighting renders correctly in the browser.
    try MyWebsite().publish(using: [
        .installPlugin(.splash(withClassPrefix: "")),
        ...
        .addMarkdownFiles()
    ])
  3. Run and test websites locally

    master

    You can run and test your website in two ways:

    1. Xcode: Open the project's Package.swift file in Xcode and use Product > Run (or ⌘+R).
    2. CLI Server: Use the publish run command to start a localhost web server for local development.
  4. Add Disqus comments to item pages in Swift

    master

    To ensure comment threads only appear on item pages, modify your theme's makeItemHTML function. You must include a div with the ID disqus_thread, a script tag pointing to your /disqus.js file, and a noscript element for users with JavaScript disabled.

    func makeItemHTML(for item: Item<Site>,
                      context: PublishingContext<Site>) throws -> HTML {
        ...
        .div(.id("disqus_thread")),
        .script(.src("/disqus.js")),
        .element(named: "noscript", text: "Please enable JavaScript to view the comments")
        ...
    }
  5. Define custom item metadata in Publish

    master

    You can define site-specific metadata by conforming to the WebsiteItemMetadata protocol within your Website implementation. Once defined, these values can be provided in Markdown files using a metadata header (front matter).

    To make a metadata field optional (so it doesn't cause an error if missing from a Markdown file), declare it as an optional type (e.g., Int?).

    struct ShoppingWebsite: Website {
        struct ItemMetadata: WebsiteItemMetadata {
            var productPrice: Int?
        }
        
        // ...
    }
  6. Create and install a Publish plugin

    master

    Plugins are used to share setup code or extend functionality by modifying the PublishingContext. To use a plugin, add the .installPlugin(_:) step to your publishing pipeline.

    // Define a plugin
    extension Plugin {
        static var ensureAllItemsAreTagged: Self {
            Plugin(name: "Ensure that all items are tagged") { context in
                let allItems = context.sections.flatMap { $0.items }
    
                for item in allItems {
                    guard !item.tags.isEmpty else {
                        throw PublishingError(
                            path: item.path,
                            infoMessage: "Item has no tags"
                        )
                    }
                }
            }
        }
    }
    
    // Install the plugin in your pipeline
    try DeliciousRecipes().publish(using: [
        // ... other steps ...
        .installPlugin(.ensureAllItemsAreTagged)
    ])
  7. Add syntax highlighting using HighlightJSPublishPlugin

    master

    You can add syntax highlighting to your website's code blocks during the page generation process using the HighlightJSPublishPlugin. This plugin uses highlight.js and JavaScriptCore to perform highlighting at build time, allowing your final webpage to remain JavaScript-free.

    To use it, you must first install the HighlightJSPublishPlugin following its specific installation guide, then add it to your publishing pipeline using .installPlugin(.highlightJS()).

    import HighlightJSPublishPlugin
    
    // ...
    
    try MyWebsite().publish(using: [
        .installPlugin(.highlightJS()),
        // ...
        .addMarkdownFiles(),
        // ...
    ])
  8. Install Publish as a Swift dependency

    master

    To use Publish within a Swift project, add it as a dependency in your Package.swift manifest using the Swift Package Manager, then import the module.

    let package = Package(
        ...
        dependencies: [
            .package(url: "https://github.com/johnsundell/publish.git", from: "0.1.0")
        ],
        ...
    )
    import Publish
  9. Define a website using the Website protocol

    master

    A website in Publish is defined as a Swift package implementing the Website protocol. This configuration determines how the site is generated and deployed using type-safe Swift code. You can define custom SectionID enums for site structure and a custom WebsiteItemMetadata struct to support site-specific metadata.

    struct DeliciousRecipes: Website {
        enum SectionID: String, WebsiteSectionID {
            case recipes
            case links
            case about
        }
    
        struct ItemMetadata: WebsiteItemMetadata {
            var ingredients: [String]
            var preparationTime: TimeInterval
        }
    
        var url = URL(string: "https://cooking-with-john.com")!
        var name = "Delicious Recipes"
        var description = "Many very delicious recipes."
        var language: Language { .english }
        var imagePath: Path? { "images/logo.png" }
    }
  10. Nest items within folders using directory structure

    master

    To organize items into nested folders (for example, by year or month), create the desired folder hierarchy directly within a section's Content folder. Publish will mirror this exact structure in the Output directory, generating an index.html for each item within its respective subfolder.

    Content
        sectionOne
            2019
                january
                    one-item.md
  11. Use nested metadata values in Markdown

    master

    Publish supports nested metadata structures. To use them, ensure your nested types also conform to WebsiteItemMetadata. In your Markdown front matter, you can express these nested values by specifying their full path using dot notation (e.g., parent.child: value).

    struct ProductInfo: WebsiteItemMetadata {
        var price: Int
        var category: String
    }
    
    struct ShoppingWebsite: Website {
        struct ItemMetadata: WebsiteItemMetadata {
            var product: ProductInfo?
        }
    }
    ---
    product.price: 250
    product.category: Electronics
    ---
    
    # A fantastic product