feat(config): introduce app config file#63
Conversation
Summary by CodeRabbit
WalkthroughThis PR adds ChangesBuild Settings Configuration for Preview Deploys
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/product/error-conventions.md (1)
157-213:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd
APP_CONFIG_INVALIDto the stable MVP list, not just the meanings section.Line 213 introduces a new public code, but the authoritative MVP list in Lines 161-200 still omits it. That makes the error contract internally inconsistent for agents and CI that treat the stable-set list as the source of truth.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/product/error-conventions.md` around lines 157 - 213, The MVP list omits the `APP_CONFIG_INVALID` code while it appears in the meanings section; update the stable MVP codes list (the bullet list under "These codes are the minimum stable set for the MVP") to include `APP_CONFIG_INVALID` so the authoritative list matches the meanings section (add the exact token `APP_CONFIG_INVALID` to the list of codes).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli/src/lib/app/preview-build-settings.ts`:
- Around line 297-346: The code currently defaults an unknown package manager to
"bun", which can write a Bun-specific build command incorrectly; change
resolvePackageManager to return Promise<PackageManager | undefined> (remove the
final return "bun") so it yields undefined when no package manager is inferred
(keep using packageManagerFromPackageJson and the lockfile checks), then update
the caller that uses resolvePackageManager (the if branch that builds command
when hasBuildScript(...) is true) to check for a truthy packageManager and only
return { command: `${packageManager} run build`, source: "package.json
scripts.build" } when packageManager is present; otherwise fall back to
returning the provided fallback.command and fallback.source. Ensure function
signatures/types are updated accordingly.
- Around line 440-457: The current regex-based readStaticDistDir incorrectly
captures distDir from comments/strings/unrelated objects in raw next.config.*;
update readStaticDistDir (and the readNextConfig → resolveNextOutputRoot flow)
to parse the source AST and extract distDir only from the exported Next config
object with high confidence: use a JS/TS parser (e.g., `@babel/parser` or
ts-morph) to find the module.exports/export default object, read a direct string
Literal value for distDir (reject computed/concatenated values), validate it is
a simple relative path (no absolute, "..", or path traversal) and only then
return the normalized posix path for outputDirectory/prisma.app.json; otherwise
return undefined so mistaken/commented values cannot persist.
- Around line 103-116: The code unconditionally writes a standalone path for
Next.js (`joinPosix(outputRoot, "standalone")`) even when next.config may not
enable output:"standalone", causing stale persisted settings; update
resolvePreviewBuildSettings to detect the actual Next.js output mode (use or
extend resolveNextOutputRoot to return the resolved outputRoot plus the output
mode or call a helper that inspects next.config for
output:"standalone"|"export") and only set outputDirectory to
joinPosix(outputRoot, "standalone") and set outputDirectorySource to "Next.js
output" when the detected mode is "standalone"; for other modes (e.g., "export"
or default) either set outputDirectory to the plain outputRoot or leave it unset
and ensure resolveOrCreatePreviewBuildSettings does not persist an inferred
standalone path before the build runs (persist only when outputDirectorySource
confirms standalone or after a successful build).
In `@packages/cli/src/lib/app/preview-build.ts`:
- Around line 363-369: The NEXT_CONFIG_FILENAMES array in preview-build.ts
should not include "next.config.cjs" because Next.js doesn't support that root
filename; remove "next.config.cjs" from that constant and update the other
in-repo Next.js candidate lists to match (remove "next.config.cjs" wherever Next
config filename lists are defined), specifically in the detectNextConfig() logic
in packages/cli/src/controllers/app.ts and the filename arrays in local-dev.ts
and preview-build-settings.ts so all detections use only supported names (e.g.,
next.config.js, .mjs, .ts, .mts) and stay consistent across functions.
---
Outside diff comments:
In `@docs/product/error-conventions.md`:
- Around line 157-213: The MVP list omits the `APP_CONFIG_INVALID` code while it
appears in the meanings section; update the stable MVP codes list (the bullet
list under "These codes are the minimum stable set for the MVP") to include
`APP_CONFIG_INVALID` so the authoritative list matches the meanings section (add
the exact token `APP_CONFIG_INVALID` to the list of codes).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 320e6bca-e80f-4a78-91d1-b0d8d81d1849
📒 Files selected for processing (11)
docs/product/command-spec.mddocs/product/error-conventions.mddocs/product/output-conventions.mddocs/product/resource-model.mdpackages/cli/src/controllers/app.tspackages/cli/src/lib/app/bun-project.tspackages/cli/src/lib/app/preview-build-settings.tspackages/cli/src/lib/app/preview-build.tspackages/cli/src/lib/app/preview-provider.tspackages/cli/tests/app-build.test.tspackages/cli/tests/app-controller.test.ts
| case "nextjs": { | ||
| const packageJson = await readBunPackageJson(options.appPath, options.signal); | ||
| const buildCommand = await resolveFrameworkBuildCommand(options.appPath, packageJson, { | ||
| command: "next build", | ||
| source: "Next.js default", | ||
| signal: options.signal, | ||
| }); | ||
| const outputRoot = await resolveNextOutputRoot(options.appPath, options.signal); | ||
| return { | ||
| buildCommand: buildCommand.command, | ||
| buildCommandSource: buildCommand.source, | ||
| outputDirectory: joinPosix(outputRoot, "standalone"), | ||
| outputDirectorySource: outputRoot === ".next" ? "Next.js output" : "next.config distDir", | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the file and show the relevant implementation region
FILE="packages/cli/src/lib/app/preview-build-settings.ts"
if [ ! -f "$FILE" ]; then
echo "Missing file: $FILE"
exit 1
fi
echo "=== File: $FILE (context around lines ~70-160) ==="
nl -ba "$FILE" | sed -n '70,180p'
# Find definitions of referenced helpers in this file
echo
echo "=== Functions referenced in review comment (rg in file) ==="
rg -n "function|const |resolveNextOutputRoot|resolveFrameworkBuildCommand|readBunPackageJson|resolvePackageManager|readStaticDistDir|standalone" "$FILE"
# Dump additional context where preview-build settings are constructed/consumed
echo
echo "=== Search for uses of outputDirectory / outputDirectorySource ==="
rg -n "outputDirectorySource|outputDirectory" packages/cli/src/lib/app -S
# If prisma.app.json is referenced anywhere in cli, locate it
echo
echo "=== Search for prisma.app.json usage ==="
rg -n "prisma\.app\.json" packages/cli/src -SRepository: prisma/prisma-cli
Length of output: 219
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="packages/cli/src/lib/app/preview-build-settings.ts"
if [ ! -f "$FILE" ]; then
echo "Missing file: $FILE"
exit 1
fi
echo "=== File: $FILE (context around lines ~70-180) ==="
awk 'NR>=70 && NR<=180 {printf "%5d %s\n", NR, $0}' "$FILE"
echo
echo "=== Functions referenced in review comment (rg in file) ==="
rg -n "resolveNextOutputRoot|resolveFrameworkBuildCommand|readBunPackageJson|resolvePackageManager|readStaticDistDir|prisma\.app\.json|standalone" "$FILE" || true
echo
echo "=== Search for uses of outputDirectory / outputDirectorySource ==="
rg -n "outputDirectorySource|outputDirectory" packages/cli/src/lib/app -S || true
echo
echo "=== Search for prisma.app.json usage ==="
rg -n "prisma\.app\.json" packages/cli/src -S || true
echo
echo "=== Locate resolveNextOutputRoot definition (ripgrep across cli lib) ==="
rg -n "resolveNextOutputRoot" packages/cli/src -S || true
echo
echo "=== Locate resolveFrameworkBuildCommand definition (ripgrep across cli lib) ==="
rg -n "resolveFrameworkBuildCommand" packages/cli/src -S || true
echo
echo "=== Locate resolveFrameworkBuildCommand callsites (for nextjs case) ==="
rg -n "case \"nextjs\"|next build|Next\.js default" packages/cli/src/lib/app/preview-build-settings.ts -n || trueRepository: prisma/prisma-cli
Length of output: 10251
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show resolveNextOutputRoot + helpers
FILE="packages/cli/src/lib/app/preview-build-settings.ts"
echo "=== preview-build-settings.ts: resolveNextOutputRoot + readStaticDistDir (lines ~380-520) ==="
awk 'NR>=360 && NR<=520 {printf "%5d %s\n", NR, $0}' "$FILE"
# Show nextOutputRootFromStandaloneDirectory helper
echo
echo "=== preview-build-settings.ts: nextOutputRootFromStandaloneDirectory (lines ~440-520) ==="
awk 'NR>=440 && NR<=520 {printf "%5d %s\n", NR, $0}' "$FILE"
# Show preview-build.ts sections that validate standalone output and compute outputRoot
FILE2="packages/cli/src/lib/app/preview-build.ts"
echo
echo "=== preview-build.ts: checks around use of settings.outputDirectory for Next.js (lines ~200-330) ==="
awk 'NR>=200 && NR<=340 {printf "%5d %s\n", NR, $0}' "$FILE2"Repository: prisma/prisma-cli
Length of output: 12317
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="packages/cli/src/lib/app/preview-build-settings.ts"
echo "=== preview-build-settings.ts: top (lines 1-120) ==="
awk 'NR>=1 && NR<=140 {printf "%5d %s\n", NR, $0}' "$FILE"
echo
echo "=== Search for writing prisma.app.json in preview-build-settings.ts ==="
rg -n "writeFile|createWriteStream|open\\(|PRISMA_APP_CONFIG_FILENAME|rename\\(" "$FILE" -S
echo
echo "=== Search for resolvePreviewBuildSettings usage/callsites ==="
rg -n "resolvePreviewBuildSettings\\(" packages/cli/src/lib/app -S
echo
echo "=== Search for resolveOrCreatePreviewBuildSettings / config lifecycle names ==="
rg -n "readPreviewBuildSettingsConfig|PRISMA_APP_CONFIG_FILENAME|invalidPrismaAppConfigError" "$FILE" -SRepository: prisma/prisma-cli
Length of output: 8753
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "resolveOrCreatePreviewBuildSettings\\(" packages/cli/src -SRepository: prisma/prisma-cli
Length of output: 302
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="packages/cli/src/controllers/app.ts"
echo "=== app.ts around resolveOrCreatePreviewBuildSettings call (lines ~280-380) ==="
awk 'NR>=280 && NR<=380 {printf "%5d %s\n", NR, $0}' "$FILE"
echo
echo "=== app.ts: search for PRISMA_APP_CONFIG_FILENAME usage and build execution order ==="
rg -n "PRISMA_APP_CONFIG_FILENAME|resolveOrCreatePreviewBuildSettings|preview-build" "$FILE" -SRepository: prisma/prisma-cli
Length of output: 4742
Avoid persisting Next.js <distDir>/standalone outputDirectory unless next.config enables output: "standalone"
resolvePreviewBuildSettingsalways setsoutputDirectorytojoinPosix(outputRoot, "standalone")forbuildType: "nextjs"(whereoutputRootis derived only fromdistDirvia regex), ignoringoutput: "standalone"/output: "export".resolveOrCreatePreviewBuildSettingswrites this inferred value intoprisma.app.jsonbeforenext buildruns; thenPreviewNextjsBuild.executethrows when the directory doesn’t exist, but the incorrectprisma.app.jsonremains to be reused on later attempts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli/src/lib/app/preview-build-settings.ts` around lines 103 - 116,
The code unconditionally writes a standalone path for Next.js
(`joinPosix(outputRoot, "standalone")`) even when next.config may not enable
output:"standalone", causing stale persisted settings; update
resolvePreviewBuildSettings to detect the actual Next.js output mode (use or
extend resolveNextOutputRoot to return the resolved outputRoot plus the output
mode or call a helper that inspects next.config for
output:"standalone"|"export") and only set outputDirectory to
joinPosix(outputRoot, "standalone") and set outputDirectorySource to "Next.js
output" when the detected mode is "standalone"; for other modes (e.g., "export"
or default) either set outputDirectory to the plain outputRoot or leave it unset
and ensure resolveOrCreatePreviewBuildSettings does not persist an inferred
standalone path before the build runs (persist only when outputDirectorySource
confirms standalone or after a successful build).
| if (hasBuildScript(packageJson)) { | ||
| const packageManager = await resolvePackageManager(appPath, packageJson, fallback.signal); | ||
| return { | ||
| command: `${packageManager} run build`, | ||
| source: "package.json scripts.build", | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| command: fallback.command, | ||
| source: fallback.source, | ||
| }; | ||
| } | ||
|
|
||
| function hasBuildScript(packageJson: BunPackageJsonLike | null): boolean { | ||
| if (!packageJson?.scripts || typeof packageJson.scripts !== "object") { | ||
| return false; | ||
| } | ||
|
|
||
| const scripts = packageJson.scripts as Record<string, unknown>; | ||
| return typeof scripts.build === "string" && scripts.build.trim().length > 0; | ||
| } | ||
|
|
||
| async function resolvePackageManager( | ||
| appPath: string, | ||
| packageJson: BunPackageJsonLike | null, | ||
| signal?: AbortSignal, | ||
| ): Promise<PackageManager> { | ||
| const fromPackageManager = packageManagerFromPackageJson(packageJson?.packageManager); | ||
| if (fromPackageManager) { | ||
| return fromPackageManager; | ||
| } | ||
|
|
||
| if (await pathExists(path.join(appPath, "bun.lock"), signal) || await pathExists(path.join(appPath, "bun.lockb"), signal)) { | ||
| return "bun"; | ||
| } | ||
|
|
||
| if (await pathExists(path.join(appPath, "pnpm-lock.yaml"), signal)) { | ||
| return "pnpm"; | ||
| } | ||
|
|
||
| if (await pathExists(path.join(appPath, "yarn.lock"), signal)) { | ||
| return "yarn"; | ||
| } | ||
|
|
||
| if (await pathExists(path.join(appPath, "package-lock.json"), signal)) { | ||
| return "npm"; | ||
| } | ||
|
|
||
| return "bun"; |
There was a problem hiding this comment.
Don't default an unknown package manager to Bun.
Lines 297-301 and 320-346 turn any project with scripts.build but no detectable package-manager signal into bun run build. That will persist a Bun-specific command into prisma.app.json for otherwise ordinary Next.js or TanStack apps that omit packageManager and do not commit a lockfile. This should either fail closed with an explicit fix, or choose a neutral fallback that matches the inferred framework/runtime instead of requiring Bun by accident.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli/src/lib/app/preview-build-settings.ts` around lines 297 - 346,
The code currently defaults an unknown package manager to "bun", which can write
a Bun-specific build command incorrectly; change resolvePackageManager to return
Promise<PackageManager | undefined> (remove the final return "bun") so it yields
undefined when no package manager is inferred (keep using
packageManagerFromPackageJson and the lockfile checks), then update the caller
that uses resolvePackageManager (the if branch that builds command when
hasBuildScript(...) is true) to check for a truthy packageManager and only
return { command: `${packageManager} run build`, source: "package.json
scripts.build" } when packageManager is present; otherwise fall back to
returning the provided fallback.command and fallback.source. Ensure function
signatures/types are updated accordingly.
| function readStaticDistDir(content: string): string | undefined { | ||
| const match = /\bdistDir\s*:\s*["'`]([^"'`]+)["'`]/.exec(content); | ||
| const rawDistDir = match?.[1]?.trim(); | ||
| if (!rawDistDir) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const normalized = path.normalize(rawDistDir); | ||
| if ( | ||
| path.isAbsolute(normalized) || | ||
| normalized === ".." || | ||
| normalized.startsWith(`..${path.sep}`) || | ||
| normalized.includes(`${path.sep}..${path.sep}`) | ||
| ) { | ||
| return undefined; | ||
| } | ||
|
|
||
| return normalized.split(path.sep).join("/"); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Locate and inspect the target function + its call sites
rg -n "function readStaticDistDir|readStaticDistDir\(" packages/cli/src/lib/app/preview-build-settings.ts
sed -n '380,520p' packages/cli/src/lib/app/preview-build-settings.ts
# 2) Find where prisma.app.json is written
rg -n "prisma\.app\.json" -S packages/cli/src/lib/app/preview-build-settings.ts packages/cli/src/lib/app -g'*.ts' -g'*.js'
rg -n "app\.json" packages/cli/src/lib/app/preview-build-settings.ts -S
# 3) Identify where the `content` argument comes from (likely next.config.* file text)
rg -n "readStaticDistDir\(" packages/cli/src/lib/app -S -g'*.ts'
rg -n "next\.config\." packages/cli/src/lib/app/preview-build-settings.ts packages/cli/src/lib/app -S -g'*.ts'Repository: prisma/prisma-cli
Length of output: 4849
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '380,520p' packages/cli/src/lib/app/preview-build-settings.ts
rg -n "PRISMA_APP_CONFIG_FILENAME|prisma\.app\.json" packages/cli/src/lib/app/preview-build-settings.ts -n
sed -n '1,120p' packages/cli/src/lib/app/preview-build-settings.ts
# Show where the app config is written
rg -n "writeFile\\(|mkdir\\(|PRISMA_APP_CONFIG_FILENAME|app config" packages/cli/src/lib/app/preview-build-settings.ts -SRepository: prisma/prisma-cli
Length of output: 8003
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# find readPreviewBuildSettingsConfig
rg -n "function readPreviewBuildSettingsConfig|readPreviewBuildSettingsConfig\\(" packages/cli/src/lib/app/preview-build-settings.ts
# show its implementation region
sed -n '200,320p' packages/cli/src/lib/app/preview-build-settings.ts
# look for any alternate distDir resolution/evaluation approach
rg -n "distDir" packages/cli/src/lib/app/preview-build-settings.ts
rg -n "readNextConfig|resolveNextOutputRoot|NEXT_CONFIG_FILENAMES" packages/cli/src/lib/app/preview-build-settings.ts
# check whether they ever parse/execute next.config.* (safer than regex)
rg -n "import\\(|require\\(|createRequire|vm\\.|eval\\(" packages/cli/src/lib/app/preview-build-settings.tsRepository: prisma/prisma-cli
Length of output: 4695
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '120,220p' packages/cli/src/lib/app/preview-build-settings.ts
sed -n '220,310p' packages/cli/src/lib/app/preview-build-settings.ts
# find all call sites of resolveOrCreatePreviewBuildSettings in app deploy flow
rg -n "resolveOrCreatePreviewBuildSettings\\(|resolvePreviewBuildSettings\\(" packages/cli/src/lib/app -S
# see whether prisma.app.json is reused without regeneration
rg -n "status: \"used\"|readPreviewBuildSettingsConfig\\(" packages/cli/src/lib/app -SRepository: prisma/prisma-cli
Length of output: 7020
Avoid regex-based distDir extraction from raw next.config.* text
readNextConfig() reads next.config.{js,mjs,cjs,ts,mts} as raw UTF-8 and readStaticDistDir() pulls distDir via /\bdistDir\s*:\s*["']([^"'`]+)["'`]/, which can match distDirinside comments/strings or unrelated objects. That feeds intoresolveNextOutputRoot()→outputDirectory(viajoinPosix(outputRoot, "standalone")) when creating prisma.app.json. Later runs reuse the existing prisma.app.json (status: "used") without re-reading next.config.*, so a mistaken/commented distDircan persist and break future deploys. Make this syntax-aware (e.g., AST parse/extract only from the exported Next config object with high confidence) or otherwise restrict acceptance ofdistDir` to well-validated shapes.
🧰 Tools
🪛 OpenGrep (1.22.0)
[ERROR] 441-441: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli/src/lib/app/preview-build-settings.ts` around lines 440 - 457,
The current regex-based readStaticDistDir incorrectly captures distDir from
comments/strings/unrelated objects in raw next.config.*; update
readStaticDistDir (and the readNextConfig → resolveNextOutputRoot flow) to parse
the source AST and extract distDir only from the exported Next config object
with high confidence: use a JS/TS parser (e.g., `@babel/parser` or ts-morph) to
find the module.exports/export default object, read a direct string Literal
value for distDir (reject computed/concatenated values), validate it is a simple
relative path (no absolute, "..", or path traversal) and only then return the
normalized posix path for outputDirectory/prisma.app.json; otherwise return
undefined so mistaken/commented values cannot persist.
| const NEXT_CONFIG_FILENAMES = [ | ||
| "next.config.js", | ||
| "next.config.mjs", | ||
| "next.config.cjs", | ||
| "next.config.ts", | ||
| "next.config.mts", | ||
| ] as const; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate NEXT_CONFIG_FILENAMES usage/definition
rg -n "NEXT_CONFIG_FILENAMES|detectNextConfig|next\.config\.cjs" packages/cli/src -SRepository: prisma/prisma-cli
Length of output: 1131
🌐 Web query:
Next.js supported next.config filenames next.config.cjs next.config.mjs next.config.ts
💡 Result:
Next.js supports multiple file formats for its configuration file, allowing you to use JavaScript or TypeScript with either CommonJS (CJS) or ECMAScript Module (ESM) syntax [1][2]. The officially supported configuration filenames are: - next.config.js: The standard configuration file using CommonJS syntax (module.exports) [3][4]. - next.config.mjs: Used for configurations requiring ECMAScript Module (ESM) syntax (export default) [3][4]. - next.config.ts: Supported since Next.js 15.0.0, this allows you to write your configuration in TypeScript [2][5]. Regarding the filenames mentioned in your query: - next.config.cjs: While often used in Node.js environments to explicitly denote CommonJS files, official Next.js documentation has historically noted that .cjs (and .cts) extensions are not explicitly supported for the main configuration file [3]. - next.config.ts: This is a fully supported configuration format [2][5]. When using next.config.ts, your configuration is processed by Next.js using a built-in resolver [6]. In environments with modern Node.js versions (v22.10.0+), Next.js can leverage native TypeScript resolution, which allows for native ESM syntax (such as top-level await) [2][6]. For projects where you wish to explicitly signal ESM usage while using TypeScript, next.config.mts is also utilized as a compatible approach [2].
Citations:
- 1: feat(next):
next.config.tsvercel/next.js#63051 - 2: https://rc.nextjs.org/docs/pages/api-reference/config/typescript
- 3: https://nextjs.org/docs/app/api-reference/config/next-config-js
- 4: https://nextjs.im/docs/13/pages/api-reference/next-config-js/
- 5: feat(next):
next.config.tsvercel/next.js#57656 - 6: feat: use Node.js native TS resolver for
next.config.tsvercel/next.js#83240
🌐 Web query:
site:nextjs.org "next.config.cjs" "next-config-js" supported
💡 Result:
As of June 3, 2026, Next.js does not support configuration files with the.cjs or.cts extensions [1][2]. According to official Next.js documentation, the supported configuration file formats are: - next.config.js: The standard configuration file [1][2]. - next.config.mjs: Used when ECMAScript modules are required [1][2]. - next.config.ts: Used for TypeScript configuration (introduced in v15.0.0) [3][4]. While Next.js projects may use.cjs or.cts files elsewhere for other purposes (such as when forced by a "type": "module" setting in package.json), these specific extensions are explicitly unsupported for the main Next.js configuration file [1][2][4].
Citations:
- 1: https://nextjs.org/docs/app/api-reference/config/next-config-js
- 2: https://nextjs.org/docs/15/pages/api-reference/config/next-config-js
- 3: https://nextjs.org/docs/15/app/api-reference/config/typescript
- 4: https://nextjs.org/docs/app/api-reference/config/typescript
🌐 Web query:
next-config-js supported filenames next.config.cjs next.config.cts
💡 Result:
As of June 3, 2026, the Next.js configuration files next.config.cjs and next.config.cts are not supported [1][2][3]. Next.js explicitly states in its official documentation that configuration files with the .cjs or .cts extensions are not supported [1][2]. Attempting to use these extensions may result in them being ignored or cause the framework to throw an error [3][4]. The officially supported configuration filenames are: - next.config.js: The standard configuration file [1][2]. - next.config.mjs: Used for projects requiring ECMAScript modules (ESM) [1][5]. - next.config.ts: Used for TypeScript-based configurations [1][6]. While there has been development activity regarding different extensions, the framework currently maintains a restricted list of supported filenames for the configuration root to ensure stability and predictable behavior [3][4][7].
Citations:
- 1: https://nextjs.org/docs/app/api-reference/config/next-config-js
- 2: https://nextjs.im/docs/app/api-reference/config/next-config-js/
- 3: chore: throw error when trying to use next.config.mts/.cjs/.cts (not supported yet) vercel/next.js#70376
- 4: Docs: Next Config Support For (Or Note not Supporting) next.config.mts vercel/next.js#70201
- 5: https://nextjs.im/docs/14/app/api-reference/next-config-js/
- 6: https://rc.nextjs.org/docs/pages/api-reference/config/typescript
- 7: feat(next):
next.config.tsvercel/next.js#63051
Remove next.config.cjs from Next.js config filename detection
Next.js does not support next.config.cjs (or next.config.cts) as the root config filename, so detecting it can misclassify Next.js projects and drive incorrect build-settings inference. Remove next.config.cjs from packages/cli/src/lib/app/preview-build.ts (NEXT_CONFIG_FILENAMES, lines 363-369) and also from the other in-repo Next.js config filename candidate lists: packages/cli/src/controllers/app.ts (detectNextConfig()), packages/cli/src/lib/app/local-dev.ts, and packages/cli/src/lib/app/preview-build-settings.ts. (nextjs.org)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/cli/src/lib/app/preview-build.ts` around lines 363 - 369, The
NEXT_CONFIG_FILENAMES array in preview-build.ts should not include
"next.config.cjs" because Next.js doesn't support that root filename; remove
"next.config.cjs" from that constant and update the other in-repo Next.js
candidate lists to match (remove "next.config.cjs" wherever Next config filename
lists are defined), specifically in the detectNextConfig() logic in
packages/cli/src/controllers/app.ts and the filename arrays in local-dev.ts and
preview-build-settings.ts so all detections use only supported names (e.g.,
next.config.js, .mjs, .ts, .mts) and stay consistent across functions.
Summary
prisma.app.jsonas a repo-owned app build settings file for config-backed deploys.buildCommandandoutputDirectoryvalues without overwriting them..prisma/local.json.Validation
pnpm --filter @prisma/cli exec vitest run tests/app-build.test.ts tests/app-controller.test.ts tests/app-bun-compat.test.tspassed.git diff --checkpassed.preview-build.tsis 780 lines; new settings module is 488 lines.pnpm --filter @prisma/cli exec tsc --noEmitreports existing unrelated baseline failures intests/helpers.ts,tests/project-real-mode.test.ts,tests/publish-prep.test.ts, andtests/resolve-cli-version.test.ts. No touched app config/build files are in the TypeScript failure set.