TS2307: a Test Importing scripts/ Broke the Vercel Build
This article may contain affiliate links. Its content is not affected by advertising.
In short
If a test imports a module under scripts/, the production type-check follows it — and a Vercel build excluding /scripts via .vercelignore fails with TS2307, because the file was never uploaded.
Conclusion
If a test file imports a module under scripts/, Next.js’s production build type-check follows that import and tries to resolve it. If .vercelignore is set to exclude /scripts from the Vercel upload, that file doesn’t exist on the production build machine, and the build fails with TS2307: Cannot find module.
Symptom
Right after upgrading to Next.js 16.3.7, the Vercel production build started failing with TS2307. Running next build locally did not reproduce it.
Tracing the cause, one test imported a .mjs file directly from under scripts/lib/. The setup shared pure validation logic for a name-reconciliation routine between a CLI script and its test, and since that file genuinely exists in the local working tree, type resolution worked without any issue locally.
But this project’s .vercelignore excluded /scripts so that setup-only files wouldn’t get uploaded to Vercel. The Vercel CLI reads .vercelignore, not .gitignore — so even though the file exists in git, the scripts/ directory itself simply isn’t present on the production build machine.
Cause
The root cause: the scope of the production build’s type-check and the scope of files actually uploaded to Vercel were never assumed to need to match.
By default, Next.js’s next build type-checks against the project-wide tsconfig.json, which includes test files. So if a test imports something under scripts/, the production build’s type-check tries to resolve that import too.
.vercelignore, meanwhile, exists to avoid uploading files unnecessary for running production — and /scripts (operational CLI scripts) was exactly that kind of file. The type-check configuration had no awareness of what .vercelignore excludes, so the production build’s type-check ended up assuming the existence of a file that’s present locally but absent in production.
Fix
next.config.ts’s typescript.tsconfigPath was pointed at a build-only tsconfig.build.json. This config extends the existing tsconfig.json but additionally excludes node_modules, __tests__, *.test.ts, *.test.tsx, as well as scripts and supabase, from the type-check scope.
With this, next build now only type-checks the application code itself — even if a test imports something under scripts/, the production build’s type-check never reaches it. Type-checking the tests themselves is still handled by npm run typecheck (tsconfig.json, the whole project), so test code doesn’t lose type safety.
Why it went unnoticed
What made this hard to see was that the type-check configuration and .vercelignore were separate files, written without any awareness of each other. tsconfig.json was left at its generic default of “type-check the whole project,” with no notion from the start of “what doesn’t get uploaded to Vercel.”
Explicitly splitting out the production build’s type-check scope in next.config.ts made the structure match “what the production build type-checks” with “what files actually exist in production.” Even if tests later import some other setup-only directory, as long as tsconfig.build.json keeps excluding it, the same failure won’t recur.
Frequently asked questions
Q1Why didn't this reproduce with a local next build?
The local working tree still has every file under scripts/, so next build's type-check resolves it fine. Only the Vercel production build excludes /scripts from the upload via .vercelignore, so the file doesn't exist on that machine, and only there does it fail with TS2307.
Q2Does this mean the typecheck script stops catching type errors in tests?
No. tsconfig.build.json only excludes scripts/ and test files from the type-check scope that next build uses. npm run typecheck (tsconfig.json, the whole project) still type-checks tests and scripts/ exactly as before.
Q3Would it be fixed by just not importing the files excluded by .vercelignore?
Possibly, but the tests were deliberately structured to test the scripts/ implementation directly, so dropping the import wasn't the direction taken. Instead, tests were excluded from next build's type-check scope, and only typecheck is left looking at that code.
Environment verified
- Next.js 16.3.7 / TypeScript ^5
- Vercel production build. Occurred and fixed same day, 2026-10-01
What this article is based on
- TypeScript file lines 60-64commit e59c9c5
- JSON file lines 1-4commit e59c9c5
- file lines 1-4commit e59c9c5
- TypeScript file lines 1-3commit e59c9c5
Every claim in this article comes from the records above. The repositories we operate are private so we cannot link to them, but which file, which lines, and at which commit we read them is recorded for every article. Nothing here is written from guesswork.