Last active
April 11, 2018 11:52
-
-
Save chrisnicola/db7eaf95fd39579c8eff8c2f6759798e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function parseName(path) { | |
const name = path.toLowerCase() | |
// Remove starting ./ | |
.replace(/^\.\//, '') | |
// Parameters | |
.replace(/\/_(\w+)(\/|\.vue$)/, '/:$1/') | |
// Remove file extension and 'index' | |
.replace(/(index)?\.vue$/, '') | |
// Remove trailing '/' | |
.replace(/\/$/, ''); | |
// Replace beginning '/' | |
if (name === '') return 'root'; | |
return name; | |
} | |
export function parsePathTemplate(path) { | |
const template = path.toLowerCase() | |
// Remove starting ./ | |
.replace(/^\.\//, '') | |
// Conditional parameters | |
.replace(/\/_(\w+).vue$/, '/:$1?') | |
// Required parameters | |
.replace(/\/_(\w+)\//, '/:$1/') | |
// Remove file extension and 'index' | |
.replace(/(index)?\.vue$/, '') | |
// Remove trailing '/' | |
.replace(/\/$/, ''); | |
// Replace beginning '/' | |
return `/${template}`; | |
} | |
export function hasChildren(path) { | |
return !path.match(/(index)\.vue$/i) && | |
!path.match(/\/_(\w+).vue$/i); | |
} | |
export function generateRoute(key, component) { | |
const route = { ...component.route, component }; | |
if (!('name' in route)) route.name = parseName(key); | |
if (!('path' in route)) route.path = parsePathTemplate(key); | |
if (hasChildren(key)) route.children = []; | |
return route; | |
} | |
export function nestRoutes([route, ...rest]) { | |
let routes = rest; | |
if (!route) return []; | |
if (route.children) { | |
const children = routes.filter(r => r.path.startsWith(route.path)); | |
routes = routes.filter(r => !r.path.startsWith(route.path)); | |
if (children.length === 0) { | |
delete route.children; | |
} else { | |
route.children = nestRoutes(children); | |
} | |
} | |
return [route, ...nestRoutes(routes)]; | |
} | |
export function buildRoutes(context) { | |
const routes = context.keys().map(key => { | |
const component = context(key).default; | |
return generateRoute(key, component); | |
}); | |
return nestRoutes(routes); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import assert from 'assert'; | |
import sinon from 'sinon'; | |
import { | |
parsePathTemplate, | |
parseName, | |
generateRoute, | |
buildRoutes, | |
} from './build_routes'; | |
describe('build_routes', () => { | |
it('parsePathTempalte', async () => { | |
const expectations = { | |
'./index.vue': '/', | |
'./clients.vue': '/clients', | |
'./clients/_id.vue': '/clients/:id?', | |
'./clients/_name.vue': '/clients/:name?', | |
'./clients/_id/index.vue': '/clients/:id', | |
'./clients/_name/index.vue': '/clients/:name', | |
'./clients/_id/accounts.vue': '/clients/:id/accounts', | |
'./my_account.vue': '/my_account', | |
'./my-account.vue': '/my-account', | |
}; | |
Object.entries(expectations).forEach(([path, expected]) => { | |
const template = parsePathTemplate(path); | |
assert.equal(template, expected, `parses '${path}' as '${expected}'`); | |
}); | |
}); | |
it('parseName', async () => { | |
const expectations = { | |
'./index.vue': 'root', | |
'./clients.vue': 'clients', | |
'./clients/_id.vue': 'clients/:id', | |
'./clients/_name.vue': 'clients/:name', | |
'./clients/_id/index.vue': 'clients/:id', | |
'./clients/_name/index.vue': 'clients/:name', | |
'./clients/_id/accounts.vue': 'clients/:id/accounts', | |
'./my_account.vue': 'my_account', | |
'./my-account.vue': 'my-account', | |
}; | |
Object.entries(expectations).forEach(([path, expected]) => { | |
const template = parseName(path); | |
assert.equal(template, expected, `parses '${path}' as '${expected}'`); | |
}); | |
}); | |
it('generateRoute', async () => { | |
const component = {}; | |
let route = generateRoute('index.vue', component); | |
assert.equal(route.path, '/', 'Generates a path for the route if there is no route.path.'); | |
assert.equal(route.name, 'root', 'Generates a name for the route if there is no route.name.'); | |
assert.equal(route.component, component, 'Sets the component for the route.'); | |
component.route = { path: '/path', name: 'name' }; | |
route = generateRoute('index.vue', component); | |
assert.equal(route.path, '/path', 'Uses component route.path if there is one'); | |
assert.equal(route.name, 'name', 'Uses component route.name if there is one.'); | |
}); | |
it('buildRoutes', async () => { | |
const context = sinon.stub(); | |
const component = {}; | |
context.returns({ default: component }); | |
context.keys = () => ['./index.vue', './clients.vue']; | |
let routes = buildRoutes(context); | |
assert.equal(routes.length, 2, 'creates 2 routes for two non-child keys in the context'); | |
context.keys = () => ['./index.vue', './clients.vue', './clients/_id.vue']; | |
routes = buildRoutes(context); | |
const parent = routes.find(r => r.name === 'clients'); | |
assert(parent.children[0].path === '/clients/:id?', 'creates a nested child route'); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Router from 'vue-router'; | |
import routes from './routes'; | |
import { installRouterPlugins } from '@/lib/vue'; | |
const router = new Router({ | |
mode: 'history', | |
routes, | |
}); | |
/* istanbul ignore next */ | |
installRouterPlugins(router); | |
export default router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { buildRoutes } from './build_routes'; | |
const context = require.context('./', true, /\.vue$/); | |
export default buildRoutes(context); |
Note 2: Also this flat file layout is just for the sake of the Gist but it would work. I would typically export build_routes
from @/lib/vue
. The fully confiugred router would be exported by /router/index.js
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note:
routes.js
goes in the root of your/views
folder and from that point any.vue
file dropped in that folder (or subfolders) will get mapped to a route. The export ofroutes.js
is the route configuration which gets passed to the vue router.