Node.js/成果物に含まれる node_modules を減量する
動機
JavaScript ( や TypeScript などの AltJS ) で記述されたプロジェクトをデプロイするとき、対象となるサービスに設定されたデプロイパッケージの容量制限を意識せねばならない場合があります。 そういった目的のために、この記事では node_modules
パッケージの容量を減らす方法についてまとめます。
node_modules
のサイズについては、以下のコマンドなどで確認できます。
$ du -d 1 -kh ./node_modules | sort -hr | head -50
...
たとえば AWS Lambda にプロジェクトをデプロイするのならば、次のような制限があります。
50 MB (zip 圧縮済み、直接アップロード)
250 MB (解凍、レイヤーを含む)
3 MB (コンソールエディタ)
なお『レイヤー』とは Lambda Layers のことです。
手段
modclean で軽量化する
npx modclean --run
などとすればお試しで実行することができます。
node-prune で軽量化する
npx node-prune
などとすればお試しで実行することができます。
uglify-js で軽量化する
$ yarn global add uglify-js
$ find node_modules -name '*.js' | while read line; do echo ${line}; uglifyjs ${line} -c -m -o ${line}; done
などとすると実行できます。
上掲した 2 つに比べると実行に時間が掛かるため、頻繁に実行するには検証が必要になります。
node_modules には含めず CDN から取得する
swagger-ui ( https://www.npmjs.com/package/swagger-ui ) を例に、
node_modules
には含めず CDN から JavaScript を取得して、それを使用してみます。
まず CDN から JavaScript を取得して、その文字列を変数に格納します。
const [bundleResponse, standaloneResponse]: Response[] = await Promise.all([
fetch('https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js'),
fetch('https://unpkg.com/swagger-ui-dist@3/swagger-ui-standalone-preset.js'),
])
const [swaggerBundle, swaggerStandalone]: string[] = await Promise.all([
bundleResponse.text(),
standaloneResponse.text()
])
script.runInNewContext を用いて文字列を JavaScript として評価します。
文字列をソースコードとして評価する方法としては他に eval や safe-eval などがあります。
const swaggerBundle = {}
const scriptBundle = new vm.Script(swaggerBundle)
await scriptBundle.runInNewContext(swaggerBundle)
const swaggerStandalone = {}
const scriptStandalone = new vm.Script(swaggerStandalone)
await scriptStandalone.runInNewContext(swaggerStandalone)
これで swagger-ui-bundle.js
は swaggerBundle
として、
swagger-ui-standalone-preset.js
は swaggerStandalone
として、
たとえば以下のように使用できるようになります。
const options: SwaggerUIOptions = {
spec, // yaml が格納された変数 spec があるとする
domNode: document.getElementById('swagger'),
deepLinking: true,
presets: [
swaggerBundle.SwaggerUIBundle.presets.apis,
swaggerStandalone.SwaggerUIStandalonePreset,
],
plugins: [swaggerBundle.SwaggerUIBundle.plugins.DownloadUrl],
layout: 'StandaloneLayout',
}
swaggerBundle.SwaggerUIBundle(options)
⚠️ 注意点
eval()
ないしそれに準ずる方法で、文字列を JavaScript として評価していることには注意が必要です。
この例のようにクライアントサイドで(ブラウザで)実行されるのならば、このページだけ CSP に script-src 'unsafe-eval'
を追加するなどして、制限を緩めてやる必要があります。
( swagger-ui の JavaScript は window
などを使用しており、クライアントサイドでしか実行できない )
...
ただし Content-Security-Policy
レスポンスヘッダにある CSP の設定を、meta タグの CSP によって緩めることはできません。
以上を考慮すると、この方法はサーバサイドで評価できる JavaScript 限定と言えるかもしれません。
--production
フラグをつける
成果物に dependencies
のみを含める ( devDependencies
は含めない ) ようにします。
また npm prune --production
すれば、devDependencies
を削除することもできます。
depcheck で使用していないパッケージを除く
npx depcheck
などとすればお試しで実行することができます。
ただし精度には限界があり、実行結果に出力されているから除いて差し支えないとはならず、いざ除くのならば精査が必要です。
devDependencies
にあれば十分なパッケージが、誤って dependencies
に記述されていると検出する
本来 eslint にルールを設定することで間接的に検出することができます。
リンク
Discussion