はじめに
みなさんご存知だとは思いますが、GitHubにはRepository内で使われているプログラミング言語の割合が表示されています。
おそらくですが、あれはファイルの拡張子でどのプログラミング言語なのかを判断しているのでしょう。
さて、JavaScriptのライブラリであるVue.jsでは1つのコンポーネントを拡張子が.vue
であるファイルを作成、すなわちSingleFileComponentという仕組みで開発していくのが主流ですね。
この場合GitHubのLanguage欄は以下のようになります。
.vue
ファイルなので当然Vueとして認識されてますね。
9割9分9厘の方は別にそれで何も気にしないと思いますが、.vue
ファイルのscript
ブロックにはJavaScriptでもTypeScriptでも記述することができるので、.vue
ファイルがJavaScriptなのか、TypeScriptなのか気になることがありませんか?
私は気になります。
というわけで、プロジェクトの.vue
ファイル全体からどれくらいの割合がTypeScriptで記述されているのかを算出できる仕組みを作ってみましょう。
SFCをパースする
まず、どのようにして.vue
ファイルの中身がJavaScriptなのか、TypeScriptなのか判断するかを考えていきたいと思います。
TypeScriptで書かれた.vue
ファイルのscript
ブロックには、lang
ディレクティブにts
が指定されています。
// JavaScriptの場合
<script>
</script>
// TypeScriptの場合
<script lang="ts">
</script>
であれば、.vue
ファイルをパースしてscript
ブロックのlang
属性の値を取れば良いですね。
早速、.vue
ファイルをパースする処理を書いて......いく必要はないです。
Vue.jsの中にはvue-template-compiler
というVue.jsのtemplateをコンパイルするためのAPIがパッケージとして存在しており、そこにparseComponent
というSFCをパースするためのメソッドが用意されています。
今回はこれを使って実現しようと思います。
https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#readme
const compiler = require('vue-template-compiler')
const sfc = `
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
`
const descriptor = compiler.parseComponent(sfc)
console.log(descriptor)
使い方としては、引数にSFCの内容を文字列として渡してあげれば、以下のようなオブジェクトを返してくれます。
{
template: {
type: 'template',
content: '\n' +
'<div id="app">\n' +
' <img alt="Vue logo" src="./assets/logo.png" />\n' +
' <HelloWorld msg="Welcome to Your Vue.js App" />\n' +
'</div>\n',
start: 11,
attrs: {},
end: 141
},
script: {
type: 'script',
content: '\n' +
"import HelloWorld from './components/HelloWorld.vue'\n" +
'\n' +
'export default {\n' +
" name: 'App',\n" +
' components: {\n' +
' HelloWorld\n' +
' }\n' +
'}\n',
start: 162,
attrs: {},
end: 286
},
styles: [
{
type: 'style',
content: '\n' +
'#app {\n' +
' font-family: Avenir, Helvetica, Arial, sans-serif;\n' +
' -webkit-font-smoothing: antialiased;\n' +
' -moz-osx-font-smoothing: grayscale;\n' +
' text-align: center;\n' +
' color: #2c3e50;\n' +
' margin-top: 60px;\n' +
'}\n',
start: 304,
attrs: {},
end: 504
}
],
customBlocks: [],
errors: []
}
script
ブロックに、lang属性が含まれているSFCをパースすると以下のようにちゃんとlang属性の値が入っています。
{
template: {
type: 'template',
content: '\n<div>Hello</div>\n',
start: 10,
attrs: {},
end: 30
},
script: {
type: 'script',
content: '',
start: 60,
attrs: { lang: 'ts' },
lang: 'ts',
end: 60
},
styles: [ { type: 'style', content: '', start: 77, attrs: {}, end: 77 } ],
customBlocks: [],
errors: []
}
問題なさそうですね。
では、この仕組を作ってサクッと入力された.vueファイルからTypeScriptであるものの割合を出力するプログラムを書いていきます
const compiler = require('vue-template-compiler')
const fs = require('fs')
function filterVueFiles(files) {
return files.filter((file) => file.match(/.vue$/))
}
function calcTypeScriptRate(files) {
const descriptors = files.map((file) => {
const component = fs.readFileSync(file).toString()
return compiler.parseComponent(component)
})
const totalCount = files.length
const tsCount = descriptors.filter(
(desc) => desc.script?.attrs?.lang === 'ts'
).length
return tsCount / totalCount
}
module.exports = {
filterVueFiles,
calcTypeScriptRate,
}
const { program } = require('commander')
const { filterVueFiles, calcTypeScriptRate } = require('./lib')
program.parse()
const options = program.opts()
const files = filterVueFiles(program.args)
const rate = calcTypeScriptRate(files)
console.log(rate)
こんな感じでしょうか。
componentsディレクトリに3つの.vue
ファイル、そのうちの1つがTypeScriptで書かれている想定で実行すると以下のように出力されます。
$ node cli.js components/**
0.3333333333333333
このプログラムをもう少し整えたものは以下のリポジトリにおいてあります。
https://github.com/fuku710/vue-sfc-ts-rate
おわりに
.vue
ファイルのTypeScriptの割合を算出するという、おおよそほとんどの人間には需要がないものですが、Vue.js内部のAPIを用いいればこのようなものがサクッと作れると思います。
本当であればGitHubActionsを用いてpushするたびにリポジトリ内のTypeScript率を算出して、README.mdなどにbadgeとして表示したかったのですが、どうやってもGitHubだけで完結する手軽な方法が存在しないため、諦めました。
GitHubActionsでパーセンテージが表示できるbadgeが出力できたらなぁ...