diff --git a/Makefile b/Makefile index 204a924..0c486e5 100644 --- a/Makefile +++ b/Makefile @@ -12,4 +12,8 @@ publish_beta: compile @echo ./bin/publish --beta --delete -.PHONY: compile publish publish_beta +test: + @echo + ./bin/test + +.PHONY: compile publish publish_beta test diff --git a/Readme.md b/Readme.md index 9a8c106..fd079dd 100644 --- a/Readme.md +++ b/Readme.md @@ -19,9 +19,9 @@ Execution, trying TDD for the first time: - [x] Write a test harness that renders a site and then checks the output with `diff -r` - - [ ] Port _layout.ejs to Swift code + - [x] Write a site generator that renders www/index.html from site.json - - [ ] Write a site generator that renders www/index.html from site.json + - [ ] Port _layout.ejs to Swift code - [ ] Add support for CSS files diff --git a/SiteGenerator/.gitignore b/SiteGenerator/.gitignore new file mode 100644 index 0000000..d3ad818 --- /dev/null +++ b/SiteGenerator/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +/.swiftpm \ No newline at end of file diff --git a/SiteGenerator/Package.resolved b/SiteGenerator/Package.resolved new file mode 100644 index 0000000..8f069df --- /dev/null +++ b/SiteGenerator/Package.resolved @@ -0,0 +1,34 @@ +{ + "object": { + "pins": [ + { + "package": "PathKit", + "repositoryURL": "https://github.com/kylef/PathKit.git", + "state": { + "branch": null, + "revision": "e2f5be30e4c8f531c9c1e8765aa7b71c0a45d7a0", + "version": "0.9.2" + } + }, + { + "package": "Spectre", + "repositoryURL": "https://github.com/kylef/Spectre.git", + "state": { + "branch": null, + "revision": "f14ff47f45642aa5703900980b014c2e9394b6e5", + "version": "0.9.0" + } + }, + { + "package": "Stencil", + "repositoryURL": "https://github.com/stencilproject/Stencil.git", + "state": { + "branch": null, + "revision": "0e9a78d6584e3812cd9c09494d5c7b483e8f533c", + "version": "0.13.1" + } + } + ] + }, + "version": 1 +} diff --git a/SiteGenerator/Package.swift b/SiteGenerator/Package.swift index e69de29..843e2d3 100644 --- a/SiteGenerator/Package.swift +++ b/SiteGenerator/Package.swift @@ -0,0 +1,21 @@ +// swift-tools-version:5.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "SiteGenerator", + dependencies: [ + .package(url: "https://github.com/stencilproject/Stencil.git", from: "0.13.0") + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + .target( + name: "SiteGenerator", + dependencies: ["Stencil"]), + .testTarget( + name: "SiteGeneratorTests", + dependencies: ["SiteGenerator"]), + ] +) diff --git a/SiteGenerator/Readme.md b/SiteGenerator/Readme.md new file mode 100644 index 0000000..55002ee --- /dev/null +++ b/SiteGenerator/Readme.md @@ -0,0 +1,5 @@ +# SiteGenerator + +A static site generator for [samhuri.net](https://samhuri.net). + +See https://github.com/samsonjs/samhuri.net for details. diff --git a/SiteGenerator/Sources/SiteGenerator/Generator.swift b/SiteGenerator/Sources/SiteGenerator/Generator.swift new file mode 100644 index 0000000..f5e5936 --- /dev/null +++ b/SiteGenerator/Sources/SiteGenerator/Generator.swift @@ -0,0 +1,34 @@ +// +// Generator.swift +// SiteGenerator +// +// Created by Sami Samhuri on 2019-12-01. +// + +import Foundation +import PathKit +import Stencil + +public final class Generator { + private let fileManager: FileManager = .default + + public let site: Site + public let sourceURL: URL + + private let renderer: Environment + + public init(sourceURL: URL) throws { + let publicURL = sourceURL.appendingPathComponent("public") + self.renderer = Environment(loader: FileSystemLoader(paths: [Path(publicURL.path)])) + let siteURL = sourceURL.appendingPathComponent("site.json") + self.site = try Site.decode(from: siteURL) + self.sourceURL = sourceURL + } + + public func generate(targetURL: URL) throws { + try fileManager.createDirectory(at: targetURL, withIntermediateDirectories: true, attributes: nil) + let indexHTML = try renderer.renderTemplate(name: "index.html", context: ["site": site]) + let indexURL = targetURL.appendingPathComponent("index.html") + try indexHTML.write(to: indexURL, atomically: true, encoding: .utf8) + } +} diff --git a/SiteGenerator/Sources/SiteGenerator/Site.swift b/SiteGenerator/Sources/SiteGenerator/Site.swift new file mode 100644 index 0000000..68e848a --- /dev/null +++ b/SiteGenerator/Sources/SiteGenerator/Site.swift @@ -0,0 +1,22 @@ +// +// Site.swift +// SiteGenerator +// +// Created by Sami Samhuri on 2019-12-01. +// + +import Foundation + +public struct Site: Codable { + public let author: String + public let email: String + public let title: String + public let url: String +} + +public extension Site { + static func decode(from url: URL) throws -> Site { + let json = try Data(contentsOf: url) + return try JSONDecoder().decode(Site.self, from: json) + } +} diff --git a/SiteGenerator/Sources/SiteGenerator/main.swift b/SiteGenerator/Sources/SiteGenerator/main.swift new file mode 100644 index 0000000..3f53f72 --- /dev/null +++ b/SiteGenerator/Sources/SiteGenerator/main.swift @@ -0,0 +1,22 @@ +// +// main.swift +// SiteGenerator +// +// Created by Sami Samhuri on 2019-12-01. +// + +import Foundation + +func main(sourcePath: String, targetPath: String) throws { + let sourceURL = URL(fileURLWithPath: sourcePath) + let targetURL = URL(fileURLWithPath: targetPath) + let generator = try Generator(sourceURL: sourceURL) + try generator.generate(targetURL: targetURL) +} + +let sourcePath = CommandLine.arguments[1] +let targetPath = CommandLine.arguments[2] + +// TODO: validate args + +try! main(sourcePath: sourcePath, targetPath: targetPath) diff --git a/SiteGenerator/Tests/LinuxMain.swift b/SiteGenerator/Tests/LinuxMain.swift new file mode 100644 index 0000000..8b7ffc0 --- /dev/null +++ b/SiteGenerator/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import SiteGeneratorTests + +var tests = [XCTestCaseEntry]() +tests += SiteGeneratorTests.allTests() +XCTMain(tests) diff --git a/SiteGenerator/Tests/SiteGeneratorTests/SiteGeneratorTests.swift b/SiteGenerator/Tests/SiteGeneratorTests/SiteGeneratorTests.swift new file mode 100644 index 0000000..3e76450 --- /dev/null +++ b/SiteGenerator/Tests/SiteGeneratorTests/SiteGeneratorTests.swift @@ -0,0 +1,47 @@ +import XCTest +import class Foundation.Bundle + +final class SiteGeneratorTests: XCTestCase { + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + + // Some of the APIs that we use below are available in macOS 10.13 and above. + guard #available(macOS 10.13, *) else { + return + } + + let fooBinary = productsDirectory.appendingPathComponent("SiteGenerator") + + let process = Process() + process.executableURL = fooBinary + + let pipe = Pipe() + process.standardOutput = pipe + + try process.run() + process.waitUntilExit() + + let data = pipe.fileHandleForReading.readDataToEndOfFile() + let output = String(data: data, encoding: .utf8) + + XCTAssertEqual(output, "") + } + + /// Returns path to the built products directory. + var productsDirectory: URL { + #if os(macOS) + for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") { + return bundle.bundleURL.deletingLastPathComponent() + } + fatalError("couldn't find the products directory") + #else + return Bundle.main.bundleURL + #endif + } + + static var allTests = [ + ("testExample", testExample), + ] +} diff --git a/SiteGenerator/Tests/SiteGeneratorTests/XCTestManifests.swift b/SiteGenerator/Tests/SiteGeneratorTests/XCTestManifests.swift new file mode 100644 index 0000000..2f0d547 --- /dev/null +++ b/SiteGenerator/Tests/SiteGeneratorTests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) +public func allTests() -> [XCTestCaseEntry] { + return [ + testCase(SiteGeneratorTests.allTests), + ] +} +#endif diff --git a/bin/compile b/bin/compile index d4e799f..1a390ec 100755 --- a/bin/compile +++ b/bin/compile @@ -8,9 +8,8 @@ SOURCE_DIR="$1" TARGET_DIR="$2" function main() { - echo "* copy files from $SOURCE_DIR to $TARGET_DIR" - mkdir -p "$TARGET_DIR" - cp -rp "$SOURCE_DIR"/* "$TARGET_DIR" + echo "* generate site from $SOURCE_DIR into $TARGET_DIR" + "$THIS_DIR/sitegen" "$SOURCE_DIR" "$TARGET_DIR" # echo "* compile rss feed" # compile_feeds diff --git a/bin/sitegen b/bin/sitegen new file mode 100755 index 0000000..c729704 Binary files /dev/null and b/bin/sitegen differ diff --git a/bin/test b/bin/test index dce731b..f24d106 100755 --- a/bin/test +++ b/bin/test @@ -2,8 +2,13 @@ set -e +pushd "SiteGenerator" >/dev/null +swift build +cp .build/x86_64-apple-macosx/debug/SiteGenerator ../bin/sitegen +popd >/dev/null + for site in Tests/test-*; do - bin/compile "$site/in" "$site/actual" >/dev/null - diff -r "$site/expected" "$site/actual" + bin/compile "$site/in" "$site/actual" # >/dev/null + diff -ru "$site/expected" "$site/actual" rm -r "$site/actual" done diff --git a/tests/test-index/expected/index.html b/tests/test-index/expected/index.html index 825c61e..ac26573 100644 --- a/tests/test-index/expected/index.html +++ b/tests/test-index/expected/index.html @@ -1,3 +1,12 @@ + + Valar Morghulis + + +

