Initial commit
This commit is contained in:
commit
030323ce7f
|
@ -0,0 +1,9 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/config/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
|
@ -0,0 +1,76 @@
|
||||||
|
excluded:
|
||||||
|
- .build
|
||||||
|
disabled_rules:
|
||||||
|
- nesting
|
||||||
|
opt_in_rules:
|
||||||
|
- anonymous_argument_in_multiline_closure
|
||||||
|
- array_init
|
||||||
|
- capture_variable
|
||||||
|
- collection_alignment
|
||||||
|
- conditional_returns_on_newline
|
||||||
|
- contains_over_filter_count
|
||||||
|
- contains_over_filter_is_empty
|
||||||
|
- contains_over_first_not_nil
|
||||||
|
- contains_over_range_nil_comparison
|
||||||
|
- convenience_type
|
||||||
|
- discarded_notification_center_observer
|
||||||
|
- discouraged_assert
|
||||||
|
- discouraged_none_name
|
||||||
|
- discouraged_object_literal
|
||||||
|
- discouraged_optional_boolean
|
||||||
|
- empty_collection_literal
|
||||||
|
- empty_count
|
||||||
|
- empty_string
|
||||||
|
- empty_xctest_method
|
||||||
|
- explicit_init
|
||||||
|
- fallthrough
|
||||||
|
- fatal_error_message
|
||||||
|
- file_name_no_space
|
||||||
|
- first_where
|
||||||
|
- flatmap_over_map_reduce
|
||||||
|
- identical_operands
|
||||||
|
- implicitly_unwrapped_optional
|
||||||
|
- joined_default_parameter
|
||||||
|
- last_where
|
||||||
|
- literal_expression_end_indentation
|
||||||
|
- lower_acl_than_parent
|
||||||
|
- modifier_order
|
||||||
|
- number_separator
|
||||||
|
- operator_usage_whitespace
|
||||||
|
- overridden_super_call
|
||||||
|
- pattern_matching_keywords
|
||||||
|
- prefer_self_in_static_references
|
||||||
|
- prefer_self_type_over_type_of_self
|
||||||
|
- prefer_zero_over_explicit_init
|
||||||
|
- prohibited_interface_builder
|
||||||
|
- prohibited_super_call
|
||||||
|
- reduce_into
|
||||||
|
- redundant_nil_coalescing
|
||||||
|
- redundant_type_annotation
|
||||||
|
- single_test_class
|
||||||
|
- sorted_first_last
|
||||||
|
- sorted_imports
|
||||||
|
- static_operator
|
||||||
|
- switch_case_on_newline
|
||||||
|
- test_case_accessibility
|
||||||
|
- toggle_bool
|
||||||
|
- trailing_closure
|
||||||
|
- unneeded_parentheses_in_closure_argument
|
||||||
|
- untyped_error_in_catch
|
||||||
|
- vertical_parameter_alignment_on_call
|
||||||
|
- yoda_condition
|
||||||
|
line_length:
|
||||||
|
warning: 150
|
||||||
|
error: 175
|
||||||
|
ignores_comments: true
|
||||||
|
type_name:
|
||||||
|
max_length:
|
||||||
|
warning: 60
|
||||||
|
error: 60
|
||||||
|
identifier_name:
|
||||||
|
allowed_symbols: "_"
|
||||||
|
max_length:
|
||||||
|
warning: 60
|
||||||
|
error: 60
|
||||||
|
min_length:
|
||||||
|
warning: 1
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,333 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1410"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "graph-deps"
|
||||||
|
BuildableName = "graph-deps"
|
||||||
|
BlueprintName = "graph-deps"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "NO"
|
||||||
|
buildForArchiving = "NO"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "GraphDepsTests"
|
||||||
|
BuildableName = "GraphDepsTests"
|
||||||
|
BlueprintName = "GraphDepsTests"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "GraphDeps"
|
||||||
|
BuildableName = "GraphDeps"
|
||||||
|
BlueprintName = "GraphDeps"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "Graph"
|
||||||
|
BuildableName = "Graph"
|
||||||
|
BlueprintName = "Graph"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "XcodeProjectReader"
|
||||||
|
BuildableName = "XcodeProjectReader"
|
||||||
|
BlueprintName = "XcodeProjectReader"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "XcodeProjectParser"
|
||||||
|
BuildableName = "XcodeProjectParser"
|
||||||
|
BlueprintName = "XcodeProjectParser"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "PackageSwiftParser"
|
||||||
|
BuildableName = "PackageSwiftParser"
|
||||||
|
BlueprintName = "PackageSwiftParser"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "ShellCommandRunner"
|
||||||
|
BuildableName = "ShellCommandRunner"
|
||||||
|
BlueprintName = "ShellCommandRunner"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "PackageSwiftFileParser"
|
||||||
|
BuildableName = "PackageSwiftFileParser"
|
||||||
|
BlueprintName = "PackageSwiftFileParser"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "PackageSwiftFileParserLive"
|
||||||
|
BuildableName = "PackageSwiftFileParserLive"
|
||||||
|
BlueprintName = "PackageSwiftFileParserLive"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "XcodeProjectParserLive"
|
||||||
|
BuildableName = "XcodeProjectParserLive"
|
||||||
|
BlueprintName = "XcodeProjectParserLive"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "ShellCommandRunnerLive"
|
||||||
|
BuildableName = "ShellCommandRunnerLive"
|
||||||
|
BlueprintName = "ShellCommandRunnerLive"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "GraphDeps_PackageSwiftFileParserLiveTests"
|
||||||
|
BuildableName = "GraphDeps_PackageSwiftFileParserLiveTests"
|
||||||
|
BlueprintName = "GraphDeps_PackageSwiftFileParserLiveTests"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "PackageSwiftFileReader"
|
||||||
|
BuildableName = "PackageSwiftFileReader"
|
||||||
|
BlueprintName = "PackageSwiftFileReader"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "PackageSwiftFileReaderLive"
|
||||||
|
BuildableName = "PackageSwiftFileReaderLive"
|
||||||
|
BlueprintName = "PackageSwiftFileReaderLive"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "DumpPackageService"
|
||||||
|
BuildableName = "DumpPackageService"
|
||||||
|
BlueprintName = "DumpPackageService"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "DumpPackageServiceLive"
|
||||||
|
BuildableName = "DumpPackageServiceLive"
|
||||||
|
BlueprintName = "DumpPackageServiceLive"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "GraphDepsTests"
|
||||||
|
BuildableName = "GraphDepsTests"
|
||||||
|
BlueprintName = "GraphDepsTests"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "PackageSwiftFileParserLiveTests"
|
||||||
|
BuildableName = "PackageSwiftFileParserLiveTests"
|
||||||
|
BlueprintName = "PackageSwiftFileParserLiveTests"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "DumpPackageServiceLiveTests"
|
||||||
|
BuildableName = "DumpPackageServiceLiveTests"
|
||||||
|
BlueprintName = "DumpPackageServiceLiveTests"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES"
|
||||||
|
viewDebuggingEnabled = "No">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "graph-deps"
|
||||||
|
BuildableName = "graph-deps"
|
||||||
|
BlueprintName = "graph-deps"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "graph-deps"
|
||||||
|
BuildableName = "graph-deps"
|
||||||
|
BlueprintName = "graph-deps"
|
||||||
|
ReferencedContainer = "container:">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "aexml",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/tadija/AEXML.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3",
|
||||||
|
"version" : "4.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "pathkit",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/kylef/PathKit.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
|
||||||
|
"version" : "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "spectre",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/kylef/Spectre.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7",
|
||||||
|
"version" : "0.10.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-argument-parser",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/apple/swift-argument-parser",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "fddd1c00396eed152c45a46bea9f47b98e59301d",
|
||||||
|
"version" : "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "xcodeproj",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/tuist/XcodeProj.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "b6de1bfe021b861c94e7c83821b595083f74b997",
|
||||||
|
"version" : "8.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 2
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// swift-tools-version: 5.7
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "GraphDeps",
|
||||||
|
platforms: [.macOS(.v13)],
|
||||||
|
products: [
|
||||||
|
.executable(name: "graph-deps", targets: ["GraphDeps"])
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.0"),
|
||||||
|
.package(url: "https://github.com/tuist/XcodeProj.git", .upToNextMajor(from: "8.8.0"))
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.executableTarget(name: "GraphDeps", dependencies: [
|
||||||
|
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||||
|
"DumpPackageService",
|
||||||
|
"DumpPackageServiceLive",
|
||||||
|
"Graph",
|
||||||
|
"PackageSwiftFileParser",
|
||||||
|
"PackageSwiftFileParserLive",
|
||||||
|
"ShellCommandRunner",
|
||||||
|
"ShellCommandRunnerLive",
|
||||||
|
"XcodeProjectParser",
|
||||||
|
"XcodeProjectParserLive"
|
||||||
|
]),
|
||||||
|
.target(name: "Graph"),
|
||||||
|
.target(name: "PackageSwiftFileParser"),
|
||||||
|
.target(name: "PackageSwiftFileParserLive", dependencies: [
|
||||||
|
"DumpPackageService",
|
||||||
|
"PackageSwiftFileParser"
|
||||||
|
]),
|
||||||
|
.target(name: "DumpPackageService"),
|
||||||
|
.target(name: "DumpPackageServiceLive", dependencies: [
|
||||||
|
"DumpPackageService",
|
||||||
|
"ShellCommandRunner"
|
||||||
|
]),
|
||||||
|
.target(name: "ShellCommandRunner"),
|
||||||
|
.target(name: "ShellCommandRunnerLive", dependencies: [
|
||||||
|
"ShellCommandRunner"
|
||||||
|
]),
|
||||||
|
.target(name: "XcodeProjectParser"),
|
||||||
|
.target(name: "XcodeProjectParserLive", dependencies: [
|
||||||
|
.product(name: "XcodeProj", package: "XcodeProj"),
|
||||||
|
"XcodeProjectParser"
|
||||||
|
]),
|
||||||
|
.testTarget(name: "DumpPackageServiceLiveTests", dependencies: [
|
||||||
|
"DumpPackageServiceLive",
|
||||||
|
"ShellCommandRunner"
|
||||||
|
]),
|
||||||
|
.testTarget(name: "PackageSwiftFileParserLiveTests", dependencies: [
|
||||||
|
"DumpPackageService",
|
||||||
|
"PackageSwiftFileParser",
|
||||||
|
"PackageSwiftFileParserLive"
|
||||||
|
], resources: [.copy("MockData")])
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol DumpPackageService {
|
||||||
|
func dumpPackageForSwiftPackageFile(at fileURL: URL) throws -> Data
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import DumpPackageService
|
||||||
|
import Foundation
|
||||||
|
import ShellCommandRunner
|
||||||
|
|
||||||
|
public enum DumpPackageServiceLiveError: LocalizedError {
|
||||||
|
case invalidPackageSwiftFileURL
|
||||||
|
case unexpectedTerminationStatus(Int32)
|
||||||
|
case failedConvertingStringToData
|
||||||
|
|
||||||
|
public var errorDescription: String? {
|
||||||
|
switch self {
|
||||||
|
case .invalidPackageSwiftFileURL:
|
||||||
|
return "Expected URL to a Package.swift file"
|
||||||
|
case .unexpectedTerminationStatus(let status):
|
||||||
|
return "Unexpected termination status: \(status)"
|
||||||
|
case .failedConvertingStringToData:
|
||||||
|
return "Failed converting string to data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct DumpPackageServiceLive: DumpPackageService {
|
||||||
|
private let shellCommandRunner: ShellCommandRunner
|
||||||
|
|
||||||
|
public init(shellCommandRunner: ShellCommandRunner) {
|
||||||
|
self.shellCommandRunner = shellCommandRunner
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dumpPackageForSwiftPackageFile(at fileURL: URL) throws -> Data {
|
||||||
|
guard fileURL.lastPathComponent == "Package.swift" else {
|
||||||
|
throw DumpPackageServiceLiveError.invalidPackageSwiftFileURL
|
||||||
|
}
|
||||||
|
let directoryURL = fileURL.deletingLastPathComponent()
|
||||||
|
return try dumpPackage(at: directoryURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension DumpPackageServiceLive {
|
||||||
|
private func dumpPackage(at directoryURL: URL) throws -> Data {
|
||||||
|
let output = shellCommandRunner.run(withArguments: ["swift", "package", "dump-package"], fromDirectoryURL: directoryURL)
|
||||||
|
guard output.status == 0 else {
|
||||||
|
throw DumpPackageServiceLiveError.unexpectedTerminationStatus(output.status)
|
||||||
|
}
|
||||||
|
guard let data = output.message.data(using: .utf8) else {
|
||||||
|
throw DumpPackageServiceLiveError.failedConvertingStringToData
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
public final class Node {
|
||||||
|
public let name: String
|
||||||
|
public private(set) var children: [Node]
|
||||||
|
|
||||||
|
public init(name: String, children: [Node] = []) {
|
||||||
|
self.name = name
|
||||||
|
self.children = children
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addChild(_ node: Node) {
|
||||||
|
children.append(node)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import DumpPackageService
|
||||||
|
import DumpPackageServiceLive
|
||||||
|
import PackageSwiftFileParser
|
||||||
|
import PackageSwiftFileParserLive
|
||||||
|
import ShellCommandRunner
|
||||||
|
import ShellCommandRunnerLive
|
||||||
|
import XcodeProjectParser
|
||||||
|
import XcodeProjectParserLive
|
||||||
|
|
||||||
|
public enum CompositionRoot {
|
||||||
|
static var xcodeProjectParser: XcodeProjectParser {
|
||||||
|
return XcodeProjectParserLive(packageSwiftFileParser: packageSwiftFileParser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension CompositionRoot {
|
||||||
|
private static var packageSwiftFileParser: PackageSwiftFileParser {
|
||||||
|
return PackageSwiftFileParserLive(dumpPackageService: dumpPackageService)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var dumpPackageService: DumpPackageService {
|
||||||
|
return DumpPackageServiceLive(shellCommandRunner: shellCommandRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var shellCommandRunner: ShellCommandRunner {
|
||||||
|
return ShellCommandRunnerLive()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct GraphDeps: ParsableCommand {
|
||||||
|
@Argument(help: "The Xcode project to parse.")
|
||||||
|
var input: String
|
||||||
|
|
||||||
|
static let configuration = CommandConfiguration(
|
||||||
|
abstract: "A Swift command-line tool to graph package dependencies"
|
||||||
|
)
|
||||||
|
|
||||||
|
func run() throws {}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
extension PackageSwiftFile {
|
||||||
|
public enum Dependency: Equatable {
|
||||||
|
public struct SourceControlParameters: Equatable {
|
||||||
|
public let identity: String
|
||||||
|
|
||||||
|
public init(identity: String) {
|
||||||
|
self.identity = identity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FileSystemParameters: Equatable {
|
||||||
|
public let identity: String
|
||||||
|
public let path: String
|
||||||
|
public let packageSwiftFile: PackageSwiftFile
|
||||||
|
|
||||||
|
public init(identity: String, path: String, packageSwiftFile: PackageSwiftFile) {
|
||||||
|
self.identity = identity
|
||||||
|
self.path = path
|
||||||
|
self.packageSwiftFile = packageSwiftFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case sourceControl(SourceControlParameters)
|
||||||
|
case fileSystem(FileSystemParameters)
|
||||||
|
|
||||||
|
public static func sourceControl(identity: String) -> Self {
|
||||||
|
let parameters = SourceControlParameters(identity: identity)
|
||||||
|
return .sourceControl(parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func fileSystem(identity: String, path: String, packageSwiftFile: PackageSwiftFile) -> Self {
|
||||||
|
let parameters = FileSystemParameters(identity: identity, path: path, packageSwiftFile: packageSwiftFile)
|
||||||
|
return .fileSystem(parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
public extension PackageSwiftFile.Target {
|
||||||
|
enum Dependency: Equatable {
|
||||||
|
public struct NameParameters: Equatable {
|
||||||
|
public let name: String
|
||||||
|
|
||||||
|
public init(name: String) {
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ProductInPackageParameters: Equatable {
|
||||||
|
public let name: String
|
||||||
|
public let packageName: String
|
||||||
|
|
||||||
|
public init(name: String, packageName: String) {
|
||||||
|
self.name = name
|
||||||
|
self.packageName = packageName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case name(NameParameters)
|
||||||
|
case productInPackage(ProductInPackageParameters)
|
||||||
|
|
||||||
|
public static func name(_ name: String) -> Self {
|
||||||
|
return .name(.init(name: name))
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func product(_ name: String, inPackage package: String) -> Self {
|
||||||
|
return .productInPackage(.init(name: name, packageName: package))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
public extension PackageSwiftFile {
|
||||||
|
struct Target: Equatable {
|
||||||
|
public let name: String
|
||||||
|
public let dependencies: [Dependency]
|
||||||
|
|
||||||
|
public init(name: String, dependencies: [Dependency] = []) {
|
||||||
|
self.name = name
|
||||||
|
self.dependencies = dependencies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
public struct PackageSwiftFile: Equatable {
|
||||||
|
public let name: String
|
||||||
|
public let targets: [Target]
|
||||||
|
public let dependencies: [Dependency]
|
||||||
|
|
||||||
|
public init(name: String, targets: [Target] = [], dependencies: [Dependency] = []) {
|
||||||
|
self.name = name
|
||||||
|
self.targets = targets
|
||||||
|
self.dependencies = dependencies
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol PackageSwiftFileParser {
|
||||||
|
func parseFile(at fileURL: URL) throws -> PackageSwiftFile
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
extension IntermediatePackageSwiftFile {
|
||||||
|
enum Dependency: Decodable {
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case sourceControl
|
||||||
|
case fileSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SourceControlParameters: Decodable {
|
||||||
|
let identity: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileSystemParameters: Decodable {
|
||||||
|
let identity: String
|
||||||
|
let path: String
|
||||||
|
}
|
||||||
|
|
||||||
|
case sourceControl(SourceControlParameters)
|
||||||
|
case fileSystem(FileSystemParameters)
|
||||||
|
|
||||||
|
init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
if container.allKeys.contains(CodingKeys.sourceControl) {
|
||||||
|
self = try .decodeSourceControl(using: container)
|
||||||
|
} else if container.allKeys.contains(CodingKeys.fileSystem) {
|
||||||
|
self = try .decodeFileSystem(using: container)
|
||||||
|
} else {
|
||||||
|
let keysString = container.allKeys.map(\.stringValue).joined(separator: ", ")
|
||||||
|
let debugDescription = "Unsupported keys: " + keysString
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension IntermediatePackageSwiftFile.Dependency {
|
||||||
|
private static func decodeSourceControl(using container: KeyedDecodingContainer<Self.CodingKeys>) throws -> Self {
|
||||||
|
let parametersContainer = try container.decode([SourceControlParameters].self, forKey: .sourceControl)
|
||||||
|
guard parametersContainer.count == 1 else {
|
||||||
|
let debugDescription = "Expected to decode exactly 1 parameter object but found \(parametersContainer.count)"
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
return .sourceControl(parametersContainer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func decodeFileSystem(using container: KeyedDecodingContainer<Self.CodingKeys>) throws -> Self {
|
||||||
|
let parametersContainer = try container.decode([FileSystemParameters].self, forKey: .fileSystem)
|
||||||
|
guard parametersContainer.count == 1 else {
|
||||||
|
let debugDescription = "Expected to decode exactly 1 parameter object but found \(parametersContainer.count)"
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
return .fileSystem(parametersContainer[0])
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
extension IntermediatePackageSwiftFile.Target {
|
||||||
|
enum Dependency: Decodable {
|
||||||
|
private enum CodingKeys: CodingKey {
|
||||||
|
case byName
|
||||||
|
case product
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ByNameParameters {
|
||||||
|
let name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProductParameters {
|
||||||
|
let name: String
|
||||||
|
let package: String
|
||||||
|
}
|
||||||
|
|
||||||
|
case byName(ByNameParameters)
|
||||||
|
case product(ProductParameters)
|
||||||
|
|
||||||
|
init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
if container.allKeys.contains(CodingKeys.byName) {
|
||||||
|
self = try .decodeByName(using: container)
|
||||||
|
} else if container.allKeys.contains(CodingKeys.product) {
|
||||||
|
self = try .decodeProduct(using: container)
|
||||||
|
} else {
|
||||||
|
let keysString = container.allKeys.map(\.stringValue).joined(separator: ", ")
|
||||||
|
let debugDescription = "Unsupported keys: " + keysString
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension IntermediatePackageSwiftFile.Target.Dependency {
|
||||||
|
private static func decodeByName(using container: KeyedDecodingContainer<Self.CodingKeys>) throws -> Self {
|
||||||
|
let values = try container.decode([String?].self, forKey: .byName)
|
||||||
|
guard values.count >= 1 else {
|
||||||
|
let debugDescription = "Expected to decode at least 1 string but found \(values.count)"
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
guard case let .some(name) = values[0] else {
|
||||||
|
let debugDescription = "Expected library name to be non-null"
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
let parameters = ByNameParameters(name: name)
|
||||||
|
return .byName(parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func decodeProduct(using container: KeyedDecodingContainer<Self.CodingKeys>) throws -> Self {
|
||||||
|
let values = try container.decode([String?].self, forKey: .product)
|
||||||
|
guard values.count >= 2 else {
|
||||||
|
let debugDescription = "Expected to decode at least 2 strings but found \(values.count)"
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
guard case let .some(name) = values[0] else {
|
||||||
|
let debugDescription = "Expected library name to be non-null"
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
guard case let .some(package) = values[1] else {
|
||||||
|
let debugDescription = "Expected package name to be non-null"
|
||||||
|
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: debugDescription))
|
||||||
|
}
|
||||||
|
let parameters = ProductParameters(name: name, package: package)
|
||||||
|
return .product(parameters)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
extension IntermediatePackageSwiftFile {
|
||||||
|
struct Target: Decodable {
|
||||||
|
let name: String
|
||||||
|
let dependencies: [Dependency]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
struct IntermediatePackageSwiftFile: Decodable {
|
||||||
|
let name: String
|
||||||
|
let targets: [Target]
|
||||||
|
let dependencies: [Dependency]
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import DumpPackageService
|
||||||
|
import Foundation
|
||||||
|
import PackageSwiftFileParser
|
||||||
|
|
||||||
|
struct PackageSwiftFileMapper {
|
||||||
|
private let dumpPackageService: DumpPackageService
|
||||||
|
private let packageSwiftFileParser: PackageSwiftFileParser
|
||||||
|
|
||||||
|
init(dumpPackageService: DumpPackageService, packageSwiftFileParser: PackageSwiftFileParser) {
|
||||||
|
self.dumpPackageService = dumpPackageService
|
||||||
|
self.packageSwiftFileParser = packageSwiftFileParser
|
||||||
|
}
|
||||||
|
|
||||||
|
func map(_ intermediate: IntermediatePackageSwiftFile) throws -> PackageSwiftFile {
|
||||||
|
let targets = intermediate.targets.map(map)
|
||||||
|
let dependencies = try intermediate.dependencies.map(map)
|
||||||
|
return PackageSwiftFile(name: intermediate.name, targets: targets, dependencies: dependencies)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension PackageSwiftFileMapper {
|
||||||
|
private func map(_ intermediate: IntermediatePackageSwiftFile.Target) -> PackageSwiftFile.Target {
|
||||||
|
let dependencies = intermediate.dependencies.map(map)
|
||||||
|
return PackageSwiftFile.Target(name: intermediate.name, dependencies: dependencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func map(_ intermediate: IntermediatePackageSwiftFile.Target.Dependency) -> PackageSwiftFile.Target.Dependency {
|
||||||
|
switch intermediate {
|
||||||
|
case .byName(let parameters):
|
||||||
|
return .name(parameters.name)
|
||||||
|
case .product(let parameters):
|
||||||
|
return .product(parameters.name, inPackage: parameters.package)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func map(_ intermediate: IntermediatePackageSwiftFile.Dependency) throws -> PackageSwiftFile.Dependency {
|
||||||
|
switch intermediate {
|
||||||
|
case .sourceControl(let parameters):
|
||||||
|
return .sourceControl(identity: parameters.identity)
|
||||||
|
case .fileSystem(let parameters):
|
||||||
|
let fileURL = URL(filePath: parameters.path).appending(path: "Package.swift")
|
||||||
|
let contents = try dumpPackageService.dumpPackageForSwiftPackageFile(at: fileURL)
|
||||||
|
let packageSwiftFile = try packageSwiftFileParser.parseFile(at: fileURL)
|
||||||
|
return .fileSystem(identity: parameters.identity, path: parameters.path, packageSwiftFile: packageSwiftFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import DumpPackageService
|
||||||
|
import Foundation
|
||||||
|
import PackageSwiftFileParser
|
||||||
|
|
||||||
|
public struct PackageSwiftFileParserLive: PackageSwiftFileParser {
|
||||||
|
private let dumpPackageService: DumpPackageService
|
||||||
|
|
||||||
|
public init(dumpPackageService: DumpPackageService) {
|
||||||
|
self.dumpPackageService = dumpPackageService
|
||||||
|
}
|
||||||
|
|
||||||
|
public func parseFile(at fileURL: URL) throws -> PackageSwiftFile {
|
||||||
|
let contents = try dumpPackageService.dumpPackageForSwiftPackageFile(at: fileURL)
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
let intermediate = try decoder.decode(IntermediatePackageSwiftFile.self, from: contents)
|
||||||
|
let mapper = PackageSwiftFileMapper(dumpPackageService: dumpPackageService, packageSwiftFileParser: self)
|
||||||
|
return try mapper.map(intermediate)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
public struct ShellCommandOutput {
|
||||||
|
public let status: Int32
|
||||||
|
public let message: String
|
||||||
|
|
||||||
|
public init(status: Int32, message: String) {
|
||||||
|
self.status = status
|
||||||
|
self.message = message
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol ShellCommandRunner {
|
||||||
|
func run(withArguments arguments: [String], fromDirectoryURL directoryURL: URL) -> ShellCommandOutput
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import Foundation
|
||||||
|
import ShellCommandRunner
|
||||||
|
|
||||||
|
public struct ShellCommandRunnerLive: ShellCommandRunner {
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public func run(withArguments arguments: [String], fromDirectoryURL directoryURL: URL) -> ShellCommandOutput {
|
||||||
|
let task = Process()
|
||||||
|
let pipe = Pipe()
|
||||||
|
task.currentDirectoryURL = directoryURL
|
||||||
|
task.standardOutput = pipe
|
||||||
|
task.standardError = pipe
|
||||||
|
task.arguments = ["-c", arguments.joined(separator: " ")]
|
||||||
|
task.launchPath = "/bin/zsh"
|
||||||
|
task.standardInput = nil
|
||||||
|
task.launch()
|
||||||
|
task.waitUntilExit()
|
||||||
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
let message = String(data: data, encoding: .utf8)!
|
||||||
|
let status = task.terminationStatus
|
||||||
|
return ShellCommandOutput(status: status, message: message)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public extension XcodeProject {
|
||||||
|
enum SwiftPackage {
|
||||||
|
public struct LocalParameters {
|
||||||
|
public let name: String
|
||||||
|
public let fileURL: URL
|
||||||
|
|
||||||
|
public init(name: String, fileURL: URL) {
|
||||||
|
self.name = name
|
||||||
|
self.fileURL = fileURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RemoteParameters {
|
||||||
|
public let name: String
|
||||||
|
public let repositoryURL: URL
|
||||||
|
|
||||||
|
public init(name: String, repositoryURL: URL) {
|
||||||
|
self.name = name
|
||||||
|
self.repositoryURL = repositoryURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case local(LocalParameters)
|
||||||
|
case remote(RemoteParameters)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension XcodeProject {
|
||||||
|
public struct Target {
|
||||||
|
public let name: String
|
||||||
|
public let packageProductDependencies: [String]
|
||||||
|
|
||||||
|
public init(name: String, packageProductDependencies: [String]) {
|
||||||
|
self.name = name
|
||||||
|
self.packageProductDependencies = packageProductDependencies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct XcodeProject {
|
||||||
|
public let name: String
|
||||||
|
public let targets: [Target]
|
||||||
|
public let swiftPackages: [SwiftPackage]
|
||||||
|
|
||||||
|
public init(name: String, targets: [Target] = [], swiftPackages: [SwiftPackage] = []) {
|
||||||
|
self.name = name
|
||||||
|
self.targets = targets
|
||||||
|
self.swiftPackages = swiftPackages
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol XcodeProjectParser {
|
||||||
|
func parseProject(at fileURL: URL) throws -> XcodeProject
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import Foundation
|
||||||
|
import PackageSwiftFileParser
|
||||||
|
import PathKit
|
||||||
|
import XcodeProj
|
||||||
|
import XcodeProjectParser
|
||||||
|
|
||||||
|
public struct XcodeProjectParserLive: XcodeProjectParser {
|
||||||
|
private let packageSwiftFileParser: PackageSwiftFileParser
|
||||||
|
|
||||||
|
public init(packageSwiftFileParser: PackageSwiftFileParser) {
|
||||||
|
self.packageSwiftFileParser = packageSwiftFileParser
|
||||||
|
}
|
||||||
|
|
||||||
|
public func parseProject(at fileURL: URL) throws -> XcodeProject {
|
||||||
|
let path = Path(fileURL.relativePath)
|
||||||
|
let project = try XcodeProj(path: path)
|
||||||
|
let sourceRoot = fileURL.deletingLastPathComponent()
|
||||||
|
let remoteSwiftPackages = remoteSwiftPackages(in: project)
|
||||||
|
let localSwiftPackages = try localSwiftPackages(in: project, atSourceRoot: sourceRoot)
|
||||||
|
return XcodeProject(
|
||||||
|
name: fileURL.lastPathComponent,
|
||||||
|
targets: targets(in: project),
|
||||||
|
swiftPackages: remoteSwiftPackages + localSwiftPackages
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension XcodeProjectParserLive {
|
||||||
|
func targets(in project: XcodeProj) -> [XcodeProject.Target] {
|
||||||
|
return project.pbxproj.nativeTargets.map { target in
|
||||||
|
let packageProductDependencies = target.packageProductDependencies.map(\.productName)
|
||||||
|
return .init(name: target.name, packageProductDependencies: packageProductDependencies)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func remoteSwiftPackages(in project: XcodeProj) -> [XcodeProject.SwiftPackage] {
|
||||||
|
return project.pbxproj.nativeTargets.reduce(into: []) { targetResult, target in
|
||||||
|
targetResult += target.dependencies.reduce(into: []) { dependencyResult, dependency in
|
||||||
|
guard let package = dependency.product?.package, let packageName = package.name else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let rawRepositoryURL = package.repositoryURL, let repositoryURL = URL(string: rawRepositoryURL) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dependencyResult.append(.remote(.init(name: packageName, repositoryURL: repositoryURL)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func localSwiftPackages(in project: XcodeProj, atSourceRoot sourceRoot: URL) throws -> [XcodeProject.SwiftPackage] {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
@testable import DumpPackageServiceLive
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class DumpPackageServiceLiveTests: XCTestCase {
|
||||||
|
func testThrowsWhenReceivingInvalidPackageSwiftFileURL() throws {
|
||||||
|
let shellCommandRunner = ShellCommandRunnerMock()
|
||||||
|
let service = DumpPackageServiceLive(shellCommandRunner: shellCommandRunner)
|
||||||
|
let fileURL = URL(fileURLWithPath: "/Users/john/Mock/NotAPackageSwiftFile")
|
||||||
|
XCTAssertThrowsError(try service.dumpPackageForSwiftPackageFile(at: fileURL))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInvokesDumpPackageCommandOnSwiftCLI() throws {
|
||||||
|
let shellCommandRunner = ShellCommandRunnerMock()
|
||||||
|
let service = DumpPackageServiceLive(shellCommandRunner: shellCommandRunner)
|
||||||
|
let fileURL = URL(fileURLWithPath: "/Users/john/Mock/Package.swift")
|
||||||
|
_ = try service.dumpPackageForSwiftPackageFile(at: fileURL)
|
||||||
|
XCTAssertEqual(shellCommandRunner.latestArguments, ["swift", "package", "dump-package"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSetsCurrentDirectoryToSwiftPackage() throws {
|
||||||
|
let shellCommandRunner = ShellCommandRunnerMock()
|
||||||
|
let service = DumpPackageServiceLive(shellCommandRunner: shellCommandRunner)
|
||||||
|
let fileURL = URL(fileURLWithPath: "/Users/john/Mock/Package.swift")
|
||||||
|
_ = try service.dumpPackageForSwiftPackageFile(at: fileURL)
|
||||||
|
XCTAssertEqual(shellCommandRunner.latestDirectoryURL, URL(filePath: "/Users/john/Mock/"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import Foundation
|
||||||
|
import ShellCommandRunner
|
||||||
|
|
||||||
|
final class ShellCommandRunnerMock: ShellCommandRunner {
|
||||||
|
private(set) var latestArguments: [String]?
|
||||||
|
private(set) var latestDirectoryURL: URL?
|
||||||
|
|
||||||
|
func run(withArguments arguments: [String], fromDirectoryURL directoryURL: URL) -> ShellCommandOutput {
|
||||||
|
latestArguments = arguments
|
||||||
|
latestDirectoryURL = directoryURL
|
||||||
|
return ShellCommandOutput(status: 0, message: "Success")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import DumpPackageService
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct DumpPackageServiceMock: DumpPackageService {
|
||||||
|
private let fileURLMap: [URL: URL]
|
||||||
|
|
||||||
|
init(fileURLMap: [URL: URL] = [:]) {
|
||||||
|
self.fileURLMap = fileURLMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpPackageForSwiftPackageFile(at fileURL: URL) throws -> Data {
|
||||||
|
let mappedFileURL = fileURLMap[fileURL] ?? fileURL
|
||||||
|
return try Data(contentsOf: mappedFileURL)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
{
|
||||||
|
"cLanguageStandard" : null,
|
||||||
|
"cxxLanguageStandard" : null,
|
||||||
|
"dependencies" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "HexColor",
|
||||||
|
"packageKind" : {
|
||||||
|
"root" : [
|
||||||
|
"/Users/simon/Developer/Foo/HexColor"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pkgConfig" : null,
|
||||||
|
"platforms" : [
|
||||||
|
{
|
||||||
|
"options" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"platformName" : "ios",
|
||||||
|
"version" : "16.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"options" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"platformName" : "maccatalyst",
|
||||||
|
"version" : "16.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"products" : [
|
||||||
|
{
|
||||||
|
"name" : "ColorCategories",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ColorCategories"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"providers" : null,
|
||||||
|
"swiftLanguageVersions" : null,
|
||||||
|
"targets" : [
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ColorCategories",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ColorCategories",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ColorCategoriesTests",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"toolsVersion" : {
|
||||||
|
"_version" : "5.7.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,622 @@
|
||||||
|
{
|
||||||
|
"cLanguageStandard" : null,
|
||||||
|
"cxxLanguageStandard" : null,
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"sourceControl" : [
|
||||||
|
{
|
||||||
|
"identity" : "runestone",
|
||||||
|
"location" : {
|
||||||
|
"remote" : [
|
||||||
|
"git@github.com:simonbs/Runestone.git"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"productFilter" : null,
|
||||||
|
"requirement" : {
|
||||||
|
"branch" : [
|
||||||
|
"main"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fileSystem" : [
|
||||||
|
{
|
||||||
|
"identity" : "hexcolor",
|
||||||
|
"path" : "/Users/simon/Developer/Foo/HexColor",
|
||||||
|
"productFilter" : null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserFeature",
|
||||||
|
"packageKind" : {
|
||||||
|
"root" : [
|
||||||
|
"/Users/simon/Developer/Foo/ScriptBrowserFeature"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pkgConfig" : null,
|
||||||
|
"platforms" : [
|
||||||
|
{
|
||||||
|
"options" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"platformName" : "ios",
|
||||||
|
"version" : "16.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"options" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"platformName" : "maccatalyst",
|
||||||
|
"version" : "16.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"products" : [
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserEntities",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserEntities"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserGroupsService",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserGroupsService"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserGroupsServiceMock",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserGroupsServiceMock"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserListService",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserListService"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserListServiceLive",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserListServiceLive"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserListServiceFactory",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserListServiceFactory"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserListServiceFactoryLive",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserListServiceFactoryLive"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserSearchService",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserSearchService"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserSidebarService",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserSidebarService"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserSidebarServiceLive",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserSidebarServiceLive"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" : "ScriptBrowserUI",
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"targets" : [
|
||||||
|
"ScriptBrowserUI"
|
||||||
|
],
|
||||||
|
"type" : {
|
||||||
|
"library" : [
|
||||||
|
"automatic"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"providers" : null,
|
||||||
|
"swiftLanguageVersions" : null,
|
||||||
|
"targets" : [
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserEntities",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserGroupsService",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserGroupsService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserGroupsServiceMock",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserListService",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserGroupsService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserSearchService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserListServiceLive",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserListServiceFactory",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserGroupsService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListServiceFactory",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListServiceLive",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserSearchService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserListServiceFactoryLive",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserSearchService",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserSidebarService",
|
||||||
|
"resources" : [
|
||||||
|
{
|
||||||
|
"path" : "Resources/Localizable.strings",
|
||||||
|
"rule" : {
|
||||||
|
"process" : {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserGroupsService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserSidebarService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserSidebarServiceLive",
|
||||||
|
"resources" : [
|
||||||
|
{
|
||||||
|
"path" : "Resources/Localizable.strings",
|
||||||
|
"rule" : {
|
||||||
|
"process" : {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListServiceFactory",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserSidebarService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"product" : [
|
||||||
|
"ColorCategories",
|
||||||
|
"HexColor",
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserUI",
|
||||||
|
"resources" : [
|
||||||
|
{
|
||||||
|
"path" : "Resources/Localizable.strings",
|
||||||
|
"rule" : {
|
||||||
|
"process" : {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "regular"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies" : [
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserGroupsService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserGroupsServiceMock",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListServiceFactory",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserListServiceLive",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserSearchService",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserSidebarServiceLive",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byName" : [
|
||||||
|
"ScriptBrowserUI",
|
||||||
|
null
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exclude" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"name" : "ScriptBrowserUITests",
|
||||||
|
"resources" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"settings" : [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type" : "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"toolsVersion" : {
|
||||||
|
"_version" : "5.7.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
import PackageSwiftFileParser
|
||||||
|
@testable import PackageSwiftFileParserLive
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class PackageSwiftFileParserLiveTests: XCTestCase {
|
||||||
|
private var dumpPackageService: DumpPackageServiceMock {
|
||||||
|
return DumpPackageServiceMock(fileURLMap: [
|
||||||
|
URL.Mock.root: Bundle.module.url(forMockDumpPackageNamed: "root"),
|
||||||
|
URL.Mock.hexColor: Bundle.module.url(forMockDumpPackageNamed: "hexcolor")
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParsesName() throws {
|
||||||
|
let parser = PackageSwiftFileParserLive(dumpPackageService: dumpPackageService)
|
||||||
|
let swiftPackageFile = try parser.parseFile(at: URL.Mock.root)
|
||||||
|
XCTAssertEqual(swiftPackageFile.name, "ScriptBrowserFeature")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParsesTargetNames() throws {
|
||||||
|
let parser = PackageSwiftFileParserLive(dumpPackageService: dumpPackageService)
|
||||||
|
let swiftPackageFile = try parser.parseFile(at: URL.Mock.root)
|
||||||
|
let targetNames = swiftPackageFile.targets.map(\.name)
|
||||||
|
XCTAssertEqual(targetNames, [
|
||||||
|
"ScriptBrowserEntities",
|
||||||
|
"ScriptBrowserGroupsService",
|
||||||
|
"ScriptBrowserGroupsServiceMock",
|
||||||
|
"ScriptBrowserListService",
|
||||||
|
"ScriptBrowserListServiceLive",
|
||||||
|
"ScriptBrowserListServiceFactory",
|
||||||
|
"ScriptBrowserListServiceFactoryLive",
|
||||||
|
"ScriptBrowserSearchService",
|
||||||
|
"ScriptBrowserSidebarService",
|
||||||
|
"ScriptBrowserSidebarServiceLive",
|
||||||
|
"ScriptBrowserUI",
|
||||||
|
"ScriptBrowserUITests"
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable:next function_body_length
|
||||||
|
func testParsesTargets() throws {
|
||||||
|
let parser = PackageSwiftFileParserLive(dumpPackageService: dumpPackageService)
|
||||||
|
let swiftPackageFile = try parser.parseFile(at: URL.Mock.root)
|
||||||
|
XCTAssertEqual(swiftPackageFile.targets, [
|
||||||
|
.init(name: "ScriptBrowserEntities", dependencies: []),
|
||||||
|
.init(name: "ScriptBrowserGroupsService", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserGroupsServiceMock", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities"),
|
||||||
|
.name("ScriptBrowserGroupsService")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserListService", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserListServiceLive", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities"),
|
||||||
|
.name("ScriptBrowserGroupsService"),
|
||||||
|
.name("ScriptBrowserListService"),
|
||||||
|
.name("ScriptBrowserSearchService")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserListServiceFactory", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities"),
|
||||||
|
.name("ScriptBrowserListService")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserListServiceFactoryLive", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities"),
|
||||||
|
.name("ScriptBrowserGroupsService"),
|
||||||
|
.name("ScriptBrowserListServiceFactory"),
|
||||||
|
.name("ScriptBrowserListService"),
|
||||||
|
.name("ScriptBrowserListServiceLive"),
|
||||||
|
.name("ScriptBrowserSearchService")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserSearchService", dependencies: []),
|
||||||
|
.init(name: "ScriptBrowserSidebarService", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserSidebarServiceLive", dependencies: [
|
||||||
|
.name("ScriptBrowserGroupsService"),
|
||||||
|
.name("ScriptBrowserSidebarService")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserUI", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities"),
|
||||||
|
.name("ScriptBrowserListService"),
|
||||||
|
.name("ScriptBrowserListServiceFactory"),
|
||||||
|
.name("ScriptBrowserSidebarService"),
|
||||||
|
.product("ColorCategories", inPackage: "HexColor")
|
||||||
|
]),
|
||||||
|
.init(name: "ScriptBrowserUITests", dependencies: [
|
||||||
|
.name("ScriptBrowserEntities"),
|
||||||
|
.name("ScriptBrowserGroupsService"),
|
||||||
|
.name("ScriptBrowserGroupsServiceMock"),
|
||||||
|
.name("ScriptBrowserListService"),
|
||||||
|
.name("ScriptBrowserListServiceFactory"),
|
||||||
|
.name("ScriptBrowserListServiceLive"),
|
||||||
|
.name("ScriptBrowserSearchService"),
|
||||||
|
.name("ScriptBrowserSidebarServiceLive"),
|
||||||
|
.name("ScriptBrowserUI")
|
||||||
|
])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testParsesDependencies() throws {
|
||||||
|
let parser = PackageSwiftFileParserLive(dumpPackageService: dumpPackageService)
|
||||||
|
let swiftPackageFile = try parser.parseFile(at: URL.Mock.root)
|
||||||
|
XCTAssertEqual(swiftPackageFile.dependencies, [
|
||||||
|
.sourceControl(identity: "runestone"),
|
||||||
|
.fileSystem(
|
||||||
|
identity: "hexcolor",
|
||||||
|
path: "/Users/simon/Developer/Foo/HexColor",
|
||||||
|
packageSwiftFile: PackageSwiftFile(
|
||||||
|
name: "HexColor",
|
||||||
|
targets: [
|
||||||
|
.init(name: "ColorCategories"),
|
||||||
|
.init(name: "ColorCategoriesTests", dependencies: [
|
||||||
|
.name("ColorCategories")
|
||||||
|
])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension URL {
|
||||||
|
enum Mock {
|
||||||
|
static var root: URL {
|
||||||
|
return URL(filePath: "/Users/simon/Developer/Foo/root/Package.swift")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var hexColor: URL {
|
||||||
|
return URL(filePath: "/Users/simon/Developer/Foo/HexColor/Package.swift")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension Bundle {
|
||||||
|
func url(forMockDumpPackageNamed filename: String) -> URL {
|
||||||
|
return url(forResource: "MockData/" + filename, withExtension: "json")!
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue