Skip to content

Add support for configurable comments in generated files #773

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Sources/_OpenAPIGeneratorCore/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public struct Config: Sendable {
/// Additional imports to add to each generated file.
public var additionalImports: [String]

/// Additional comments to add to the top of each generated file.
public var additionalFileComments: [String]

/// Filter to apply to the OpenAPI document before generation.
public var filter: DocumentFilter?

Expand All @@ -68,6 +71,7 @@ public struct Config: Sendable {
/// - mode: The mode to use for generation.
/// - access: The access modifier to use for generated declarations.
/// - additionalImports: Additional imports to add to each generated file.
/// - additionalFileComments: Additional comments to add to the top of each generated file.
/// - filter: Filter to apply to the OpenAPI document before generation.
/// - namingStrategy: The naming strategy to use for deriving Swift identifiers from OpenAPI identifiers.
/// Defaults to `defensive`.
Expand All @@ -78,6 +82,7 @@ public struct Config: Sendable {
mode: GeneratorMode,
access: AccessModifier,
additionalImports: [String] = [],
additionalFileComments: [String] = [],
filter: DocumentFilter? = nil,
namingStrategy: NamingStrategy,
nameOverrides: [String: String] = [:],
Expand All @@ -86,6 +91,7 @@ public struct Config: Sendable {
self.mode = mode
self.access = access
self.additionalImports = additionalImports
self.additionalFileComments = additionalFileComments
self.filter = filter
self.namingStrategy = namingStrategy
self.nameOverrides = nameOverrides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct ClientFileTranslator: FileTranslator {

let doc = parsedOpenAPI

let topComment: Comment = .inline(Constants.File.topComment)
let topComment = self.topComment

let imports =
Constants.File.clientServerImports + config.additionalImports.map { ImportDescription(moduleName: $0) }
Expand Down
6 changes: 6 additions & 0 deletions Sources/_OpenAPIGeneratorCore/Translator/FileTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ extension FileTranslator {
)
return TranslatorContext(safeNameGenerator: overridingGenerator)
}

/// Creates a top comment that includes the default "do not modify" comment
/// and any additional file comments from the configuration.
var topComment: Comment {
.inline(([Constants.File.topComment] + config.additionalFileComments).joined(separator: "\n"))
}
}

/// A set of configuration values for concrete file translators.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct ServerFileTranslator: FileTranslator {

let doc = parsedOpenAPI

let topComment: Comment = .inline(Constants.File.topComment)
let topComment = self.topComment

let imports =
Constants.File.clientServerImports + config.additionalImports.map { ImportDescription(moduleName: $0) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct TypesFileTranslator: FileTranslator {

let doc = parsedOpenAPI

let topComment: Comment = .inline(Constants.File.topComment)
let topComment = self.topComment

let imports = Constants.File.imports + config.additionalImports.map { ImportDescription(moduleName: $0) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The configuration file has the following keys:
- `package`: Generated API is accessible from other modules within the same package or project.
- `internal` (default): Generated API is accessible from the containing module only.
- `additionalImports` (optional): array of strings. Each string value is a Swift module name. An import statement will be added to the generated source files for each module.
- `additionalFileComments` (optional): array of strings. Each string value is a comment that will be added to the top of each generated file (after the do-not-edit comment). Useful for adding directives like `swift-format-ignore-file` or `swiftlint:disable all`.
- `filter` (optional): Filters to apply to the OpenAPI document before generation.
- `operations`: Operations with these operation IDs will be included in the filter.
- `tags`: Operations tagged with these tags will be included in the filter.
Expand Down Expand Up @@ -95,6 +96,18 @@ additionalImports:
accessModifier: public
```

To add file comments to exclude generated files from formatting tools:

```yaml
generate:
- types
- client
namingStrategy: idiomatic
additionalFileComments:
- "swift-format-ignore-file"
- "swiftlint:disable all"
```

### Document filtering

The generator supports filtering the OpenAPI document prior to generation, which can be useful when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extension _GenerateOptions {
let sortedModes = try resolvedModes(config)
let resolvedAccessModifier = resolvedAccessModifier(config)
let resolvedAdditionalImports = resolvedAdditionalImports(config)
let resolvedAdditionalFileComments = resolvedAdditionalFileComments(config)
let resolvedNamingStragy = resolvedNamingStrategy(config)
let resolvedNameOverrides = resolvedNameOverrides(config)
let resolvedFeatureFlags = resolvedFeatureFlags(config)
Expand All @@ -40,6 +41,7 @@ extension _GenerateOptions {
mode: $0,
access: resolvedAccessModifier,
additionalImports: resolvedAdditionalImports,
additionalFileComments: resolvedAdditionalFileComments,
filter: config?.filter,
namingStrategy: resolvedNamingStragy,
nameOverrides: resolvedNameOverrides,
Expand Down Expand Up @@ -67,6 +69,7 @@ extension _GenerateOptions {
- Plugin source: \(pluginSource?.rawValue ?? "<none>")
- Is dry run: \(isDryRun)
- Additional imports: \(resolvedAdditionalImports.isEmpty ? "<none>" : resolvedAdditionalImports.joined(separator: ", "))
- Additional file comments: \(resolvedAdditionalFileComments.isEmpty ? "<none>" : resolvedAdditionalFileComments.joined(separator: ", "))
"""
)
do {
Expand Down
13 changes: 13 additions & 0 deletions Sources/swift-openapi-generator/GenerateOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct _GenerateOptions: ParsableArguments {

@Option(help: "Additional import to add to all generated files.") var additionalImport: [String] = []

@Option(help: "Additional file comment to add to all generated files.") var additionalFileComment: [String] = []

@Option(help: "Pre-release feature to enable. Options: \(FeatureFlag.prettyListing).") var featureFlag:
[FeatureFlag] = []

Expand Down Expand Up @@ -81,6 +83,17 @@ extension _GenerateOptions {
return []
}

/// Returns a list of additional file comments requested by the user.
/// - Parameter config: The configuration specified by the user.
/// - Returns: A list of additional file comments requested by the user.
func resolvedAdditionalFileComments(_ config: _UserConfig?) -> [String] {
if !additionalFileComment.isEmpty { return additionalFileComment }
if let additionalFileComments = config?.additionalFileComments, !additionalFileComments.isEmpty {
return additionalFileComments
}
return []
}

/// Returns the naming strategy requested by the user.
/// - Parameter config: The configuration specified by the user.
/// - Returns: The naming strategy requestd by the user.
Expand Down
5 changes: 5 additions & 0 deletions Sources/swift-openapi-generator/UserConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ struct _UserConfig: Codable {
/// generated Swift file.
var additionalImports: [String]?

/// A list of additional comments that are added to the top of every
/// generated Swift file.
var additionalFileComments: [String]?

/// Filter to apply to the OpenAPI document before generation.
var filter: DocumentFilter?

Expand All @@ -51,6 +55,7 @@ struct _UserConfig: Codable {
case generate
case accessModifier
case additionalImports
case additionalFileComments
case filter
case namingStrategy
case nameOverrides
Expand Down
13 changes: 13 additions & 0 deletions Tests/OpenAPIGeneratorCoreTests/Test_Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,17 @@ import OpenAPIKit

final class Test_Config: Test_Core {
func testDefaultAccessModifier() { XCTAssertEqual(Config.defaultAccessModifier, .internal) }
func testAdditionalFileComments() {
let config = Config(
mode: .types,
access: .public,
additionalFileComments: ["swift-format-ignore-file", "swiftlint:disable all"],
namingStrategy: .defensive
)
XCTAssertEqual(config.additionalFileComments, ["swift-format-ignore-file", "swiftlint:disable all"])
}
func testEmptyAdditionalFileComments() {
let config = Config(mode: .types, access: .public, namingStrategy: .defensive)
XCTAssertEqual(config.additionalFileComments, [])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5862,8 +5862,42 @@ final class SnippetBasedReferenceTests: XCTestCase {
"""
)
}
}

func testAdditionalFileComments() throws {
let additionalFileComments = ["hello world", "foo bar baz"]
let config = Config(
mode: .types,
access: .private,
additionalImports: [],
additionalFileComments: additionalFileComments,
filter: nil,
namingStrategy: .idiomatic,
nameOverrides: [:]
)
let translator = TypesFileTranslator(
config: config,
diagnostics: XCTestDiagnosticCollector(test: self),
components: OpenAPI.Components()
)
let documentYAML = """
openapi: 3.1.0
info:
title: Minimal API
version: 1.0.0
paths: {}
"""
let document = try YAMLDecoder().decode(OpenAPI.Document.self, from: documentYAML)
let translation = try translator.translateFile(parsedOpenAPI: document)
try XCTAssertSwiftEquivalent(
XCTUnwrap(translation.file.contents.topComment),
"""
// Generated by swift-openapi-generator, do not modify.
// hello world
// foo bar baz
"""
)
}
}
extension SnippetBasedReferenceTests {
func makeTypesTranslator(openAPIDocumentYAML: String) throws -> TypesFileTranslator {
let document = try YAMLDecoder().decode(OpenAPI.Document.self, from: openAPIDocumentYAML)
Expand Down Expand Up @@ -6245,6 +6279,18 @@ private func XCTAssertSwiftEquivalent(
try XCTAssertEqualWithDiff(contents, expectedSwift, file: file, line: line)
}

private func XCTAssertSwiftEquivalent(
_ comment: _OpenAPIGeneratorCore.Comment,
_ expectedSwift: String,
file: StaticString = #filePath,
line: UInt = #line
) throws {
let renderer = TextBasedRenderer.default
renderer.renderComment(comment)
let contents = renderer.renderedContents()
try XCTAssertEqualWithDiff(contents, expectedSwift, file: file, line: line)
}

private func diff(expected: String, actual: String) throws -> String {
let process = Process()
process.executableURL = try resolveExecutable("bash")
Expand Down
Loading