はじめに
こんにちは、Never3924です。
NodeのSingle executable applicationsを使って簡単にビルドができるようにしよう、という記事です。
環境
NodeJSのv20.1.2
Windows 10
で動作確認。
本題
早速やりましょう。ていうか以下のコードをコピペです。
@echo off
setlocal enabledelayedexpansion
chcp 65001
mkdir dist
cd dist
set CALLPOWERSHELL_RETURN="";
echo Collecting information...
call :CALLPOWERSHELL "$text = Get-Content -Path "../package.json" -Raw; $pattern = '\s+\"main\"\s?:\s?\"(.*)\"'; if($text -match $pattern) { $filename = $matches[1] };echo $filename"
set FILENAME=%CALLPOWERSHELL_RETURN%
call :CALLPOWERSHELL "$text = Get-Content -Path "../package.json" -Raw; $pattern = '\s+\"main\"\s?:\s?\"(.*)\..+\"'; if($text -match $pattern) { $filename = $matches[1] }; echo $filename"
set FILENAMEONLY=%CALLPOWERSHELL_RETURN%
call :CALLPOWERSHELL "$signtoolPath = Get-Command signtool -ErrorAction SilentlyContinue; if ($signtoolPath) {echo True} else {echo False}"
set ISINSTALL_SIGNTOOL=%CALLPOWERSHELL_RETURN%
call :CALLPOWERSHELL "$npm = npm list --depth=0; $foundEsbuild = $false; foreach ($line in $npm.Split(\"`n\")) {if ($line -match \"esbuild\") {$foundEsbuild = $true; break}}; echo $foundEsbuild"
set ISINSTALL_ESBUILD=%CALLPOWERSHELL_RETURN%
if not "%~1"=="" (
set FILENAME=%~1
call :CALLPOWERSHELL "[System.IO.Path]::GetFileNameWithoutExtension(\"!FILENAME!\")"
set FILENAMEONLY=!CALLPOWERSHELL_RETURN!
if "%2"=="debug" (
echo !CALLPOWERSHELL_RETURN!
)
)
set OUTPUTNAME=!FILENAMEONLY!.exe
if "%2"=="debug" (
echo !FILENAME!
echo !FILENAMEONLY!
echo %ISINSTALL_SIGNTOOL%
echo %ISINSTALL_ESBUILD%
echo !OUTPUTNAME!
)
echo Collection has been completed.
if %ISINSTALL_SIGNTOOL%==False (
echo The signature tool is not installed.
echo Skip the signature processing.
)
if %ISINSTALL_ESBUILD%==False (
echo esbuild is not installed.
echo installing...
echo npm i -D esbuild > tmp.bat
call tmp.bat
)
echo Bundle the js file.
echo @echo off > tmp.bat
echo %~dp0node_modules\.bin\esbuild !FILENAME! --bundle --outfile=./dist/out.js --platform=node --minify=true >> tmp.bat
echo exit /b >> tmp.bat
cd ../
call ./dist/tmp.bat
cd dist
echo Prepare for the build.
echo { "main": "out.js", "output": "sea-prep.blob" } > sea-config.json
node --experimental-sea-config sea-config.json
echo Copy the node.
node -e "require('fs').copyFileSync(process.execPath, '!OUTPUTNAME!')"
if %ISINSTALL_SIGNTOOL%==True (
echo I will verify the signature.
echo Temporarily remove the signature.
signtool remove /s !OUTPUTNAME!
echo The signature is complete.
)
echo Starting the build.
echo npx postject !OUTPUTNAME! NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 > tmp.bat
call tmp.bat
if %ISINSTALL_SIGNTOOL%==True (
echo I will sign.
signtool sign /fd SHA256 !OUTPUTNAME!
echo The signature is complete.
)
echo Build has finished.
echo Delete the generated file.
del sea-config.json > nul
del sea-prep.blob > nul
del out.js > nul
del tmp.bat > nul
if %ISINSTALL_ESBUILD%==False (
echo uninstall esbuild
echo npm uninstall esbuild > tmp.bat
call tmp.bat
)
if "%2"=="debug" pause
goto :EOF
:CALLPOWERSHELL
echo @echo off > tmp.bat
echo powershell -Command %1 >> tmp.bat
echo exit /b >> tmp.bat
for /f "usebackq" %%A in (`call tmp.bat`) do set CALLPOWERSHELL_RETURN=%%A
exit /b
:EOF
ながーい。予定の2倍ぐらいデカくなった。
なんかハイライトが変ですが気にしないでください
(2024/11/12 18:24 コード変更 npmでesbuildをインストール後、終了してしまう問題の修正)
package.jsonにbuildコマンドを追加しておくと楽ですね。
{
...
"scripts": {
"build": "build.bat",
},
...
}
使い方
基本build.bat
をコマンドラインで実行すればメインファイルを読み出してビルドしてくれます。
事前にインストールしておくべきものはありません。自動で判断してインストールしてくれます。
インストールしたものは最後にアンインストールされます。
また、JSファイルをD&Dでもビルドしてくれます。
解説
@echo off
setlocal enabledelayedexpansion
chcp 65001
言うまでもないですね。ログを出さない、変数をローカルに、UTF-8を使うの三点セットです。
しかし遅延変数が使えるようにしてあります
mkdir dist
cd dist
出力場所を作成しています
set CALLPOWERSHELL_RETURN="";
echo Collecting information...
call :CALLPOWERSHELL "$text = Get-Content -Path "../package.json" -Raw; $pattern = '\s+\"main\"\s?:\s?\"(.*)\"'; if($text -match $pattern) { $filename = $matches[1] };echo $filename"
set FILENAME=%CALLPOWERSHELL_RETURN%
call :CALLPOWERSHELL "$text = Get-Content -Path "../package.json" -Raw; $pattern = '\s+\"main\"\s?:\s?\"(.*)\..+\"'; if($text -match $pattern) { $filename = $matches[1] }; echo $filename"
set FILENAMEONLY=%CALLPOWERSHELL_RETURN%
call :CALLPOWERSHELL "$signtoolPath = Get-Command signtool -ErrorAction SilentlyContinue; if ($signtoolPath) {echo True} else {echo False}"
set ISINSTALL_SIGNTOOL=%CALLPOWERSHELL_RETURN%
call :CALLPOWERSHELL "$npm = npm list --depth=0; $foundEsbuild = $false; foreach ($line in $npm.Split(\"`n\")) {if ($line -match \"esbuild\") {$foundEsbuild = $true; break}}; echo $foundEsbuild"
set ISINSTALL_ESBUILD=%CALLPOWERSHELL_RETURN%
powershellのコマンドを実行して情報を取得しています
取得情報は
・対象のファイルパス
・対象の拡張子抜きのファイル名
・signtoolがインストールされているか
・esbuildがインストールされているか
if not "%~1"=="" (
set FILENAME=%~1
call :CALLPOWERSHELL "[System.IO.Path]::GetFileNameWithoutExtension(\"!FILENAME!\")"
set FILENAMEONLY=!CALLPOWERSHELL_RETURN!
if "%2"=="debug" (
echo !CALLPOWERSHELL_RETURN!
)
)
ファイルを指定する引数があったらファイルパスとファイル名を更新。
if %ISINSTALL_SIGNTOOL%==False (
echo The signature tool is not installed.
echo Skip the signature processing.
)
if %ISINSTALL_ESBUILD%==False (
echo esbuild is not installed.
echo installing...
npm i -D esbuild
)
signtoolがなかったら署名をスキップする旨のメッセージを出す。
esbuildがインストールされてなかったらインストールする。
echo @echo off > tmp.bat
echo %~dp0node_modules\.bin\esbuild !FILENAME! --bundle --outfile=./dist/out.js --platform=node --minify=true >> tmp.bat
echo exit /b >> tmp.bat
cd ../
call ./dist/tmp.bat
cd dist
jsをバンドルする。勝手に処理を終了するので、別ファイルに書き込み。
以下ほぼチュートリアル通り。
echo { "main": "out.js", "output": "sea-prep.blob" } > sea-config.json
node --experimental-sea-config sea-config.json
sea-config.json
を作ってsea-prep.blob
を生成する。
ここらでやってることはよくわからない。
node -e "require('fs').copyFileSync(process.execPath, '!OUTPUTNAME!')"
nodeそのものをコピー。
if %ISINSTALL_SIGNTOOL%==True (
echo I will verify the signature.
echo Temporarily remove the signature.
signtool remove /s !OUTPUTNAME!
echo The signature is complete.
)
signtoolが入っていたら、署名を削除。入ってなかったら素通り。
echo npx postject !OUTPUTNAME! NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2 > tmp.bat
call tmp.bat
バイナリを結合してるっぽい。ようわからん
if %ISINSTALL_SIGNTOOL%==True (
echo I will sign.
signtool sign /fd SHA256 !OUTPUTNAME!
echo The signature is complete.
)
signtoolが入っていたら、署名する。
del sea-config.json > nul
del sea-prep.blob > nul
del out.js > nul
del tmp.bat > nul
if %ISINSTALL_ESBUILD%==False (
echo uninstall esbuild
npm uninstall esbuild
)
各種生成されたファイルの削除。また、先ほど入れたesbuildの削除。
以上で、解説終わり!
おわりに
すげぇbatの分量多くなりました。すいません。
でも汎用性のあるスクリプト作りたかったから良し
6時間近く溶かしました
ではまた。