diff --git a/Sources/Docs/Run.swift b/Sources/Docs/Run.swift index 340bdee..55e37eb 100644 --- a/Sources/Docs/Run.swift +++ b/Sources/Docs/Run.swift @@ -35,6 +35,7 @@ struct Run { writers: [ .itemWriter(swim(renderArticle)), .listWriter(swim(renderArticles)), + .listWriter(renderJson, output: "../static/search.json"), .tagWriter(swim(renderTag), tags: \.metadata.tags), .yearWriter(swim(renderYear)), // Atom feed for all articles, and a feed per tag @@ -72,5 +73,18 @@ struct Run { // All the remaining files that were not parsed to markdown, so for example images, raw html files and css, // are copied as-is to the output folder. .staticFiles() + + // Run saga again on articles, to collect search index. + // try await Saga(input: "content", output: "deploy") + // .register( + // folder: "articles", + // metadata: ArticleMetadata.self, + // readers: [.plainReader], + // filter: \.public, + // writers: [ + // .listWriter(renderJson, output: "../search.json") + // ] + // ) + // .run() } } diff --git a/Sources/Docs/Templates/RenderArticles.swift b/Sources/Docs/Templates/RenderArticles.swift index 8567abe..3547bd9 100644 --- a/Sources/Docs/Templates/RenderArticles.swift +++ b/Sources/Docs/Templates/RenderArticles.swift @@ -63,6 +63,60 @@ func renderYear(context: PartitionedRenderingContext) -> baseRenderArticles(context.items, canocicalURL: "/articles/\(context.key)/", title: "Articles in \(context.key)") } +private struct SearchData: Encodable, Identifiable { + let id: String + let title: String + let content: String + + init(article: Item) throws { + self.id = article.url + self.title = article.title + let rawContent: String = try article.absoluteSource.read() + self.content = Self.parse(rawContent) + } + + /// Grabs the metadata (wrapped within `---`), the first title, and the body of the document. + static func parts(from content: String) -> (String?, String?, String) { + let scanner = Scanner(string: content) + + var header: String? = nil + var title: String? = nil + + if scanner.scanString("---") == "---" { + header = scanner.scanUpToString("---") + _ = scanner.scanString("---") + } + + if scanner.scanString("# ") == "# " { + title = scanner.scanUpToString("\n") + } + + let body = String(scanner.string[scanner.currentIndex...]) + + return (header, title, body) + } + + static func parse(_ content: String) -> String { + let (_, _, body) = parts(from: content) + return body + .replacingOccurrences(of: "\n", with: " ") + .replacingOccurrences(of: "#", with: "") + } +} + +func renderJson(_ articles: ItemsRenderingContext) throws -> String { + print(articles.items.count) + print(articles.items) + let data = try jsonEncoder.encode(articles.items.map(SearchData.init(article:))) + return String(data: data, encoding: .utf8)! +} + +private let jsonEncoder: JSONEncoder = { + let encoder = JSONEncoder() + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + return encoder +}() + private func baseRenderArticles( _ articles: [Item], canocicalURL: String,