Skip to content

Commit aa15657

Browse files
Merge pull request #370 from swiftwasm/yt/add-import-ts-runtime-tests
BridgeJS: Require placing `bridge-js.config.json` in target directory
2 parents 7eb770e + bfa4854 commit aa15657

File tree

20 files changed

+188
-97
lines changed

20 files changed

+188
-97
lines changed

Benchmarks/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ let package = Package(
1111
.executableTarget(
1212
name: "Benchmarks",
1313
dependencies: ["JavaScriptKit"],
14-
exclude: ["Generated/JavaScript", "bridge.d.ts"],
14+
exclude: ["Generated/JavaScript", "bridge-js.d.ts"],
1515
swiftSettings: [
1616
.enableExperimentalFeature("Extern")
1717
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
File renamed without changes.

Examples/ImportTS/Sources/main.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import JavaScriptKit
22

33
// This function is automatically generated by the @JS plugin
4-
// It demonstrates how to use TypeScript functions and types imported from bridge.d.ts
4+
// It demonstrates how to use TypeScript functions and types imported from bridge-js.d.ts
55
@JS public func run() {
6-
// Call the imported consoleLog function defined in bridge.d.ts
6+
// Call the imported consoleLog function defined in bridge-js.d.ts
77
consoleLog("Hello, World!")
88

99
// Get the document object - this comes from the imported getDocument() function

Plugins/BridgeJS/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ graph LR
2222
A.swift --> E1[[bridge-js export]]
2323
B.swift --> E1
2424
E1 --> G1[ExportSwift.swift]
25-
B1[bridge.d.ts]-->I1[[bridge-js import]]
25+
B1[bridge-js.d.ts]-->I1[[bridge-js import]]
2626
I1 --> G2[ImportTS.swift]
2727
end
2828
I1 --> G4[ImportTS.json]
@@ -32,7 +32,7 @@ graph LR
3232
C.swift --> E2[[bridge-js export]]
3333
D.swift --> E2
3434
E2 --> G5[ExportSwift.swift]
35-
B2[bridge.d.ts]-->I2[[bridge-js import]]
35+
B2[bridge-js.d.ts]-->I2[[bridge-js import]]
3636
I2 --> G6[ImportTS.swift]
3737
end
3838
I2 --> G8[ImportTS.json]
@@ -42,8 +42,8 @@ graph LR
4242
G7 --> L1
4343
G8 --> L1
4444
45-
L1 --> F1[bridge.js]
46-
L1 --> F2[bridge.d.ts]
45+
L1 --> F1[bridge-js.js]
46+
L1 --> F2[bridge-js.d.ts]
4747
ModuleA -----> App[App.wasm]
4848
ModuleB -----> App
4949

Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,32 @@ struct BridgeJSBuildPlugin: BuildToolPlugin {
1111
guard let swiftSourceModuleTarget = target as? SwiftSourceModuleTarget else {
1212
return []
1313
}
14-
return try [
15-
createExportSwiftCommand(context: context, target: swiftSourceModuleTarget),
16-
createImportTSCommand(context: context, target: swiftSourceModuleTarget),
17-
]
14+
var commands: [Command] = []
15+
commands.append(try createExportSwiftCommand(context: context, target: swiftSourceModuleTarget))
16+
if let importCommand = try createImportTSCommand(context: context, target: swiftSourceModuleTarget) {
17+
commands.append(importCommand)
18+
}
19+
return commands
20+
}
21+
22+
private func pathToConfigFile(target: SwiftSourceModuleTarget) -> URL {
23+
return target.directoryURL.appending(path: "bridge-js.config.json")
1824
}
1925

2026
private func createExportSwiftCommand(context: PluginContext, target: SwiftSourceModuleTarget) throws -> Command {
2127
let outputSwiftPath = context.pluginWorkDirectoryURL.appending(path: "ExportSwift.swift")
2228
let outputSkeletonPath = context.pluginWorkDirectoryURL.appending(path: "ExportSwift.json")
23-
let inputFiles = target.sourceFiles.filter { !$0.url.path.hasPrefix(context.pluginWorkDirectoryURL.path + "/") }
24-
.map(\.url)
29+
let inputSwiftFiles = target.sourceFiles.filter {
30+
!$0.url.path.hasPrefix(context.pluginWorkDirectoryURL.path + "/")
31+
}
32+
.map(\.url)
33+
let configFile = pathToConfigFile(target: target)
34+
let inputFiles: [URL]
35+
if FileManager.default.fileExists(atPath: configFile.path) {
36+
inputFiles = inputSwiftFiles + [configFile]
37+
} else {
38+
inputFiles = inputSwiftFiles
39+
}
2540
return .buildCommand(
2641
displayName: "Export Swift API",
2742
executable: try context.tool(named: "BridgeJSTool").url,
@@ -31,21 +46,32 @@ struct BridgeJSBuildPlugin: BuildToolPlugin {
3146
outputSkeletonPath.path,
3247
"--output-swift",
3348
outputSwiftPath.path,
49+
// Generate the output files even if nothing is exported not to surprise
50+
// the build system.
3451
"--always-write", "true",
35-
] + inputFiles.map(\.path),
52+
] + inputSwiftFiles.map(\.path),
3653
inputFiles: inputFiles,
3754
outputFiles: [
3855
outputSwiftPath
3956
]
4057
)
4158
}
4259

43-
private func createImportTSCommand(context: PluginContext, target: SwiftSourceModuleTarget) throws -> Command {
60+
private func createImportTSCommand(context: PluginContext, target: SwiftSourceModuleTarget) throws -> Command? {
4461
let outputSwiftPath = context.pluginWorkDirectoryURL.appending(path: "ImportTS.swift")
4562
let outputSkeletonPath = context.pluginWorkDirectoryURL.appending(path: "ImportTS.json")
46-
let inputFiles = [
47-
target.directoryURL.appending(path: "bridge.d.ts")
48-
]
63+
let inputTSFile = target.directoryURL.appending(path: "bridge-js.d.ts")
64+
guard FileManager.default.fileExists(atPath: inputTSFile.path) else {
65+
return nil
66+
}
67+
68+
let configFile = pathToConfigFile(target: target)
69+
let inputFiles: [URL]
70+
if FileManager.default.fileExists(atPath: configFile.path) {
71+
inputFiles = [inputTSFile, configFile]
72+
} else {
73+
inputFiles = [inputTSFile]
74+
}
4975
return .buildCommand(
5076
displayName: "Import TypeScript API",
5177
executable: try context.tool(named: "BridgeJSTool").url,
@@ -57,10 +83,13 @@ struct BridgeJSBuildPlugin: BuildToolPlugin {
5783
outputSwiftPath.path,
5884
"--module-name",
5985
target.name,
86+
// Generate the output files even if nothing is imported not to surprise
87+
// the build system.
6088
"--always-write", "true",
6189
"--project",
6290
context.package.directoryURL.appending(path: "tsconfig.json").path,
63-
] + inputFiles.map(\.path),
91+
inputTSFile.path,
92+
],
6493
inputFiles: inputFiles,
6594
outputFiles: [
6695
outputSwiftPath

Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ struct BridgeJSCommandPlugin: CommandPlugin {
1212

1313
struct Options {
1414
var targets: [String]
15+
var verbose: Bool
1516

1617
static func parse(extractor: inout ArgumentExtractor) -> Options {
1718
let targets = extractor.extractOption(named: "target")
18-
return Options(targets: targets)
19+
let verbose = extractor.extractFlag(named: "verbose")
20+
return Options(targets: targets, verbose: verbose != 0)
1921
}
2022

2123
static func help() -> String {
@@ -29,13 +31,13 @@ struct BridgeJSCommandPlugin: CommandPlugin {
2931
OPTIONS:
3032
--target <target> Specify target(s) to generate bridge code for. If omitted,
3133
generates for all targets with JavaScriptKit dependency.
34+
--verbose Print verbose output.
3235
"""
3336
}
3437
}
3538

3639
func performCommand(context: PluginContext, arguments: [String]) throws {
3740
// Check for help flags to display usage information
38-
// This allows users to run `swift package plugin bridge-js --help` to understand the plugin's functionality
3941
if arguments.contains(where: { ["-h", "--help"].contains($0) }) {
4042
printStderr(Options.help())
4143
return
@@ -45,83 +47,103 @@ struct BridgeJSCommandPlugin: CommandPlugin {
4547
let options = Options.parse(extractor: &extractor)
4648
let remainingArguments = extractor.remainingArguments
4749

50+
let context = Context(options: options, context: context)
51+
4852
if options.targets.isEmpty {
49-
try runOnTargets(
50-
context: context,
53+
try context.runOnTargets(
5154
remainingArguments: remainingArguments,
5255
where: { target in
5356
target.hasDependency(named: Self.JAVASCRIPTKIT_PACKAGE_NAME)
5457
}
5558
)
5659
} else {
57-
try runOnTargets(
58-
context: context,
60+
try context.runOnTargets(
5961
remainingArguments: remainingArguments,
6062
where: { options.targets.contains($0.name) }
6163
)
6264
}
6365
}
6466

65-
private func runOnTargets(
66-
context: PluginContext,
67+
struct Context {
68+
let options: Options
69+
let context: PluginContext
70+
}
71+
}
72+
73+
extension BridgeJSCommandPlugin.Context {
74+
func runOnTargets(
6775
remainingArguments: [String],
6876
where predicate: (SwiftSourceModuleTarget) -> Bool
6977
) throws {
7078
for target in context.package.targets {
7179
guard let target = target as? SwiftSourceModuleTarget else {
7280
continue
7381
}
82+
let configFilePath = target.directoryURL.appending(path: "bridge-js.config.json")
83+
if !FileManager.default.fileExists(atPath: configFilePath.path) {
84+
printVerbose("No bridge-js.config.json found for \(target.name), skipping...")
85+
continue
86+
}
7487
guard predicate(target) else {
7588
continue
7689
}
77-
try runSingleTarget(context: context, target: target, remainingArguments: remainingArguments)
90+
try runSingleTarget(target: target, remainingArguments: remainingArguments)
7891
}
7992
}
8093

8194
private func runSingleTarget(
82-
context: PluginContext,
8395
target: SwiftSourceModuleTarget,
8496
remainingArguments: [String]
8597
) throws {
86-
Diagnostics.progress("Exporting Swift API for \(target.name)...")
98+
printStderr("Generating bridge code for \(target.name)...")
99+
100+
printVerbose("Exporting Swift API for \(target.name)...")
87101

88102
let generatedDirectory = target.directoryURL.appending(path: "Generated")
89103
let generatedJavaScriptDirectory = generatedDirectory.appending(path: "JavaScript")
90104

91105
try runBridgeJSTool(
92-
context: context,
93106
arguments: [
94107
"export",
95108
"--output-skeleton",
96109
generatedJavaScriptDirectory.appending(path: "ExportSwift.json").path,
97110
"--output-swift",
98111
generatedDirectory.appending(path: "ExportSwift.swift").path,
112+
"--verbose",
113+
options.verbose ? "true" : "false",
99114
]
100115
+ target.sourceFiles.filter {
101116
!$0.url.path.hasPrefix(generatedDirectory.path + "/")
102117
}.map(\.url.path) + remainingArguments
103118
)
104119

105-
try runBridgeJSTool(
106-
context: context,
107-
arguments: [
108-
"import",
109-
"--output-skeleton",
110-
generatedJavaScriptDirectory.appending(path: "ImportTS.json").path,
111-
"--output-swift",
112-
generatedDirectory.appending(path: "ImportTS.swift").path,
113-
"--module-name",
114-
target.name,
115-
"--project",
116-
context.package.directoryURL.appending(path: "tsconfig.json").path,
117-
target.directoryURL.appending(path: "bridge.d.ts").path,
118-
] + remainingArguments
119-
)
120+
printVerbose("Importing TypeScript API for \(target.name)...")
121+
122+
let bridgeDtsPath = target.directoryURL.appending(path: "bridge-js.d.ts")
123+
// Execute import only if bridge-js.d.ts exists
124+
if FileManager.default.fileExists(atPath: bridgeDtsPath.path) {
125+
try runBridgeJSTool(
126+
arguments: [
127+
"import",
128+
"--output-skeleton",
129+
generatedJavaScriptDirectory.appending(path: "ImportTS.json").path,
130+
"--output-swift",
131+
generatedDirectory.appending(path: "ImportTS.swift").path,
132+
"--verbose",
133+
options.verbose ? "true" : "false",
134+
"--module-name",
135+
target.name,
136+
"--project",
137+
context.package.directoryURL.appending(path: "tsconfig.json").path,
138+
bridgeDtsPath.path,
139+
] + remainingArguments
140+
)
141+
}
120142
}
121143

122-
private func runBridgeJSTool(context: PluginContext, arguments: [String]) throws {
144+
private func runBridgeJSTool(arguments: [String]) throws {
123145
let tool = try context.tool(named: "BridgeJSTool").url
124-
printStderr("$ \(tool.path) \(arguments.joined(separator: " "))")
146+
printVerbose("$ \(tool.path) \(arguments.joined(separator: " "))")
125147
let process = Process()
126148
process.executableURL = tool
127149
process.arguments = arguments
@@ -133,6 +155,12 @@ struct BridgeJSCommandPlugin: CommandPlugin {
133155
exit(process.terminationStatus)
134156
}
135157
}
158+
159+
private func printVerbose(_ message: String) {
160+
if options.verbose {
161+
printStderr(message)
162+
}
163+
}
136164
}
137165

138166
private func printStderr(_ message: String) {

Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ import SwiftParser
5757
"""
5858
)
5959
}
60-
let progress = ProgressReporting()
6160
switch subcommand {
6261
case "import":
6362
let parser = ArgumentParser(
@@ -71,6 +70,10 @@ import SwiftParser
7170
help: "Always write the output files even if no APIs are imported",
7271
required: false
7372
),
73+
"verbose": OptionRule(
74+
help: "Print verbose output",
75+
required: false
76+
),
7477
"output-swift": OptionRule(help: "The output file path for the Swift source code", required: true),
7578
"output-skeleton": OptionRule(
7679
help: "The output file path for the skeleton of the imported TypeScript APIs",
@@ -85,6 +88,7 @@ import SwiftParser
8588
let (positionalArguments, _, doubleDashOptions) = try parser.parse(
8689
arguments: Array(arguments.dropFirst())
8790
)
91+
let progress = ProgressReporting(verbose: doubleDashOptions["verbose"] == "true")
8892
var importer = ImportTS(progress: progress, moduleName: doubleDashOptions["module-name"]!)
8993
for inputFile in positionalArguments {
9094
if inputFile.hasSuffix(".json") {
@@ -145,11 +149,16 @@ import SwiftParser
145149
help: "Always write the output files even if no APIs are exported",
146150
required: false
147151
),
152+
"verbose": OptionRule(
153+
help: "Print verbose output",
154+
required: false
155+
),
148156
]
149157
)
150158
let (positionalArguments, _, doubleDashOptions) = try parser.parse(
151159
arguments: Array(arguments.dropFirst())
152160
)
161+
let progress = ProgressReporting(verbose: doubleDashOptions["verbose"] == "true")
153162
let exporter = ExportSwift(progress: progress)
154163
for inputFile in positionalArguments {
155164
let sourceURL = URL(fileURLWithPath: inputFile)
@@ -253,7 +262,11 @@ private func printStderr(_ message: String) {
253262
struct ProgressReporting {
254263
let print: (String) -> Void
255264

256-
init(print: @escaping (String) -> Void = { Swift.print($0) }) {
265+
init(verbose: Bool) {
266+
self.init(print: verbose ? { Swift.print($0) } : { _ in })
267+
}
268+
269+
private init(print: @escaping (String) -> Void) {
257270
self.print = print
258271
}
259272

Plugins/BridgeJS/Sources/BridgeJSTool/ExportSwift.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ExportSwift {
1919
private var exportedClasses: [ExportedClass] = []
2020
private var typeDeclResolver: TypeDeclResolver = TypeDeclResolver()
2121

22-
init(progress: ProgressReporting = ProgressReporting()) {
22+
init(progress: ProgressReporting) {
2323
self.progress = progress
2424
}
2525

0 commit comments

Comments
 (0)