Skip to content

Commit

Permalink
feat: support 3rd party resolver
Browse files Browse the repository at this point in the history
closes #188
  • Loading branch information
hannoeru committed Apr 5, 2022
1 parent 35e6bd4 commit f269335
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 100 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"test": "vitest",
"test:ui": "vitest --ui",
"lint": "eslint .",
"lint:fix": "eslint --fix ."
"lint:fix": "eslint --fix .",
"type-check": "tsc --noEmit"
},
"peerDependencies": {
"@vue/compiler-sfc": ">=3",
Expand Down
66 changes: 10 additions & 56 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { extname, join, resolve } from 'path'
import deepEqual from 'deep-equal'
import { slash, toArray } from '@antfu/utils'
import colors from 'picocolors'
import { resolveOptions } from './options'
import { getPageFiles } from './files'
import { debug, invalidatePagesModule, isTarget } from './utils'
import { resolveReactRoutes } from './resolvers/react'
import { resolveVueRoutes } from './resolvers/vue'
import { resolveSolidRoutes } from './resolvers/solid'
import { getRouteBlock } from './customBlock'

import type { FSWatcher } from 'fs'
import type { Logger, ViteDevServer } from 'vite'
import type { CustomBlock, PageOptions, ResolvedOptions, UserOptions } from './types'
import type { PageOptions, ResolvedOptions, UserOptions } from './types'

export interface PageRoute {
path: string
Expand All @@ -22,7 +16,6 @@ export interface PageRoute {
export class PageContext {
private _server: ViteDevServer | undefined
private _pageRouteMap = new Map<string, PageRoute>()
private _customBlockMap: Map<string, CustomBlock> = new Map()

rawOptions: UserOptions
root: string
Expand Down Expand Up @@ -51,11 +44,11 @@ export class PageContext {

setupWatcher(watcher: FSWatcher) {
watcher
.on('unlink', (path) => {
.on('unlink', async(path) => {
path = slash(path)
if (!isTarget(path, this.options))
return
this.removePage(path)
await this.removePage(path)
this.onUpdate()
})
watcher
Expand All @@ -75,7 +68,7 @@ export class PageContext {
return
const page = this._pageRouteMap.get(path)
if (page)
this.checkCustomBlockChange(path)
await this.options.resolver.hmr?.changed?.(this, path)
})
}

Expand All @@ -88,45 +81,16 @@ export class PageContext {
path: p,
route,
})
await this.checkCustomBlockChange(p)
await this.options.resolver.hmr?.added?.(this, p)
}
}

removePage(path: string | string[]) {
async removePage(path: string | string[]) {
debug.pages('remove', path)
toArray(path).forEach((p) => {
await Promise.all(toArray(path).map(async(p) => {
this._pageRouteMap.delete(p)
this._customBlockMap.delete(p)
})
}

async checkCustomBlockChange(path: string) {
if (this.options.resolver !== 'vue')
return

const exitsCustomBlock = this._customBlockMap.get(path)
let customBlock: CustomBlock | undefined
try {
customBlock = await getRouteBlock(path, this.options)
} catch (error: any) {
// eslint-disable-next-line no-console
this.logger?.error(colors.red(`[vite-plugin-pages] ${error.message}`))
return
}
if (!exitsCustomBlock && !customBlock)
return

if (!customBlock) {
this._customBlockMap.delete(path)
debug.routeBlock('%s deleted', path)
return
}
if (!exitsCustomBlock || !deepEqual(exitsCustomBlock, customBlock)) {
debug.routeBlock('%s old: %O', path, exitsCustomBlock)
debug.routeBlock('%s new: %O', path, customBlock)
this._customBlockMap.set(path, customBlock)
this.onUpdate()
}
await this.options.resolver.hmr?.removed?.(this, p)
}))
}

onUpdate() {
Expand All @@ -141,12 +105,7 @@ export class PageContext {
}

async resolveRoutes() {
if (this.options.resolver === 'vue')
return await resolveVueRoutes(this)
if (this.options.resolver === 'react')
return await resolveReactRoutes(this)
if (this.options.resolver === 'solid')
return await resolveSolidRoutes(this)
return this.options.resolver.resolveRoutes(this)
}

async searchGlob() {
Expand All @@ -164,7 +123,6 @@ export class PageContext {
await this.addPage(page.files, page)

debug.cache(this.pageRouteMap)
debug.cache(this.customBlockMap)
}

get debug() {
Expand All @@ -174,8 +132,4 @@ export class PageContext {
get pageRouteMap() {
return this._pageRouteMap
}

get customBlockMap() {
return this._customBlockMap
}
}
22 changes: 17 additions & 5 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { resolve } from 'path'
import { slash, toArray } from '@antfu/utils'
import { getPageDirs } from './files'

import { ReactResolver, SolidResolver, VueResolver } from './resolvers'
import type { ImportModeResolver, ResolvedOptions, UserOptions } from './types'

function resolvePageDirs(dirs: UserOptions['dirs'], root: string, exclude: string[]) {
Expand All @@ -26,16 +27,26 @@ export const syncIndexResolver: ImportModeResolver = (filepath, options) => {
return 'async'
}

const getExtensions = (resolver: ResolvedOptions['resolver']) => {
const getResolver = (originalResolver: UserOptions['resolver']) => {
let resolver = originalResolver || 'vue'

if (typeof resolver !== 'string')
return resolver

switch (resolver) {
case 'vue':
return ['vue', 'ts', 'js']
resolver = VueResolver()
break
case 'react':
resolver = ReactResolver()
break
case 'solid':
return ['tsx', 'jsx', 'ts', 'js']
resolver = SolidResolver()
break
default:
throw new Error(`Unsupported resolver: ${resolver}`)
}
return resolver
}

export function resolveOptions(userOptions: UserOptions, viteRoot?: string): ResolvedOptions {
Expand All @@ -44,7 +55,6 @@ export function resolveOptions(userOptions: UserOptions, viteRoot?: string): Res
routeBlockLang = 'json5',
exclude = [],
caseSensitive = false,
resolver = 'vue',
syncIndex = true,
extendRoute,
onRoutesGenerated,
Expand All @@ -55,7 +65,9 @@ export function resolveOptions(userOptions: UserOptions, viteRoot?: string): Res

const importMode = userOptions.importMode || (syncIndex ? syncIndexResolver : 'async')

const extensions = userOptions.extensions || getExtensions(resolver)
const resolver = getResolver(userOptions.resolver)

const extensions = userOptions.extensions || resolver.resolveExtensions()

const extensionsRE = new RegExp(`\\.(${extensions.join('|')})$`)

Expand Down
20 changes: 18 additions & 2 deletions src/resolvers/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from '../utils'
import { generateClientCode } from '../stringify'

import type { Optional, ResolvedOptions } from '../types'
import type { Optional, PageResolver, ResolvedOptions } from '../types'
import type { PageContext } from '../context'

export interface ReactRouteBase {
Expand Down Expand Up @@ -45,7 +45,7 @@ function prepareRoutes(
return routes
}

export async function resolveReactRoutes(ctx: PageContext) {
async function resolveReactRoutes(ctx: PageContext) {
const { routeStyle, caseSensitive } = ctx.options
const nuxtStyle = routeStyle === 'nuxt'

Expand Down Expand Up @@ -111,3 +111,19 @@ export async function resolveReactRoutes(ctx: PageContext) {
client = (await ctx.options.onClientGenerated?.(client)) || client
return client
}

export function ReactResolver(): PageResolver {
return {
resolveExtensions() {
return ['tsx', 'jsx', 'ts', 'js']
},
async resolveRoutes(ctx) {
return await resolveReactRoutes(ctx)
},
stringify: {
component: path => `React.createElement(${path})`,
dynamicImport: path => `React.lazy(() => import("${path}"))`,
final: code => `import React from "react";\n${code}`,
},
}
}
19 changes: 17 additions & 2 deletions src/resolvers/solid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from '../utils'
import { generateClientCode } from '../stringify'

import type { Optional, ResolvedOptions } from '../types'
import type { Optional, PageResolver, ResolvedOptions } from '../types'
import type { PageContext } from '../context'

export interface SolidRouteBase {
Expand Down Expand Up @@ -41,7 +41,7 @@ function prepareRoutes(
return routes
}

export async function resolveSolidRoutes(ctx: PageContext) {
async function resolveSolidRoutes(ctx: PageContext) {
const { routeStyle, caseSensitive } = ctx.options
const nuxtStyle = routeStyle === 'nuxt'

Expand Down Expand Up @@ -112,3 +112,18 @@ export async function resolveSolidRoutes(ctx: PageContext) {
client = (await ctx.options.onClientGenerated?.(client)) || client
return client
}

export function SolidResolver(): PageResolver {
return {
resolveExtensions() {
return ['tsx', 'jsx', 'ts', 'js']
},
async resolveRoutes(ctx) {
return await resolveSolidRoutes(ctx)
},
stringify: {
dynamicImport: path => `Solid.lazy(() => import("${path}"))`,
final: code => `import * as Solid from "solid-js";\n${code}`,
},
}
}
55 changes: 52 additions & 3 deletions src/resolvers/vue.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import colors from 'picocolors'
import deepEqual from 'deep-equal'
import {
countSlash,
isCatchAllRoute,
Expand All @@ -7,7 +9,8 @@ import {
} from '../utils'
import { generateClientCode } from '../stringify'

import type { CustomBlock, Optional } from '../types'
import { getRouteBlock } from '../customBlock'
import type { CustomBlock, Optional, PageResolver } from '../types'
import type { PageContext } from '../context'

export interface VueRouteBase {
Expand Down Expand Up @@ -57,7 +60,7 @@ function prepareRoutes(
return routes
}

export async function resolveVueRoutes(ctx: PageContext) {
async function resolveVueRoutes(ctx: PageContext, customBlockMap: Map<string, CustomBlock>) {
const { routeStyle, caseSensitive } = ctx.options

const pageRoutes = [...ctx.pageRouteMap.values()]
Expand All @@ -71,7 +74,7 @@ export async function resolveVueRoutes(ctx: PageContext) {

// add leading slash to component path if not already there
const component = page.path.replace(ctx.root, '')
const customBlock = ctx.customBlockMap.get(page.path)
const customBlock = customBlockMap.get(page.path)

const route: VueRouteBase = {
name: '',
Expand Down Expand Up @@ -137,3 +140,49 @@ export async function resolveVueRoutes(ctx: PageContext) {
client = (await ctx.options.onClientGenerated?.(client)) || client
return client
}

export function VueResolver(): PageResolver {
const customBlockMap = new Map<string, CustomBlock>()

async function checkCustomBlockChange(ctx: PageContext, path: string) {
const exitsCustomBlock = customBlockMap.get(path)
let customBlock: CustomBlock | undefined
try {
customBlock = await getRouteBlock(path, ctx.options)
} catch (error: any) {
// eslint-disable-next-line no-console
ctx.logger?.error(colors.red(`[vite-plugin-pages] ${error.message}`))
return
}
if (!exitsCustomBlock && !customBlock)
return

if (!customBlock) {
customBlockMap.delete(path)
ctx.debug.routeBlock('%s deleted', path)
return
}
if (!exitsCustomBlock || !deepEqual(exitsCustomBlock, customBlock)) {
ctx.debug.routeBlock('%s old: %O', path, exitsCustomBlock)
ctx.debug.routeBlock('%s new: %O', path, customBlock)
customBlockMap.set(path, customBlock)
ctx.onUpdate()
}
}

return {
resolveExtensions() {
return ['vue', 'ts', 'js']
},
async resolveRoutes(ctx) {
return await resolveVueRoutes(ctx, customBlockMap)
},
hmr: {
added: async(ctx, path) => checkCustomBlockChange(ctx, path),
changed: async(ctx, path) => checkCustomBlockChange(ctx, path),
removed: async(_ctx, path) => {
customBlockMap.delete(path)
},
},
}
}

0 comments on commit f269335

Please sign in to comment.