Skip to content

Commit

Permalink
refactor: expose resolver option
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 27, 2020
1 parent 519b9dd commit fd1ddaa
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 37 deletions.
1 change: 1 addition & 0 deletions src/node/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './server'
export * from './build'
export { cachedRead } from './utils'
39 changes: 39 additions & 0 deletions src/node/resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import path from 'path'

export interface Resolver {
publicToFile(publicPath: string, root: string): string | undefined
fileToPublic(filePath: string, root: string): string | undefined
}

export interface InternalResolver {
publicToFile(publicPath: string): string
fileToPublic(filePath: string): string
}

const defaultPublicToFile = (publicPath: string, root: string) =>
path.join(root, publicPath.slice(1))

const defaultFileToPublic = (filePath: string, root: string) =>
`/${path.relative(root, filePath)}`

export function createResolver(
root: string,
resolvers: Resolver[]
): InternalResolver {
return {
publicToFile: (publicPath) => {
for (const r of resolvers) {
const filepath = r.publicToFile(publicPath, root)
if (filepath) return filepath
}
return defaultPublicToFile(publicPath, root)
},
fileToPublic: (filePath) => {
for (const r of resolvers) {
const filepath = r.fileToPublic(filePath, root)
if (filepath) return filepath
}
return defaultFileToPublic(filePath, root)
}
}
}
15 changes: 10 additions & 5 deletions src/node/server.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import http, { Server } from 'http'
import Koa from 'koa'
import chokidar, { FSWatcher } from 'chokidar'
import { Resolver, createResolver, InternalResolver } from './resolver'
import { modulesPlugin } from './serverPluginModules'
import { vuePlugin } from './serverPluginVue'
import { hmrPlugin } from './serverPluginHmr'
import { servePlugin } from './serverPluginServe'

export { Resolver }

export type Plugin = (ctx: PluginContext) => void

export interface PluginContext {
root: string
app: Koa
server: Server
watcher: FSWatcher
resolver: InternalResolver
}

export interface ServerConfig {
root?: string
plugins?: Plugin[]
resolvers?: Resolver[]
}

