Skip to content

Commit b740f13

Browse files
committed
Add --preview and --port option for CLI to easily start a web server
1 parent f597705 commit b740f13

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

Package.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ let package = Package(
3737
.package(url: "https://github.com/pavel-trafimuk/Stencil", branch: "master"),
3838
.package(url: "https://github.com/tid-kijyun/Kanna.git", from: "5.2.2"),
3939

40+
// Web server for preview:
41+
42+
// A server-side Swift web framework.
43+
.package(url: "https://github.com/vapor/vapor.git", from: "4.99.3"),
44+
// Non-blocking, event-driven networking for Swift. Used for custom executors
45+
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
4046
],
4147
targets: [
4248
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
@@ -56,6 +62,9 @@ let package = Package(
5662
"HyloStandardLibrary",
5763
.product(name: "FrontEnd", package: "hylo"),
5864
.product(name: "ArgumentParser", package: "swift-argument-parser"),
65+
.product(name: "Vapor", package: "vapor"),
66+
.product(name: "NIOCore", package: "swift-nio"),
67+
.product(name: "NIOPosix", package: "swift-nio"),
5968
],
6069
path: "Sources/CLI",
6170
exclude: ["module.md"],

Sources/CLI/CLI.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import FrontEnd
66
import HyloStandardLibrary
77
import WebsiteGen
88

9+
// import Vapor
10+
911
extension DiagnosticSet {
1012
fileprivate var niceErrorMessage: String {
1113
return "Diagnostics: \n" + elements.map { " - " + $0.description + "\n" }.joined(by: "")
@@ -211,9 +213,15 @@ public struct CLI: ParsableCommand {
211213
@Option(name: .shortAndLong, help: "The output path for the HTML files. ")
212214
var outputPath: String = "./dist"
213215

214-
@Flag(name: .shortAndLong, help: "Document the standard library. ")
216+
@Flag(name: .customShort("s"), help: "Document the standard library. ")
215217
var documentingStandardLibrary: Bool = false
216218

219+
@Flag(help: "Start a web server to preview the generated documentation. ")
220+
var preview: Bool = false
221+
222+
@Option(name: .customShort("p"), help: "The port number for the web server. ")
223+
var port: Int = 8080
224+
217225
public init() {}
218226

219227
public func run() throws {
@@ -230,5 +238,9 @@ public struct CLI: ParsableCommand {
230238
})
231239

232240
print("Documentation successfully generated at \(outputPath) in \(duration).")
241+
242+
guard preview else { return }
243+
print("Starting preview server... 🚀")
244+
try startWebserverSync(port: port, publicDirectory: URL(fileURLWithPath: outputPath))
233245
}
234246
}

Sources/CLI/WebServer.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Logging
2+
import NIOCore
3+
import NIOPosix
4+
import Vapor
5+
6+
/// Start a web server asynchronously to serve the files in the given directory.
7+
func startWebServer(port: Int = 8080, publicDirectory: URL) async throws {
8+
var env = Environment(name: "development", arguments: ["vapor"])
9+
try LoggingSystem.bootstrap(from: &env)
10+
11+
let app = try await Application.make(env)
12+
app.http.server.configuration.port = port
13+
defer { app.shutdown() }
14+
15+
app.middleware.use(
16+
FileMiddleware(
17+
publicDirectory: publicDirectory.absoluteURL.path,
18+
defaultFile: "index.html"
19+
)
20+
)
21+
22+
try await app.execute()
23+
try await app.asyncShutdown()
24+
}
25+
26+
/// Start a web server synchronously to serve the files in the given directory.
27+
///
28+
/// This function will exit when the user sends a SIGINT signal (Ctrl+C).
29+
func startWebserverSync(port: Int, publicDirectory: URL) throws {
30+
// Set up signal handler for SIGINT (Ctrl+C)
31+
let signalSource = DispatchSource.makeSignalSource(signal: SIGINT, queue: .main)
32+
signal(SIGINT, SIG_IGN) // Ignore default handling
33+
signalSource.setEventHandler {
34+
print("Exiting...")
35+
signalSource.cancel()
36+
exit(0)
37+
}
38+
// Start monitoring the signal
39+
signalSource.resume()
40+
41+
// Run the task asynchronously
42+
Task {
43+
try await startWebServer(port: port, publicDirectory: publicDirectory)
44+
}
45+
46+
// Keep the run loop running
47+
RunLoop.main.run()
48+
}

0 commit comments

Comments
 (0)