lambda-service
Version:
189 lines (169 loc) • 4.67 kB
JavaScript
import { readdirSync, statSync, readFileSync } from 'fs'
import { join, extname, basename, relative } from 'path'
import { winPath, findJS } from 'umi-utils'
import assert from 'assert'
import getYamlConfig from './getYamlConfig'
const debug = require('debug')('umi-build-dev:getRouteConfigFromDir')
const JS_EXTNAMES = ['.js', '.jsx', '.ts', '.tsx']
export function sortRoutes(routes) {
const paramsRoutes = []
const exactRoutes = []
const layoutRoutes = []
routes.forEach(route => {
const { _isParamsRoute, exact } = route
if (_isParamsRoute) {
paramsRoutes.push(route)
} else if (exact) {
exactRoutes.push(route)
} else {
layoutRoutes.push(route)
}
})
assert(
paramsRoutes.length <= 1,
`We should not have multiple dynamic routes under a directory.`
)
return [...exactRoutes, ...layoutRoutes, ...paramsRoutes].reduce(
(memo, route) => {
if (route._toMerge) {
memo = memo.concat(route.routes)
} else {
delete route._isParamsRoute
memo.push(route)
}
return memo
},
[]
)
}
export default function getRouteConfigFromDir(paths) {
const { cwd, absPagesPath, absSrcPath, dirPath = '' } = paths
const absPath = join(absPagesPath, dirPath)
const files = readdirSync(absPath)
const absLayoutFile = findJS(absPagesPath, '_layout')
if (absLayoutFile) {
throw new Error(
'root _layout.js is not supported, use layouts/index.js instead'
)
}
const routes = sortRoutes(
files
.filter(file => {
if (
file.charAt(0) === '.' ||
file.charAt(0) === '_' ||
/\.(test|spec)\.(j|t)sx?$/.test(file)
)
return false
return true
})
.reduce(handleFile.bind(null, paths, absPath), [])
)
if (dirPath === '' && absSrcPath) {
const globalLayoutFile =
findJS(absSrcPath, 'layouts/index') || findJS(absSrcPath, 'layout/index')
if (globalLayoutFile) {
const wrappedRoutes = []
addRoute(
wrappedRoutes,
{
path: '/',
component: `./${winPath(relative(cwd, globalLayoutFile))}`,
routes
},
{
componentFile: globalLayoutFile
}
)
return wrappedRoutes
}
}
return routes
}
function handleFile(paths, absPath, memo, file) {
const { cwd, absPagesPath, dirPath = '' } = paths
const absFilePath = join(absPath, file)
const stats = statSync(absFilePath)
const isParamsRoute = file.charAt(0) === '$'
if (stats.isDirectory()) {
const newDirPath = join(dirPath, file)
// routes & _layout
const routes = getRouteConfigFromDir({
...paths,
dirPath: newDirPath
})
const absLayoutFile = findJS(join(absPagesPath, newDirPath), '_layout')
if (absLayoutFile) {
addRoute(
memo,
{
path: normalizePath(newDirPath),
exact: false,
component: `./${winPath(relative(cwd, absLayoutFile))}`,
routes,
_isParamsRoute: isParamsRoute
},
{
componentFile: absLayoutFile
}
)
} else {
memo.push({
_toMerge: true,
path: normalizePath(newDirPath),
exact: true,
_isParamsRoute: isParamsRoute,
routes
})
}
} else if (stats.isFile() && isValidJS(file)) {
const bName = basename(file, extname(file))
const path = normalizePath(join(dirPath, bName))
addRoute(
memo,
{
path,
exact: true,
component: `./${winPath(relative(cwd, absFilePath))}`,
_isParamsRoute: isParamsRoute
},
{
componentFile: absFilePath
}
)
}
return memo
}
function normalizePath(path) {
let newPath = `/${winPath(path)
.split('/')
.map(path => path.replace(/^\$/, ':').replace(/\$$/, '?'))
.join('/')}`
// /index/index -> /
if (newPath === '/index/index') {
newPath = '/'
}
// /xxxx/index -> /xxxx/
newPath = newPath.replace(/\/index$/, '/')
// remove the last slash
// e.g. /abc/ -> /abc
if (newPath !== '/' && newPath.slice(-1) === '/') {
newPath = newPath.slice(0, -1)
}
return newPath
}
function addRoute(memo, route, { componentFile }) {
const code = readFileSync(componentFile, 'utf-8')
debug(`parse yaml from ${componentFile}`)
const config = getYamlConfig(code)
;['path', 'exact', 'component', 'routes'].forEach(key => {
assert(!(key in config), `Unexpected key ${key} in file ${componentFile}`)
})
memo.push({
...route,
...config
})
}
function isValidJS(file) {
return JS_EXTNAMES.includes(extname(file))
}