PowerShell

【Node.js】一定期間更新無しプロジェクトのnode_modulesを削除する

更新日:2026/01/19

最近はReact等のライブラリやExpressなどのフレームワークを使うことが増えてきました。
これらはプログラム開発を効率化してくれるのでとても有難いです。
ですがプロジェクト単位でローカルにダウンロードするため、プロジェクトが増えるたびにファイルサイズが増大します。
そして、ディスク容量を圧迫します。

最初はファイルサイズは微々たるものなので放置していることが多いです。
でもOSから警告が出るレベルになってくるとプロジェクト数が多くなっていて、一つづつ削除するのは現実的でなくなっているでしょう。

そこで今回は、一定期間更新していないプロジェクトのnode_modulesを削除するスクリプトを作成しました。

 

実行環境

今回はPowerShellスクリプトで、node_modulesを削除します。
またPowerShellスクリプト呼び出しをWindowsのバッチファイルで行っています。

PowerShellはWindows以外でも利用可能ですが、パスの除外処理をおこなっているため今回作成したPowerShellスクリプトはWindows専用です。
※PowerShell7以降はクロスプラットフォーム対応されているので共通コード化できますが、今回は古いPowerShellでも実行させます。

 

ファイル構成

今回は、PowerShellスクリプトを一つ、PowerShellスクリプトを呼び出すWindowsバッチファイルを一つ作成しました。

cleanup_node_modules
 ┣━ check_node_modules.bat
 ┣━ cleanup_node_modules.bat
 ┗━ cleanup_node_modules.ps1

cleanup_node_modules.ps1

PowerShellスクリプトの本体

check_node_modules.bat

削除対象のnode_modulesを表示するWindowsバッチファイル。
削除は行わない。

cleanup_node_modules.bat

削除対象のnode_modulesを削除するWindowsバッチファイル。

 

スクリプトの仕様

PowerShellスクリプトは、次の引数を受け付けます。

-delete

削除モードにする。この引数が指定されていないときは、確認モードとする。

-d 数値

削除対象の経過日数
デフォルトは14
0以下の場合はエラー

-f 文字列

検索を行うフォルダを指定
デフォルトは現在のフォルダ

-force

削除モード時、確認しないで削除

-h

ヘルプ表示(引数一覧を表示)

 

PowerShellスクリプトのコード

cleanup_node_modules.ps1

[CmdletBinding()]
param(
    [switch]$delete,
    [Nullable[int]]$d,
    [string]$f,
    [switch]$h,
    [switch]$force
)

if( $h ){
    Write-Host @"
引数:
-delete 削除モード
-d 対象日数(1以上)デフォルト:14
-f 対象フォルダ デフォルト:現在のフォルダ
-force 確認しないで削除
-h ヘルプ表示
"@
exit
}

# パラメーターチェック

# -f 対象フォルダ
if ([string]::IsNullOrWhiteSpace($f)) {
    $f = (Get-Location).Path
}

if (-not (Test-Path $f -PathType Container)) {
    Write-Error "フォルダが存在しません: $f"
    exit 1
}

# -d 対象日数
if ($null -eq $d) {
    $d = 14
}
if( $d -lt 1 ){
    Write-Error "対象日数は1以上の数値を指定してください"
    exit 1
}

# 現在日時から削除対象日時を算出
$limit = (Get-Date).AddDays(-1 * $d)

if ($delete) {
    Write-Host "[削除モード]"
}else{
    Write-Host "[確認モード]"
}

Write-Host "検索日数:$($d)"
Write-Host "検索対象:$($limit)より前のnode_modules"
Write-Host "検索フォルダ:$($f)"

$targets = @()

# 検索対象除外設定 node_modules と .から始まるフォルダ
# (Windows パス前提)
$excludePattern = '\\(node_modules)|\\\.[^\\]+\\'

<#
 ビルド結果を除外する場合
 $excludePattern = '\\(node_modules|dist|build|out)|\\\.[^\\]+\\'
#>

# 削除対象 node_modules を検索して $targets にセット
Get-ChildItem $f -Recurse -Directory -Filter node_modules | ForEach-Object {

    $nodeModules = $_
    $projectRoot = $_.Parent.FullName

    # プロジェクトフォルダ内で最終に更新したファイルの日付を取得
    $latest = Get-ChildItem $projectRoot -Recurse -File |
        Where-Object {
            $_.FullName -notmatch $excludePattern
        } |
        Sort-Object LastWriteTime -Descending |
        Select-Object -First 1

    if (-not $latest) {
        return
    }

    if ($latest.LastWriteTime -lt $limit) {
        $targets += [PSCustomObject]@{
            Path       = $nodeModules.FullName
            LastUpdate = $latest.LastWriteTime
        }
    }
}

if ($targets.Count -eq 0) {
    Write-Host "削除対象はありません。"
    return
}

# 検索結果の表示
Write-Host "`n削除対象の node_modules:"

$targets | ForEach-Object {
    Write-Host "`n* $($_.Path)"
    Write-Host "  プロジェクトの最終更新日: $($_.LastUpdate)"
}

if (!$delete) {
    exit
}

# 削除のユーザー確認
$answer = "y";
if( !$force ){
    Write-Host ""
    $answer = Read-Host "これらを削除しますか? (y/N)"
}

if ($answer -notmatch '^[Yy]$') {
    Write-Host "削除をキャンセルしました。"
    exit
}

# 削除
foreach ($t in $targets) {
    Remove-Item $t.Path -Recurse -Force
}

Write-Host "`n削除が完了しました。"


最終更新日の検査対象フォルダから、node_modulesフォルダと . から始まるフォルダを除外しています。

除外対象を正規表現で指定

$excludePattern = '\\(node_modules)|\\\.[^\\]+\\'

ビルド結果を対象から除外していないのは、『コードが更新されていなくてもビルドしているなら近日中に使用する可能性が高い』と判断したからです。
異論は認めます...

なお、上記コードはWindows前提の正規表現です。
PowerShell 7なら、次のように記述することでクロスプラットフォーム対応できます。

$excludePattern = '[\\/](node_modules)[\\/]|[\\/]\.[^\\/]+[\\/]'

 

PowerShellスクリプトを呼び出すバッチファイル

check_node_modules.batは、削除対象node_modules確認用バッチファイルです。

check_node_modules.bat

@echo off
chcp 65001 > nul
setlocal
set "script=%~dp0cleanup_node_modules.ps1"

where pwsh.exe >nul 2>nul
if %errorlevel%==0 (
    set "cmd=pwsh"
) else (
    set "cmd=powershell"
)

%cmd% -NoProfile -ExecutionPolicy Bypass -File %script% %*
endlocal

このバッチファイルに指定された引数は、そのままPowerShellスクリプトに送っています。

また、バージョン6以降以降のPowerShellはコマンド名がpwsh、それより前はpowershellです。
そのためwhereコマンドでpwsh.exeを検索して、実行するコマンドを特定しています。

cleanup_node_modules.batはcheck_node_modules.batとほとんど同じですが、PowerShellスクリプトに -delete 引数を渡している点だけ異なります。

cleanup_node_modules.bat

@echo off
chcp 65001 > nul
setlocal
set "script=%~dp0cleanup_node_modules.ps1"

where pwsh.exe >nul 2>nul
if %errorlevel%==0 (
    set "cmd=pwsh"
) else (
    set "cmd=powershell"
)

%cmd% -NoProfile -ExecutionPolicy Bypass -File %script% -delete %*
endlocal

更新日:2026/01/19

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。