Skip to content
Closed
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
26 changes: 26 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Build and Test

on:
pull_request:
branches:
- main
push:
branches:
- main

permissions:
contents: read

jobs:
test:
name: Run Tests
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: swift-actions/setup-swift@v2
with:
swift-version: "6.1.0"
- name: Build
run: swift build
- name: Tests
run: swift test
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v4
- uses: swift-actions/setup-swift@v2
with:
swift-version: "5.9"
swift-version: "6.1.0"
- name: Build for Release
run: swift build -c release --arch arm64 --arch x86_64 --product cap2spm
- name: Zip Executable
Expand Down
11 changes: 6 additions & 5 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 41 additions & 13 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,33 +1,61 @@
// swift-tools-version: 5.9
// swift-tools-version: 6.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "capacitor-plugin-converter",
platforms: [.macOS(.v13)],
platforms: [.macOS(.v15)],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.1"),
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"),
.package(url: "https://github.com/apple/swift-syntax.git", from: "601.0.1"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "cap2spm",
dependencies: [
.target(name: "CapacitorPluginSyntaxTools"),
.target(name: "JavascriptPackageTools"),
.target(name: "CapacitorPluginTools"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftParser", package: "swift-syntax")
]
],
path: "Sources/CommandLineTool"
),
.testTarget(name: "CapacitorConverterTests",
.target(name: "CapacitorPluginSyntaxTools",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftParser", package: "swift-syntax")
]
),
.target(name: "CapacitorPluginTools",
dependencies: [
.target(name: "CapacitorPluginSyntaxTools"),
.target(name: "JavascriptPackageTools")
]
),
.target(name: "JavascriptPackageTools"),

// Test Targets
.testTarget(name: "JavascriptPackageToolsTests",
dependencies: [
.target(name: "cap2spm"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftParser", package: "swift-syntax")
]
)
.target(name: "JavascriptPackageTools"),
],
resources: [.copy("package-test.json")]
),
.testTarget(name: "CapacitorPluginSyntaxToolsTests",
dependencies: [
.target(name: "CapacitorPluginSyntaxTools"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftParser", package: "swift-syntax")
]
),
.testTarget(name: "CapacitorPluginToolsTests",
dependencies: [
.target(name: "CapacitorPluginTools")
]
),
]
)
27 changes: 27 additions & 0 deletions Sources/CapacitorPluginSyntaxTools/CapacitorPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation
import SwiftSyntax
import SwiftParser

public struct CapacitorPlugin {
public let identifier: String
public let jsName: String
public var methods: [CapacitorPluginMethod] = []

public init(identifier: String, jsName: String) {
self.identifier = identifier
self.jsName = jsName
}

public func modifySwiftFile(at fileURL: URL) throws {
let source = try String(contentsOf: fileURL, encoding: .utf8)
let sourceFile = Parser.parse(source: source)

let capSyntax = CapacitorPluginSyntax(plugin: self)

let incremented = AddPluginToClass(with: capSyntax).visit(sourceFile)

var outputString: String = ""
incremented.write(to: &outputString)
try outputString.write(to: fileURL, atomically: true, encoding: .utf8)
}
}
17 changes: 17 additions & 0 deletions Sources/CapacitorPluginSyntaxTools/CapacitorPluginMethod.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Foundation
import SwiftSyntax
import SwiftParser

