From eb0a88514df344cbe4be3165cfa1a26af4f9f6ef Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 7 May 2020 16:44:33 -0400 Subject: [PATCH] fix: support linked monorepos (close #56) --- src/node/resolver.ts | 25 ++++++++++++++++---- src/node/server/serverPluginHmr.ts | 1 + src/node/server/serverPluginModuleResolve.ts | 17 +++++++++---- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/node/resolver.ts b/src/node/resolver.ts index 284ca569791443..6c2b7f3df61c77 100644 --- a/src/node/resolver.ts +++ b/src/node/resolver.ts @@ -2,6 +2,11 @@ import path from 'path' import slash from 'slash' import { statSync } from 'fs' import { cleanUrl } from './utils' +import { + idToFileMap, + moduleRE, + fileToRequestMap +} from './server/serverPluginModuleResolve' export interface Resolver { requestToFile(publicPath: string, root: string): string | undefined @@ -15,11 +20,23 @@ export interface InternalResolver { idToRequest(id: string): string | undefined } -const defaultRequestToFile = (publicPath: string, root: string) => - path.join(root, publicPath.slice(1)) +const defaultRequestToFile = (publicPath: string, root: string): string => { + if (moduleRE.test(publicPath)) { + const moduleFilePath = idToFileMap.get(publicPath.replace(moduleRE, '')) + if (moduleFilePath) { + return moduleFilePath + } + } + return path.join(root, publicPath.slice(1)) +} -const defaultFileToRequest = (filePath: string, root: string) => - `/${slash(path.relative(root, filePath))}` +const defaultFileToRequest = (filePath: string, root: string): string => { + const moduleRequest = fileToRequestMap.get(filePath) + if (moduleRequest) { + return moduleRequest + } + return `/${slash(path.relative(root, filePath))}` +} const defaultIdToRequest = (id: string) => { if (id.startsWith('@') && id.indexOf('/') < 0) { diff --git a/src/node/server/serverPluginHmr.ts b/src/node/server/serverPluginHmr.ts index c1774439c55e89..4ff04b7c23ffe5 100644 --- a/src/node/server/serverPluginHmr.ts +++ b/src/node/server/serverPluginHmr.ts @@ -151,6 +151,7 @@ export const hmrPlugin: Plugin = ({ root, app, server, watcher, resolver }) => { const prevDescriptor = cacheEntry && cacheEntry.descriptor if (!prevDescriptor) { // the file has never been accessed yet + debugHmr(`no existing descriptor found for ${file}`) return } diff --git a/src/node/server/serverPluginModuleResolve.ts b/src/node/server/serverPluginModuleResolve.ts index f4b4242e16198b..a416556cc2109c 100644 --- a/src/node/server/serverPluginModuleResolve.ts +++ b/src/node/server/serverPluginModuleResolve.ts @@ -9,10 +9,11 @@ import slash from 'slash' const debug = require('debug')('vite:resolve') const idToEntryMap = new Map() -const idToFileMap = new Map() +export const idToFileMap = new Map() +export const fileToRequestMap = new Map() const webModulesMap = new Map() -const moduleRE = /^\/@modules\// +export const moduleRE = /^\/@modules\// const getDebugPath = (root: string, p: string) => { const relative = path.relative(root, p) @@ -20,7 +21,7 @@ const getDebugPath = (root: string, p: string) => { } // plugin for resolving /@modules/:id requests. -export const moduleResolvePlugin: Plugin = ({ root, app }) => { +export const moduleResolvePlugin: Plugin = ({ root, app, watcher }) => { app.use(async (ctx, next) => { if (!moduleRE.test(ctx.path)) { return next() @@ -31,10 +32,16 @@ export const moduleResolvePlugin: Plugin = ({ root, app }) => { const serve = async (id: string, file: string, type: string) => { idToFileMap.set(id, file) + fileToRequestMap.set(file, ctx.path) debug(`(${type}) ${id} -> ${getDebugPath(root, file)}`) - // cached read sets etag, body and status on ctx so there is no need - // to go further to other middlewares. 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() } // special handling for vue's runtime.