Functions/Workspace/Update-Workspace.ps1

<#
    .SYNOPSIS
        Update the workspace configuration for Visual Studio Code which is used
        by the extension Project Manager.
 
    .DESCRIPTION
        By default the path $HOME\Workspace is used to prepare the project list.
        It's possible to add multiple workspace paths to the configuration.
 
        The module dynamically detects if a subfolder of the workspace is a git
        repository or not. If not, it will recurse for one more level and add
        the following folders as projects.
 
        All .code-workspace files in the root of the path or in the .vscode
        subfolder are used for grouped Visual Studio Code workspaces for the
        vscode-open-project extension.
 
    .EXAMPLE
        PS C:\> Update-Workspace
        Update the workspace using the default paths.
 
    .LINK
        https://github.com/claudiospizzi/ProfileFever
        https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager
#>

function Update-Workspace
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param
    (
        # Path to the workspace. $HOME\Workspace is used by default.
        [Parameter(Mandatory = $false)]
        [ValidateScript({Test-Path -Path $_})]
        [System.String[]]
        $Path = "$HOME\Workspace",

        # Path to the JSON config file of the Project Manager extension.
        [Parameter(Mandatory = $false)]
        [System.String]
        $ProjectManagerPath = "$Env:AppData\Code\User\globalStorage\alefragnani.project-manager\projects.json",

        # Specify the mode for orphaned projects. By default, the orphaned
        # projects are disabled.
        [Parameter(Mandatory = $false)]
        [ValidateSet('Disable', 'Remove')]
        [System.String]
        $OrphanedProjectMode = 'Disable'
    )

    try
    {
        if (Test-Path -Path $ProjectManagerPath)
        {
            $config = @(Get-Content -Path $ProjectManagerPath -Encoding 'UTF8' | ConvertFrom-Json)
        }
        else
        {
            $config = @()
        }

        foreach ($currentPath in $Path)
        {
            $currentPath = Get-Item -Path $currentPath

            $detectedPaths = @()

            # Add VS Code workspaces as projects
            $detectedPaths +=
                Get-ChildItem -Path $currentPath -Filter '*.code-workspace' -File |
                    Select-Object @{ N = 'BaseName'; E = { "Workspace $($_.BaseName)" } }, 'FullName', @{ N = 'Tags'; E = { @($currentPath.BaseName) } }
            if (Test-Path -Path "$currentPath\.vscode")
            {
                $detectedPaths +=
                    Get-ChildItem -Path "$currentPath\.vscode" -Filter '*.code-workspace' -File |
                        Select-Object @{ N = 'BaseName'; E = { "Workspace $($_.BaseName)" } }, 'FullName', @{ N = 'Tags'; E = { @($currentPath.BaseName) } }
            }

            # Add root git folders as projects
            $detectedPaths +=
                Get-ChildItem -Path $currentPath.FullName -Directory |
                    Where-Object { Test-Path -Path "$($_.FullName)\.git" } |
                        Select-Object 'BaseName', 'FullName', @{ N = 'Tags'; E = { @($currentPath.BaseName) } }

            # Add child git folders as projects
            $detectedPaths +=
                Get-ChildItem -Path $currentPath.FullName -Directory |
                    Where-Object { -not (Test-Path -Path "$($_.FullName)\.git") } |
                        ForEach-Object {
                            $childPath = $_
                            Get-ChildItem -Path $childPath.FullName -Directory |
                                Select-Object 'BaseName', 'FullName', @{ N = 'Tags'; E = { @($currentPath.BaseName, $childPath.BaseName) } }
                        }

            foreach ($detectedPath in $detectedPaths)
            {
                # Add a new project if it does not exist
                if ($config.Count -eq 0 -or $config.rootPath -notcontains $detectedPath.FullName)
                {
                    $config += [PSCustomObject] @{
                        name     = $detectedPath.BaseName
                        rootPath = $detectedPath.FullName
                        paths    = @()
                        tags     = @($detectedPath.Tags -join ' / ')
                        enabled  = $true
                    }
                }
                else
                {
                    # Search for the existing entry and patch the details.
                    for ($i = 0; $i -lt $config.Count; $i++)
                    {
                        if ($config[$i].rootPath -eq $detectedPath.FullName)
                        {
                            $config[$i] = [PSCustomObject] @{
                                name     = $detectedPath.BaseName
                                rootPath = $detectedPath.FullName
                                paths    = @()
                                tags     = @($detectedPath.Tags -join ' / ')
                                enabled  = $true
                            }
                            break
                        }
                    }
                }
            }
        }

        # Remove or disable all projects not existing on the file system.
        switch ($OrphanedProjectMode)
        {
            'Disable'
            {
                for ($i = 0; $i -lt $config.Count; $i++)
                {
                    if (-not (Test-Path -Path $config[$i].rootPath))
                    {
                        $config[$i].enabled = $false
                    }
                }
            }
            'Remove'
            {
                $config = @($config | Where-Object { Test-Path -Path $_.rootPath })
            }
        }

        # Use the .NET class to write the JSON file to prevent writing the UTF-8
        # BOM header, as the Project Manager is not able to read the profile
        # file with an UTF-8 BOM header.
        if ($PSCmdlet.ShouldProcess($ProjectManagerPath, 'Update the Project Manager workspace configuration.'))
        {
            [System.IO.File]::WriteAllLines($ProjectManagerPath, ($config | ConvertTo-Json))
        }
    }
    catch
    {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}