← ./articles-ja

node_modulesのjunctionはmklinkよりPowerShell New-Itemで作る

OneDrive配下のNode.jsプロジェクトで node_modules を外へ逃がすとき、よく出てくるのが mklink /J です。

ただし、スクリプト化するなら mklink よりPowerShellの New-Item -ItemType Junction を使うほうが安全です。

特にWindowsでは、パスに日本語、空白、ローカライズされたフォルダ名が入ることがあります。このとき cmd.exe 経由の mklink は、PowerShellとの境界で引用符や文字コードの問題を起こしやすくなります。

mklink はPowerShellのコマンドではなく、cmd.exe の組み込みコマンドです。

PowerShellから呼ぶと、こうなります。

cmd.exe /c "mklink /J `"$JunctionPath`" `"$TargetPath`""

この時点で、PowerShellの文字列、cmd.exeの解釈、引用符、エスケープが絡みます。パスに日本語が入ると、存在するはずのパスが壊れて渡り、The system cannot find the path specified のようなエラーになることがあります。

New-Itemを使う

PowerShellにはjunctionを作るためのcmdletがあります。

New-Item -ItemType Junction -Path $JunctionPath -Target $TargetPath

PowerShellの文字列をそのまま扱えるため、cmd.exe を挟むより安定します。スクリプトにするならこちらを標準にしたほうがよいです。

繰り返し実行できる形にする

実用では、junction作成を一度だけ実行するのではなく、何度実行しても壊れないscriptにします。

$ErrorActionPreference = "Stop"

$ProjectRoot = Split-Path -Parent $PSScriptRoot
$JunctionPath = Join-Path $ProjectRoot "node_modules"
$TargetPath = "<external-store>\my-project\node_modules"

New-Item -ItemType Directory -Path $TargetPath -Force | Out-Null

if (Test-Path $JunctionPath) {
  $item = Get-Item $JunctionPath -Force
  if ($item.LinkType -eq "Junction" -and $item.Target -eq $TargetPath) {
    Write-Host "node_modules junction already correct"
    exit 0
  }

  Remove-Item $JunctionPath -Recurse -Force
}

New-Item -ItemType Junction -Path $JunctionPath -Target $TargetPath | Out-Null
Write-Host "node_modules junction created"

このscriptは、junctionが正しければ何もしません。違う場所を指していたり、普通のフォルダになっていたりすれば作り直します。

npm scriptに組み込む

predevprebuild に入れておくと、開発やbuild前に自動確認できます。

{
  "scripts": {
    "predev": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\\ensure-node-modules.ps1",
    "prebuild": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\\ensure-node-modules.ps1"
  }
}

npm installがjunctionを普通のフォルダに置き換えることがあるため、毎回確認する価値があります。

まとめ

Windowsで node_modules junctionをスクリプト作成するなら、mklink /J より New-Item -ItemType Junction を優先してください。

理由は単純です。PowerShellの中で完結し、引用符と文字コードの事故が減るからです。特に日本語パスやOneDrive配下では、この差がそのまま安定性に出ます。

参考