Skip to content

Commit

Permalink
Merge pull request #6 from h1d3mun3/topic/needle
Browse files Browse the repository at this point in the history
Needle対応のRIBを生成できるようにし、各種コマンドで処理できるように機能追加する
  • Loading branch information
imairi authored Aug 13, 2024
2 parents a401195 + 523d599 commit 285dc22
Show file tree
Hide file tree
Showing 29 changed files with 577 additions and 80 deletions.
9 changes: 7 additions & 2 deletions .ribscodegen_rename
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,16 @@ parentRouter:
- " __RIB_NAME__ViewControllable"
- "detach__RIB_NAME__("
- "remove__RIB_NAME__("
parentBuilder:
parentNormalBuilder:
- "__PARENT_RIB_NAME__Dependency__RIB_NAME__"
- "__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder = __RIB_NAME__Builder"
- "__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder: __RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder"
parentComponentExtension:
parentNeedleBuilder:
- "__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder = __RIB_NAME__Builder"
- "__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder: __RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder"
- "__RIB_NAME_LOWER_CASED_FIRST_LETTER__Component"
- "__RIB_NAME__Component"
parentComponentExtension:
- "__PARENT_RIB_NAME__Component+__RIB_NAME__.swift"
- "scope of __PARENT_RIB_NAME__ to provide for the __RIB_NAME__ scope."
- "__PARENT_RIB_NAME__Dependency__RIB_NAME__"
Expand Down
9 changes: 7 additions & 2 deletions .ribscodegen_unlink
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
parentBuilder:
parentNormalBuilder:
- "\\,\\n\\s+__PARENT_RIB_NAME__Dependency__RIB_NAME__"
- "__PARENT_RIB_NAME__Dependency__RIB_NAME__\\,\\n\\s+"
- "let\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder\\s+\\=\\s+__RIB_NAME__Builder.*\\n\\s+"
- "\\,\\n\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder\\:\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder"
parentNeedleBuilder:
- "\\s+var\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Component[\\s\\S]*?\\n\\s{4}\\}"
- "\\s+func\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Component\\([\\s\\S]*?\\n\\s{4}\\}"
- "\\s+let\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder\\s+\\=\\s+__RIB_NAME__Builder[\\s\\S]*?\\n\\s{8}\\}"
- "\\,\\n\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder\\:\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder"
parentRouter:
- "\\,\\n\\s+__RIB_NAME__ViewControllable"
- "__RIB_NAME__ViewControllable\\,\\n\\s+"
- "\\,\\n\\s+__RIB_NAME__Listener"
- "\\n.*let\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder\\:\\s+__RIB_NAME__Buildable"
- "\\n.*let\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder\\:\\s+__RIB_NAME__Buildable\\n"
- "\\n.*var\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__\\:\\s+Routing\\?"
- "\\,\\n\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder\\:\\s+__RIB_NAME__Buildable"
- "\\n\\s+self\\.__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder\\s+\\=\\s+__RIB_NAME_LOWER_CASED_FIRST_LETTER__Builder"
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.3
// swift-tools-version:5.9
import PackageDescription

