[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-6846":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":18,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":28,"readmeContent":29,"aiSummary":30,"trendingCount":16,"starSnapshotCount":16,"syncStatus":31,"lastSyncTime":32,"discoverSource":33},6846,"Publish","JohnSundell\u002FPublish","JohnSundell","A static site generator for Swift developers","",null,"Swift",4968,357,89,35,0,4,3,62.06,"MIT License",false,"master",[24,25,26,27],"publish","static-site-generator","swift","web-development","2026-06-12 04:00:30","\u003Cp align=\"center\">\n    \u003Cimg src=\"Logo.png\" width=\"400\" max-width=\"90%\" alt=\"Publish\" \u002F>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSwift-5.5-orange.svg\" \u002F>\n    \u003Ca href=\"https:\u002F\u002Fswift.org\u002Fpackage-manager\">\n        \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fswiftpm-compatible-brightgreen.svg?style=flat\" alt=\"Swift Package Manager\" \u002F>\n    \u003C\u002Fa>\n     \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fplatforms-mac+linux-brightgreen.svg?style=flat\" alt=\"Mac + Linux\" \u002F>\n    \u003Ca href=\"https:\u002F\u002Ftwitter.com\u002Fjohnsundell\">\n        \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Ftwitter-@johnsundell-blue.svg?style=flat\" alt=\"Twitter: @johnsundell\" \u002F>\n    \u003C\u002Fa>\n\u003C\u002Fp>\n\nWelcome to **Publish**, a static site generator built specifically for Swift developers. It enables entire websites to be built using Swift, and supports themes, plugins and tons of other powerful customization options.\n\nPublish is used to build all of [swiftbysundell.com](https:\u002F\u002Fswiftbysundell.com).\n\n## Websites as Swift packages\n\nWhen using Publish, each website is defined as a Swift package, which acts as the configuration as to how the website should be generated and deployed — all using native, type-safe Swift code. For example, here’s what the configuration for a food recipe website might look like:\n\n```swift\nstruct DeliciousRecipes: Website {\n    enum SectionID: String, WebsiteSectionID {\n        case recipes\n        case links\n        case about\n    }\n\n    struct ItemMetadata: WebsiteItemMetadata {\n        var ingredients: [String]\n        var preparationTime: TimeInterval\n    }\n\n    var url = URL(string: \"https:\u002F\u002Fcooking-with-john.com\")!\n    var name = \"Delicious Recipes\"\n    var description = \"Many very delicious recipes.\"\n    var language: Language { .english }\n    var imagePath: Path? { \"images\u002Flogo.png\" }\n}\n```\n\nEach website built using Publish can freely decide what kind of sections and metadata that it wants to support. Above, we’ve added three sections — *Recipes*, *Links*, and *About* — which can then contain any number of items. We’ve also added support for our own, site-specific item metadata through the `ItemMetadata` type, which we’ll be able to use in a fully type-safe manner all throughout our publishing process.\n\n## Start out simple, and customize when needed\n\nWhile Publish offers a really powerful API that enables almost every aspect of the website generation process to be customized and tweaked, it also ships with a suite of convenience APIs that aims to make it as quick and easy as possible to get started.\n\nTo start generating the *Delicious Recipes* website we defined above, all we need is a single line of code, that tells Publish which theme to use to generate our website’s HTML:\n\n```swift\ntry DeliciousRecipes().publish(withTheme: .foundation)\n```\n\n*Not only does the above call render our website’s HTML, it also generates an RSS feed, a site map, and more.*\n\nAbove we’re using Publish’s built-in Foundation theme, which is a very basic theme mostly provided as a starting point, and as an example of how Publish themes may be built. We can of course at any time replace that theme with our own, custom one, which can include any sort of HTML and resources that we’d like.\n\nBy default, Publish will generate a website’s content based on Markdown files placed within that project’s `Content` folder, but any number of content items and custom pages can also be added programmatically.\n\n**Publish supports three types of content:**\n\n**Sections**, which are created based on the members of each website’s `SectionID` enum. Each section both has its own HTML page, and can also act as a container for a list of **Items**, which represent the nested HTML pages within that section. Finally, **Pages** provide a way to build custom free-form pages that can be placed into any kind of folder hierarchy.\n\nEach `Section`, `Item`, and `Page` can define its own set of Content — which can range from text (like titles and descriptions), to HTML, audio, video and various kinds of metadata.\n\nHere’s how we could extend our basic `publish()` call from before to inject our own custom publishing pipeline — which enables us to define new items, modify sections, and much more:\n\n```swift\ntry DeliciousRecipes().publish(\n    withTheme: .foundation,\n    additionalSteps: [\n        \u002F\u002F Add an item programmatically\n        .addItem(Item(\n            path: \"my-favorite-recipe\",\n            sectionID: .recipes,\n            metadata: DeliciousRecipes.ItemMetadata(\n                ingredients: [\"Chocolate\", \"Coffee\", \"Flour\"],\n                preparationTime: 10 * 60\n            ),\n            tags: [\"favorite\", \"featured\"],\n            content: Content(\n                title: \"Check out my favorite recipe!\"\n            )\n        )),\n        \u002F\u002F Add default titles to all sections\n        .step(named: \"Default section titles\") { context in\n            context.mutateAllSections { section in\n                guard section.title.isEmpty else { return }\n                \n                switch section.id {\n                case .recipes:\n                    section.title = \"My recipes\"\n                case .links:\n                    section.title = \"External links\"\n                case .about:\n                    section.title = \"About this site\"\n                }\n            }\n        }\n    ]\n)\n```\n\nOf course, defining all of a program’s code in one single place is rarely a good idea, so it’s recommended to split up a website’s various generation operations into clearly separated steps — which can be defined by extending the `PublishingStep` type with static properties or methods, like this:\n\n```swift\nextension PublishingStep where Site == DeliciousRecipes {\n    static func addDefaultSectionTitles() -> Self {\n        .step(named: \"Default section titles\") { context in\n            context.mutateAllSections { section in\n                guard section.title.isEmpty else { return }\n\n                switch section.id {\n                case .recipes:\n                    section.title = \"My recipes\"\n                case .links:\n                    section.title = \"External links\"\n                case .about:\n                    section.title = \"About this site\"\n                }\n            }\n        }\n    }\n}\n```\n\n*Each publishing step is passed an instance of `PublishingContext`, which it can use to mutate the current context in which the website is being published — including its files, folders, and content.*\n\nUsing the above pattern, we can implement any number of custom publishing steps that’ll fit right in alongside all of the default steps that Publish ships with. This enables us to construct really powerful pipelines in which each step performs a single part of the generation process:\n\n```swift\ntry DeliciousRecipes().publish(using: [\n    .addMarkdownFiles(),\n    .copyResources(),\n    .addFavoriteItems(),\n    .addDefaultSectionTitles(),\n    .generateHTML(withTheme: .delicious),\n    .generateRSSFeed(including: [.recipes]),\n    .generateSiteMap()\n])\n```\n\n*Above we’re constructing a completely custom publishing pipeline by calling the `publish(using:)` API.*\n\nTo learn more about Publish’s built-in publishing steps, [check out this file](https:\u002F\u002Fgithub.com\u002FJohnSundell\u002FPublish\u002Fblob\u002Fmaster\u002FSources\u002FPublish\u002FAPI\u002FPublishingStep.swift).\n\n## Building an HTML theme\n\nPublish uses [Plot](https:\u002F\u002Fgithub.com\u002Fjohnsundell\u002Fplot) as its HTML theming engine, which enables entire HTML pages to be defined using Swift. When using Publish, it’s recommended that you build your own website-specific theme — that can make full use of your own custom metadata, and be completely tailored to fit your website’s design.\n\nThemes are defined using the `Theme` type, which uses an `HTMLFactory` implementation to create all of a website’s HTML pages. Here’s an excerpt of what the implementation for the custom `.delicious` theme used above may look like:\n\n```swift\nextension Theme where Site == DeliciousRecipes {\n    static var delicious: Self {\n        Theme(htmlFactory: DeliciousHTMLFactory())\n    }\n\n    private struct DeliciousHTMLFactory: HTMLFactory {\n        ...\n        func makeItemHTML(\n            for item: Item\u003CDeliciousRecipes>,\n            context: PublishingContext\u003CDeliciousRecipes>\n        ) throws -> HTML {\n            HTML(\n                .head(for: item, on: context.site),\n                .body(\n                    .ul(\n                        .class(\"ingredients\"),\n                        .forEach(item.metadata.ingredients) {\n                            .li(.text($0))\n                        }\n                    ),\n                    .p(\n                        \"This will take around \",\n                        \"\\(Int(item.metadata.preparationTime \u002F 60)) \",\n                        \"minutes to prepare\"\n                    ),\n                    .contentBody(item.body)\n                )\n            )\n        }\n        ...\n    }\n}\n```\n\nAbove we’re able to access both built-in item properties, and the custom metadata properties that we defined earlier as part of our website’s `ItemMetadata` struct, all in a way that retains full type safety.\n\n*More thorough documentation on how to build Publish themes, and some of the recommended best practices for doing so, will be added shortly.*\n\n## Building plugins\n\nPublish also supports plugins, which can be used to share setup code between various projects, or to extend Publish’s built-in functionality in various ways. Just like publishing steps, plugins perform their work by modifying the current `PublishingContext` — for example by adding files or folders, by mutating the website’s existing content, or by adding Markdown parsing modifiers.\n\nHere’s an example of a plugin that ensures that all of a website’s items have tags:\n\n```swift\nextension Plugin {\n    static var ensureAllItemsAreTagged: Self {\n        Plugin(name: \"Ensure that all items are tagged\") { context in\n            let allItems = context.sections.flatMap { $0.items }\n\n            for item in allItems {\n                guard !item.tags.isEmpty else {\n                    throw PublishingError(\n                        path: item.path,\n                        infoMessage: \"Item has no tags\"\n                    )\n                }\n            }\n        }\n    }\n}\n```\n\nPlugins are then installed by adding the `installPlugin` step to any publishing pipeline:\n\n```swift\ntry DeliciousRecipes().publish(using: [\n    ...\n    .installPlugin(.ensureAllItemsAreTagged)\n])\n```\n\n*If your plugin is hosted on GitHub you can use the `publish-plugin` [topic](https:\u002F\u002Fhelp.github.com\u002Fen\u002Fgithub\u002Fadministering-a-repository\u002Fclassifying-your-repository-with-topics#adding-topics-to-your-repository) so it can be found with the rest of [community plugins](https:\u002F\u002Fgithub.com\u002Ftopics\u002Fpublish-plugin?l=swift).*\n\nFor a real-world example of a Publish plugin, check out the [official Splash plugin](https:\u002F\u002Fgithub.com\u002Fjohnsundell\u002Fsplashpublishplugin), which makes it really easy to integrate the [Splash syntax highlighter](https:\u002F\u002Fgithub.com\u002Fjohnsundell\u002Fsplash) with Publish.\n\n## System requirements\n\nTo be able to successfully use Publish, make sure that your system has Swift version 5.4 (or later) installed. If you’re using a Mac, also make sure that `xcode-select` is pointed at an Xcode installation that includes the required version of Swift, and that you’re running macOS Big Sur (11.0) or later. \n\nPlease note that Publish **does not** officially support any form of beta software, including beta versions of Xcode and macOS, or unreleased versions of Swift.\n\n## Installation\n\nPublish is distributed using the [Swift Package Manager](https:\u002F\u002Fswift.org\u002Fpackage-manager). To install it into a project, add it as a dependency within your `Package.swift` manifest:\n\n```swift\nlet package = Package(\n    ...\n    dependencies: [\n        .package(url: \"https:\u002F\u002Fgithub.com\u002Fjohnsundell\u002Fpublish.git\", from: \"0.1.0\")\n    ],\n    ...\n)\n```\n\nThen import Publish wherever you’d like to use it:\n\n```swift\nimport Publish\n```\n\nFor more information on how to use the Swift Package Manager, check out [this article](https:\u002F\u002Fwww.swiftbysundell.com\u002Farticles\u002Fmanaging-dependencies-using-the-swift-package-manager), or [its official documentation](https:\u002F\u002Fswift.org\u002Fpackage-manager).\n\nPublish also ships with a command line tool that makes it easy to set up new website projects, and to generate and deploy existing ones. To install that command line tool, simply run `make` within a local copy of the Publish repo:\n\n```\n$ git clone https:\u002F\u002Fgithub.com\u002FJohnSundell\u002FPublish.git\n$ cd Publish\n$ make\n```\n\nThen run `publish help` for instructions on how to use it.\n\nThe Publish command line tool is also available via [Homebrew](https:\u002F\u002Fbrew.sh) and can be installed using the following command if you have Homebrew installed:\n\n```\nbrew install publish\n```\n\nHowever, please note that Homebrew support is not officially maintained by John Sundell, and you might therefore be installing an older version of the Publish command line tool when using Homebrew. Using `make`, as described above, is the preferred way to install the Publish command line tool.\n\n## Running and deploying\n\nSince all Publish websites are implemented as Swift packages, they can be generated simply by opening up a website’s package in Xcode (by opening its `Package.swift` file), and then running it using the `Product > Run` command (or `⌘+R`).\n\nPublish can also facilitate the deployment of websites to external servers through its `DeploymentMethod` API, and ships with built-in implementations for Git and GitHub-based deployments. To define a deployment method for a website, add the `deploy` step to your publishing pipeline:\n\n```swift\ntry DeliciousRecipes().publish(using: [\n    ...\n    .deploy(using: .gitHub(\"johnsundell\u002Fdelicious-recipes\"))\n])\n```\n\nEven when added to a pipeline, deployment steps are disabled by default, and are only executed when the `--deploy` command line flag was passed (which can be added through Xcode’s `Product > Scheme > Edit Scheme...` menu), or by running the command line tool using `publish deploy`.\n\nPublish can also start a `localhost` web server for local testing and development, by using the `publish run` command. To regenerate site content with the server running, use Product > Run on your site's package in Xcode.\n\n## Quick start\n\nTo quickly get started with Publish, install the command line tool by first cloning this repository, and then run `make` within the cloned folder:\n\n```\n$ git clone https:\u002F\u002Fgithub.com\u002FJohnSundell\u002FPublish.git\n$ cd Publish\n$ make\n```\n\n_**Note**: If you encounter an error while running `make`, ensure that you have your Command Line Tools location set from Xcode's preferences. It's in Preferences > Locations > Locations > Command Line Tools. The dropdown will be blank if it hasn't been set yet._\n\nThen, create a new folder for your new website project and simply run `publish new` within it to get started:\n\n```\n$ mkdir MyWebsite\n$ cd MyWebsite\n$ publish new\n```\n\nFinally, run `open Package.swift` to open up the project in Xcode to start building your new website.\n\n## Additional documentation\n\nYou can find a growing collection of additional documentation about Publish’s various features and capabilities within the [Documentation folder](Documentation).\n\n## Design and goals\n\nPublish was first and foremost designed to be a powerful and heavily customizable tool for building static websites in Swift — starting with [Swift by Sundell](https:\u002F\u002Fswiftbysundell.com), a website which has over 300 individual pages and a pipeline consisting of over 25 publishing steps.\n\nWhile the goal is definitely also to make Publish as accessible and easy to use as possible, it will most likely keep being a quite low-level tool that favors code-level control over file system configuration files, and customizability over strongly held conventions.\n\nThe main trade-off of that design is that Publish will likely have a steeper learning curve than most other static site generators, but hopefully it’ll also offer a much greater degree of power, flexibility and type safety as a result. Over time, and with the community’s help, we should be able to make that learning curve much less steep though — through much more thorough documentation and examples, and through shared tools and convenience APIs.\n\nPublish was also designed with code reuse in mind, and hopefully a much larger selection of themes, tools, plugins and other extensions will be developed by the community over time.\n\n## Contributions and support\n\nPublish is developed completely in the open, and your contributions are more than welcome.\n\nBefore you start using Publish in any of your projects, it’s highly recommended that you spend a few minutes familiarizing yourself with its documentation and internal implementation, so that you’ll be ready to tackle any issues or edge cases that you might encounter.\n\nSince this is a very young project, it’s likely to have many limitations and missing features, which is something that can really only be discovered and addressed as more people start using it. While Publish is used in production to build all of [Swift by Sundell](https:\u002F\u002Fswiftbysundell.com), it’s recommended that you first try it out for your specific use case, to make sure it supports the features that you need.\n\nThis project does not come with GitHub Issues-based support, and users are instead encouraged to become active participants in its continued development — by fixing any bugs that they encounter, or by improving the documentation wherever it’s found to be lacking.\n\nIf you wish to make a change, [open a Pull Request](https:\u002F\u002Fgithub.com\u002FJohnSundell\u002FPublish\u002Fpull\u002Fnew) — even if it just contains a draft of the changes you’re planning, or a test that reproduces an issue — and we can discuss it further from there.\n\nHope you’ll enjoy using Publish!\n","Publish 是一个专为 Swift 开发者设计的静态网站生成器。它支持使用 Swift 语言构建整个网站，并提供了主题、插件等丰富的自定义选项，使得开发者能够以类型安全的方式配置和生成网站内容。项目采用 Swift 5.5 编写，兼容 Swift Package Manager，并且可以在 macOS 和 Linux 平台上运行。适合于需要快速搭建个性化博客或文档站点的 Swift 开发者，尤其是那些希望利用 Swift 的强大功能来管理网页内容结构与样式的人士。通过 Publish，用户可以从简单的配置开始，随着需求的增长逐步引入更复杂的定制化设置。",2,"2026-06-11 03:09:10","top_language"]