forked from swiftwasm/JavaScriptKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBridgeJSBuildPlugin.swift
More file actions
142 lines (127 loc) · 5.79 KB
/
Copy pathBridgeJSBuildPlugin.swift
File metadata and controls
142 lines (127 loc) · 5.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#if canImport(PackagePlugin)
import PackagePlugin
import Foundation
/// Build plugin for runtime code generation with BridgeJS.
/// This plugin automatically generates bridge code between Swift and JavaScript
/// during each build process.
@main
struct BridgeJSBuildPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
guard let swiftSourceModuleTarget = target as? SwiftSourceModuleTarget else {
return []
}
return [try createGenerateCommand(context: context, target: swiftSourceModuleTarget)]
}
private func pathToConfigFile(target: SwiftSourceModuleTarget) -> URL {
return target.directoryURL.appending(path: "bridge-js.config.json")
}
private func createGenerateCommand(context: PluginContext, target: SwiftSourceModuleTarget) throws -> Command {
let outputSwiftPath = context.pluginWorkDirectoryURL.appending(path: "BridgeJS.swift")
let inputSwiftFiles = target.sourceFiles.filter {
!$0.url.path.hasPrefix(context.pluginWorkDirectoryURL.path + "/")
}
.map(\.url)
let configFile = pathToConfigFile(target: target)
var inputFiles: [URL] = inputSwiftFiles
if FileManager.default.fileExists(atPath: configFile.path) {
inputFiles.append(configFile)
}
// Include Swift files generated by other plugins applied to this
// target (available in tools-version 6.0+). This lets BridgeJS
// process @JS annotations in files produced by earlier plugins
// without requiring any extra configuration.
let pluginGeneratedSwiftFiles = target.pluginGeneratedSources.filter {
$0.pathExtension == "swift"
}
inputFiles.append(contentsOf: pluginGeneratedSwiftFiles)
let inputTSFile = target.directoryURL.appending(path: "bridge-js.d.ts")
let tsconfigPath = context.package.directoryURL.appending(path: "tsconfig.json")
var arguments: [String] = [
"generate",
"--module-name",
target.name,
"--target-dir",
target.directoryURL.path,
"--output-dir",
context.pluginWorkDirectoryURL.path,
"--always-write", "true",
]
if FileManager.default.fileExists(atPath: inputTSFile.path) {
inputFiles.append(contentsOf: [inputTSFile, tsconfigPath])
arguments.append(contentsOf: [
"--project",
tsconfigPath.path,
])
}
for skeleton in dependencySkeletons(context: context, target: target) {
arguments.append(contentsOf: [
"--dependency-skeleton",
"\(skeleton.moduleName)=\(skeleton.skeletonURL.path)",
])
// We have to use the Swift file, not the skeleton, as the input file,
// since we can’t make the skeleton file an output file without it being
// treated as a resource by the build system (and thus included in the
// resource bundle). We need to use something as the inputFile to maintain
// correct ordering.
inputFiles.append(skeleton.bridgeJSSwiftURL)
}
let allSwiftFiles = inputSwiftFiles + pluginGeneratedSwiftFiles
arguments.append(contentsOf: allSwiftFiles.map(\.path))
return .buildCommand(
displayName: "Generate BridgeJS code",
executable: try context.tool(named: "BridgeJSTool").url,
arguments: arguments,
inputFiles: inputFiles,
outputFiles: [outputSwiftPath]
)
}
private struct DependencySkeleton {
let moduleName: String
let skeletonURL: URL
let bridgeJSSwiftURL: URL
}
/// We only read skeletons from dependencies with a `bridge-js.config.json` file.
/// For the build system to correctly order the plugins, we need to set the skeleton
/// files as input. However, I don’t think we have enough information here to determine
/// whether the plugin which generates this is applied to the dependency, so we use
/// the presence of `bridge-js.config.json` instead.
private func dependencySkeletons(
context: PluginContext,
target: SwiftSourceModuleTarget
) -> [DependencySkeleton] {
let localTargets: [SwiftSourceModuleTarget] = target.recursiveTargetDependencies
.compactMap { dependency in
guard
let swiftTarget = dependency as? SwiftSourceModuleTarget,
context.package.targets.contains(where: { $0.id == swiftTarget.id }),
FileManager.default.fileExists(atPath: pathToConfigFile(target: swiftTarget).path)
else {
return nil
}
return swiftTarget
}
var skeletons: [DependencySkeleton] = []
var seenTargetNames = Set<String>()
for swiftTarget in localTargets where seenTargetNames.insert(swiftTarget.name).inserted {
let skeletonURL = BridgeJSPluginPaths.skeletonURL(
targetName: swiftTarget.name,
packageID: context.package.id,
buildPluginWorkDirectoryURL: context.pluginWorkDirectoryURL
)
let bridgeJSSwiftURL = BridgeJSPluginPaths.bridgeJSSwiftURL(
targetName: swiftTarget.name,
packageID: context.package.id,
buildPluginWorkDirectoryURL: context.pluginWorkDirectoryURL
)
skeletons.append(
DependencySkeleton(
moduleName: swiftTarget.name,
skeletonURL: skeletonURL,
bridgeJSSwiftURL: bridgeJSSwiftURL
)
)
}
return skeletons
}
}
#endif