@@ -31,6 +31,123 @@ param(
3131
3232Set-StrictMode - Version Latest
3333
34+ function Update-FableNullableReferenceTypes {
35+ param (
36+ [Parameter (Mandatory = $true )]
37+ [string ]
38+ $ProjectPath ,
39+ [Parameter (Mandatory = $true )]
40+ [string ]
41+ $FableOutputPath
42+ )
43+
44+ $projectFolder = Split-Path - Path $ProjectPath - Parent
45+ [xml ]$projectXml = Get-Content - Path $ProjectPath - Raw
46+ $nullableFieldsByType = @ {}
47+
48+ foreach ($compileItem in $projectXml.SelectNodes (' //Compile' )) {
49+ $sourcePath = Join-Path - Path $projectFolder - ChildPath $compileItem.Include
50+ if (-not (Test-Path - Path $sourcePath )) {
51+ continue
52+ }
53+
54+ $currentType = $null
55+
56+ foreach ($line in Get-Content - Path $sourcePath ) {
57+ if ($line -match ' ^\s*type\s+([A-Za-z_][A-Za-z0-9_'' `]*)' ) {
58+ $currentType = $Matches [1 ] -replace ' `.*$' , ' '
59+ }
60+
61+ if ($currentType -and ($line -match ' ^\s*\{?\s*([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(?!Nullable<)(.+?)\s*\|\s*null\b' )) {
62+ if (-not $nullableFieldsByType.ContainsKey ($currentType )) {
63+ $nullableFieldsByType [$currentType ] = [System.Collections.Generic.HashSet [string ]]::new()
64+ }
65+
66+ [void ]$nullableFieldsByType [$currentType ].Add($Matches [1 ])
67+ }
68+ }
69+ }
70+
71+ if ($nullableFieldsByType.Count -eq 0 ) {
72+ return
73+ }
74+
75+ foreach ($typescriptFile in Get-ChildItem - Path $FableOutputPath - Filter ' *.ts' - Recurse - File) {
76+ $currentType = $null
77+ $fileChanged = $false
78+ $usesNullable = $false
79+ $usesNonNullValue = $false
80+ $updatedLines = foreach ($line in Get-Content - Path $typescriptFile.FullName ) {
81+ $updatedLine = $line
82+
83+ if ($updatedLine -match ' ^\s*export\s+class\s+([A-Za-z_][A-Za-z0-9_$]*)\b' ) {
84+ $currentType = $Matches [1 ] -replace ' \$.*$' , ' '
85+ }
86+
87+ if ($currentType -and $nullableFieldsByType.ContainsKey ($currentType )) {
88+ foreach ($fieldName in $nullableFieldsByType [$currentType ]) {
89+ $escapedFieldName = [regex ]::Escape($fieldName )
90+
91+ $fieldPattern = " ^(\s*readonly\s+$escapedFieldName \s*:\s*)(?!Nullable<)([^;]+)(;.*)$"
92+ if ($updatedLine -match $fieldPattern ) {
93+ $updatedLine = [regex ]::Replace($updatedLine , $fieldPattern , ' $1Nullable<$2>$3' )
94+ $fileChanged = $true
95+ $usesNullable = $true
96+ }
97+
98+ if ($updatedLine -match ' ^\s*constructor\(' ) {
99+ $parameterPattern = " (\b$escapedFieldName \s*:\s*)(?!Nullable<)([^,)]+)"
100+ $nextLine = [regex ]::Replace($updatedLine , $parameterPattern , ' $1Nullable<$2>' )
101+
102+ if ($nextLine -ne $updatedLine ) {
103+ $updatedLine = $nextLine
104+ $fileChanged = $true
105+ $usesNullable = $true
106+ }
107+ }
108+ }
109+ }
110+
111+ $nonNullValuePattern = ' UrlPart_op_Implicit_Z721C83C5\((?!nonNullValue\()([A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*)\)'
112+ $nextLine = [regex ]::Replace($updatedLine , $nonNullValuePattern , ' UrlPart_op_Implicit_Z721C83C5(nonNullValue($1))' )
113+
114+ if ($nextLine -ne $updatedLine ) {
115+ $updatedLine = $nextLine
116+ $fileChanged = $true
117+ $usesNonNullValue = $true
118+ }
119+
120+ $updatedLine
121+ }
122+
123+ if ($fileChanged ) {
124+ if ($usesNullable -and -not ($updatedLines -match ' import\s+\{[^}]*\bNullable\b[^}]*\}\s+from\s+"@fable-org/fable-library-ts/Util\.ts"' )) {
125+ $updatedLines = foreach ($line in $updatedLines ) {
126+ if ($line -match ' ^(import\s+\{)([^}]+)(\}\s+from\s+"@fable-org/fable-library-ts/Util\.ts";)$' ) {
127+ " $ ( $Matches [1 ]) $ ( $Matches [2 ]) , Nullable $ ( $Matches [3 ]) "
128+ }
129+ else {
130+ $line
131+ }
132+ }
133+ }
134+
135+ if ($usesNonNullValue -and -not ($updatedLines -match ' import\s+\{[^}]*\bnonNullValue\b[^}]*\}\s+from\s+"@fable-org/fable-library-ts/Option\.ts"' )) {
136+ $updatedLines = foreach ($line in $updatedLines ) {
137+ if ($line -match ' ^(import\s+\{)([^}]+)(\}\s+from\s+"@fable-org/fable-library-ts/Option\.ts";)$' ) {
138+ " $ ( $Matches [1 ]) $ ( $Matches [2 ]) , nonNullValue $ ( $Matches [3 ]) "
139+ }
140+ else {
141+ $line
142+ }
143+ }
144+ }
145+
146+ Set-Content - Path $typescriptFile.FullName - Value $updatedLines
147+ }
148+ }
149+ }
150+
34151# Synopsis: Initialize folders and variables
35152Task Init {
36153 $trashFolder = Join-Path - Path . - ChildPath ' .trash'
@@ -238,6 +355,7 @@ Task PackNPM Build, Test, {
238355 $projectPath = Resolve-Path - Path ' src/Bridge/Bridge.fsproj'
239356
240357 Exec { dotnet fable $projectPath -- outDir $fableOutputArtifactsFolder -- language typescript -- fableLib @fable - org/ fable- library- ts }
358+ Update-FableNullableReferenceTypes - ProjectPath $projectPath - FableOutputPath $fableOutputArtifactsFolder
241359 Exec { npm version $nextVersion -- no- git- tag- version -- allow- same- version }
242360 Exec { npm install }
243361
0 commit comments