JavaScript
Vue.js
webpack
vue-cli

@vue/cli-serviceの実装からcache-loaderのcacheIdentifierの算出方法を読み解く


はじめに

Vue.jsを使わないTypeScriptのプロジェクトで、ビルドを高速化するためにcache-loaderを導入してデフォルト設定で使っていたのですが、tsconfig.jsを変更した際にビルド結果が変わらないなど問題が発生してしまいました。

そこで、vue-cliで自動生成されるwebpackの設定ではcache-loaderにどのような設定をしているのか調べた結果、cacheIdentifierを設定していたため、この値がどのように算出されているのか実装を追ってみました。

具体的には下記の値です。


vue inspect --mode production --rule js

/* config.module.rule('js') */

{
test: /\.jsx?$/,
exclude: [
function () { /* omitted long function */ }
],
use: [
{
loader: 'cache-loader',
options: {
cacheDirectory: '/path/to/node_modules/.cache/babel-loader',
cacheIdentifier: '3f3945d2' // <- これ
}
},
{
loader: 'thread-loader'
},
{
loader: 'babel-loader'
}
]
}


対象バージョン

vue-cli@3.2.2


実装

結論としては、ビルド結果に影響するような設定値やファイルを集めたオブジェクトを作り、hash-sumで計算したhash値をcacheIdentifierに使用しているようです。

@vue/cli-plugin-typescriptの場合、ts-loaderとtypescriptのpackage.jsonから取得したversionと、tsconfig.jsonの中身をhash計算に使用しています。

https://github.com/vuejs/vue-cli/blob/v3.2.2/packages/%40vue/cli-plugin-typescript/index.js#L31


vue-cli/packages/@vue/cli-plugin-typescript/index.js から抜粋

  addLoader({

loader: 'cache-loader',
options: api.genCacheConfig('ts-loader', {
'ts-loader': require('ts-loader/package.json').version,
'typescript': require('typescript/package.json').version,
modern: !!process.env.VUE_CLI_MODERN_BUILD
}, 'tsconfig.json')
})

上記で使われているapi.getCacheConfigの実装を追っていくと、渡されたファイルに加え、@vue/cli-service, cache-loaderのpackage.jsonから取得したversion、vue.config.jsのchainWebpack, configureWebpackの内容もhash計算対象に加えているようです。

https://github.com/vuejs/vue-cli/blob/v3.2.2/packages/%40vue/cli-service/lib/PluginAPI.js#L134


vue-cli/packages/@vue/cli-service/lib/PluginAPI.js から抜粋

genCacheConfig (id, partialIdentifier, configFiles) {

const fs = require('fs')
const cacheDirectory = this.resolve(`node_modules/.cache/${id}`)

const variables = {
partialIdentifier,
'cli-service': require('../package.json').version,
'cache-loader': require('cache-loader/package.json').version,
env: process.env.NODE_ENV,
test: !!process.env.VUE_CLI_TEST,
config: [
this.service.projectOptions.chainWebpack,
this.service.projectOptions.configureWebpack
]
}

if (configFiles) {
// (中略) ファイルを読んでvariableに追加する処理
}

const cacheIdentifier = hash(variables)
return { cacheDirectory, cacheIdentifier }
}


この実装によって、ソースコード以外にビルド結果に影響を及ぼすような設定が変更された場合にもcacheが無効化され、常にビルド結果が期待した内容になっているようです。


@vue/cli-serviceの実装を参考にしたwebpack.config.js

調べた実装を元に、素のwebpackプロジェクトでcache-loaderのcacheIdentifierにhash値を設定する簡単なサンプルを組んでみました。


webpack.config.js

const fs = require('fs')

const hash = require('hash-sum')

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

const cacheIdentifier = hash([
require('typescript/package.json').version,
require('ts-loader/package.json').version,
require('cache-loader/package.json').version,
require('./tsconfig.json'),
fs.readFileSync('./webpack.config.js', 'utf-8'),
process.env.NODE_ENV
])

module.exports = {
mode: process.env.NODE_ENV || 'production',
entry: ['./src/index.ts'],
output: {
path: __dirname + '/dist',
filename: 'app.js'
},
resolve: {
extensions: ['.js', '.json', '.ts']
},
module: {
rules: [
{
test: /\.[jt]s$/,
use: [
{
loader: 'cache-loader',
options: {
cacheDirectory: __dirname + '/node_modules/.cache/ts-loader',
cacheIdentifier
}
},
{ loader: 'thread-loader' },
{
loader: 'ts-loader',
options: {
transpileOnly: true,
happyPackMode: true
}
}
]
}
]
},
plugins: [
new ForkTsCheckerWebpackPlugin({
tslint: false,
formatter: 'codeframe',
checkSyntacticErrors: true
})
]
}


リポジトリは下記においてあります。

https://github.com/clomie/typescript-webpack-cache-identifier-example