Skip to content

Commit 7b7dc71

Browse files
authored
fix: get auto type-gen to work on turbo, by running type gen in a child process outside turbo/webpack (#6714)
Before on turbo: vercel/next.js#66723
1 parent ba513d5 commit 7b7dc71

File tree

8 files changed

+69
-37
lines changed

8 files changed

+69
-37
lines changed

packages/next/src/utilities/getPayloadHMR.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { GeneratedTypes, Payload } from 'payload'
22
import type { InitOptions, SanitizedConfig } from 'payload/config'
33

44
import { BasePayload } from 'payload'
5-
import { generateTypes } from 'payload/bin'
65
import WebSocket from 'ws'
76

87
let cached: {
@@ -39,7 +38,12 @@ export const reload = async (config: SanitizedConfig, payload: Payload): Promise
3938

4039
// Generate types
4140
if (config.typescript.autoGenerate !== false) {
42-
void generateTypes(config, { log: false })
41+
// We cannot run it directly here, as generate-types imports json-schema-to-typescript, which breaks on turbopack.
42+
// see: https://github.com/vercel/next.js/issues/66723
43+
void payload.bin({
44+
args: ['generate:types'],
45+
log: false,
46+
})
4347
}
4448

4549
await payload.db.init()

packages/next/src/withPayload.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ export const withPayload = (nextConfig = {}) => {
6868
'pino',
6969
'pino-pretty',
7070
'graphql',
71-
'json-schema-to-typescript',
7271
],
7372
webpack: (webpackConfig, webpackOptions) => {
7473
const incomingWebpackConfig =
@@ -84,7 +83,6 @@ export const withPayload = (nextConfig = {}) => {
8483
'drizzle-kit/payload',
8584
'sharp',
8685
'libsql',
87-
'json-schema-to-typescript',
8886
],
8987
ignoreWarnings: [
9088
...(incomingWebpackConfig?.ignoreWarnings || []),

packages/next/webpack.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const componentWebpackConfig = {
1818
'payload/config',
1919
'react-image-crop',
2020
'payload/operations',
21-
'payload/bin',
2221
],
2322
mode: 'production',
2423
module: {

packages/payload/bin.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import { register } from 'node:module'
44
import path from 'node:path'
55
import { fileURLToPath, pathToFileURL } from 'node:url'
66

7-
import { bin } from './dist/bin/index.js'
8-
97
// Allow disabling SWC for debugging
108
if (process.env.DISABLE_SWC !== 'true') {
119
const filename = fileURLToPath(import.meta.url)
@@ -15,4 +13,9 @@ if (process.env.DISABLE_SWC !== 'true') {
1513
register('./dist/bin/loader/index.js', url)
1614
}
1715

18-
bin()
16+
const start = async () => {
17+
const { bin } = await import('./dist/bin/index.js')
18+
bin()
19+
}
20+
21+
void start()

packages/payload/src/config/find.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { findUpSync, pathExistsSync } from 'find-up'
2-
import fs from 'fs'
2+
import { getTsconfig } from 'get-tsconfig'
33
import path from 'path'
44

55
/**
@@ -12,37 +12,30 @@ const getTSConfigPaths = (): {
1212
outPath?: string
1313
rootPath?: string
1414
srcPath?: string
15+
tsConfigPath?: string
1516
} => {
16-
const tsConfigPath = findUpSync('tsconfig.json')
17-
18-
if (!tsConfigPath) {
19-
return {
20-
rootPath: process.cwd(),
21-
}
22-
}
17+
const tsConfigResult = getTsconfig()
18+
const tsConfig = tsConfigResult.config
19+
const tsConfigDir = path.dirname(tsConfigResult.path)
2320

2421
try {
25-
// Read the file as a string and remove trailing commas
26-
const rawTsConfig = fs
27-
.readFileSync(tsConfigPath, 'utf-8')
28-
.replace(/,\s*\]/g, ']')
29-
.replace(/,\s*\}/g, '}')
30-
31-
const tsConfig = JSON.parse(rawTsConfig)
32-
33-
const rootPath = process.cwd()
22+
const rootConfigDir = path.resolve(tsConfigDir, tsConfig.compilerOptions.baseUrl || '')
3423
const srcPath = tsConfig.compilerOptions?.rootDir || path.resolve(process.cwd(), 'src')
3524
const outPath = tsConfig.compilerOptions?.outDir || path.resolve(process.cwd(), 'dist')
36-
const tsConfigDir = path.dirname(tsConfigPath)
37-
let configPath = tsConfig.compilerOptions?.paths?.['@payload-config']?.[0]
25+
let configPath = path.resolve(
26+
rootConfigDir,
27+
tsConfig.compilerOptions?.paths?.['@payload-config']?.[0],
28+
)
29+
3830
if (configPath) {
39-
configPath = path.resolve(tsConfigDir, configPath)
31+
configPath = path.resolve(rootConfigDir, configPath)
4032
}
4133
return {
4234
configPath,
4335
outPath,
44-
rootPath,
36+
rootPath: rootConfigDir,
4537
srcPath,
38+
tsConfigPath: tsConfigResult.path,
4639
}
4740
} catch (error) {
4841
console.error(`Error parsing tsconfig.json: ${error}`) // Do not throw the error, as we can still continue with the other config path finding methods
@@ -70,6 +63,11 @@ export const findConfig = (): string => {
7063

7164
const { configPath, outPath, rootPath, srcPath } = getTSConfigPaths()
7265

66+
// if configPath is absolute file, not folder, return it
67+
if (path.extname(configPath) === '.js' || path.extname(configPath) === '.ts') {
68+
return configPath
69+
}
70+
7371
const searchPaths =
7472
process.env.NODE_ENV === 'production'
7573
? [configPath, outPath, srcPath, rootPath]

packages/payload/src/exports/bin.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/payload/src/index.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql'
22
import type { OperationArgs, Request as graphQLRequest } from 'graphql-http'
33
import type pino from 'pino'
44

5+
import { spawn } from 'child_process'
56
import crypto from 'crypto'
7+
import { fileURLToPath } from 'node:url'
8+
import path from 'path'
69

710
import type { AuthArgs } from './auth/operations/auth.js'
811
import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword.js'
@@ -47,7 +50,6 @@ import type { TypeWithVersion } from './versions/types.js'
4750
import { decrypt, encrypt } from './auth/crypto.js'
4851
import { APIKeyAuthentication } from './auth/strategies/apiKey.js'
4952
import { JWTAuthentication } from './auth/strategies/jwt.js'
50-
import { generateTypes } from './bin/generateTypes.js'
5153
import localOperations from './collections/operations/local/index.js'
5254
import { validateSchema } from './config/validate.js'
5355
import { consoleEmailAdapter } from './email/consoleEmailAdapter.js'
@@ -57,6 +59,9 @@ import flattenFields from './utilities/flattenTopLevelFields.js'
5759
import Logger from './utilities/logger.js'
5860
import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit.js'
5961

62+
const filename = fileURLToPath(import.meta.url)
63+
const dirname = path.dirname(filename)
64+
6065
/**
6166
* @description Payload
6267
*/
@@ -302,6 +307,31 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
302307
[slug: string]: any // TODO: Type this
303308
} = {}
304309

310+
async bin({
311+
args,
312+
cwd,
313+
log,
314+
}: {
315+
args: string[]
316+
cwd?: string
317+
log?: boolean
318+
}): Promise<{ code: number }> {
319+
return new Promise((resolve, reject) => {
320+
const spawned = spawn('node', [path.resolve(dirname, '../bin.js'), ...args], {
321+
cwd,
322+
stdio: log || log === undefined ? 'inherit' : 'ignore',
323+
})
324+
325+
spawned.on('exit', (code) => {
326+
resolve({ code })
327+
})
328+
329+
spawned.on('error', (error) => {
330+
reject(error)
331+
})
332+
})
333+
}
334+
305335
/**
306336
* @description delete one or more documents
307337
* @param options
@@ -366,7 +396,12 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
366396

367397
// Generate types on startup
368398
if (process.env.NODE_ENV !== 'production' && this.config.typescript.autoGenerate !== false) {
369-
void generateTypes(this.config, { log: false })
399+
// We cannot run it directly here, as generate-types imports json-schema-to-typescript, which breaks on turbopack.
400+
// see: https://github.com/vercel/next.js/issues/66723
401+
void this.bin({
402+
args: ['generate:types'],
403+
log: false,
404+
})
370405
}
371406

372407
this.db = this.config.db.init({ payload: this })

test/testHooks.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const createTestHooks = async (testSuiteName = '_community') => {
3232
tsConfig.compilerOptions.paths['@payload-config'] = [`./test/${testSuiteName}/config.ts`]
3333
await writeFile(tsConfigPath, stringify(tsConfig, null, 2) + '\n')
3434

35-
process.env.PAYLOAD_CONFIG_PATH = path.resolve(testSuiteName, 'config')
35+
process.env.PAYLOAD_CONFIG_PATH = path.resolve(dirname, testSuiteName, 'config.ts')
3636
},
3737
}
3838
}

0 commit comments

Comments
 (0)