Skip to content

Commit

Permalink
feat: watch aliased files that are out of root
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed May 29, 2020
1 parent 0172511 commit 8fe4284
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 35 deletions.
6 changes: 6 additions & 0 deletions src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ export function createServer(config: ServerConfig): Server {
config
}

// attach server context to koa context
app.use((ctx, next) => {
ctx._viteContext = context
return next()
})

const resolvedPlugins = [
// the import rewrite and html rewrite both take highest priority and runs
// after all other middlewares have finished
Expand Down
8 changes: 1 addition & 7 deletions src/node/server/serverPluginModuleResolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,7 @@ export const moduleResolvePlugin: ServerPlugin = ({ root, app, watcher }) => {
moduleFileToIdMap.set(file, ctx.path)
debug(`(${type}) ${id} -> ${getDebugPath(root, file)}`)
await cachedRead(ctx, file)

// resolved module file is outside of root dir, but is not in node_modules.
// this is likely a linked monorepo/workspace, watch the file for HMR.
if (!file.startsWith(root) && !/node_modules/.test(file)) {
watcher.add(file)
}
await next()
return next()
}

// special handling for vue runtime in case it's not installed
Expand Down
39 changes: 20 additions & 19 deletions src/node/server/serverPluginServeStatic.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'fs'
import path from 'path'
import { ServerPlugin } from '.'
import { isStaticAsset } from '../utils'
import { isStaticAsset, cachedRead } from '../utils'
import chalk from 'chalk'

const send = require('koa-send')
Expand All @@ -13,29 +13,16 @@ export const serveStaticPlugin: ServerPlugin = ({
root,
app,
resolver,
config
config,
watcher
}) => {
app.use((ctx, next) => {
app.use(async (ctx, next) => {
// short circuit requests that have already been explicitly handled
if (ctx.body || ctx.status !== 404) {
return
}
return next()
})

if (!config.serviceWorker) {
app.use(async (ctx, next) => {
await next()
// the first request to the server should never 304
if (seenUrls.has(ctx.url) && ctx.fresh) {
ctx.status = 304
}
seenUrls.add(ctx.url)
})
}
app.use(require('koa-etag')())

app.use((ctx, next) => {
// warn non-root references to assets under /public/
if (ctx.path.startsWith('/public/') && isStaticAsset(ctx.path)) {
console.error(
chalk.yellow(
Expand All @@ -46,16 +33,30 @@ export const serveStaticPlugin: ServerPlugin = ({
)
)
}

// handle possible user request -> file aliases
const filePath = resolver.requestToFile(ctx.path)
if (
filePath !== ctx.path &&
fs.existsSync(filePath) &&
fs.statSync(filePath).isFile()
) {
return send(ctx, filePath, { root: '/' })
await cachedRead(ctx, filePath)
}
return next()
})

if (!config.serviceWorker) {
app.use(async (ctx, next) => {
await next()
// the first request to the server should never 304
if (seenUrls.has(ctx.url) && ctx.fresh) {
ctx.status = 304
}
seenUrls.add(ctx.url)
})
}
app.use(require('koa-etag')())
app.use(require('koa-static')(root))
app.use(require('koa-static')(path.join(root, 'public')))

Expand Down
20 changes: 14 additions & 6 deletions src/node/server/serverPluginVue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ import {
ensureMapEntry,
hmrClientPublicPath
} from './serverPluginHmr'
import { resolveFrom, cachedRead, genSourceMapString, cleanUrl } from '../utils'
import {
resolveFrom,
cachedRead,
genSourceMapString,
cleanUrl,
watchFileIfOutOfRoot
} from '../utils'
import { Context } from 'koa'
import { transform } from '../esbuildService'
import { InternalResolver } from '../resolver'
Expand Down Expand Up @@ -81,6 +87,9 @@ export const vuePlugin: ServerPlugin = ({
}

if (!query.type) {
// watch potentially out of root vue file since we do a custom read here
watchFileIfOutOfRoot(watcher, root, filePath)

if (descriptor.script && descriptor.script.src) {
filePath = await resolveSrcImport(
root,
Expand Down Expand Up @@ -259,7 +268,8 @@ export const vuePlugin: ServerPlugin = ({
function isEqualBlock(a: SFCBlock | null, b: SFCBlock | null) {
if (!a && !b) return true
if (!a || !b) return false
if (a.content.length !== b.content.length) return false
// src imports will trigger their own updates
if (a.src && b.src && a.src === b.src) return true
if (a.content !== b.content) return false
const keysA = Object.keys(a.attrs)
const keysB = Object.keys(b.attrs)
Expand All @@ -276,9 +286,7 @@ async function resolveSrcImport(
resolver: InternalResolver
) {
const importer = ctx.path
const importee = cleanUrl(
resolveImport(process.cwd(), importer, block.src!, resolver)
)
const importee = cleanUrl(resolveImport(root, importer, block.src!, resolver))
const filePath = resolver.requestToFile(importee)
await cachedRead(ctx, filePath)
block.content = ctx.body
Expand All @@ -290,7 +298,7 @@ async function resolveSrcImport(
return filePath
}

export async function parseSFC(
async function parseSFC(
root: string,
filePath: string,
content?: string | Buffer
Expand Down
26 changes: 23 additions & 3 deletions src/node/utils/fsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Context } from 'koa'
import { Readable } from 'stream'
import { seenUrls } from '../server/serverPluginServeStatic'
import mime from 'mime-types'
import { HMRWatcher } from '../server/serverPluginHmr'
import { ServerPluginContext } from '../server'

const getETag = require('etag')

Expand All @@ -14,7 +16,7 @@ interface CacheEntry {
content: string
}

const moduleReadCache = new LRUCache<string, CacheEntry>({
const fsReadCache = new LRUCache<string, CacheEntry>({
max: 10000
})

Expand All @@ -27,7 +29,7 @@ export async function cachedRead(
file: string
): Promise<string> {
const lastModified = fs.statSync(file).mtimeMs
const cached = moduleReadCache.get(file)
const cached = fsReadCache.get(file)
if (ctx) {
ctx.set('Cache-Control', 'no-cache')
ctx.type = mime.lookup(path.extname(file)) || 'js'
Expand All @@ -50,7 +52,7 @@ export async function cachedRead(
}
const content = await fs.readFile(file, 'utf-8')
const etag = getETag(content)
moduleReadCache.set(file, {
fsReadCache.set(file, {
content,
etag,
lastModified
Expand All @@ -60,6 +62,10 @@ export async function cachedRead(
ctx.lastModified = new Date(lastModified)
ctx.body = content
ctx.status = 200

// watch the file if it's out of root.
const { root, watcher } = ctx._viteContext as ServerPluginContext
watchFileIfOutOfRoot(watcher, root, file)
}
return content
}
Expand Down Expand Up @@ -102,3 +108,17 @@ export function lookupFile(
return lookupFile(parentDir, formats, pathOnly)
}
}

/**
* Files under root are watched by default, but with user aliases we can still
* serve files out of root. Add such files to the watcher (if not node_modules)
*/
export function watchFileIfOutOfRoot(
watcher: HMRWatcher,
root: string,
file: string
) {
if (!file.startsWith(root) && !/node_modules/.test(file)) {
watcher.add(file)
}
}

0 comments on commit 8fe4284

Please sign in to comment.