public struct CapacitorPluginMethod {
public let methodName: String
public let returnType: CapacitorPluginReturnType

var syntax: CapacitorPluginMethodSyntax {
CapacitorPluginMethodSyntax(methodName: methodName, returnType: returnType)
}

public init(methodName: String, returnType: CapacitorPluginReturnType) {
self.methodName = methodName
self.returnType = returnType
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import Foundation
import SwiftSyntax
import SwiftParser

struct CapacitorPluginMethod {
struct CapacitorPluginMethodSyntax {
let methodName: String
let returnType: CapacitorPluginReturnType

private let nameIdent = TokenSyntax.identifier("name")
private let returnTypeIdent = TokenSyntax.identifier("returnType")
private let declRef = DeclReferenceExprSyntax(baseName: .identifier("CAPPluginMethod"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import Foundation
import SwiftSyntax
import SwiftParser

enum CapacitorPluginReturnType: String {
public enum CapacitorPluginReturnType: String {
case none
case promise
case callback

init?(with typeString: String) {
public init?(with typeString: String) {
switch typeString {
case "CAPPluginReturnNone":
self = .none
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import SwiftSyntax
import SwiftParser

struct CapacitorPluginSyntax {
let identifier: String
let jsName: String
var methods: [CapacitorPluginMethod] = []

let plugin: CapacitorPlugin

let defaultIndent = 4

func createMemberBlock() -> MemberBlockItemListSyntax {
let pluginIdentifier = addPublicStringConstantVariable(variableName: "identifier", stringValue: identifier)
let pluginIdentifier = addPublicStringConstantVariable(variableName: "identifier", stringValue: plugin.identifier)

let jsName = addPublicStringConstantVariable(variableName: "jsName", stringValue: jsName)
let jsName = addPublicStringConstantVariable(variableName: "jsName", stringValue: plugin.jsName)

let pluginMethods = addPluginMethodDeclarations()

Expand Down Expand Up @@ -88,9 +86,9 @@ struct CapacitorPluginSyntax {

var functionArray: [ArrayElementSyntax] = []

for method in methods {
for method in plugin.methods {
let arrayElement = ArrayElementSyntax(leadingTrivia: .spaces(defaultIndent*2),
expression: method.functionCallExpr,
expression: method.syntax.functionCallExpr,
trailingComma: .commaToken(trailingTrivia: .newline))

functionArray.append(arrayElement)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
//
// File.swift
//
//
// Created by Mark Anderson on 10/16/23.
//

import Foundation
import CapacitorPluginSyntaxTools
import JavascriptPackageTools

enum CapacitorPluginError: Error {
case objcFileCount(Int)
Expand All @@ -24,18 +19,22 @@ enum CapacitorPluginError: Error {
}
}

class CapacitorPluginPackage {
let pluginDirectoryName: String
let basePathURL: URL
let packageJSONURL: URL
let pluginSrcDirectoryURL: URL
let iosSrcDirectoryURL: URL
let files: [URL]
public class CapacitorPluginPackage {
public let pluginDirectoryName: String
public let basePathURL: URL
public let packageJSONURL: URL
public let pluginSrcDirectoryURL: URL
public let iosSrcDirectoryURL: URL
public let files: [URL]

var oldPlugin: OldPlugin?
private var oldPlugin: OldPlugin?
private var packageJSONParser: PackageJSONParser

public var plugin: CapacitorPlugin? {
oldPlugin?.capacitorPlugin
}

init(directoryName: String) throws {
public init(directoryName: String) throws {
pluginDirectoryName = directoryName

let fileManager = FileManager.default
Expand All @@ -53,7 +52,7 @@ class CapacitorPluginPackage {
files = try fileManager.contentsOfDirectory(at: pluginSrcDirectoryURL, includingPropertiesForKeys: nil)
}

func findObjCPluginFile() throws -> URL {
public func findObjCPluginFile() throws -> URL {
let mfiles = files.filter { $0.absoluteString.hasSuffix(".m") }

guard mfiles.count == 1, let url = mfiles.first else { throw CapacitorPluginError.objcFileCount(mfiles.count) }
Expand All @@ -63,26 +62,26 @@ class CapacitorPluginPackage {
return url
}

func parseObjCPluginFile(at url: URL) throws {
public func parseObjCPluginFile(at url: URL) throws {
oldPlugin = try OldPlugin(at: url)
}

func findObjCHeaderFile() throws -> URL {
public func findObjCHeaderFile() throws -> URL {
let headerFiles = files.filter { $0.absoluteString.hasSuffix(".h") }
guard headerFiles.count == 1, let url = headerFiles.first else { throw CapacitorPluginError.objcFileCount(headerFiles.count) }

return url
}

func findSwiftPluginFile() throws -> URL {
public func findSwiftPluginFile() throws -> URL {
guard let oldPlugin else { throw CapacitorPluginError.oldPluginMissing }

let fileName = "\(oldPlugin.capacitorPlugin.identifier).swift"

return URL(filePath: fileName, directoryHint: .notDirectory, relativeTo: pluginSrcDirectoryURL)
}

func findPodspecFile() throws -> URL {
public func findPodspecFile() throws -> URL {
let fileName = packageJSONParser.podspec

return URL(filePath: fileName, directoryHint: .notDirectory, relativeTo: basePathURL)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import Foundation
import RegexBuilder
import CapacitorPluginSyntaxTools

enum OldPluginParserError: Error {
case pluginMissing
case podspecNameMissing
}

class OldPlugin {
var capacitorPlugin: CapacitorPluginSyntax
package class OldPlugin {
var capacitorPlugin: CapacitorPlugin

init(at fileURL: URL) throws {
let pluginSourceText = try String(contentsOf: fileURL, encoding: .utf8)
capacitorPlugin = try OldPlugin.matchPlugin(text: pluginSourceText)
try matchMethods(text: pluginSourceText)
}

private static func matchPlugin(text: String) throws -> CapacitorPluginSyntax {
private static func matchPlugin(text: String) throws -> CapacitorPlugin {
let identiferRef = Reference(Substring.self)
let jsNameRef = Reference(Substring.self)

Expand All @@ -38,7 +39,7 @@ class OldPlugin {
}

if let match = text.firstMatch(of: pluginNameRegex) {
return CapacitorPluginSyntax(identifier: String(match[identiferRef]), jsName: String(match[jsNameRef]))
return CapacitorPlugin(identifier: String(match[identiferRef]), jsName: String(match[jsNameRef]))
} else {
throw OldPluginParserError.pluginMissing
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

class GeneratePackageFile {
public class PackageFileGenerator {
let packageName: String
let libName: String
let capRepoName = "capacitor-swift-pm"
Expand Down Expand Up @@ -40,8 +40,15 @@ class GeneratePackageFile {
"""
}

init(packageName: String, libName: String) {
public init(packageName: String, libName: String) {
self.packageName = packageName
self.libName = libName
}

public func generateFile(at fileURL: URL) throws {
let packageFileURL = URL(filePath: "Package.swift", directoryHint: .notDirectory, relativeTo: fileURL.baseURL)
let packageFileString = packageText

try packageFileString.write(to: packageFileURL, atomically: true, encoding: .utf8)
}
}
Loading