Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: optimized processing folder renaming in win (fix #7939) #8019

Merged
merged 1 commit into from May 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 9 additions & 14 deletions packages/vite/src/node/optimizer/index.ts
Expand Up @@ -13,7 +13,9 @@ import {
normalizePath,
writeFile,
flattenId,
normalizeId
normalizeId,
removeDirSync,
renameDir
} from '../utils'
import { esbuildDepPlugin } from './esbuildDepPlugin'
import { init, parse } from 'es-module-lexer'
Expand Down Expand Up @@ -116,7 +118,7 @@ export interface DepOptimizationResult {
* the page reload will be delayed until the next rerun so we need
* to be able to discard the result
*/
commit: () => void
commit: () => Promise<void>
cancel: () => void
}

Expand Down Expand Up @@ -194,7 +196,7 @@ export async function optimizeDeps(

const result = await runOptimizeDeps(config, depsInfo)

result.commit()
await result.commit()

return result.metadata
}
Expand Down Expand Up @@ -376,7 +378,7 @@ export async function runOptimizeDeps(
metadata,
commit() {
// Write metadata file, delete `deps` folder and rename the `processing` folder to `deps`
commitProcessingDepsCacheSync()
return commitProcessingDepsCacheSync()
},
cancel
}
Expand Down Expand Up @@ -529,30 +531,23 @@ export async function runOptimizeDeps(
metadata,
commit() {
// Write metadata file, delete `deps` folder and rename the new `processing` folder to `deps` in sync
commitProcessingDepsCacheSync()
return commitProcessingDepsCacheSync()
},
cancel
}

function commitProcessingDepsCacheSync() {
async function commitProcessingDepsCacheSync() {
// Processing is done, we can now replace the depsCacheDir with processingCacheDir
// Rewire the file paths from the temporal processing dir to the final deps cache dir
removeDirSync(depsCacheDir)
fs.renameSync(processingCacheDir, depsCacheDir)
await renameDir(processingCacheDir, depsCacheDir)
}

function cancel() {
removeDirSync(processingCacheDir)
}
}

function removeDirSync(dir: string) {
if (fs.existsSync(dir)) {
const rmSync = fs.rmSync ?? fs.rmdirSync // TODO: Remove after support for Node 12 is dropped
rmSync(dir, { recursive: true })
}
}

export async function findKnownImports(
config: ResolvedConfig
): Promise<string[]> {
Expand Down
8 changes: 4 additions & 4 deletions packages/vite/src/node/optimizer/registerMissing.ts
Expand Up @@ -189,8 +189,8 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps {
)
})

const commitProcessing = () => {
processingResult.commit()
const commitProcessing = async () => {
await processingResult.commit()

// While optimizeDeps is running, new missing deps may be discovered,
// in which case they will keep being added to metadata.discovered
Expand Down Expand Up @@ -240,7 +240,7 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps {
}

if (!needsReload) {
commitProcessing()
await commitProcessing()

if (!isDebugEnabled) {
if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle)
Expand Down Expand Up @@ -270,7 +270,7 @@ export function createOptimizedDeps(server: ViteDevServer): OptimizedDeps {
}
)
} else {
commitProcessing()
await commitProcessing()

if (!isDebugEnabled) {
if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle)
Expand Down
48 changes: 48 additions & 0 deletions packages/vite/src/node/utils.ts
Expand Up @@ -3,6 +3,7 @@ import colors from 'picocolors'
import fs from 'fs'
import os from 'os'
import path from 'path'
import { promisify } from 'util'
import { pathToFileURL, URL } from 'url'
import {
FS_PREFIX,
Expand Down Expand Up @@ -522,6 +523,15 @@ export function copyDir(srcDir: string, destDir: string): void {
}
}

export function removeDirSync(dir: string) {
if (fs.existsSync(dir)) {
const rmSync = fs.rmSync ?? fs.rmdirSync // TODO: Remove after support for Node 12 is dropped
rmSync(dir, { recursive: true })
}
}

export const renameDir = isWindows ? promisify(gracefulRename) : fs.renameSync

export function ensureWatchedFile(
watcher: FSWatcher,
file: string | null,
Expand Down Expand Up @@ -737,3 +747,41 @@ export function parseRequest(id: string): Record<string, string> | null {
}

export const blankReplacer = (match: string) => ' '.repeat(match.length)

// Based on node-graceful-fs

// The ISC License
// Copyright (c) 2011-2022 Isaac Z. Schlueter, Ben Noordhuis, and Contributors
// https://github.com/isaacs/node-graceful-fs/blob/main/LICENSE

// On Windows, A/V software can lock the directory, causing this
// to fail with an EACCES or EPERM if the directory contains newly
// created files. The original tried for up to 60 seconds, we only
// wait for 5 seconds, as a longer time would be seen as an error

const GRACEFUL_RENAME_TIMEOUT = 5000
function gracefulRename(
from: string,
to: string,
cb: (error: NodeJS.ErrnoException | null) => void
) {
const start = Date.now()
let backoff = 0
fs.rename(from, to, function CB(er) {
if (
er &&
(er.code === 'EACCES' || er.code === 'EPERM') &&
Date.now() - start < GRACEFUL_RENAME_TIMEOUT
) {
setTimeout(function () {
fs.stat(to, function (stater, st) {
if (stater && stater.code === 'ENOENT') gracefulRename(from, to, CB)
else cb(er)
})
}, backoff)
if (backoff < 100) backoff += 10
return
}
if (cb) cb(er)
})
}