chore: start parsing pages

This commit is contained in:
Sébastien Chopin 2020-08-18 19:08:06 +02:00
parent e892f5f019
commit d14d695190
3 changed files with 130 additions and 36 deletions

View File

@ -1,7 +1,12 @@
import { resolve } from 'path' import { resolve } from 'path'
import globby from 'globby'
import { Builder } from './builder'
export interface NuxtRoute { export interface NuxtRoute {
path: '' name?: string
path: string
component: string
children?: NuxtRoute[]
} }
export interface NuxtApp { export interface NuxtApp {
@ -11,12 +16,18 @@ export interface NuxtApp {
} }
// Scan project structure // Scan project structure
export function resolveApp (nuxt, srcDir: string): NuxtApp { export async function resolveApp (builder: Builder, srcDir: string): Promise<NuxtApp> {
const { nuxt } = builder
// resolve App.vue // resolve App.vue
const main = nuxt.resolver.tryResolvePath('~/App') || const main = nuxt.resolver.tryResolvePath('~/App') ||
nuxt.resolver.tryResolvePath('~/app') || nuxt.resolver.tryResolvePath('~/app') ||
resolve(nuxt.options.appDir, 'app.vue') resolve(nuxt.options.appDir, 'app.vue')
const pagesPattern = `${nuxt.options.dir.pages}/**/*.{${nuxt.options.extensions.join(',')}}`
const pages = await resolveFiles(builder, pagesPattern, srcDir)
const routes = buildRoutes(pages, srcDir, nuxt.options.dir.pages, nuxt.options.extensions)
console.log('routes', routes)
// TODO: Read pages/ and create routes // TODO: Read pages/ and create routes
// TODO: Detect store // TODO: Detect store
// Use hooks? // Use hooks?
@ -32,3 +43,97 @@ export function resolveApp (nuxt, srcDir: string): NuxtApp {
routes: [] routes: []
} }
} }
async function resolveFiles (builder: Builder, pattern: string, srcDir: string) {
const { nuxt } = builder
return builder.ignore.filter(await globby(pattern, {
cwd: srcDir,
followSymbolicLinks: nuxt.options.build.followSymlinks
}))
}
const isDynamicRoute = (s: string) => /^\[.+\]$/.test(s)
export function buildRoutes (
files: string[],
srcDir: string,
pagesDir: string,
extensions: string[]
) {
const routes: NuxtRoute[] = []
for (const file of files) {
const pathParts = file
.replace(new RegExp(`^${pagesDir}`), '')
.replace(new RegExp(`\\.(${extensions.join('|')})$`), '')
.split('/')
.slice(1) // removing the pagesDir means that the path begins with a '/'
const route: NuxtRoute = {
name: '',
path: '',
component: resolve(srcDir, file)
}
let parent = routes
for (let i = 0; i < pathParts.length; i++) {
const part = pathParts[i]
// Remove square brackets at the start and end.
const isDynamicPart = isDynamicRoute(part)
const normalizedPart = (isDynamicPart
? part.replace(/^\[(\.{3})?/, '').replace(/\]$/, '')
: part
).toLowerCase()
route.name += route.name ? `-${normalizedPart}` : normalizedPart
const child = parent.find(
parentRoute => parentRoute.name === route.name
)
if (child) {
child.children = child.children || []
parent = child.children
route.path = ''
} else if (normalizedPart === 'index' && !route.path) {
route.path += '/'
} else if (normalizedPart !== 'index') {
if (isDynamicPart) {
route.path += `/:${normalizedPart}`
// Catch-all route
if (/^\[\.{3}/.test(part)) {
route.path += '(.*)'
} else if (i === pathParts.length - 1) {
route.path += '?'
}
} else {
route.path += `/${normalizedPart}`
}
}
}
parent.push(route)
}
return prepareRoutes(routes)
}
function prepareRoutes (routes: NuxtRoute[], hasParent = false) {
for (const route of routes) {
if (route.name) {
route.name = route.name.replace(/-index$/, '')
}
if (hasParent) {
route.path = route.path.replace(/^\//, '').replace(/\?$/, '')
}
if (route.children) {
delete route.name
route.children = prepareRoutes(route.children, true)
}
}
return routes
}

View File

@ -1,6 +1,5 @@
import { join, relative } from 'path' import { join, relative } from 'path'
import fsExtra from 'fs-extra' import fsExtra from 'fs-extra'
import consola from 'consola'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import { BundleBuilder } from 'src/webpack' import { BundleBuilder } from 'src/webpack'
import { Nuxt } from '../core' import { Nuxt } from '../core'
@ -16,11 +15,18 @@ import Ignore from './ignore'
export class Builder { export class Builder {
nuxt: Nuxt nuxt: Nuxt
ignore: Ignore
app: NuxtApp app: NuxtApp
templates: NuxtTemplate[] templates: NuxtTemplate[]
constructor (nuxt) { constructor (nuxt) {
this.nuxt = nuxt this.nuxt = nuxt
this.ignore = new Ignore({
rootDir: nuxt.options.srcDir,
ignoreArray: nuxt.options.ignore.concat(
relative(nuxt.options.rootDir, nuxt.options.buildDir)
)
})
} }
build () { build () {
@ -42,46 +48,29 @@ async function build (builder: Builder) {
} }
function watch (builder: Builder) { function watch (builder: Builder) {
const { nuxt } = builder const { nuxt, ignore } = builder
const ignore = new Ignore({
rootDir: nuxt.options.srcDir,
ignoreArray: nuxt.options.ignore.concat(
relative(nuxt.options.rootDir, nuxt.options.buildDir)
)
})
// Watch internal templates // Watch internal templates
const options = nuxt.options.watchers.chokidar const options = nuxt.options.watchers.chokidar
const nuxtAppWatcher = createWatcher(nuxt.options.appDir, options, ignore) const nuxtAppWatcher = createWatcher(nuxt.options.appDir, { ...options, cwd: nuxt.options.appDir }, ignore)
nuxtAppWatcher.watchAll(async () => { nuxtAppWatcher.watchAll(debounce(() => compileTemplates(builder.templates, nuxt.options.buildDir), 100))
consola.log('Re-generate templates')
await compileTemplates(builder.templates, nuxt.options.buildDir)
})
// Watch user app // Watch user app
const appWatcher = createWatcher(builder.app.srcDir, options, ignore) const appPattern = `${builder.app.srcDir}/**/*.{${nuxt.options.extensions.join(',')}}`
// Watch for App.vue creation const appWatcher = createWatcher(appPattern, { ...options, cwd: builder.app.srcDir }, ignore)
// appWatcher.debug('srcDir') // appWatcher.debug('srcDir')
appWatcher.watch( const refreshTemplates = debounce(() => generate(builder), 100)
/^(A|a)pp\.[a-z]{2,3}/, // Watch for App.vue creation
debounce(({ event }) => { appWatcher.watch(/^(A|a)pp\.[a-z]{2,3}/, refreshTemplates, ['add', 'unlink'])
if (['add', 'unlink'].includes(event)) {
generate(builder)
}
}, 50)
)
// Watch for page changes // Watch for page changes
appWatcher.watch('pages/', async () => { appWatcher.watch(new RegExp(`^${nuxt.options.dir.pages}/`), refreshTemplates, ['add', 'unlink'])
consola.log('Re-generate routes')
await compileTemplates(builder.templates, nuxt.options.buildDir)
})
} }
export async function generate (builder: Builder) { export async function generate (builder: Builder) {
const { nuxt } = builder const { nuxt } = builder
await fsExtra.mkdirp(nuxt.options.buildDir) await fsExtra.mkdirp(nuxt.options.buildDir)
builder.app = resolveApp(nuxt, nuxt.options.srcDir) builder.app = await resolveApp(builder, nuxt.options.srcDir)
const templatesDir = join(builder.nuxt.options.appDir, '_templates') const templatesDir = join(builder.nuxt.options.appDir, '_templates')
const appTemplates = await scanTemplates(templatesDir, templateData(builder)) const appTemplates = await scanTemplates(templatesDir, templateData(builder))

View File

@ -4,15 +4,15 @@ import consola from 'consola'
import Ignore from './ignore' import Ignore from './ignore'
export function createWatcher ( export function createWatcher (
dir: string, pattern: string,
options?: WatchOptions, options?: WatchOptions,
ignore?: Ignore ignore?: Ignore
) { ) {
const opts = defu({ cwd: dir }, options, { const opts = defu(options, {
ignored: [], ignored: [],
ignoreInitial: true ignoreInitial: true
}) })
const watcher = chokidar.watch(dir, opts) const watcher = chokidar.watch(pattern, opts)
const watchAll = (cb: Function, filter?: Function) => { const watchAll = (cb: Function, filter?: Function) => {
watcher.on('all', (event, path: string) => { watcher.on('all', (event, path: string) => {
if (ignore && ignore.ignores(path)) { if (ignore && ignore.ignores(path)) {
@ -25,11 +25,11 @@ export function createWatcher (
}) })
} }
const watch = (pattern: string | RegExp, cb: Function) => const watch = (pattern: string | RegExp, cb: Function, events?: string[]) =>
watchAll(cb, e => e.path.match(pattern)) watchAll(cb, ({ event, path }) => path.match(pattern) && (!events || events.includes(event)))
const debug = (tag: string = '[Watcher]') => { const debug = (tag: string = '[Watcher]') => {
consola.log(tag, 'Watching ', dir) consola.log(tag, 'Watching ', pattern)
watchAll((e) => { watchAll((e) => {
consola.log(tag, e.event, e.path) consola.log(tag, e.event, e.path)
}) })