Skip to content

Commit b940ec8

Browse files
committed
fix: use fs.existsSync to avoid race condition
1 parent 515784c commit b940ec8

16 files changed

+76
-98
lines changed

packages/next/src/build/entries.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type { AppLoaderOptions } from './webpack/loaders/next-app-loader'
1515
import { cyan } from '../lib/picocolors'
1616
import { posix, join, dirname, extname } from 'path'
1717
import { stringify } from 'querystring'
18+
import fs from 'fs'
1819
import {
1920
PAGES_DIR_ALIAS,
2021
ROOT_DIR_ALIAS,
@@ -51,7 +52,6 @@ import { encodeMatchers } from './webpack/loaders/next-middleware-loader'
5152
import { EdgeFunctionLoaderOptions } from './webpack/loaders/next-edge-function-loader'
5253
import { isAppRouteRoute } from '../lib/is-app-route-route'
5354
import { normalizeMetadataRoute } from '../lib/metadata/get-metadata-route'
54-
import { fileExists } from '../lib/file-exists'
5555
import { getRouteLoaderEntry } from './webpack/loaders/next-route-loader'
5656
import {
5757
isInternalComponent,
@@ -123,7 +123,7 @@ export async function getStaticInfoIncludingLayouts({
123123
while (dir.startsWith(appDir)) {
124124
for (const potentialLayoutFile of potentialLayoutFiles) {
125125
const layoutFile = join(dir, potentialLayoutFile)
126-
if (!(await fileExists(layoutFile))) {
126+
if (!fs.existsSync(layoutFile)) {
127127
continue
128128
}
129129
layoutFiles.unshift(layoutFile)

packages/next/src/build/get-babel-config-file.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { fileExists } from '../lib/file-exists'
21
import { join } from 'path'
2+
import { existsSync } from 'fs'
33

44
const BABEL_CONFIG_FILES = [
55
'.babelrc',
@@ -13,12 +13,10 @@ const BABEL_CONFIG_FILES = [
1313
'babel.config.cjs',
1414
]
1515

16-
export async function getBabelConfigFile(
17-
dir: string
18-
): Promise<string | undefined> {
16+
export function getBabelConfigFile(dir: string): string | undefined {
1917
for (const filename of BABEL_CONFIG_FILES) {
2018
const configFilePath = join(dir, filename)
21-
const exists = await fileExists(configFilePath)
19+
const exists = existsSync(configFilePath)
2220
if (!exists) {
2321
continue
2422
}

packages/next/src/build/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { loadEnvConfig } from '@next/env'
1212
import { bold, yellow, green } from '../lib/picocolors'
1313
import crypto from 'crypto'
1414
import { isMatch, makeRe } from 'next/dist/compiled/micromatch'
15-
import fs from 'fs/promises'
15+
import { existsSync, promises as fs } from 'fs'
1616
import os from 'os'
1717
import { Worker } from '../lib/worker'
1818
import { defaultConfig } from '../server/config-shared'
@@ -408,7 +408,7 @@ export default async function build(
408408

409409
const cacheDir = path.join(distDir, 'cache')
410410
if (ciEnvironment.isCI && !ciEnvironment.hasNextSupport) {
411-
const hasCache = await fileExists(cacheDir)
411+
const hasCache = existsSync(cacheDir)
412412

413413
if (!hasCache) {
414414
// Intentionally not piping to stderr in case people fail in CI when
@@ -433,7 +433,7 @@ export default async function build(
433433
const isSrcDir = path
434434
.relative(dir, pagesDir || appDir || '')
435435
.startsWith('src')
436-
const hasPublicDir = await fileExists(publicDir)
436+
const hasPublicDir = existsSync(publicDir)
437437

438438
telemetry.record(
439439
eventCliSession(dir, config, {
@@ -719,7 +719,7 @@ export default async function build(
719719
mappedPages['/_error'].startsWith(PAGES_DIR_ALIAS)
720720

721721
if (hasPublicDir) {
722-
const hasPublicUnderScoreNextDir = await fileExists(
722+
const hasPublicUnderScoreNextDir = existsSync(
723723
path.join(publicDir, '_next')
724724
)
725725
if (hasPublicUnderScoreNextDir) {
@@ -2426,7 +2426,7 @@ export default async function build(
24262426
.join('pages', '404.html')
24272427
.replace(/\\/g, '/')
24282428

2429-
if (await fileExists(orig)) {
2429+
if (existsSync(orig)) {
24302430
await fs.copyFile(
24312431
orig,
24322432
path.join(distDir, 'server', updatedRelativeDest)
@@ -2901,7 +2901,7 @@ export default async function build(
29012901
SERVER_DIRECTORY,
29022902
'app'
29032903
)
2904-
if (await fileExists(originalServerApp)) {
2904+
if (existsSync(originalServerApp)) {
29052905
await recursiveCopy(
29062906
originalServerApp,
29072907
path.join(

packages/next/src/build/load-jsconfig.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import path from 'path'
2-
import { fileExists } from '../lib/file-exists'
2+
import fs from 'fs'
33
import { NextConfigComplete } from '../server/config-shared'
44
import * as Log from './output/log'
55
import { getTypeScriptConfiguration } from '../lib/typescript/getTypeScriptConfiguration'
@@ -53,9 +53,7 @@ export default async function loadJsConfig(
5353
typeScriptPath = deps.resolved.get('typescript')
5454
} catch {}
5555
const tsConfigPath = path.join(dir, config.typescript.tsconfigPath)
56-
const useTypeScript = Boolean(
57-
typeScriptPath && (await fileExists(tsConfigPath))
58-
)
56+
const useTypeScript = Boolean(typeScriptPath && fs.existsSync(tsConfigPath))
5957

6058
let implicitBaseurl
6159
let jsConfig: { compilerOptions: Record<string, any> } | undefined
@@ -78,7 +76,7 @@ export default async function loadJsConfig(
7876
}
7977

8078
const jsConfigPath = path.join(dir, 'jsconfig.json')
81-
if (!useTypeScript && (await fileExists(jsConfigPath))) {
79+
if (!useTypeScript && fs.existsSync(jsConfigPath)) {
8280
jsConfig = parseJsonFile(jsConfigPath)
8381
implicitBaseurl = path.dirname(jsConfigPath)
8482
}

packages/next/src/build/webpack-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ export default async function getBaseWebpackConfig(
496496
? '-experimental'
497497
: ''
498498

499-
const babelConfigFile = await getBabelConfigFile(dir)
499+
const babelConfigFile = getBabelConfigFile(dir)
500500
const distDir = path.join(dir, config.distDir)
501501

502502
let useSWCLoader = !babelConfigFile || config.experimental.forceSwcTransforms

packages/next/src/build/webpack/loaders/next-metadata-image-loader.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ import type {
77
MetadataImageModule,
88
PossibleImageFileNameConvention,
99
} from './metadata/types'
10-
import fs from 'fs/promises'
10+
import { existsSync, promises as fs } from 'fs'
1111
import path from 'path'
1212
import loaderUtils from 'next/dist/compiled/loader-utils3'
1313
import { getImageSize } from '../../../server/image-optimizer'
1414
import { imageExtMimeTypeMap } from '../../../lib/mime-type'
15-
import { fileExists } from '../../../lib/file-exists'
1615
import { WEBPACK_RESOURCE_QUERIES } from '../../../lib/constants'
1716
import { normalizePathSep } from '../../../shared/lib/page-path/normalize-path-sep'
1817

@@ -181,7 +180,7 @@ async function nextMetadataImageLoader(this: any, content: Buffer) {
181180
fileNameBase + '.alt.txt'
182181
)
183182

184-
if (await fileExists(altPath)) {
183+
if (existsSync(altPath)) {
185184
imageData.alt = await fs.readFile(altPath, 'utf8')
186185
}
187186
}

packages/next/src/export/index.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plug
99

1010
import { bold, yellow } from '../lib/picocolors'
1111
import findUp from 'next/dist/compiled/find-up'
12-
import fs from 'fs/promises'
12+
import { existsSync, promises as fs } from 'fs'
1313

1414
import '../server/require-hook'
1515

@@ -52,7 +52,6 @@ import { isAppRouteRoute } from '../lib/is-app-route-route'
5252
import { isAppPageRoute } from '../lib/is-app-page-route'
5353
import isError from '../lib/is-error'
5454
import { needsExperimentalReact } from '../lib/needs-experimental-react'
55-
import { fileExists } from '../lib/file-exists'
5655
import { formatManifest } from '../build/manifests/formatter/format-manifest'
5756

5857
function divideSegments(number: number, segments: number): number[] {
@@ -240,7 +239,7 @@ export async function exportAppImpl(
240239
)
241240
return null
242241
}
243-
if (await fileExists(join(distDir, 'server', 'app'))) {
242+
if (existsSync(join(distDir, 'server', 'app'))) {
244243
throw new ExportError(
245244
'"next export" does not work with App Router. Please use "output: export" in next.config.js https://nextjs.org/docs/advanced-features/static-html-export'
246245
)
@@ -284,7 +283,7 @@ export async function exportAppImpl(
284283

285284
const buildIdFile = join(distDir, BUILD_ID_FILE)
286285

287-
if (!(await fileExists(buildIdFile))) {
286+
if (!existsSync(buildIdFile)) {
288287
throw new ExportError(
289288
`Could not find a production build in the '${distDir}' directory. Try building your app with 'next build' before starting the static export. https://nextjs.org/docs/messages/next-export-no-build-id`
290289
)
@@ -407,7 +406,7 @@ export async function exportAppImpl(
407406
)
408407

409408
// Copy static directory
410-
if (!options.buildExport && (await fileExists(join(dir, 'static')))) {
409+
if (!options.buildExport && existsSync(join(dir, 'static'))) {
411410
if (!options.silent) {
412411
Log.info('Copying "static" directory')
413412
}
@@ -421,7 +420,7 @@ export async function exportAppImpl(
421420
// Copy .next/static directory
422421
if (
423422
!options.buildExport &&
424-
(await fileExists(join(distDir, CLIENT_STATIC_FILES_PATH)))
423+
existsSync(join(distDir, CLIENT_STATIC_FILES_PATH))
425424
) {
426425
if (!options.silent) {
427426
Log.info('Copying "static build" directory')
@@ -664,7 +663,7 @@ export async function exportAppImpl(
664663

665664
const publicDir = join(dir, CLIENT_PUBLIC_FILES_PATH)
666665
// Copy public directory
667-
if (!options.buildExport && (await fileExists(publicDir))) {
666+
if (!options.buildExport && existsSync(publicDir)) {
668667
if (!options.silent) {
669668
Log.info('Copying "public" directory')
670669
}
@@ -818,7 +817,7 @@ export async function exportAppImpl(
818817
const handlerSrc = `${orig}.body`
819818
const handlerDest = join(outDir, route)
820819

821-
if (isAppRouteHandler && (await fileExists(handlerSrc))) {
820+
if (isAppRouteHandler && existsSync(handlerSrc)) {
822821
await fs.mkdir(dirname(handlerDest), { recursive: true })
823822
await fs.copyFile(handlerSrc, handlerDest)
824823
return
@@ -852,7 +851,7 @@ export async function exportAppImpl(
852851
await fs.copyFile(htmlSrc, htmlDest)
853852
await fs.copyFile(jsonSrc, jsonDest)
854853

855-
if (await fileExists(`${orig}.amp.html`)) {
854+
if (existsSync(`${orig}.amp.html`)) {
856855
await fs.mkdir(dirname(ampHtmlDest), { recursive: true })
857856
await fs.copyFile(`${orig}.amp.html`, ampHtmlDest)
858857
}

packages/next/src/lib/download-swc.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const { fetch } = require('next/dist/compiled/undici') as {
88
const { WritableStream } = require('node:stream/web') as {
99
WritableStream: typeof global.WritableStream
1010
}
11-
import { fileExists } from './file-exists'
1211
import { getRegistry } from './helpers/get-registry'
1312
import { getCacheDirectory } from './helpers/get-cache-directory'
1413

@@ -19,20 +18,19 @@ async function extractBinary(
1918
pkgName: string,
2019
tarFileName: string
2120
) {
22-
const cacheDirectory = await getCacheDirectory(
21+
const cacheDirectory = getCacheDirectory(
2322
'next-swc',
2423
process.env['NEXT_SWC_PATH']
2524
)
2625

27-
const extractFromTar = async () => {
28-
await tar.x({
26+
const extractFromTar = () =>
27+
tar.x({
2928
file: path.join(cacheDirectory, tarFileName),
3029
cwd: outputDirectory,
3130
strip: 1,
3231
})
33-
}
3432

35-
if (!(await fileExists(path.join(cacheDirectory, tarFileName)))) {
33+
if (!fs.existsSync(path.join(cacheDirectory, tarFileName))) {
3634
Log.info(`Downloading swc package ${pkgName}...`)
3735
await fs.promises.mkdir(cacheDirectory, { recursive: true })
3836
const tempFile = path.join(
@@ -99,7 +97,7 @@ export async function downloadNativeNextSwc(
9997
const tarFileName = `${pkgName.substring(6)}-${version}.tgz`
10098
const outputDirectory = path.join(bindingsDirectory, pkgName)
10199

102-
if (await fileExists(outputDirectory)) {
100+
if (fs.existsSync(outputDirectory)) {
103101
// if the package is already downloaded a different
104102
// failure occurred than not being present
105103
return
@@ -119,7 +117,7 @@ export async function downloadWasmSwc(
119117
const tarFileName = `${pkgName.substring(6)}-${version}.tgz`
120118
const outputDirectory = path.join(wasmDirectory, pkgName)
121119

122-
if (await fileExists(outputDirectory)) {
120+
if (fs.existsSync(outputDirectory)) {
123121
// if the package is already downloaded a different
124122
// failure occurred than not being present
125123
return

packages/next/src/lib/file-exists.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { constants, promises } from 'fs'
1+
import { existsSync, promises } from 'fs'
22
import isError from './is-error'
33

44
export enum FileType {
@@ -17,10 +17,9 @@ export async function fileExists(
1717
} else if (type === FileType.Directory) {
1818
const stats = await promises.stat(fileName)
1919
return stats.isDirectory()
20-
} else {
21-
await promises.access(fileName, constants.F_OK)
2220
}
23-
return true
21+
22+
return existsSync(fileName)
2423
} catch (err) {
2524
if (
2625
isError(err) &&

packages/next/src/lib/has-necessary-dependencies.ts

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { promises as fs } from 'fs'
2-
import { fileExists } from './file-exists'
1+
import { existsSync, promises as fs } from 'fs'
32
import { resolveFrom } from './resolve-from'
43
import { dirname, join, relative } from 'path'
54

@@ -23,29 +22,25 @@ export async function hasNecessaryDependencies(
2322

2423
await Promise.all(
2524
requiredPackages.map(async (p) => {
26-
try {
27-
const pkgPath = await fs.realpath(
28-
resolveFrom(baseDir, `${p.pkg}/package.json`)
29-
)
30-
const pkgDir = dirname(pkgPath)
25+
const pkgPath = await fs.realpath(
26+
resolveFrom(baseDir, `${p.pkg}/package.json`)
27+
)
28+
const pkgDir = dirname(pkgPath)
3129

32-
if (p.exportsRestrict) {
33-
const fileNameToVerify = relative(p.pkg, p.file)
34-
if (fileNameToVerify) {
35-
const fileToVerify = join(pkgDir, fileNameToVerify)
36-
if (await fileExists(fileToVerify)) {
37-
resolutions.set(p.pkg, fileToVerify)
38-
} else {
39-
return missingPackages.push(p)
40-
}
30+
if (p.exportsRestrict) {
31+
const fileNameToVerify = relative(p.pkg, p.file)
32+
if (fileNameToVerify) {
33+
const fileToVerify = join(pkgDir, fileNameToVerify)
34+
if (existsSync(fileToVerify)) {
35+
resolutions.set(p.pkg, fileToVerify)
4136
} else {
42-
resolutions.set(p.pkg, pkgPath)
37+
return missingPackages.push(p)
4338
}
4439
} else {
45-
resolutions.set(p.pkg, resolveFrom(baseDir, p.file))
40+
resolutions.set(p.pkg, pkgPath)
4641
}
47-
} catch (_) {
48-
return missingPackages.push(p)
42+
} else {
43+
resolutions.set(p.pkg, resolveFrom(baseDir, p.file))
4944
}
5045
})
5146
)

packages/next/src/lib/helpers/get-cache-directory.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import os from 'os'
22
import path from 'path'
3-
import { fileExists } from '../file-exists'
3+
import fs from 'fs'
44

55
// get platform specific cache directory adapted from playwright's handling
66
// https://github.com/microsoft/playwright/blob/7d924470d397975a74a19184c136b3573a974e13/packages/playwright-core/src/utils/registry.ts#L141
7-
export async function getCacheDirectory(
8-
fileDirectory: string,
9-
envPath?: string
10-
) {
7+
export function getCacheDirectory(fileDirectory: string, envPath?: string) {
118
let result
129

1310
if (envPath) {
@@ -29,7 +26,7 @@ export async function getCacheDirectory(
2926
path.join(os.homedir(), '.cache'),
3027
path.join(os.tmpdir()),
3128
]) {
32-
if (await fileExists(dir)) {
29+
if (fs.existsSync(dir)) {
3330
systemCacheDirectory = dir
3431
break
3532
}

packages/next/src/lib/mkcert.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function getBinaryName() {
3636
async function downloadBinary() {
3737
try {
3838
const binaryName = getBinaryName()
39-
const cacheDirectory = await getCacheDirectory('mkcert')
39+
const cacheDirectory = getCacheDirectory('mkcert')
4040
const binaryPath = path.join(cacheDirectory, binaryName)
4141

4242
if (fs.existsSync(binaryPath)) {

0 commit comments

Comments
 (0)