diff --git a/Plugins/PackageToJS/Sources/PackageToJS.swift b/Plugins/PackageToJS/Sources/PackageToJS.swift index 1949527d..80934c24 100644 --- a/Plugins/PackageToJS/Sources/PackageToJS.swift +++ b/Plugins/PackageToJS/Sources/PackageToJS.swift @@ -38,6 +38,8 @@ struct PackageToJS { var environment: String? /// Whether to run tests in the browser with inspector enabled var inspect: Bool + /// The extra arguments to pass to node + var extraNodeArguments: [String] /// The options for packaging var packageOptions: PackageOptions } @@ -82,7 +84,8 @@ struct PackageToJS { try PackageToJS.runSingleTestingLibrary( testRunner: testRunner, currentDirectoryURL: currentDirectoryURL, - extraArguments: extraArguments + extraArguments: extraArguments, + testOptions: testOptions ) } let swiftTestingCoverageFile = outputDir.appending(path: "SwiftTesting.profraw") @@ -97,7 +100,8 @@ struct PackageToJS { try PackageToJS.runSingleTestingLibrary( testRunner: testRunner, currentDirectoryURL: currentDirectoryURL, - extraArguments: extraArguments + extraArguments: extraArguments, + testOptions: testOptions ) } @@ -114,10 +118,11 @@ struct PackageToJS { static func runSingleTestingLibrary( testRunner: URL, currentDirectoryURL: URL, - extraArguments: [String] + extraArguments: [String], + testOptions: TestOptions ) throws { let node = try which("node") - let arguments = ["--experimental-wasi-unstable-preview1", testRunner.path] + extraArguments + let arguments = ["--experimental-wasi-unstable-preview1"] + testOptions.extraNodeArguments + [testRunner.path] + extraArguments print("Running test...") logCommandExecution(node.path, arguments) diff --git a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift index 49e60fce..96102376 100644 --- a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift +++ b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift @@ -340,10 +340,13 @@ extension PackageToJS.TestOptions { let prelude = extractor.extractOption(named: "prelude").last let environment = extractor.extractOption(named: "environment").last let inspect = extractor.extractFlag(named: "inspect") + let extraNodeArguments = extractor.extractSingleDashOption(named: "Xnode") let packageOptions = PackageToJS.PackageOptions.parse(from: &extractor) var options = PackageToJS.TestOptions( buildOnly: buildOnly != 0, listTests: listTests != 0, - filter: filter, prelude: prelude, environment: environment, inspect: inspect != 0, packageOptions: packageOptions + filter: filter, prelude: prelude, environment: environment, inspect: inspect != 0, + extraNodeArguments: extraNodeArguments, + packageOptions: packageOptions ) if !options.buildOnly, !options.packageOptions.useCDN { @@ -379,6 +382,39 @@ extension PackageToJS.TestOptions { // MARK: - PackagePlugin helpers +extension ArgumentExtractor { + fileprivate mutating func extractSingleDashOption(named name: String) -> [String] { + let parts = remainingArguments.split(separator: "--", maxSplits: 1, omittingEmptySubsequences: false) + var args = Array(parts[0]) + let literals = Array(parts.count == 2 ? parts[1] : []) + + var values: [String] = [] + var idx = 0 + while idx < args.count { + var arg = args[idx] + if arg == "-\(name)" { + args.remove(at: idx) + if idx < args.count { + let val = args[idx] + values.append(val) + args.remove(at: idx) + } + } + else if arg.starts(with: "-\(name)=") { + args.remove(at: idx) + arg.removeFirst(2 + name.count) + values.append(arg) + } + else { + idx += 1 + } + } + + self = ArgumentExtractor(args + literals) + return values + } +} + /// Derive default product from the package /// - Returns: The name of the product to build /// - Throws: `PackageToJSError` if there's no executable product or if there's more than one diff --git a/Plugins/PackageToJS/Tests/ExampleTests.swift b/Plugins/PackageToJS/Tests/ExampleTests.swift index 743504e3..53048e00 100644 --- a/Plugins/PackageToJS/Tests/ExampleTests.swift +++ b/Plugins/PackageToJS/Tests/ExampleTests.swift @@ -148,6 +148,20 @@ extension Trait where Self == ConditionTrait { let swiftSDKID = try #require(Self.getSwiftSDKID()) try withPackage(at: "Examples/Testing") { packageDir, runSwift in try runSwift(["package", "--swift-sdk", swiftSDKID, "js", "test"], [:]) + try withTemporaryDirectory(body: { tempDir, _ in + let scriptContent = """ + const fs = require('fs'); + const path = require('path'); + const scriptPath = path.join(__dirname, 'test.txt'); + fs.writeFileSync(scriptPath, 'Hello, world!'); + """ + try scriptContent.write(to: tempDir.appending(path: "script.js"), atomically: true, encoding: .utf8) + let scriptPath = tempDir.appending(path: "script.js") + try runSwift(["package", "--swift-sdk", swiftSDKID, "js", "test", "-Xnode=--require=\(scriptPath.path)"], [:]) + let testPath = tempDir.appending(path: "test.txt") + try #require(FileManager.default.fileExists(atPath: testPath.path), "test.txt should exist") + try #require(try String(contentsOf: testPath, encoding: .utf8) == "Hello, world!", "test.txt should be created by the script") + }) try runSwift(["package", "--swift-sdk", swiftSDKID, "js", "test", "--environment", "browser"], [:]) } }