Skip to content

Commit

Permalink
fix(typescript): worker execution failed with custom next.config.js (
Browse files Browse the repository at this point in the history
…#37125)

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

The PR fixes #37122, an issue introduced by #37105.

`next.config.js` might/will include functions (custom `webpack`, `generateBuildId`, `exportPathsMap`, etc.) that are not able to pass from the main thread to a worker. The PR fixes the issue by only passing primitive args to the worker.
  • Loading branch information
SukkaW committed May 23, 2022
1 parent d64512f commit e57e275
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 19 deletions.
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

0 comments on commit e57e275

Please sign in to comment.