-
-
Notifications
You must be signed in to change notification settings - Fork 328
Expand file tree
/
Copy pathFileSystem.fs
More file actions
109 lines (93 loc) · 5.39 KB
/
FileSystem.fs
File metadata and controls
109 lines (93 loc) · 5.39 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
namespace ShopifySharp.GraphQL.Parser
open System
open System.IO
open System.Linq
open System.Text
open System.Threading.Tasks
open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.CSharp
open Microsoft.CodeAnalysis.CSharp.Syntax
[<RequireQualifiedAccess>]
module FileSystem =
let writeFileToPath filePath (fileText: string) cancellationToken: ValueTask =
ValueTask(task {
if File.Exists(filePath) then
File.Delete(filePath)
let directory = Path.GetDirectoryName filePath
if directory <> "" && not (Directory.Exists directory) then
Directory.CreateDirectory directory
|> ignore
do! File.WriteAllTextAsync(filePath, fileText, cancellationToken)
})
let private extractSubdirectoryFromNamespace (namespaceName: string): string =
// Map namespace suffix to subdirectory
// Operations go in QueryBuilders/Operations, all other QueryBuilders in QueryBuilders/Types
if namespaceName.Contains(".QueryBuilders.Operations") then "Operations"
elif namespaceName.Contains(".QueryBuilders.Types") then "Types"
else ""
let private parseCsharpStringToGeneratedFiles (csharpCode: string) cancellationToken: ValueTask<GeneratedCsharpFile[]> =
ValueTask<GeneratedCsharpFile[]>(task {
let! csharpTree = CSharpSyntaxTree.ParseText(csharpCode).GetRootAsync(cancellationToken)
let syntaxRoot = csharpTree :?> CompilationUnitSyntax
let usings = syntaxRoot.Usings;
let externals = syntaxRoot.Externs
let rootMembers = syntaxRoot.Members |> Array.ofSeq
let rootTypes = rootMembers |> Array.filter (fun m -> m :? BaseTypeDeclarationSyntax)
let namespaces = syntaxRoot.Members.OfType<BaseNamespaceDeclarationSyntax>() |> Array.ofSeq
let fileScopedNamespaces = syntaxRoot.Members.OfType<FileScopedNamespaceDeclarationSyntax>() |> Array.ofSeq
let allTypes =
if fileScopedNamespaces.Length > 0 then
// Handle file-scoped namespaces - collect ALL types (both from namespace and root)
let nsTypes =
fileScopedNamespaces
|> Array.collect (fun ns ->
let types = ns.Members.OfType<BaseTypeDeclarationSyntax>() |> Array.ofSeq
types |> Array.map (fun t -> (ns :> BaseNamespaceDeclarationSyntax, t)))
// Also collect types that ended up at root level due to parsing issues
let rootLevelTypes = rootTypes |> Array.map (fun t ->
// Use the file-scoped namespace for root-level types
(fileScopedNamespaces[0] :> BaseNamespaceDeclarationSyntax, t :?> BaseTypeDeclarationSyntax))
Array.concat [nsTypes; rootLevelTypes]
else
// Handle regular namespaces
namespaces
|> Array.collect (fun ns ->
let types = ns.Members.OfType<BaseTypeDeclarationSyntax>() |> Array.ofSeq
types |> Array.map (fun t -> (ns, t)))
return
allTypes
|> Array.map (fun (ns, type') ->
let unit = SyntaxFactory.CompilationUnit()
.WithExterns(externals)
.WithUsings(usings)
.AddMembers(ns.WithMembers(SyntaxFactory.SingletonList<MemberDeclarationSyntax>(type')))
.NormalizeWhitespace(eol = Environment.NewLine)
let namespaceName = ns.Name.ToString()
let subdirectory = extractSubdirectoryFromNamespace namespaceName
let fileName =
if subdirectory <> "" then
Path.Join(subdirectory, type'.Identifier.Text + ".generated.cs")
else
type'.Identifier.Text + ".generated.cs"
{ FileName = fileName
FileText = unit.ToFullString() }
)
})
let private parseCsharpCodeAndWriteToDirectoryPath directoryPath (csharpCodeStringBuilder: StringBuilder) cancellationToken =
ValueTask(task {
let! generatedFiles = parseCsharpStringToGeneratedFiles (csharpCodeStringBuilder.ToString()) cancellationToken
for file in generatedFiles do
let filePath = Path.Join(directoryPath, "/", file.FileName)
do! writeFileToPath filePath file.FileText cancellationToken
})
let writeCsharpCodeToFileSystem destination csharpCodeStringBuilder cancellationToken: ValueTask =
ValueTask(task {
match destination with
| SingleFile filePath ->
do! writeFileToPath filePath (csharpCodeStringBuilder.ToString()) cancellationToken
| Directory directoryPath ->
do! parseCsharpCodeAndWriteToDirectoryPath directoryPath csharpCodeStringBuilder cancellationToken
| DirectoryAndTemporaryFile(directoryPath, temporaryFilePath) ->
do! writeFileToPath temporaryFilePath (csharpCodeStringBuilder.ToString()) cancellationToken
do! parseCsharpCodeAndWriteToDirectoryPath directoryPath csharpCodeStringBuilder cancellationToken
})