const internalPlugins: Plugin[] = [
Expand All @@ -27,22 +32,22 @@ const internalPlugins: Plugin[] = [
servePlugin
]

export function createServer({
root = process.cwd(),
plugins = []
}: ServerConfig = {}): Server {
export function createServer(config: ServerConfig = {}): Server {
const { root = process.cwd(), plugins = [], resolvers = [] } = config
const app = new Koa()
const server = http.createServer(app.callback())
const watcher = chokidar.watch(root, {
ignored: [/node_modules/]
})
const resolver = createResolver(root, resolvers)

;[...plugins, ...internalPlugins].forEach((m) =>
m({
root,
app,
server,
watcher
watcher,
resolver
})
)

Expand Down
28 changes: 15 additions & 13 deletions src/node/serverPluginHmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ interface HMRPayload {
index?: number
}

export const hmrPlugin: Plugin = ({ root, app, server, watcher }) => {
export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => {
app.use(async (ctx, next) => {
if (ctx.path !== hmrClientPublicPath) {
return next()
Expand Down Expand Up @@ -90,22 +90,23 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher }) => {

watcher.on('change', async (file) => {
const timestamp = Date.now()
const servedPath = '/' + path.relative(root, file)
const publicPath = resolver.fileToPublic(file)

if (file.endsWith('.vue')) {
handleVueSFCReload(file, servedPath, timestamp)
handleVueSFCReload(file, publicPath, timestamp)
} else {
handleJSReload(servedPath, timestamp)
handleJSReload(publicPath, timestamp)
}
})

function handleJSReload(servedPath: string, timestamp: number) {
function handleJSReload(publicPath: string, timestamp: number) {
// normal js file
const importers = importerMap.get(servedPath)
const importers = importerMap.get(publicPath)
if (importers) {
const vueImporters = new Set<string>()
const jsHotImporters = new Set<string>()
const hasDeadEnd = walkImportChain(
servedPath,
publicPath,
importers,
vueImporters,
jsHotImporters
Expand Down Expand Up @@ -171,7 +172,7 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher }) => {

async function handleVueSFCReload(
file: string,
servedPath: string,
publicPath: string,
timestamp: number
) {
const cacheEntry = vueCache.get(file)
Expand Down Expand Up @@ -201,7 +202,7 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher }) => {
needRerender = true
}

const styleId = hash_sum(servedPath)
const styleId = hash_sum(publicPath)
const prevStyles = prevDescriptor.styles || []
const nextStyles = descriptor.styles || []
if (
Expand All @@ -224,7 +225,7 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher }) => {
if (!prevStyles[i] || !isEqual(prevStyles[i], nextStyles[i])) {
notify({
type: 'vue-style-update',
path: servedPath,
path: publicPath,
index: i,
id: `${styleId}-${i}`,
timestamp
Expand All @@ -237,7 +238,7 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher }) => {
prevStyles.slice(nextStyles.length).forEach((_, i) => {
notify({
type: 'vue-style-remove',
path: servedPath,
path: publicPath,
id: `${styleId}-${i + nextStyles.length}`,
timestamp
})
Expand All @@ -246,20 +247,21 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher }) => {
if (needReload) {
notify({
type: 'vue-reload',
path: servedPath,
path: publicPath,
timestamp
})
} else if (needRerender) {
notify({
type: 'vue-rerender',
path: servedPath,
path: publicPath,
timestamp
})
}
}
}

function isEqual(a: SFCBlock | null, b: SFCBlock | null) {
if (!a && !b) return true
if (!a || !b) return false
if (a.content !== b.content) return false
const keysA = Object.keys(a.attrs)
Expand Down
5 changes: 3 additions & 2 deletions src/node/serverPluginModules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const rewriteCache = new LRUCache({ max: 65535 })
export const modulesPlugin: Plugin = ({ root, app, watcher }) => {
// bust module rewrite cache on file change
watcher.on('change', (file) => {
// TODO also need logic for reverse mapping file to servedPath
const servedPath = '/' + path.relative(root, file)
debugImportRewrite(`${servedPath}: cache busted`)
rewriteCache.del(servedPath)
Expand All @@ -37,11 +38,11 @@ export const modulesPlugin: Plugin = ({ root, app, watcher }) => {
return
}

if (ctx.url === '/index.html') {
if (ctx.path === '/index.html') {
if (rewriteCache.has('/index.html')) {
debugImportRewrite('/index.html: serving from cache')
ctx.body = rewriteCache.get('/index.html')
} else {
} else if (ctx.body) {
const html = await readBody(ctx.body)
await initLexer
ctx.body = html.replace(
Expand Down
40 changes: 23 additions & 17 deletions src/node/serverPluginVue.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Plugin } from './server'
import path from 'path'
import {
SFCDescriptor,
SFCTemplateBlock,
Expand All @@ -26,16 +25,16 @@ export const vueCache = new LRUCache<string, CacheEntry>({
max: 65535
})

export const vuePlugin: Plugin = ({ root, app }) => {
export const vuePlugin: Plugin = ({ root, app, resolver }) => {
app.use(async (ctx, next) => {
if (!ctx.path.endsWith('.vue')) {
return next()
}

const pathname = ctx.path
const query = ctx.query
const filename = path.join(root, pathname.slice(1))
const descriptor = await parseSFC(root, filename)
const publicPath = ctx.path
const filePath = resolver.publicToFile(publicPath)
const descriptor = await parseSFC(root, filePath)

if (!descriptor) {
debug(`${ctx.url} - 404`)
Expand All @@ -47,8 +46,8 @@ export const vuePlugin: Plugin = ({ root, app }) => {
ctx.type = 'js'
ctx.body = compileSFCMain(
descriptor,
filename,
pathname,
filePath,
publicPath,
query.t as string
)
return
Expand All @@ -59,8 +58,8 @@ export const vuePlugin: Plugin = ({ root, app }) => {
ctx.body = compileSFCTemplate(
root,
descriptor.template!,
filename,
pathname,
filePath,
publicPath,
descriptor.styles.some((s) => s.scoped)
)
return
Expand All @@ -73,8 +72,8 @@ export const vuePlugin: Plugin = ({ root, app }) => {
root,
styleBlock,
index,
filename,
pathname
filePath,
publicPath
)
if (query.module != null) {
ctx.type = 'js'
Expand All @@ -92,19 +91,26 @@ export const vuePlugin: Plugin = ({ root, app }) => {

export async function parseSFC(
root: string,
filename: string
filename: string,
content?: string | Buffer
): Promise<SFCDescriptor | undefined> {
let cached = vueCache.get(filename)
if (cached && cached.descriptor) {
return cached.descriptor
}

let content: string
try {
content = await cachedRead(filename, 'utf-8')
} catch (e) {
return
if (!content) {
try {
content = await cachedRead(filename, 'utf-8')
} catch (e) {
return
}
}

if (typeof content !== 'string') {
content = content.toString()
}

const { descriptor, errors } = resolveCompiler(root).parse(content, {
filename
})
Expand Down

0 comments on commit fd1ddaa

Please sign in to comment.