From 945c94d5d590a7e84d336a5bbf6311422bad534e Mon Sep 17 00:00:00 2001 From: patak Date: Thu, 30 Nov 2023 13:46:27 +0100 Subject: [PATCH 01/10] perf: in-memory public files check --- packages/vite/src/node/server/index.ts | 2 +- .../src/node/server/middlewares/static.ts | 43 +++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 7aeb6cb9065593..3299bcb9b5ecc6 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -733,7 +733,7 @@ export async function _createServer( // this applies before the transform middleware so that these files are served // as-is without transforms. if (config.publicDir) { - middlewares.use(servePublicMiddleware(server)) + middlewares.use(await servePublicMiddleware(server)) } // main transform middleware diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index 0260af1d7109c5..92be3ca60b8195 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -1,4 +1,6 @@ import path from 'node:path' +import fs from 'node:fs' +import fsp from 'node:fs/promises' import type { OutgoingHttpHeaders, ServerResponse } from 'node:http' import type { Options } from 'sirv' import sirv from 'sirv' @@ -16,6 +18,7 @@ import { isParentDirectory, isSameFileUri, isWindows, + normalizePath, removeLeadingSlash, shouldServeFile, slash, @@ -55,9 +58,23 @@ const sirvOptions = ({ } } -export function servePublicMiddleware( +async function recursiveReaddir(dir: string): Promise { + if (!fs.existsSync(dir)) { + return [] + } + const dirents = await fsp.readdir(dir, { withFileTypes: true }) + const files = await Promise.all( + dirents.map((dirent) => { + const res = path.resolve(dir, dirent.name) + return dirent.isDirectory() ? recursiveReaddir(res) : res + }), + ) + return Array.prototype.concat(...files) +} + +export async function servePublicMiddleware( server: ViteDevServer, -): Connect.NextHandleFunction { +): Promise { const dir = server.config.publicDir const serve = sirv( dir, @@ -66,11 +83,29 @@ export function servePublicMiddleware( shouldServe: (filePath) => shouldServeFile(filePath, dir), }), ) + const publicFiles = new Set(await recursiveReaddir(dir)) + const toFilePath = (url: string) => { + let filePath = cleanUrl(url) + if (filePath.indexOf('%') !== -1) { + try { + filePath = decodeURI(filePath) + } catch (err) { + /* malform uri */ + } + } + return normalizePath(path.join(dir, filePath.slice(1))) + } // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` return function viteServePublicMiddleware(req, res, next) { - // skip import request and internal requests `/@fs/ /@vite-client` etc... - if (isImportRequest(req.url!) || isInternalRequest(req.url!)) { + // To avoid the performance impact of `existsSync` on every request, we check against an + // in-memory set of known public files. This set is updated on restarts. + // also skip import request and internal requests `/@fs/ /@vite-client` etc... + if ( + !publicFiles.has(toFilePath(req.url!)) || + isImportRequest(req.url!) || + isInternalRequest(req.url!) + ) { return next() } serve(req, res, next) From be155a35a9f9c9d51ec62c6622e664e9eb954793 Mon Sep 17 00:00:00 2001 From: patak Date: Thu, 30 Nov 2023 14:01:13 +0100 Subject: [PATCH 02/10] fix: windows --- packages/vite/src/node/server/middlewares/static.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index 92be3ca60b8195..e17e770f15620d 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -66,7 +66,7 @@ async function recursiveReaddir(dir: string): Promise { const files = await Promise.all( dirents.map((dirent) => { const res = path.resolve(dir, dirent.name) - return dirent.isDirectory() ? recursiveReaddir(res) : res + return dirent.isDirectory() ? recursiveReaddir(res) : normalizePath(res) }), ) return Array.prototype.concat(...files) From 1391ca82583874185db50df9f120fe683274c792 Mon Sep 17 00:00:00 2001 From: patak Date: Thu, 30 Nov 2023 19:04:19 +0100 Subject: [PATCH 03/10] refactor: server._publicFiles --- packages/vite/src/node/server/index.ts | 24 +++++++++++++----- .../src/node/server/middlewares/static.ts | 25 ++----------------- packages/vite/src/node/utils.ts | 15 +++++++++++ playground/html/main.js | 2 +- playground/html/public/sprite.svg | 2 +- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 3299bcb9b5ecc6..3c5f151a1584e7 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -32,6 +32,7 @@ import { isParentDirectory, mergeConfig, normalizePath, + recursiveReaddir, resolveHostname, resolveServerUrls, } from '../utils' @@ -320,6 +321,10 @@ export interface ViteDevServer { * @internal */ _setInternalServer(server: ViteDevServer): void + /** + * @internal + */ + _publicFiles: Set /** * @internal */ @@ -601,6 +606,7 @@ export async function _createServer( // server instance after a restart server = _server }, + _publicFiles: new Set(await recursiveReaddir(config.publicDir)), _restartPromise: null, _importGlobMap: new Map(), _forceOptimizeOnRestart: false, @@ -639,17 +645,23 @@ export async function _createServer( const onFileAddUnlink = async (file: string, isUnlink: boolean) => { file = normalizePath(file) await container.watchChange(file, { event: isUnlink ? 'delete' : 'create' }) - await handleFileAddUnlink(file, server, isUnlink) - await onHMRUpdate(file, true) + + if (config.publicDir && file.startsWith(config.publicDir)) { + server._publicFiles[isUnlink ? 'delete' : 'add'](file) + } else { + await handleFileAddUnlink(file, server, isUnlink) + await onHMRUpdate(file, true) + } } watcher.on('change', async (file) => { file = normalizePath(file) await container.watchChange(file, { event: 'update' }) - // invalidate module graph cache on file change - moduleGraph.onFileChange(file) - - await onHMRUpdate(file, false) + if (!(config.publicDir && file.startsWith(config.publicDir))) { + // invalidate module graph cache on file change + moduleGraph.onFileChange(file) + await onHMRUpdate(file, false) + } }) watcher.on('add', (file) => onFileAddUnlink(file, false)) diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index e17e770f15620d..f5a94d5609e425 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -1,6 +1,4 @@ import path from 'node:path' -import fs from 'node:fs' -import fsp from 'node:fs/promises' import type { OutgoingHttpHeaders, ServerResponse } from 'node:http' import type { Options } from 'sirv' import sirv from 'sirv' @@ -20,7 +18,6 @@ import { isWindows, normalizePath, removeLeadingSlash, - shouldServeFile, slash, withTrailingSlash, } from '../../utils' @@ -29,10 +26,8 @@ const knownJavascriptExtensionRE = /\.[tj]sx?$/ const sirvOptions = ({ getHeaders, - shouldServe, }: { getHeaders: () => OutgoingHttpHeaders | undefined - shouldServe?: (p: string) => void }): Options => { return { dev: true, @@ -54,24 +49,9 @@ const sirvOptions = ({ } } }, - shouldServe, } } -async function recursiveReaddir(dir: string): Promise { - if (!fs.existsSync(dir)) { - return [] - } - const dirents = await fsp.readdir(dir, { withFileTypes: true }) - const files = await Promise.all( - dirents.map((dirent) => { - const res = path.resolve(dir, dirent.name) - return dirent.isDirectory() ? recursiveReaddir(res) : normalizePath(res) - }), - ) - return Array.prototype.concat(...files) -} - export async function servePublicMiddleware( server: ViteDevServer, ): Promise { @@ -80,10 +60,9 @@ export async function servePublicMiddleware( dir, sirvOptions({ getHeaders: () => server.config.server.headers, - shouldServe: (filePath) => shouldServeFile(filePath, dir), }), ) - const publicFiles = new Set(await recursiveReaddir(dir)) + const toFilePath = (url: string) => { let filePath = cleanUrl(url) if (filePath.indexOf('%') !== -1) { @@ -102,7 +81,7 @@ export async function servePublicMiddleware( // in-memory set of known public files. This set is updated on restarts. // also skip import request and internal requests `/@fs/ /@vite-client` etc... if ( - !publicFiles.has(toFilePath(req.url!)) || + !server._publicFiles.has(toFilePath(req.url!)) || isImportRequest(req.url!) || isInternalRequest(req.url!) ) { diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 4e893968c44444..ccfdfc40ac4028 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -8,6 +8,7 @@ import { builtinModules, createRequire } from 'node:module' import { promises as dns } from 'node:dns' import { performance } from 'node:perf_hooks' import type { AddressInfo, Server } from 'node:net' +import fsp from 'node:fs/promises' import type { FSWatcher } from 'chokidar' import remapping from '@ampproject/remapping' import type { DecodedSourceMap, RawSourceMap } from '@ampproject/remapping' @@ -622,6 +623,20 @@ export function copyDir(srcDir: string, destDir: string): void { } } +export async function recursiveReaddir(dir: string): Promise { + if (!fs.existsSync(dir)) { + return [] + } + const dirents = await fsp.readdir(dir, { withFileTypes: true }) + const files = await Promise.all( + dirents.map((dirent) => { + const res = path.resolve(dir, dirent.name) + return dirent.isDirectory() ? recursiveReaddir(res) : normalizePath(res) + }), + ) + return Array.prototype.concat(...files) +} + // `fs.realpathSync.native` resolves differently in Windows network drive, // causing file read errors. skip for now. // https://github.com/nodejs/node/issues/37737 diff --git a/playground/html/main.js b/playground/html/main.js index cd769880553ced..01c327757920ad 100644 --- a/playground/html/main.js +++ b/playground/html/main.js @@ -1,4 +1,4 @@ import { msg } from './shared' import './common.css' -console.log(msg + ' from main') +console.log(msg + ' from main 2') diff --git a/playground/html/public/sprite.svg b/playground/html/public/sprite.svg index b948cff92b6e39..90938fc7013de1 100644 --- a/playground/html/public/sprite.svg +++ b/playground/html/public/sprite.svg @@ -1,4 +1,4 @@ - + Date: Thu, 30 Nov 2023 21:20:34 +0100 Subject: [PATCH 04/10] perf: checkPublicFile using publicFiles cache --- packages/vite/src/node/plugins/asset.ts | 30 ++--------- packages/vite/src/node/plugins/css.ts | 2 +- packages/vite/src/node/plugins/html.ts | 2 +- .../vite/src/node/plugins/importAnalysis.ts | 3 +- packages/vite/src/node/publicDir.ts | 50 +++++++++++++++++++ packages/vite/src/node/server/index.ts | 15 +++--- .../src/node/server/middlewares/indexHtml.ts | 2 +- .../src/node/server/middlewares/static.ts | 7 +-- .../vite/src/node/server/transformRequest.ts | 2 +- 9 files changed, 70 insertions(+), 43 deletions(-) create mode 100644 packages/vite/src/node/publicDir.ts diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 6b9f90fc77155c..e8855a43d1683b 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -1,6 +1,5 @@ import path from 'node:path' import { parse as parseUrl } from 'node:url' -import fs from 'node:fs' import fsp from 'node:fs/promises' import { Buffer } from 'node:buffer' import * as mrmime from 'mrmime' @@ -17,6 +16,7 @@ import { } from '../build' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' +import { checkPublicFile } from '../publicDir' import { cleanUrl, getHash, @@ -249,32 +249,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { } } -export function checkPublicFile( - url: string, - { publicDir }: ResolvedConfig, -): string | undefined { - // note if the file is in /public, the resolver would have returned it - // as-is so it's not going to be a fully resolved path. - if (!publicDir || url[0] !== '/') { - return - } - const publicFile = path.join(publicDir, cleanUrl(url)) - if ( - !normalizePath(publicFile).startsWith( - withTrailingSlash(normalizePath(publicDir)), - ) - ) { - // can happen if URL starts with '../' - return - } - if (fs.existsSync(publicFile)) { - return publicFile - } else { - return - } -} - -export async function fileToUrl( +export async function fileToUrl( // TODO id: string, config: ResolvedConfig, ctx: PluginContext, @@ -287,6 +262,7 @@ export async function fileToUrl( } function fileToDevUrl(id: string, config: ResolvedConfig) { + // TODO let rtn: string if (checkPublicFile(id, config)) { // in public dir during dev, keep the url as-is diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 9707273c1ad6dc..fafd0ddf38a08e 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -36,6 +36,7 @@ import { } from '../constants' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' +import { checkPublicFile } from '../publicDir' import { arraify, asyncReplace, @@ -62,7 +63,6 @@ import type { Logger } from '../logger' import { addToHTMLProxyTransformResult } from './html' import { assetUrlRE, - checkPublicFile, fileToUrl, generatedAssets, publicAssetUrlCache, diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 3cb5ac20bfd3f4..48abc6aebda1a9 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -24,12 +24,12 @@ import { urlCanParse, } from '../utils' import type { ResolvedConfig } from '../config' +import { checkPublicFile } from '../publicDir' import { toOutputFilePathInHtml } from '../build' import { resolveEnvPrefix } from '../env' import type { Logger } from '../logger' import { assetUrlRE, - checkPublicFile, getPublicAssetFilename, publicAssetUrlRE, urlToBuiltUrl, diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 08b827d4d8fb75..0988d2550c92ef 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -52,12 +52,13 @@ import { withTrailingSlash, wrapId, } from '../utils' +import { checkPublicFile } from '../publicDir' import { getDepOptimizationConfig } from '../config' import type { ResolvedConfig } from '../config' import type { Plugin } from '../plugin' import { shouldExternalizeForSSR } from '../ssr/ssrExternal' import { getDepsOptimizer, optimizedDepNeedsInterop } from '../optimizer' -import { checkPublicFile, urlRE } from './asset' +import { urlRE } from './asset' import { throwOutdatedRequest } from './optimizedDeps' import { isCSSRequest, isDirectCSSRequest } from './css' import { browserExternalId } from './resolve' diff --git a/packages/vite/src/node/publicDir.ts b/packages/vite/src/node/publicDir.ts new file mode 100644 index 00000000000000..02b81b0756196e --- /dev/null +++ b/packages/vite/src/node/publicDir.ts @@ -0,0 +1,50 @@ +import fs from 'node:fs' +import path from 'node:path' +import type { ResolvedConfig } from './config' +import { + cleanUrl, + normalizePath, + recursiveReaddir, + withTrailingSlash, +} from './utils' + +const publicFilesMap = new WeakMap>() + +export async function initPublicFiles( + config: ResolvedConfig, +): Promise> { + const publicFiles = new Set(await recursiveReaddir(config.publicDir)) + publicFilesMap.set(config, publicFiles) + return publicFiles +} + +function getPublicFiles(config: ResolvedConfig): Set | undefined { + return publicFilesMap.get(config) +} + +export function checkPublicFile( + url: string, + config: ResolvedConfig, +): string | undefined { + // note if the file is in /public, the resolver would have returned it + // as-is so it's not going to be a fully resolved path. + const { publicDir } = config + if (!publicDir || url[0] !== '/') { + return + } + + const publicFile = normalizePath(path.join(publicDir, cleanUrl(url))) + + // short-circuit if we have an in-memory publicFiles cache + const publicFiles = getPublicFiles(config) + if (publicFiles) { + return publicFiles.has(publicFile) ? publicFile : undefined + } + + if (!publicFile.startsWith(withTrailingSlash(normalizePath(publicDir)))) { + // can happen if URL starts with '../' + return + } + + return fs.existsSync(publicFile) ? publicFile : undefined +} diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 3c5f151a1584e7..0b7cb4e288ce8b 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -32,7 +32,6 @@ import { isParentDirectory, mergeConfig, normalizePath, - recursiveReaddir, resolveHostname, resolveServerUrls, } from '../utils' @@ -51,6 +50,7 @@ import { CLIENT_DIR, DEFAULT_DEV_PORT } from '../constants' import type { Logger } from '../logger' import { printServerUrls } from '../logger' import { createNoopWatcher, resolveChokidarOptions } from '../watch' +import { initPublicFiles } from '../publicDir' import type { PluginContainer } from './pluginContainer' import { ERR_CLOSED_SERVER, createPluginContainer } from './pluginContainer' import type { WebSocketServer } from './ws' @@ -321,10 +321,6 @@ export interface ViteDevServer { * @internal */ _setInternalServer(server: ViteDevServer): void - /** - * @internal - */ - _publicFiles: Set /** * @internal */ @@ -383,6 +379,8 @@ export async function _createServer( ): Promise { const config = await resolveConfig(inlineConfig, 'serve') + const initPublicFilesPromise = initPublicFiles(config) + const { root, server: serverConfig } = config const httpsOptions = await resolveHttpsConfig(config.server.https) const { middlewareMode } = serverConfig @@ -606,7 +604,6 @@ export async function _createServer( // server instance after a restart server = _server }, - _publicFiles: new Set(await recursiveReaddir(config.publicDir)), _restartPromise: null, _importGlobMap: new Map(), _forceOptimizeOnRestart: false, @@ -629,6 +626,8 @@ export async function _createServer( } } + const publicFiles = await initPublicFilesPromise + const onHMRUpdate = async (file: string, configOnly: boolean) => { if (serverConfig.hmr !== false) { try { @@ -647,7 +646,7 @@ export async function _createServer( await container.watchChange(file, { event: isUnlink ? 'delete' : 'create' }) if (config.publicDir && file.startsWith(config.publicDir)) { - server._publicFiles[isUnlink ? 'delete' : 'add'](file) + publicFiles[isUnlink ? 'delete' : 'add'](file) } else { await handleFileAddUnlink(file, server, isUnlink) await onHMRUpdate(file, true) @@ -745,7 +744,7 @@ export async function _createServer( // this applies before the transform middleware so that these files are served // as-is without transforms. if (config.publicDir) { - middlewares.use(await servePublicMiddleware(server)) + middlewares.use(servePublicMiddleware(server, publicFiles)) } // main transform middleware diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index f4247558fe4c67..ee20ae24695cc2 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -41,8 +41,8 @@ import { unwrapId, wrapId, } from '../../utils' +import { checkPublicFile } from '../../publicDir' import { isCSSRequest } from '../../plugins/css' -import { checkPublicFile } from '../../plugins/asset' import { getCodeWithSourcemap, injectSourcesContent } from '../sourcemap' interface AssetNode { diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index f5a94d5609e425..0aecf569df5e18 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -52,9 +52,10 @@ const sirvOptions = ({ } } -export async function servePublicMiddleware( +export function servePublicMiddleware( server: ViteDevServer, -): Promise { + publicFiles: Set, +): Connect.NextHandleFunction { const dir = server.config.publicDir const serve = sirv( dir, @@ -81,7 +82,7 @@ export async function servePublicMiddleware( // in-memory set of known public files. This set is updated on restarts. // also skip import request and internal requests `/@fs/ /@vite-client` etc... if ( - !server._publicFiles.has(toFilePath(req.url!)) || + !publicFiles.has(toFilePath(req.url!)) || isImportRequest(req.url!) || isInternalRequest(req.url!) ) { diff --git a/packages/vite/src/node/server/transformRequest.ts b/packages/vite/src/node/server/transformRequest.ts index 0881354a6ec4e5..a37fdbe83fff7e 100644 --- a/packages/vite/src/node/server/transformRequest.ts +++ b/packages/vite/src/node/server/transformRequest.ts @@ -22,7 +22,7 @@ import { timeFrom, unwrapId, } from '../utils' -import { checkPublicFile } from '../plugins/asset' +import { checkPublicFile } from '../publicDir' import { getDepsOptimizer } from '../optimizer' import { applySourcemapIgnoreList, injectSourcesContent } from './sourcemap' import { isFileServingAllowed } from './middlewares/static' From 4bb5f1415b0e8c99783581dedf775f8e713311d8 Mon Sep 17 00:00:00 2001 From: patak Date: Thu, 30 Nov 2023 21:52:54 +0100 Subject: [PATCH 05/10] perf: avoid path.join calls --- packages/vite/src/node/publicDir.ts | 12 +++++++++--- packages/vite/src/node/server/index.ts | 4 +++- packages/vite/src/node/server/middlewares/static.ts | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/vite/src/node/publicDir.ts b/packages/vite/src/node/publicDir.ts index 02b81b0756196e..f2055fff8afb9d 100644 --- a/packages/vite/src/node/publicDir.ts +++ b/packages/vite/src/node/publicDir.ts @@ -13,7 +13,10 @@ const publicFilesMap = new WeakMap>() export async function initPublicFiles( config: ResolvedConfig, ): Promise> { - const publicFiles = new Set(await recursiveReaddir(config.publicDir)) + const fileNames = await recursiveReaddir(config.publicDir) + const publicFiles = new Set( + fileNames.map((fileName) => fileName.slice(config.publicDir.length)), + ) publicFilesMap.set(config, publicFiles) return publicFiles } @@ -33,14 +36,17 @@ export function checkPublicFile( return } - const publicFile = normalizePath(path.join(publicDir, cleanUrl(url))) + const fileName = cleanUrl(url) // short-circuit if we have an in-memory publicFiles cache const publicFiles = getPublicFiles(config) if (publicFiles) { - return publicFiles.has(publicFile) ? publicFile : undefined + return publicFiles.has(fileName) + ? normalizePath(path.join(publicDir, fileName)) + : undefined } + const publicFile = normalizePath(path.join(publicDir, fileName)) if (!publicFile.startsWith(withTrailingSlash(normalizePath(publicDir)))) { // can happen if URL starts with '../' return diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 0b7cb4e288ce8b..f78f559263607e 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -646,7 +646,9 @@ export async function _createServer( await container.watchChange(file, { event: isUnlink ? 'delete' : 'create' }) if (config.publicDir && file.startsWith(config.publicDir)) { - publicFiles[isUnlink ? 'delete' : 'add'](file) + publicFiles[isUnlink ? 'delete' : 'add']( + file.slice(config.publicDir.length), + ) } else { await handleFileAddUnlink(file, server, isUnlink) await onHMRUpdate(file, true) diff --git a/packages/vite/src/node/server/middlewares/static.ts b/packages/vite/src/node/server/middlewares/static.ts index 0aecf569df5e18..57f995d8e1b0f8 100644 --- a/packages/vite/src/node/server/middlewares/static.ts +++ b/packages/vite/src/node/server/middlewares/static.ts @@ -73,7 +73,7 @@ export function servePublicMiddleware( /* malform uri */ } } - return normalizePath(path.join(dir, filePath.slice(1))) + return normalizePath(filePath) } // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` From b41f3f1ab87121419ccbbac00d3356d7cb551370 Mon Sep 17 00:00:00 2001 From: patak Date: Thu, 30 Nov 2023 22:02:45 +0100 Subject: [PATCH 06/10] chore: remove test changes --- playground/html/main.js | 2 +- playground/html/public/sprite.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playground/html/main.js b/playground/html/main.js index 01c327757920ad..cd769880553ced 100644 --- a/playground/html/main.js +++ b/playground/html/main.js @@ -1,4 +1,4 @@ import { msg } from './shared' import './common.css' -console.log(msg + ' from main 2') +console.log(msg + ' from main') diff --git a/playground/html/public/sprite.svg b/playground/html/public/sprite.svg index 90938fc7013de1..b948cff92b6e39 100644 --- a/playground/html/public/sprite.svg +++ b/playground/html/public/sprite.svg @@ -1,4 +1,4 @@ - + Date: Thu, 30 Nov 2023 23:11:06 +0100 Subject: [PATCH 07/10] chore: remove TODOs --- packages/vite/src/node/plugins/asset.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index e8855a43d1683b..4cdd846e4cab42 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -249,7 +249,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin { } } -export async function fileToUrl( // TODO +export async function fileToUrl( id: string, config: ResolvedConfig, ctx: PluginContext, @@ -262,7 +262,6 @@ export async function fileToUrl( // TODO } function fileToDevUrl(id: string, config: ResolvedConfig) { - // TODO let rtn: string if (checkPublicFile(id, config)) { // in public dir during dev, keep the url as-is From 61d6191c4ecea32274a6c590bd9e463253767a2d Mon Sep 17 00:00:00 2001 From: patak Date: Sat, 2 Dec 2023 20:55:06 +0100 Subject: [PATCH 08/10] refactor: use .flat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 / green --- packages/vite/src/node/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index ccfdfc40ac4028..3c502a029aa721 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -634,7 +634,7 @@ export async function recursiveReaddir(dir: string): Promise { return dirent.isDirectory() ? recursiveReaddir(res) : normalizePath(res) }), ) - return Array.prototype.concat(...files) + return files.flat(1) } // `fs.realpathSync.native` resolves differently in Windows network drive, From ee1d6a2be3b5f22c64e2999a790ba5e346dfdaad Mon Sep 17 00:00:00 2001 From: patak Date: Sat, 2 Dec 2023 21:07:39 +0100 Subject: [PATCH 09/10] fix: handle EACCES in recursiveReaddir --- packages/vite/src/node/utils.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index 3c502a029aa721..d885cf7a5a727b 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -627,7 +627,16 @@ export async function recursiveReaddir(dir: string): Promise { if (!fs.existsSync(dir)) { return [] } - const dirents = await fsp.readdir(dir, { withFileTypes: true }) + let dirents: fs.Dirent[] + try { + dirents = await fsp.readdir(dir, { withFileTypes: true }) + } catch (e) { + if (e.code === 'EACCES') { + // Ignore permission errors + return [] + } + throw e + } const files = await Promise.all( dirents.map((dirent) => { const res = path.resolve(dir, dirent.name) From 88ed4c4beb4b1311067ac4db2083a44f2e537cc6 Mon Sep 17 00:00:00 2001 From: patak Date: Mon, 4 Dec 2023 11:56:21 +0100 Subject: [PATCH 10/10] fix: remove public file guards on watch change --- packages/vite/src/node/server/index.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index f78f559263607e..2b799dbfd2b8e7 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -649,20 +649,17 @@ export async function _createServer( publicFiles[isUnlink ? 'delete' : 'add']( file.slice(config.publicDir.length), ) - } else { - await handleFileAddUnlink(file, server, isUnlink) - await onHMRUpdate(file, true) } + await handleFileAddUnlink(file, server, isUnlink) + await onHMRUpdate(file, true) } watcher.on('change', async (file) => { file = normalizePath(file) await container.watchChange(file, { event: 'update' }) - if (!(config.publicDir && file.startsWith(config.publicDir))) { - // invalidate module graph cache on file change - moduleGraph.onFileChange(file) - await onHMRUpdate(file, false) - } + // invalidate module graph cache on file change + moduleGraph.onFileChange(file) + await onHMRUpdate(file, false) }) watcher.on('add', (file) => onFileAddUnlink(file, false))