diff --git a/Sources/SwiftDoc/Helpers.swift b/Sources/SwiftDoc/Helpers.swift index d23e8384..3541fcaf 100644 --- a/Sources/SwiftDoc/Helpers.swift +++ b/Sources/SwiftDoc/Helpers.swift @@ -14,10 +14,20 @@ public func path(for symbol: Symbol, with baseURL: String) -> String { } public func path(for identifier: CustomStringConvertible, with baseURL: String) -> String { - let url = URL(string: baseURL)?.appendingPathComponent("\(identifier)") ?? URL(string: "\(identifier)") + let tail: String = path(for: "\(identifier)") + let url = URL(string: baseURL)?.appendingPathComponent(tail) ?? URL(string: tail) guard let string = url?.absoluteString else { fatalError("Unable to construct path for \(identifier) with baseURL \(baseURL)") } return string } + +private let reservedCharacters: CharacterSet = [ + // Windows Reserved Characters + "<", ">", ":", "\"", "/", "\\", "|", "?", "*", +] + +public func path(for identifier: String) -> String { + return identifier.components(separatedBy: reservedCharacters).joined(separator: "_") +} diff --git a/Sources/swift-doc/Subcommands/Generate.swift b/Sources/swift-doc/Subcommands/Generate.swift index 82c9ef32..52ac8e28 100644 --- a/Sources/swift-doc/Subcommands/Generate.swift +++ b/Sources/swift-doc/Subcommands/Generate.swift @@ -134,11 +134,11 @@ extension SwiftDoc { let filename: String switch format { case .commonmark: - filename = "\($0.key).md" + filename = "\(path(for: $0.key)).md" case .html where $0.key == "Home": filename = "index.html" case .html: - filename = "\($0.key)/index.html" + filename = "\(path(for: $0.key))/index.html" } let url = outputDirectoryURL.appendingPathComponent(filename) diff --git a/Tests/SwiftDocTests/PathTests.swift b/Tests/SwiftDocTests/PathTests.swift index a33804d7..1c586af0 100644 --- a/Tests/SwiftDocTests/PathTests.swift +++ b/Tests/SwiftDocTests/PathTests.swift @@ -6,42 +6,42 @@ final class PathTests: XCTestCase { func testEmptyBaseURL() { XCTAssertEqual(path(for: "Class", with: ""), "Class") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: ""), "(lhs:rhs:)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: ""), "(lhs_rhs_)") } func testRootDirectoryBaseURL() { XCTAssertEqual(path(for: "Class", with: "/"), "/Class") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: "/"), "/(lhs:rhs:)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: "/"), "/(lhs_rhs_)") } func testCurrentDirectoryBaseURL() { XCTAssertEqual(path(for: "Class", with: "./"), "./Class") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: "./"), "./(lhs:rhs:)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: "./"), "./(lhs_rhs_)") } func testNestedSubdirectoryBaseURL() { XCTAssertEqual(path(for: "Class", with: "/path/to/directory"), "/path/to/directory/Class") XCTAssertEqual(path(for: "Class", with: "/path/to/directory/"), "/path/to/directory/Class") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: "/path/to/directory"), "/path/to/directory/(lhs:rhs:)") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: "/path/to/directory/"), "/path/to/directory/(lhs:rhs:)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: "/path/to/directory"), "/path/to/directory/(lhs_rhs_)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: "/path/to/directory/"), "/path/to/directory/(lhs_rhs_)") } func testDomainBaseURL() { XCTAssertEqual(path(for: "Class", with: "https://example.com"), "https://example.com/Class") XCTAssertEqual(path(for: "Class", with: "https://example.com/"), "https://example.com/Class") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: "https://example.com"), "https://example.com/(lhs:rhs:)") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: "https://example.com/"), "https://example.com/(lhs:rhs:)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: "https://example.com"), "https://example.com/(lhs_rhs_)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: "https://example.com/"), "https://example.com/(lhs_rhs_)") } func testDomainSubdirectoryBaseURL() { XCTAssertEqual(path(for: "Class", with: "https://example.com/docs"), "https://example.com/docs/Class") XCTAssertEqual(path(for: "Class", with: "https://example.com/docs/"), "https://example.com/docs/Class") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: "https://example.com/docs"), "https://example.com/docs/(lhs:rhs:)") - XCTAssertEqual(path(for: "(lhs:rhs:)", with: "https://example.com/docs/"), "https://example.com/docs/(lhs:rhs:)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: "https://example.com/docs"), "https://example.com/docs/(lhs_rhs_)") + XCTAssertEqual(path(for: "(lhs:rhs:)", with: "https://example.com/docs/"), "https://example.com/docs/(lhs_rhs_)") } }