概要
Vue3 で router/index.ts
が Pull Request / Marge Request のたびに毎回コンフリクトして困っていたので、views
以下のディレクトリ構成とファイル名から自動で router/index.ts
が入るようにしてみました!
背景
Vue3 とても便利なのですが、vue-cli で vue-router とともに使っていて、
router/index.ts
のコンフリクト解決が面倒に感じています。
Nuxt 3 が使えれば解決なのですがまだ Beta 版で IntelliJ/WebStorm の Vue プラグインも対応していないのでちょっと使いづらいです。
そこで簡単に router/index.ts
を npm run
するときに生成してしまうのを作ってしまおうというのが今回の取り組みです。
目指すもの
-
router/index.ts
の内容はnpm run dev
,npm run build
時に自動で入り、メンテナンス不要。 - ついでに、すべてのページにデバッグ用にアクセスできる索引的なコンポーネントも生成する。
実装方法
1. メインとなるプログラムの dynamicRouterGenerator を配備
1-1. src/lib/dynamicRouterGenerator/.gitignore
generated-router.ts
GeneratedIndex.vue
1-2. src/lib/dynamicRouterGenerator/.gitignore
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require('fs')
let files = []
/**
* @param dirPath 「/」始まり or ''
*/
const searchFiles = (dirPath) => {
const allDirents = fs.readdirSync(`${__dirname}/../../views${dirPath}`, { withFileTypes: true })
for (const dirent of allDirents) {
if (dirent.isDirectory() && dirent.name !== 'components') {
searchFiles(`${dirPath}/${dirent.name}`)
} else if (dirent.isFile() && dirent.name.match(/\.vue$/)) {
const importPath = `@/views${dirPath}/${dirent.name}`
const accessPath = `${dirPath}/${dirent.name}`
.replace(/\.vue$/, '')
.replace(/\/index$/, '')
|| '/'
const moduleName = `${dirPath}/${dirent.name}`
.replace(/^\//, '')
.replace(/\.vue$/, '')
.replace(/\//g, '__')
.replace(/-/g, '_')
const title = `${dirPath}/${dirent.name}`
.replace(/^\//, '')
.replace(/\.vue$/, '')
.replace(/-/g, ' ')
.replace(/\b(\w)/g, (w) => w.toUpperCase())
.replace(/\//g, '<span style="margin-left: 40px;"></span>')
files.push({
accessPath,
importPath,
moduleName,
title,
})
}
}
}
searchFiles('')
files.sort((a, b) => a.accessPath < b.accessPath ? -1 : 0)
fs.writeFileSync(`${__dirname}/generated-router.ts`, `\
import { RouteRecordRaw } from 'vue-router'
${files.map(f => `\
import ${f.moduleName} from '${f.importPath}'
`).join('')}\
export const dynamicRoutes: Array<RouteRecordRaw> = [
${files.map(f => `\
{ path: '${f.accessPath}', component: ${f.moduleName} },
`).join('')}\
]
`)
fs.writeFileSync(`${__dirname}/GeneratedIndex.vue`, `\
<template>
<ul>
${files.map(f => `\
<li><router-link to="${f.accessPath}">${f.title}</router-link></li>
`).join('')}\
</ul>
</template>
`)
2. router 書き換え
2-1. src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { dynamicRoutes } from '@/lib/dynamicRouterGenerator/generated-router'
const routes: Array<RouteRecordRaw> = [
...dynamicRoutes,
]
const index = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default index
3. 索引ページ配置
3-1. src/views/index-for-debug.vue
<template>
<GeneratedIndex style="font-size: 40px;" />
</template>
<script setup lang="ts">
import GeneratedIndex from '@/lib/dynamicRouterGenerator/GeneratedIndex.vue'
</script>
<style scoped>
li { font-size: 40px; }
</style>
4. package.json
4-1. npm install
npm install --save-dev @types/node
4-2. scripts
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint"
},
↓
"scripts": {
"dev": "node src/lib/dynamicRouterGenerator && vue-cli-service serve",
"build": "node src/lib/dynamicRouterGenerator && vue-cli-service build",
"test:unit": "node src/lib/dynamicRouterGenerator && vue-cli-service test:unit",
"lint": "node src/lib/dynamicRouterGenerator && vue-cli-service lint"
},
使い方
通常通り npm run dev
や npm run build
するだけ。
必要なファイルが自動で入ります。
新規ページを生成したときは npm run dev
を再起動します。
http://localhost:8080/index-for-debug
にアクセスすることで索引も表示されます。
src/
views/
index.vue
aaa/
bbb/
ccc-ddd.vue
ccc-eee.vue
という構成の場合、下記のような索引ページが生成されます。