mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-03-25 09:05:47 +00:00
Make the site generator render index.html with a layout
This commit is contained in:
parent
57bdf5d14a
commit
ea2b53d625
21 changed files with 247 additions and 18 deletions
6
Makefile
6
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
5
SiteGenerator/.gitignore
vendored
Normal file
5
SiteGenerator/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
/.swiftpm
|
||||
34
SiteGenerator/Package.resolved
Normal file
34
SiteGenerator/Package.resolved
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"]),
|
||||
]
|
||||
)
|
||||
5
SiteGenerator/Readme.md
Normal file
5
SiteGenerator/Readme.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# SiteGenerator
|
||||
|
||||
A static site generator for [samhuri.net](https://samhuri.net).
|
||||
|
||||
See https://github.com/samsonjs/samhuri.net for details.
|
||||
34
SiteGenerator/Sources/SiteGenerator/Generator.swift
Normal file
34
SiteGenerator/Sources/SiteGenerator/Generator.swift
Normal file
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
22
SiteGenerator/Sources/SiteGenerator/Site.swift
Normal file
22
SiteGenerator/Sources/SiteGenerator/Site.swift
Normal file
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
22
SiteGenerator/Sources/SiteGenerator/main.swift
Normal file
22
SiteGenerator/Sources/SiteGenerator/main.swift
Normal file
|
|
@ -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)
|
||||
7
SiteGenerator/Tests/LinuxMain.swift
Normal file
7
SiteGenerator/Tests/LinuxMain.swift
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import XCTest
|
||||
|
||||
import SiteGeneratorTests
|
||||
|
||||
var tests = [XCTestCaseEntry]()
|
||||
tests += SiteGeneratorTests.allTests()
|
||||
XCTMain(tests)
|
||||
|
|
@ -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),
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import XCTest
|
||||
|
||||
#if !canImport(ObjectiveC)
|
||||
public func allTests() -> [XCTestCaseEntry] {
|
||||
return [
|
||||
testCase(SiteGeneratorTests.allTests),
|
||||
]
|
||||
}
|
||||
#endif
|
||||
|
|
@ -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
|
||||
|
|
|
|||
BIN
bin/sitegen
Executable file
BIN
bin/sitegen
Executable file
Binary file not shown.
9
bin/test
9
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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,12 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Valar Morghulis</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="http://example.net">Valar Morghulis</a></h1>
|
||||
|
||||
<p>hello world</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"title": "Valar Morghulis",
|
||||
"author": "A man has no name",
|
||||
"email": "jaqen@hotmail.com",
|
||||
"url": "http://example.net"
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
</html>
|
||||
4
tests/test-index/in/public/index.html
Normal file
4
tests/test-index/in/public/index.html
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<p>hello world</p>
|
||||
{% endblock %}
|
||||
11
tests/test-index/in/public/layout.html
Normal file
11
tests/test-index/in/public/layout.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ site.title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="{{ site.url }}">{{ site.title }}</a></h1>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"title": "Valar Morghulis",
|
||||
"author": "A man has no name",
|
||||
"email": "jaqen@hotmail.com",
|
||||
"title": "Valar Morghulis",
|
||||
"url": "http://example.net"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue