Files
vs-code-setup/scripts/dev-link.ps1
T
maxandClaude Fable 5.1 9085266dd3 Scripts work under pwsh on Linux and macOS
Home, VS Code user folder and CLI are resolved per platform (with
VSCODE_USER_DIR as an override); dev-link makes a symlink where a
junction is not available.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0169iPWwKHZoBTNN9qwXiwqk
2026-09-08 19:30:43 +02:00

114 lines
4.1 KiB
PowerShell

<#
.SYNOPSIS
Links the extension sources into VS Code's extensions folder for development.
.DESCRIPTION
Packaging is the wrong loop while you are actually changing code. VS Code loads any
folder under %USERPROFILE%\.vscode\extensions that has a package.json, so linking the
source there reduces the edit cycle to "npm run compile" plus a window reload.
The link is a directory junction rather than a symlink because junctions need no
elevation and no Developer Mode on Windows, and VS Code cannot tell the difference.
Run -Unlink before install.ps1: a linked source and an installed .vsix are two copies
of the same extension id, and VS Code will load one of them arbitrarily.
.PARAMETER Only
Restrict to these extension ids.
.PARAMETER Unlink
Remove the links.
.PARAMETER Compile
Run npm run compile in each extension first.
.EXAMPLE
.\scripts\dev-link.ps1 -Compile
.\scripts\dev-link.ps1 -Unlink
#>
[CmdletBinding()]
param(
[string[]] $Only,
[switch] $Unlink,
[switch] $Compile
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'common.ps1')
$config = Get-SetupConfig
$extensionsDir = Join-Path (Get-HomeDir) '.vscode/extensions'
if (-not (Test-Path $extensionsDir)) {
New-Item -ItemType Directory -Path $extensionsDir -Force | Out-Null
}
$extensions = $config.extensions
if ($Only) {
$extensions = $extensions | Where-Object { $Only -contains $_.id }
$missing = $Only | Where-Object { $config.extensions.id -notcontains $_ }
if ($missing) { throw "not in config.json: $($missing -join ', ')" }
}
foreach ($extension in $extensions) {
$source = Get-ExtensionPath $extension
$manifestPath = Join-Path $source 'package.json'
if (-not (Test-Path $manifestPath)) {
Write-Warning "no package.json in $source, skipping $($extension.id)"
continue
}
$manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json
$linkName = "$($manifest.publisher).$($manifest.name)-$($manifest.version)"
$link = Join-Path $extensionsDir $linkName
if ($Unlink) {
if (Test-Path $link) {
# Remove-Item on a junction deletes the link, not the target -- but only
# without -Recurse, which would walk into the source and delete the files.
[System.IO.Directory]::Delete($link)
Write-Host "unlinked $linkName" -ForegroundColor Green
} else {
Write-Host "$linkName was not linked" -ForegroundColor DarkGray
}
continue
}
if ($Compile) {
Push-Location $source
try {
Write-Host "compiling $($extension.id)..." -ForegroundColor DarkGray
if (-not (Test-Path (Join-Path $source 'node_modules'))) {
& npm install --silent --no-audit --no-fund
if ($LASTEXITCODE -ne 0) { throw "npm install failed for $($extension.id)" }
}
& npm run compile --silent
if ($LASTEXITCODE -ne 0) { throw "compile failed for $($extension.id)" }
} finally { Pop-Location }
}
$main = $manifest.main -replace '^\./', ''
if (-not (Test-Path (Join-Path $source $main))) {
Write-Warning ("$($extension.id) has no built output at $main -- it will fail to " +
"activate. Pass -Compile.")
}
if (Test-Path $link) {
$item = Get-Item $link -Force
$target = $item.Target
if ($target -and @($target)[0] -eq $source) {
Write-Host "$linkName already linked" -ForegroundColor DarkGray
continue
}
throw ("$link already exists and does not point at $source. Remove it by hand " +
"(it may be a real installed copy).")
}
# A junction needs no privilege on Windows; elsewhere a plain symlink does the same job.
$linkType = if (Test-IsWindows) { 'Junction' } else { 'SymbolicLink' }
New-Item -ItemType $linkType -Path $link -Target $source | Out-Null
Write-Host "linked $linkName -> $source" -ForegroundColor Green
}
Write-Host ''
Write-Host 'Reload the window (Developer: Reload Window) to pick this up.' -ForegroundColor Cyan