Skip to content

Instantly share code, notes, and snippets.

@nick-ChenZe
Created July 13, 2018 05:37
Show Gist options
  • Save nick-ChenZe/6167ec30cd709b16b37d1ccb20bc7ee7 to your computer and use it in GitHub Desktop.
Save nick-ChenZe/6167ec30cd709b16b37d1ccb20bc7ee7 to your computer and use it in GitHub Desktop.
const fs = require('fs-extra')
const path = require('path')
const resolve = dir => path.resolve(__dirname, '..', 'src', dir)
const viewsDir = resolve('views')
const routeFile = resolve('routes/index.js')
// 移除JSON字符串中component的引号
const excludeQuote = str => str.replace(/(\s*component":\s*)"([^""]+)"(.*)/g, '$1$2$3')
// 将对象字面量转为导出格式的JSON字符串
const exportString = obj => `export default ${excludeQuote(JSON.stringify(obj, '', 2))}`
// 判断当前目录是否为components
const isComponents = dir => path.basename(dir) === 'components'
// 从对象数组中用某个键值对找到符合条件的唯一项
const findChildByName = (parent, name, key = 'name') => parent.find(item => item[key] === name)
// 将:转为by格式,user/:id 转为 user/by/id
const parseQueryColon = dir => dir.replace(/\/(:)/g, '/by/')
// 将路径格式转为驼峰格式 /user/id 转为 UserId
const parsePath2CamelCase = dir => dir.replace(/(^|\/){1}([a-zA-Z:])/g, ($1, $2, $3) => $3.toUpperCase())
// 将路径格式转为capital格式 /user/id 转为 userId
const parsePath2CapitalCase = dir => dir.replace(/(\/)([a-zA-Z])/g, ($1, $2, $3) => $3.toUpperCase())
// 将字符串写入路由文件
const writeFile = (content, callback) => fs.writeFile(routeFile, content, callback)
/**
* 展平所有文件结构
* @param {String} dir 根路由
* [
* {
* name: capital格式文件名
* path: 单级的路径
* relpath: 完整的相对路径
* component: 驼峰格式文件名
* importPath: 完整的导入行字符串
* }
* ]
*/
let getAllFiles = dir =>
fs.readdirSync(dir).reduce((files, file) => {
const name = path.join(dir, file)
const isDirectory = fs.statSync(name).isDirectory()
if (isDirectory && isComponents(name)) return [...files]
let relpath = path.relative(viewsDir, name)
let relpathWithoutExt = relpath.split('.')[0]
let component = parsePath2CamelCase(parseQueryColon(relpathWithoutExt))
let componentName = parsePath2CapitalCase(parseQueryColon(relpathWithoutExt))
let componentPath = `../views/${relpath}`
let fileInfo = {
name: componentName,
relpath,
component,
importPath: `const ${component} = resolve => require(['${componentPath}'], resolve)`
}
return isDirectory ? [...files, ...getAllFiles(name)] : [...files, fileInfo]
}, [])
/**
* 重新将文件格式转化为嵌套的路由格式,并写入路由文件
* @param {Function} callback
*/
function writeRoutes(callback) {
let lines = []
let routeDefault = { path: '/', redirect: '/dashboard' }
let route404 = { path: '*', redirect: 'notFound' }
let routeConfig = [routeDefault, route404]
const lineBreak = '\n'
const emptyComponent = "{ render: h => h('router-view') }"
getAllFiles(viewsDir).forEach(item => {
let paths = item.relpath.split('/')
let length = paths.length
lines.push(item.importPath)
delete item.importPath
let collection = routeConfig
while (paths.length) {
let isEnd = paths.length === 1
let isRoot = paths.length === length
let prefix = isRoot ? '/' : ''
let pathname = path.basename(paths.shift(), '.vue')
let found = findChildByName(collection, prefix + pathname, 'path')
let name = isEnd ? item.name : ''
let component = isEnd ? item.component : emptyComponent
let route
if (found) {
if (found.children.length) {
component = emptyComponent
name = ''
isEnd && found.children.push({ path: '/', component: item.component, name: item.name })
}
route = Object.assign(found, { name, component, children: (collection = found.children) })
} else collection.push((route = { name, path: prefix + pathname, component, children: (collection = []) }))
if (!route.name) delete route.name
if (!route.component) delete route.component
}
})
lines.push(lineBreak)
fs.ensureFileSync(routeFile)
writeFile(lines.join(lineBreak) + exportString(routeConfig), callback)
}
module.exports = writeRoutes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment