diff --git a/packages/cli/src/util/dev/server.ts b/packages/cli/src/util/dev/server.ts index 3a66340b3..fb5ce4ea0 100644 --- a/packages/cli/src/util/dev/server.ts +++ b/packages/cli/src/util/dev/server.ts @@ -329,6 +329,8 @@ export default class DevServer { ): Promise { const name = relative(this.cwd, fsPath); try { + await this.getVercelConfig(); + this.files[name] = await FileFsRef.fromFsPath({ fsPath }); const extensionless = this.getExtensionlessFile(name); if (extensionless) { diff --git a/packages/cli/test/dev/fixtures/no-api/vercel.json b/packages/cli/test/dev/fixtures/no-api/vercel.json new file mode 100644 index 000000000..85deee8e2 --- /dev/null +++ b/packages/cli/test/dev/fixtures/no-api/vercel.json @@ -0,0 +1,3 @@ +{ + "version": 2 +} diff --git a/packages/cli/test/dev/integration-2.test.ts b/packages/cli/test/dev/integration-2.test.ts index 0d5e1bc50..9e3380f92 100644 --- a/packages/cli/test/dev/integration-2.test.ts +++ b/packages/cli/test/dev/integration-2.test.ts @@ -1,7 +1,17 @@ // eslint-disable-next-line -import path from 'path'; +import { join } from 'path'; +import ms from 'ms'; +import fs, { mkdirp } from 'fs-extra'; -const { exec, fixture, testFixture, testFixtureStdio } = require('./utils.js'); +const { + exec, + fetch, + fixture, + sleep, + testFixture, + testFixtureStdio, + validateResponseHeaders, +} = require('./utils.js'); test('[vercel dev] validate redirects', async () => { const directory = fixture('invalid-redirects'); @@ -334,3 +344,44 @@ test( await testPath(200, '/', /A simple deployment with the Vercel API!/m); }) ); + +test( + '[vercel dev] add a `api/fn.ts` when `api` does not exist at startup`', + testFixtureStdio('no-api', async (_testPath: any, port: any) => { + const directory = fixture('no-api'); + const apiDir = join(directory, 'api'); + + try { + { + const response = await fetch(`http://localhost:${port}/api/new-file`); + validateResponseHeaders(response); + expect(response.status).toBe(404); + } + + const fileContents = ` + export const config = { + runtime: 'experimental-edge' + } + + export default async function edge(request, event) { + return new Response('from new file'); + } + `; + + await mkdirp(apiDir); + await fs.writeFile(join(apiDir, 'new-file.js'), fileContents); + + // Wait until file events have been processed + await sleep(ms('1s')); + + { + const response = await fetch(`http://localhost:${port}/api/new-file`); + validateResponseHeaders(response); + const body = await response.text(); + expect(body.trim()).toBe('from new file'); + } + } finally { + await fs.remove(apiDir); + } + }) +);