$Source = "C:\sources\github\BIADemo"; $SourceBackEnd = $Source + "\DotNet" $SourceFrontEnd = $Source + "\Angular" $currentDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path $ExcludeDir = ('dist', 'node_modules', 'docs', 'scss', '.git', '.vscode', '.angular', '.dart_tool', 'bia-shared', 'bia-features', 'bia-domains', 'bia-core', '.bia') function ReplaceInProject { param ( [string]$Source, [string]$OldRegexp, [string]$NewRegexp, [string]$Include ) Write-Host "ReplaceInProject $OldRegexp by $NewRegexp"; #Write-Host $Source; #Write-Host $OldRegexp; #Write-Host $NewRegexp; #Write-Host $Filter; ReplaceInProjectRec -Source $Source -OldRegexp $OldRegexp -NewRegexp $NewRegexp -Include $Include } function ReplaceInProjectRec { param ( [string]$Source, [string]$OldRegexp, [string]$NewRegexp, [string]$Include ) foreach ($childDirectory in Get-ChildItem -Force -Path $Source -Directory -Exclude $ExcludeDir) { ReplaceInProjectRec -Source $childDirectory.FullName -OldRegexp $OldRegexp -NewRegexp $NewRegexp -Include $Include } Get-ChildItem -LiteralPath $Source -File -Filter $Include | ForEach-Object { $oldContent = [System.IO.File]::ReadAllText($_.FullName); $found = $oldContent | select-string -Pattern $OldRegexp if ($found.Matches) { $newContent = $oldContent -Replace $OldRegexp, $NewRegexp $match = $newContent -match '#capitalize#([a-z])' if ($match) { [string]$lower = $Matches[1] [string]$upper = $lower.ToUpper() [string]$newContent = $newContent -Replace "#capitalize#([a-z])", $upper } if ($oldContent -cne $newContent) { Write-Host " => " $_.FullName [System.IO.File]::WriteAllText($_.FullName, $newContent) } } } } function GetPresentationApiFolder { param ( [string]$SourceFolder ) $folderPath = Get-ChildItem -Path $SourceFolder -Recurse | Where-Object { $_.PSIsContainer -and $_.FullName.EndsWith("Presentation.Api") -and -not $_.FullName.StartsWith("BIA.") } if ($null -ne $folderPath -and $folderPath.Count -gt 0) { $folderPath = $folderPath[0].FullName.ToString() return $folderPath } else { return $null } } function InsertFunctionInClass() { param ( [string]$Source, [string]$MatchBegin, [string]$FunctionBody, [string]$ReplaceByExpr, [string]$NoMatchCondition, [string]$MatchCondition, [string[]]$ReplaceSeqences, [string[]]$ReplaceByMatch1 ) foreach ($childDirectory in Get-ChildItem -Force -Path $Source -Directory -Exclude $ExcludeDir) { InsertFunctionInClassRec -Source $childDirectory.FullName -MatchBegin $MatchBegin -NoMatchCondition $NoMatchCondition -FunctionBody $FunctionBody -ReplaceByExpr $ReplaceByExpr -MatchCondition $MatchCondition -ReplaceSeqences $ReplaceSeqences -ReplaceByMatch1 $ReplaceByMatch1 } } function InsertFunctionInClassRec() { param ( [string]$Source, [string]$MatchBegin, [string]$FunctionBody, [string]$ReplaceByExpr, [string]$NoMatchCondition, [string]$MatchCondition, [string[]]$ReplaceSeqences, [string[]]$ReplaceByMatch1 ) foreach ($childDirectory in Get-ChildItem -Force -Path $Source -Directory -Exclude $ExcludeDir) { InsertFunctionInClassRec -Source $childDirectory.FullName -MatchBegin $MatchBegin -NoMatchCondition $NoMatchCondition -FunctionBody $FunctionBody -ReplaceByExpr $ReplaceByExpr -MatchCondition $MatchCondition -ReplaceSeqences $ReplaceSeqences -ReplaceByMatch1 $ReplaceByMatch1 } $fichiersTypeScript = Get-ChildItem -Path $Source -Filter "*.ts" foreach ($fichier in $fichiersTypeScript) { $contenuFichier = Get-Content -Path $fichier.FullName -Raw # Vérifiez si la classe hérite de CrudItemService if ($contenuFichier -match $MatchBegin) { $nomClasse = $matches[1] if ($MatchCondition -eq "" -or $contenuFichier -match $MatchCondition) { # Vérifiez si les fonctions ne sont pas déjà présentes if ($contenuFichier -notmatch $NoMatchCondition) { # Utilisez une fonction pour trouver la position de la fermeture de la classe $positionFermetureClasse = TrouverPositionFermetureClasse $contenuFichier $MatchBegin $FunctionBodyRep = $FunctionBody; For ($i = 0; $i -lt $ReplaceSeqences.Length; $i++) { $ReplaceByMatch = $ReplaceByMatch1[$i] if ($contenuFichier -match $ReplaceByMatch) { $Match = $matches[1] Write-Host "Replacement found : $ReplaceByMatch : $Match" $FunctionBodyRep = $FunctionBodyRep.Replace($ReplaceSeqences[$i], $Match) } else { Write-Host "Replacement not found : $ReplaceByMatch" } } # Insérez les fonctions avant la fermeture de la classe $contenuFichier = $contenuFichier.Insert($positionFermetureClasse, $FunctionBodyRep + "`n") # Écrivez les modifications dans le fichier $contenuFichier | Set-Content -Path $fichier.FullName -NoNewline Write-Host "Fonctions ajoutées à la classe ou namespace $nomClasse dans le fichier $($fichier.FullName)" } } } } } # Fonction pour trouver la position de la fermeture de la classe function TrouverPositionFermetureClasse ($contenuFichier, $MatchBegin) { $nombreAccoladesOuvrantes = 0 $nombreAccoladesFermantes = 0 $index = 0 $trouveClasse = $false $positionFermeture = 0 # Parcourez le contenu du fichier ligne par ligne $contenuFichier -split "`n" | ForEach-Object { # Vérifiez si la ligne contient la déclaration de la classe if ($trouveClasse -eq $false -and $_ -match $MatchBegin) { $trouveClasse = $true } # Si la classe a été trouvée, mettez à jour les compteurs d'accolades if ($trouveClasse) { $nombreAccoladesOuvrantes += ($_ -split "{").Count - 1 $nombreAccoladesFermantes += ($_ -split "}").Count - 1 } # Si le nombre d'accolades fermantes est égal au nombre d'accolades ouvrantes # pour la classe en cours, retournez l'index actuel if ($trouveClasse -and $nombreAccoladesFermantes -gt 0 -and $nombreAccoladesFermantes -eq $nombreAccoladesOuvrantes -and $positionFermeture -eq 0) { $positionFermeture = $index } $index += $_.Length + 1 } # Retournez la dernière position si la classe n'a pas de fermeture explicite return $positionFermeture } function RemoveWebApiRepositoryFunctionsThirdParameter ($contenuFichier, $MatchBegin) { # Define the name of the base class $baseClassName = "WebApiRepository" # Define the regular expression patterns to match the function invocations $getAsyncPattern = 'this.GetAsync<([^,]+)>\s*\(([^,]+),\s*([^,]+),\s*([^)]+)(,\s*[^)]+)*\)' $deleteAsyncPattern = 'this.DeleteAsync<([^,]+)>\s*\(([^,]+),\s*([^,]+),\s*([^)]+)(,\s*[^)]+)*\)' $putAsyncPattern = 'this.PutAsync<([^,]+)>\s*\(([^,]+),\s*([^,]+),\s*([^)]+)(,\s*[^)]+)*\)' $putAsyncWithBodyPattern = 'this.PutAsync<([^,]+),([^,]+)>\s*\(([^,]+),\s*([^,]+),\s*([^)]+)(,\s*[^)]+)*\)' $postAsyncPattern = 'this.PostAsync<([^,]+)>\s*\(([^,]+),\s*([^,]+),\s*([^)]+)(,\s*[^)]+)*\)' $postAsyncWithBodyPattern = 'this.PostAsync<([^,]+),([^,]+)>\s*\(([^,]+),\s*([^,]+),\s*([^)]+)(,\s*[^)]+)*\)' $constructorPattern = 'public\s+(\w+)\s*\(([^)]*)\)\s*:\s*base\s*\(([^)]*)\)' $modifiedConstructorPattern = 'public\s+(\w+)\s*\(([^)]*)\)\s*:\s*base\s*\(([^)]*), new AuthenticationConfiguration\(\) { Mode = AuthenticationMode\.Token }\)' # Get all .cs files in the source directory $files = Get-ChildItem -Path $sourceDirectory -Recurse -Include *.cs foreach ($file in $files) { $content = Get-Content -Path $file.FullName -Raw # Check if the file contains the base class if ($content -match "class\s+\w+\s*:\s*$baseClassName") { # Replace the PostAsync function invocation $content = [regex]::Replace($content, $getAsyncPattern, 'this.GetAsync<$1>($2, $3$5)') # Replace the PostAsync function invocation $content = [regex]::Replace($content, $deleteAsyncPattern, 'this.DeleteAsync<$1>($2, $3$5)') # Replace the PostAsync function invocation $content = [regex]::Replace($content, $putAsyncPattern, 'this.PutAsync<$1>($2, $3$5)') # Replace the PostAsync function invocation $content = [regex]::Replace($content, $putAsyncWithBodyPattern, 'this.PutAsync<$1,$2>($3, $4$6)') # Replace the PostAsync function invocation $content = [regex]::Replace($content, $postAsyncPattern, 'this.PostAsync<$1>($2, $3$5)') # Replace the PostAsync function invocation $content = [regex]::Replace($content, $postAsyncWithBodyPattern, 'this.PostAsync<$1,$2>($3, $4$6)') # Check if the class overrides the GetBearerTokenAsync method if ($content -match "override\s+async\s+Task\s+GetBearerTokenAsync\s*\(" -and $content -notmatch $modifiedConstructorPattern) { # Replace the constructor to add the new parameter to the base() call $content = [regex]::Replace($content, $constructorPattern, 'public $1($2) : base($3, new AuthenticationConfiguration() { Mode = AuthenticationMode.Token })') } # Write the modified content back to the file Set-Content -Path $file.FullName -Value $content Write-Host "Modified file: $($file.FullName)" } } } function ApplyChangesAngular19 { Write-Host "[Apply Angular 19 changes]" $replacementsTS = @( @{Pattern = "import { PrimeNGConfig } from 'primeng/api'"; Replacement = "import { PrimeNG } from 'primeng/config'"}, @{Pattern = "PrimeNGConfig"; Replacement = "PrimeNG"}, @{Pattern = "InputTextareaModule"; Replacement = "Textarea"}, @{Pattern = "primeng/tristatecheckbox"; Replacement = "primeng/checkbox"}, @{Pattern = "TriStateCheckboxModule"; Replacement = "Checkbox"}, @{Pattern = "\bMessage\b"; Replacement = "ToastMessageOptions"}, @{Pattern = "primeng/calendar"; Replacement = "primeng/datepicker"}, @{Pattern = "\bCalendar\b"; Replacement = "DatePicker"}, @{Pattern = "primeng/dropdown"; Replacement = "primeng/select"}, @{Pattern = "DropdownModule"; Replacement = "SelectModule"}, @{Pattern = "primeng/tabview"; Replacement = "primeng/tabs"}, @{Pattern = "TabViewModule"; Replacement = "TabsModule"}, @{Pattern = "primeng/inputswitch"; Replacement = "primeng/toggleswitch"}, @{Pattern = "InputSwitchModule"; Replacement = "ToggleSwitchModule"}, @{Pattern = "primeng/overlaypanel"; Replacement = "primeng/popover"}, @{Pattern = "OverlayPanelModule"; Replacement = "PopoverModule"}, @{Pattern = "primeng/sidebar"; Replacement = "primeng/drawer"}, @{Pattern = "SidebarModule"; Replacement = "DrawerModule"}, @{Pattern = "\bKeycloakEvent\b"; Replacement = "KeycloakEventLegacy"}, @{Pattern = "\bKeycloakEventType\b"; Replacement = "KeycloakEventTypeLegacy"}, @{Pattern = "\bCalendarModule\b"; Replacement = "DatePickerModule"}, @{Pattern = "InputSwitchChangeEvent"; Replacement = "ToggleSwitchChangeEvent"} ) $replacementsHTML = @( @{Pattern = "(p-autoComplete[^>]*?)\[\s*size\s*\]=\s*""[^""]*"""; Replacement = "`$1size=""small"""}, @{Pattern = "(button[^>]*?(?:pButton|p-button)[^>]*?)\s+severity=""warning"""; Replacement = "`$1severity=""warn"""}, @{Pattern = "(.*?)"; Replacement = "`$1"}, @{Pattern = "(?s)]*)>\s*(.*?)<\/h[1-6]>"; Replacement = ""}, @{Pattern = '(?s)<(\w+)(\s+[^>]*class="[^"]*p-float-label[^"]*"[^>]*)>((?:[^<]+|<(?!\/?\1\b)|<\1\b[^>]*>(?:[^<]+|<(?!\/?\1\b)|<\1\b[^>]*>(?:[^<]+|<(?!\/?\1\b))*<\/\1\s*>)*<\/\1\s*>)*)<\/\1\s*>'; Replacement = '$3'}, @{Pattern = '(?s)]*class="[^"]*p-link[^"]*"[^>]*)>(.*?)<\/button'; Replacement = '$2"}, @{Pattern = '(?s)(]*?)\s+label="([^"]+)"([^>]*?>)'; Replacement = '${1}${3}'} ) $extensions = "*.ts", "*.html", "*.scss" Get-ChildItem -Path $SourceFrontEnd -Recurse -Include $extensions| Where-Object { $excluded = $false foreach ($exclude in $ExcludeDir) { if ($_.FullName -match [regex]::Escape("\$exclude\")) { $excluded = $true break } } -not $excluded } | ForEach-Object { $content = Get-Content $_.FullName -Raw $fileModified = $false $fileReplacements = @() foreach ($rule in $replacementsTS + $replacementsHTML) { $newContent = $content -creplace $rule.Pattern, $rule.Replacement if ($newContent -cne $content) { $content = $newContent $fileModified = $true $fileReplacements += " => replaced $($rule.Pattern) by $($rule.Replacement)" } } if ($fileModified) { Write-Host $_.FullName -ForegroundColor Green $fileReplacements | ForEach-Object { Write-Host $_ -ForegroundColor Yellow } [System.IO.File]::WriteAllText($_.FullName, $content, [System.Text.Encoding]::UTF8) } } } function ChangeImportToUseBiaVariables( [string]$Source, [string]$Include) { $importPattern = "@import( '[\S]*?bia-variables';)" $replaceImportPattern = '@use$1' $OldRegexp = "(\`$bia[Red|Orange|Blue|Green|Yellow])" $NewRegexp = 'bia-variables.$1' foreach ($childDirectory in Get-ChildItem -Force -Path $Source -Directory -Exclude $ExcludeDir) { ChangeImportToUseBiaVariables -Source $childDirectory.FullName -OldRegexp $OldRegexp -NewRegexp $NewRegexp -Include $Include } Get-ChildItem -LiteralPath $Source -File -Filter $Include | ForEach-Object { $oldContent = [System.IO.File]::ReadAllText($_.FullName); if ($oldContent -match $importPattern) { $found = $oldContent | select-string -Pattern $OldRegexp if ($found.Matches) { $newContent = $oldContent -Replace $OldRegexp, $NewRegexp $match = $newContent -match '#capitalize#([a-z])' if ($match) { [string]$lower = $Matches[1] [string]$upper = $lower.ToUpper() [string]$newContent = $newContent -Replace "#capitalize#([a-z])", $upper } $newContent = $newContent -Replace $importPattern, $replaceImportPattern if ($oldContent -cne $newContent) { Write-Host $_.FullName -ForegroundColor Green Write-Host " => " $_.FullName [System.IO.File]::WriteAllText($_.FullName, $newContent) } } } } } # FRONT END ApplyChangesAngular19 # New DTO location ReplaceInProject -Source $SourceFrontEnd -OldRegexp "bia-shared/model/base-dto';" -NewRegexp "bia-shared/model/dto/base-dto';" -Include *.ts ReplaceInProject -Source $SourceFrontEnd -OldRegexp "bia-shared/model/base-team-dto';" -NewRegexp "bia-shared/model/dto/base-team-dto';" -Include *.ts # BEGIN - filterWithDisplay for options ReplaceInProject -Source $SourceFrontEnd -OldRegexp '(type: (?:PropType\.OneToMany|PropType\.ManyToMany))(?!,[\s]*?filterWithDisplay:)' -NewRegexp '$1, filterWithDisplay: true' -Include *.ts # END - filterWithDisplay for options # BACK END # BEGIN - Base Mapper ReplaceInProject -Source $SourceBackEnd -OldRegexp "public override void DtoToEntity\(([\w]*)Dto dto, ([\w]*) entity(, .*)?\)" -NewRegexp 'public override void DtoToEntity($1Dto dto, ref $2 entity$3)' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "\.DtoToEntity\(dto, entity(, .*)?\);" -NewRegexp '.DtoToEntity(dto, ref entity$1);' -Include *.cs # END - Base Mapper # BEGIN - CrudItemService Injector ReplaceInProject -Source $SourceFrontEnd -OldRegexp "(public optionsService: .*OptionsService,)" -NewRegexp '$1protected injector: Injector,' -Include *service.ts ReplaceInProject -Source $SourceFrontEnd -OldRegexp "super\(dasService, signalRService, optionsService\);" -NewRegexp 'super(dasService, signalRService, optionsService, injector);' -Include *service.ts ReplaceInProject -Source $SourceFrontEnd -OldRegexp "import \{ Injectable \} from '@angular/core';" -NewRegexp 'import { Injectable, Injector } from ''@angular/core'';' -Include *service.ts # END - CrudItemService Injector # BEGIN - AdditionalInfos modifications ReplaceInProject -Source $SourceFrontEnd -OldRegexp "additionalInfos\.userInfo\.id" -NewRegexp 'decryptedToken.id' -Include *.ts ReplaceInProject -Source $SourceFrontEnd -OldRegexp "additionalInfos\.userInfo\.login" -NewRegexp 'decryptedToken.identityKey' -Include *.ts ReplaceInProject -Source $SourceFrontEnd -OldRegexp "additionalInfos\.userInfo" -NewRegexp 'decryptedToken.userData' -Include *.ts # END - AdditionalInfos modifications # BEGIN - BaseEntity ReplaceInProject -Source $SourceBackEnd -OldRegexp ": VersionedTable, IEntity<" -NewRegexp ': BaseEntityVersioned<' -Include *.cs # TODO verify in a V4 project with archiving: ReplaceInProject -Source $SourceBackEnd -OldRegexp ": VersionedTable, IEntityArchivable<" -NewRegexp ': BaseEntityVersionedArchivable<' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp ": VersionedTable, IEntityFixable<" -NewRegexp ': BaseEntityVersionedFixable<' -Include *.cs # END - BaseEntity # BEGIN - TeamDto in BaseDtoVersionedTeam ReplaceInProject -Source $SourceBackEnd -OldRegexp "(\W|^)TeamDto(\W|$)" -NewRegexp '$1BaseDtoVersionedTeam$2' -Include *.cs # END - TeamDto in BaseDtoVersionedTeam # BEGIN - TeamDto in BaseTeamMapper ReplaceInProject -Source $SourceBackEnd -OldRegexp "TTeamMapper<" -NewRegexp 'BaseTeamMapper<' -Include *.cs # END - TeamDto in BaseTeamMapper # BEGIN - TeamTypeId => BiaTeamTypeId ReplaceInProject -Source $SourceBackEnd -OldRegexp "(\W|^)TeamTypeId\.All(\W|$)" -NewRegexp '$1BiaTeamTypeId.All$2' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "(\W|^)TeamTypeId\.Root(\W|$)" -NewRegexp '$1BiaTeamTypeId.Root$2' -Include *.cs # END - TeamTypeId => BiaTeamTypeId # BEGIN - Team in BaseEntityTeam ReplaceInProject -Source $SourceBackEnd -OldRegexp ": Team\n" -NewRegexp ': BaseEntityTeam\n' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp ": Team," -NewRegexp ': BaseEntityTeam,' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp ", Team>" -NewRegexp ', BaseEntityTeam>' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "" -NewRegexp '' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp ", Team," -NewRegexp ', BaseEntityTeam,' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "\(Team\)" -NewRegexp '(BaseEntityTeam)' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "virtual Team " -NewRegexp 'virtual BaseEntityTeam ' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "public Team " -NewRegexp 'public BaseEntityTeam ' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "protected Team " -NewRegexp 'public BaseEntityTeam ' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "private Team " -NewRegexp 'private BaseEntityTeam ' -Include *. ReplaceInProject -Source $SourceBackEnd -OldRegexp "override Team " -NewRegexp 'override BaseEntityTeam ' -Include *.cs # END - Team in BaseEntityTeam # BEGIN - RoleId => BiaRoleId ReplaceInProject -Source $SourceBackEnd -OldRegexp "(\W|^)RoleId\.Admin(\W|$)" -NewRegexp '$1BiaRoleId.Admin$2' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "(\W|^)RoleId\.BackAdmin(\W|$)" -NewRegexp '$1BiaRoleId.BackAdmin$2' -Include *.cs ReplaceInProject -Source $SourceBackEnd -OldRegexp "(\W|^)RoleId\.BackReadOnly(\W|$)" -NewRegexp '$1BiaRoleId.BackReadOnly$2' -Include *.cs # END - RoleId => BiaRoleId # BEGIN - pFrozenColumn => biaFrozenColumn ReplaceInProject -Source $SourceFrontEnd -OldRegexp 'pFrozenColumn' -NewRegexp 'biaFrozenColumn' -Include *.ts ReplaceInProject -Source $SourceFrontEnd -OldRegexp 'pFrozenColumn' -NewRegexp 'biaFrozenColumn' -Include *.html # END - pFrozenColumn => biaFrozenColumn # BEGIN - uncryptedToken => decryptedToken ReplaceInProject -Source $SourceFrontEnd -OldRegexp 'uncryptedToken' -NewRegexp 'decryptedToken' -Include *.ts # END - uncryptedToken => decryptedToken # BEGIN - SaveItemAsFlatTextCompressedAsync => CreateArchiveAsync ReplaceInProject -Source $SourceBackEnd -OldRegexp 'SaveItemAsFlatTextCompressedAsync' -NewRegexp 'CreateArchiveAsync' -Include *.cs # END - SaveItemAsFlatTextCompressedAsync => CreateArchiveAsync # BEGIN - Service provider CrudItemService IndexComponent ReplaceInProject ` -Source $SourceFrontEnd -OldRegexp "(?s)\s*(@Component\(\s*{.*?imports\s*:\s*\[[^\]]*?\][^}]*)(}\s*\)\s*export\s+class\s+\w+\s+extends\s+CrudItemsIndexComponent<(\w+)>)" -NewRegexp "`nimport { CrudItemService } from 'src/app/shared/bia-shared/feature-templates/crud-items/services/crud-item.service';`n`n`$1 providers: [{ provide: CrudItemService, useExisting: `$3Service }],`n`$2" -Include "*-index.component.ts" # END - Service provider CrudItemService IndexComponent # BEGIN - Add formReadOnlyMode binding form component ReplaceInProject ` -Source $SourceFrontEnd -OldRegexp "( (cancelled)= ReplaceInProject ` -Source $SourceFrontEnd -OldRegexp "\(cancel\)=" -NewRegexp "(cancelled)=" -Include "*.html" # END - (cancel)= -> (cancelled)= # BEGIN - Change use of bia-variables @import in scss to @use ChangeImportToUseBiaVariables -Source $SourceFrontEnd -Include *.scss # END - Change use of bia-variables @import in scss to @use # Front end migration conclusion $standaloneCatchUpScript = "standalone-catch-up.js" Copy-Item "$currentDirectory\$standaloneCatchUpScript" "$SourceFrontEnd\$standaloneCatchUpScript" Set-Location $SourceFrontEnd node $standaloneCatchUpScript Remove-Item "$SourceFrontEnd\$standaloneCatchUpScript" # FRONT END # Set-Location $SourceFrontEnd # npm run clean # BACK END # Set-Location $SourceBackEnd # dotnet restore --no-cache Write-Host "Finish" pause