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(typescript): worker execution failed with custom next.config.js #37125

Merged
merged 8 commits into from
May 23, 2022
9 changes: 6 additions & 3 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ export default async function build(
dir,
[pagesDir, viewsDir].filter(Boolean) as string[],
!ignoreTypeScriptErrors,
config,
config.typescript.tsconfigPath,
config.images.disableStaticImages,
cacheDir,
config.experimental.cpus,
config.experimental.workerThreads
Expand Down Expand Up @@ -2353,7 +2354,8 @@ function verifyTypeScriptSetup(
dir: string,
intentDirs: string[],
typeCheckPreflight: boolean,
config: NextConfigComplete,
tsconfigPath: string,
disableStaticImages: boolean,
cacheDir: string | undefined,
numWorkers: number | undefined,
enableWorkerThreads: boolean | undefined
Expand All @@ -2376,7 +2378,8 @@ function verifyTypeScriptSetup(
dir,
intentDirs,
typeCheckPreflight,
config,
tsconfigPath,
disableStaticImages,
cacheDir
)
.then((result) => {
Expand Down
17 changes: 9 additions & 8 deletions packages/next/lib/typescript/getTypeScriptIntent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { promises as fs } from 'fs'
import path from 'path'
import { NextConfigComplete } from '../../server/config-shared'
import { fileExists } from '../file-exists'
import { recursiveReadDir } from '../recursive-readdir'

Expand All @@ -9,18 +8,20 @@ export type TypeScriptIntent = { firstTimeSetup: boolean }
export async function getTypeScriptIntent(
baseDir: string,
intentDirs: string[],
config: NextConfigComplete
tsconfigPath: string
): Promise<TypeScriptIntent | false> {
const tsConfigPath = path.join(baseDir, config.typescript.tsconfigPath)
const resolvedTsConfigPath = path.join(baseDir, tsconfigPath)

// The integration turns on if we find a `tsconfig.json` in the user's
// project.
const hasTypeScriptConfiguration = await fileExists(tsConfigPath)
const hasTypeScriptConfiguration = await fileExists(resolvedTsConfigPath)
if (hasTypeScriptConfiguration) {
const content = await fs.readFile(tsConfigPath, { encoding: 'utf8' }).then(
(txt) => txt.trim(),
() => null
)
const content = await fs
.readFile(resolvedTsConfigPath, { encoding: 'utf8' })
.then(
(txt) => txt.trim(),
() => null
)
return { firstTimeSetup: content === '' || content === '{}' }
}

Expand Down
18 changes: 11 additions & 7 deletions packages/next/lib/verifyTypeScriptSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { TypeCheckResult } from './typescript/runTypeCheck'
import { writeAppTypeDeclarations } from './typescript/writeAppTypeDeclarations'
import { writeConfigurationDefaults } from './typescript/writeConfigurationDefaults'
import { missingDepsError } from './typescript/missingDependencyError'
import { NextConfigComplete } from '../server/config-shared'

const requiredPackages = [
{ file: 'typescript', pkg: 'typescript', exportsRestrict: false },
Expand All @@ -34,14 +33,15 @@ export async function verifyTypeScriptSetup(
dir: string,
intentDirs: string[],
typeCheckPreflight: boolean,
config: NextConfigComplete,
tsconfigPath: string,
disableStaticImages: boolean,
cacheDir?: string
): Promise<{ result?: TypeCheckResult; version: string | null }> {
const tsConfigPath = path.join(dir, config.typescript.tsconfigPath)
const resolvedTsConfigPath = path.join(dir, tsconfigPath)

try {
// Check if the project uses TypeScript:
const intent = await getTypeScriptIntent(dir, intentDirs, config)
const intent = await getTypeScriptIntent(dir, intentDirs, tsconfigPath)
if (!intent) {
return { version: null }
}
Expand All @@ -68,17 +68,21 @@ export async function verifyTypeScriptSetup(
}

// Reconfigure (or create) the user's `tsconfig.json` for them:
await writeConfigurationDefaults(ts, tsConfigPath, intent.firstTimeSetup)
await writeConfigurationDefaults(
ts,
resolvedTsConfigPath,
intent.firstTimeSetup
)
// Write out the necessary `next-env.d.ts` file to correctly register
// Next.js' types:
await writeAppTypeDeclarations(dir, !config.images.disableStaticImages)
await writeAppTypeDeclarations(dir, !disableStaticImages)

let result
if (typeCheckPreflight) {
const { runTypeCheck } = require('./typescript/runTypeCheck')

// Verify the project passes type-checking before we go to webpack phase:
result = await runTypeCheck(ts, dir, tsConfigPath, cacheDir)
result = await runTypeCheck(ts, dir, resolvedTsConfigPath, cacheDir)
}
return { result, version: ts.version }
} catch (err) {
Expand Down
3 changes: 2 additions & 1 deletion packages/next/server/dev/next-dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ export default class DevServer extends Server {
this.dir,
[this.pagesDir!, this.viewsDir].filter(Boolean) as string[],
false,
this.nextConfig
this.nextConfig.typescript.tsconfigPath,
this.nextConfig.images.disableStaticImages
)

this.customRoutes = await loadCustomRoutes(this.nextConfig)
Expand Down
23 changes: 23 additions & 0 deletions test/integration/typescript/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,29 @@ export default function EvilPage(): JSX.Element {
expect(output.code).toBe(0)
})

it('should build the app with functions in next.config.js', async () => {
const nextConfig = new File(join(appDir, 'next.config.js'))

nextConfig.write(`
module.exports = {
webpack(config) { return config },
onDemandEntries: {
// Make sure entries are not getting disposed.
maxInactiveAge: 1000 * 60 * 60,
},
}
`)

try {
const output = await nextBuild(appDir, [], { stdout: true })

expect(output.stdout).toMatch(/Compiled successfully/)
expect(output.code).toBe(0)
} finally {
nextConfig.restore()
}
})

it('should not inform when using default tsconfig path', async () => {
const output = await nextBuild(appDir, [], { stdout: true })
expect(output.stdout).not.toMatch(/Using tsconfig file:/)
Expand Down