let package = Package(
Expand All @@ -12,7 +12,7 @@ let package = Package(
.package(url: "https://github.com/onevcat/Rainbow", from: "3.2.0")
],
targets: [
.target(
.executableTarget(
name: "RIBsCodeGen",
dependencies: [
.product(name: "SourceKittenFramework", package: "SourceKitten"),
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ The below files are generated.

If wants to create viewless RIB, add `--noview` command.

RIBsCodeGen is also support Needle.

https://github.com/uber/needle

If you needs to specify Needle dependency injection type, you can specify that via pass `--needle` parameter.

If passed `--needle`, will create RIBs use Needle as dependency injection.

## Add with parent

Expand Down
9 changes: 7 additions & 2 deletions Sources/RIBsCodeGen/Commands/CreateRIBCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ struct CreateRIBCommand: Command {
let templateDirectory: String
let target: String
let isOwnsView: Bool
let isNeedle: Bool

init(paths: [String],
setting: Setting,
target: String,
isOwnsView: Bool) {
isOwnsView: Bool,
isNeedle: Bool) {
var targetPaths = [String?]()

let targetRouterPath = paths.filter({ $0.contains("/" + target + "Router.swift") }).first
Expand All @@ -39,10 +41,13 @@ struct CreateRIBCommand: Command {
needsCreateTargetFile = targetPaths.contains(nil)

targetDirectory = setting.targetDirectory
templateDirectory = isOwnsView ? setting.templateDirectory + "/OwnsView" : setting.templateDirectory + "/Default"
let parentDirectory = isNeedle ? setting.templateDirectory + "/Needle" : setting.templateDirectory + "/Normal"
templateDirectory = isOwnsView ? parentDirectory + "/OwnsView" : parentDirectory + "/Default"


self.target = target
self.isOwnsView = isOwnsView
self.isNeedle = isNeedle
}

func run() -> Result {
Expand Down
79 changes: 76 additions & 3 deletions Sources/RIBsCodeGen/Commands/DependencyCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,14 @@ private extension DependencyCommand {
}

func resolveDependencyForBuilder() -> Result {
let childIsNeedle = validateBuilderIsNeedle(builderFilePath: childBuilderPath)

do {
try addChildDependency(parentBuilderPath: parentBuilderPath)
if childIsNeedle {
try addChildComponentInitialize(parentBuilderPath: parentBuilderPath)
} else {
try addChildDependency(parentBuilderPath: parentBuilderPath)
}
try addChildBuilderInitialize(parentBuilderPath: parentBuilderPath)
try addChildBuilderToRouterInit(parentBuilderPath: parentBuilderPath)
} catch {
Expand Down Expand Up @@ -289,6 +295,38 @@ private extension DependencyCommand {
write(text: text, toPath: parentBuilderPath)
}

func addChildComponentInitialize(parentBuilderPath: String) throws {
print(" Adding child builder instance to parent Component.")
let parentBuilderFile = File(path: parentBuilderPath)!
let parentBuilderFileStructure = try Structure(file: parentBuilderFile)

let parentBuilderClasses = parentBuilderFileStructure.dictionary
.getSubStructures()
.filterByKeyKind(.class)

guard let parentComponentClass = parentBuilderClasses.filterByKeyName("\(parent)Component").first else {
print(" Not found \(parent)Component class.".red.bold)
throw Error.failedToAddChildBuilder
}

let insertPosition = parentComponentClass.getInnerTrailingPosition()

var text = try String.init(contentsOfFile: parentBuilderPath, encoding: .utf8)

let dependencyInsertIndex = text.utf8.index(text.startIndex, offsetBy: insertPosition)

let initArguments = try getChildComponentInitArguments(childBuilderPath: childBuilderPath)
if initArguments.isEmpty {
text.insert(contentsOf: "\nvar \(child.lowercasedFirstLetter())Component: \(child)Component {\n\(child)Component(parent: self)\n}\n", at: dependencyInsertIndex)
} else {
let arguments = initArguments.map { "\($0.name): \($0.type)" }.joined(separator: ", ")
let innerArguments = initArguments.map { $0.name }.map { "\($0): \($0)" }.joined(separator: ", ")
text.insert(contentsOf: "\nfunc \(child.lowercasedFirstLetter())Component(\(arguments)) -> \(child)Component {\n\(child)Component(parent: self, \(innerArguments))\n}\n", at: dependencyInsertIndex)
}

write(text: text, toPath: parentBuilderPath)
}

func addChildBuilderInitialize(parentBuilderPath: String) throws {
print(" Adding child builder initialize to parent Builder.")
let parentBuilderFile = File(path: parentBuilderPath)!
Expand Down Expand Up @@ -334,10 +372,43 @@ private extension DependencyCommand {
var text = try String.init(contentsOfFile: parentBuilderPath, encoding: .utf8)

let dependencyInsertIndex = text.utf8.index(text.startIndex, offsetBy: insertPosition)
text.insert(contentsOf: "let \(child.lowercasedFirstLetter())Builder = \(child)Builder(dependency: component)\n", at: dependencyInsertIndex)

let childIsNeedle = validateBuilderIsNeedle(builderFilePath: childBuilderPath)

if childIsNeedle {
let initArguments = try getChildComponentInitArguments(childBuilderPath: childBuilderPath)
if initArguments.isEmpty {
text.insert(contentsOf: "let \(child.lowercasedFirstLetter())Builder = \(child)Builder {\n component.\(child.lowercasedFirstLetter())Component\n}\n", at: dependencyInsertIndex)
} else {
let arguments = initArguments.map { $0.name }.joined(separator: ", ")
let innerArguments = initArguments.map { $0.name }.map { "\($0): \($0)" }.joined(separator: ", ")
text.insert(contentsOf: "let \(child.lowercasedFirstLetter())Builder = \(child)Builder { \(arguments) in\n component.\(child.lowercasedFirstLetter())Component(\(innerArguments))\n}\n", at: dependencyInsertIndex)
}
} else {
text.insert(contentsOf: "let \(child.lowercasedFirstLetter())Builder = \(child)Builder(dependency: component)\n", at: dependencyInsertIndex)
}
write(text: text, toPath: parentBuilderPath)
}

func getChildComponentInitArguments(childBuilderPath: String) throws -> [(name: String, type: String)] {
let childBuilderFile = File(path: childBuilderPath)!
let childBuilderFileStructure = try Structure(file: childBuilderFile)
let childBuilderClasses = childBuilderFileStructure.dictionary.getSubStructures().filterByKeyKind(.class)

guard let childComponentClass = childBuilderClasses.filterByKeyName("\(child)Component").first else {
print(" Not found \(child)Component class.".red.bold)
throw Error.failedToAddChildBuilder
}

let initStructure = childComponentClass.getSubStructures().extractDictionaryContainsKeyName("init")
let initArguments = initStructure.getSubStructures().filterByKeyKind(.varParameter)
let childComponentArguments = initArguments
.filter { $0.getKeyName() != "parent" }
.map { (name: $0.getKeyName(), type: $0.getTypeName()) }

return childComponentArguments
}

func addChildBuilderToRouterInit(parentBuilderPath: String) throws {
print(" Adding child builder for parent Router initialize argument.")
let parentBuilderFile = File(path: parentBuilderPath)!
Expand All @@ -352,7 +423,9 @@ private extension DependencyCommand {
throw Error.failedToAddChildBuilder
}

let initStructure = parentBuilderClass.getSubStructures().extractDictionaryContainsKeyName("build")
let isNeedle = validateBuilderIsNeedle(builderFilePath: parentBuilderPath)

let initStructure = isNeedle ? parentBuilderClass.getSubStructures().extractDictionaryContainsKeyNameLast("build") : parentBuilderClass.getSubStructures().extractDictionaryContainsKeyName("build")
let parentRouter = initStructure.getSubStructures().filterByKeyKind(.call).extractDictionaryContainsKeyName("\(parent)Router")

let childBuilderArguments = parentRouter.getSubStructures().filterByKeyKind(.argument).filterByKeyName("\(child.lowercasedFirstLetter())Builder")
Expand Down
93 changes: 65 additions & 28 deletions Sources/RIBsCodeGen/Commands/RenameCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ final class RenameCommand: Command {
private let builderPath: String
private let viewControllerPath: String?
private let currentDependenciesPath: [String]
private let parents: [String]
private var parents: [String] = []

private var replacedFilePaths = [String]()

init(paths: [String], renameSetting: RenameSetting, currentName: String, newName: String) {
self.paths = paths
self.renameSetting = renameSetting
Expand Down Expand Up @@ -83,28 +83,27 @@ final class RenameCommand: Command {
self.builderPath = builderPath
viewControllerPath = paths.filter({ $0.contains("/" + currentName + "ViewController.swift") }).first
currentDependenciesPath = paths.filter({ $0.contains("/" + currentName + "/Dependencies/") })

parents = paths
.filter({ $0.contains("Component+\(currentName).swift") })
.flatMap { $0.split(separator: "/") }
.filter({ $0.contains("Component+\(currentName).swift") })
.compactMap { $0.split(separator: "+").first }
.map { $0.dropLast("Component".count) }
.map { String($0) }
}

func run() -> Result {
getParents()

print("\nStart renaming ".bold + currentName.applyingBackgroundColor(.magenta).bold + " to ".bold + newName.applyingBackgroundColor(.blue).bold + ".".bold)

var result: Result?

print("\n\tStart renaming codes for related files.".bold)
do {
try renameForInteractor()
} catch {
result = .failure(error: .failedToRename("Failed to rename operation for target Interactor."))

let builderIsNeedle = validateBuilderIsNeedle(builderFilePath: builderPath)

if builderIsNeedle {
do {
try renameForInteractor()
} catch {
result = .failure(error: .failedToRename("Failed to rename operation for target Interactor."))
}
}

do {
try renameForRouter()
} catch {
Expand Down Expand Up @@ -147,12 +146,14 @@ final class RenameCommand: Command {
result = .failure(error: .failedToRename("Failed to rename operation for target parent Builder."))
}

do {
try renameForParentsComponentExtensions()
} catch {
result = .failure(error: .failedToRename("Failed to rename operation for target parent Component Extensions."))
if builderIsNeedle {
do {
try renameForParentsComponentExtensions()
} catch {
result = .failure(error: .failedToRename("Failed to rename operation for target parent Component Extensions."))
}
}

do {
try formatAllReplacedFiles()
} catch {
Expand All @@ -171,6 +172,46 @@ final class RenameCommand: Command {

// MARK: - Run
private extension RenameCommand {
func getParents() {
let childIsNeedle = validateBuilderIsNeedle(builderFilePath: extractBuilderPathFrom(targetName: currentName)!)
if childIsNeedle {
parents = paths
.filter({ $0.contains("Builder.swift") })
.filter({ $0.lastElementSplittedBySlash != "\(currentName)Builder.swift" })
.filter({ [weak self] builderFilePath in
self?.hasChildConponent(parentBuilderPath: builderFilePath) ?? false
})
.map { $0.lastElementSplittedBySlash }
.map { $0.dropLast("Builder.swift".count) }
.map { String($0) }
} else {
parents = paths
.filter({ $0.contains("Component+\(currentName).swift") })
.flatMap { $0.split(separator: "/") }
.filter({ $0.contains("Component+\(currentName).swift") })
.compactMap { $0.split(separator: "+").first }
.map { $0.dropLast("Component".count) }
.map { String($0) }
}
}

func hasChildConponent(parentBuilderPath: String) -> Bool {
let parentBuilderFile = File(path: parentBuilderPath)!
let parentBuilderFileStructure = try! Structure(file: parentBuilderFile)
print(parentBuilderPath.lastElementSplittedBySlash)
var parent = parentBuilderPath.lastElementSplittedBySlash
parent.removeLast("Builder.swift".count)

let parentBuilderClasses = parentBuilderFileStructure.dictionary.getSubStructures().filterByKeyKind(.class)

if let parentComponentClass = parentBuilderClasses.filterByKeyName("\(parent)Component").first,
let _ = parentComponentClass.getSubStructures().filterByKeyTypeName("\(currentName)Component").first {
return true
} else {
return false
}
}

func renameForInteractor() throws {
print("\t\trename for \(interactorPath.lastElementSplittedBySlash)")
let text = try String.init(contentsOfFile: interactorPath, encoding: .utf8)
Expand Down Expand Up @@ -292,7 +333,9 @@ private extension RenameCommand {

print("\t\trename for \(parentBuilderPath.lastElementSplittedBySlash)")
let text = try String.init(contentsOfFile: parentBuilderPath, encoding: .utf8)
let replacedText = renameSetting.parentBuilder.reduce(text) { (result, parentBuilderSearchText) in
let parentIsNeedle = validateBuilderIsNeedle(builderFilePath: parentBuilderPath)
let parentBuilder = parentIsNeedle ? renameSetting.parentNeedleBuilder : renameSetting.parentNormalBuilder
let replacedText = parentBuilder.reduce(text) { (result, parentBuilderSearchText) in
let searchText = replacePlaceHolder(for: parentBuilderSearchText, with: currentName, and: parentName)
let replaceText = replacePlaceHolder(for: parentBuilderSearchText, with: newName, and: parentName)
return result.replacingOccurrences(of: searchText, with: replaceText)
Expand Down Expand Up @@ -449,12 +492,6 @@ private extension RenameCommand {
}
}

private extension String {
var lastElementSplittedBySlash: String {
String(self.split(separator: "/").last ?? "")
}
}

private extension Path {
var relativePath: String {
self.description.replacingOccurrences(of: ".*\(setting.targetDirectory)", with: setting.targetDirectory, options: .regularExpression)
Expand Down
Loading

0 comments on commit 285dc22

Please sign in to comment.