6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeScriptで書かれている.vueファイルの割合を算出する

Last updated at Posted at 2021-12-24

はじめに

みなさんご存知だとは思いますが、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が指定されています。

.vue
// 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

.js
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の内容を文字列として渡してあげれば、以下のようなオブジェクトを返してくれます。

.js
{
  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属性の値が入っています。

.js
{
  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であるものの割合を出力するプログラムを書いていきます

lib/index.js
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,
}
cli.js
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が出力できたらなぁ...

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?