Valar Morghulis

+ +

hello world

+ + diff --git a/tests/test-index/expected/site.json b/tests/test-index/expected/site.json deleted file mode 100644 index 473e83e..0000000 --- a/tests/test-index/expected/site.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title": "Valar Morghulis", - "author": "A man has no name", - "email": "jaqen@hotmail.com", - "url": "http://example.net" -} diff --git a/tests/test-index/in/index.html b/tests/test-index/in/index.html deleted file mode 100644 index 825c61e..0000000 --- a/tests/test-index/in/index.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/tests/test-index/in/public/index.html b/tests/test-index/in/public/index.html new file mode 100644 index 0000000..99a41fc --- /dev/null +++ b/tests/test-index/in/public/index.html @@ -0,0 +1,4 @@ +{% extends "layout.html" %} +{% block content %} +

hello world

+{% endblock %} diff --git a/tests/test-index/in/public/layout.html b/tests/test-index/in/public/layout.html new file mode 100644 index 0000000..6c6d145 --- /dev/null +++ b/tests/test-index/in/public/layout.html @@ -0,0 +1,11 @@ + + + + {{ site.title }} + + +

{{ site.title }}

+ {% block content %} + {% endblock %} + + diff --git a/tests/test-index/in/site.json b/tests/test-index/in/site.json index 473e83e..b72b697 100644 --- a/tests/test-index/in/site.json +++ b/tests/test-index/in/site.json @@ -1,6 +1,6 @@ { - "title": "Valar Morghulis", "author": "A man has no name", "email": "jaqen@hotmail.com", + "title": "Valar Morghulis", "url": "http://example.net" }