From e3d5f227a9d8d3a25d72c9d7ee6827cd318136bb Mon Sep 17 00:00:00 2001 From: Rohit Rajendran Date: Thu, 27 Apr 2023 00:29:38 -0400 Subject: [PATCH 1/5] fix: use custom page extensions (if applicable) when looking for source files --- packages/runtime/src/helpers/files.ts | 4 ++-- packages/runtime/src/helpers/functions.ts | 8 ++++---- packages/runtime/src/index.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/runtime/src/helpers/files.ts b/packages/runtime/src/helpers/files.ts index b45a968723..aca93aa06b 100644 --- a/packages/runtime/src/helpers/files.ts +++ b/packages/runtime/src/helpers/files.ts @@ -344,9 +344,9 @@ const getServerFile = (root: string, includeBase = true) => { /** * Find the source file for a given page route */ -export const getSourceFileForPage = (page: string, roots: string[]) => { +export const getSourceFileForPage = (page: string, roots: string[], pageExtensions = SOURCE_FILE_EXTENSIONS) => { for (const root of roots) { - for (const extension of SOURCE_FILE_EXTENSIONS) { + for (const extension of pageExtensions) { const file = join(root, `${page}.${extension}`) if (existsSync(file)) { return file diff --git a/packages/runtime/src/helpers/functions.ts b/packages/runtime/src/helpers/functions.ts index 5d0f8032b7..cfe9edc577 100644 --- a/packages/runtime/src/helpers/functions.ts +++ b/packages/runtime/src/helpers/functions.ts @@ -199,7 +199,7 @@ export const setupImageFunction = async ({ /** * Look for API routes, and extract the config from the source file. */ -export const getApiRouteConfigs = async (publish: string, baseDir: string): Promise> => { +export const getApiRouteConfigs = async (publish: string, baseDir: string, pageExtensions: string[]): Promise> => { const pages = await readJSON(join(publish, 'server', 'pages-manifest.json')) const apiRoutes = Object.keys(pages).filter((page) => page.startsWith('/api/')) // two possible places @@ -209,7 +209,7 @@ export const getApiRouteConfigs = async (publish: string, baseDir: string): Prom return await Promise.all( apiRoutes.map(async (apiRoute) => { - const filePath = getSourceFileForPage(apiRoute, [pagesDir, srcPagesDir]) + const filePath = getSourceFileForPage(apiRoute, [pagesDir, srcPagesDir], pageExtensions) return { route: apiRoute, config: await extractConfigFromFile(filePath), compiled: pages[apiRoute] } }), ) @@ -218,8 +218,8 @@ export const getApiRouteConfigs = async (publish: string, baseDir: string): Prom /** * Looks for extended API routes (background and scheduled functions) and extract the config from the source file. */ -export const getExtendedApiRouteConfigs = async (publish: string, baseDir: string): Promise> => { - const settledApiRoutes = await getApiRouteConfigs(publish, baseDir) +export const getExtendedApiRouteConfigs = async (publish: string, baseDir: string, pageExtensions: string[]): Promise> => { + const settledApiRoutes = await getApiRouteConfigs(publish, baseDir, pageExtensions) // We only want to return the API routes that are background or scheduled functions return settledApiRoutes.filter((apiRoute) => apiRoute.config.type !== undefined) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 6fabcb5e0d..41f5329c78 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -79,7 +79,7 @@ const plugin: NetlifyPlugin = { checkNextSiteHasBuilt({ publish, failBuild }) - const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental, routesManifest } = + const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental, routesManifest, pageExtensions} = await getNextConfig({ publish, failBuild, @@ -150,7 +150,7 @@ const plugin: NetlifyPlugin = { const buildId = readFileSync(join(publish, 'BUILD_ID'), 'utf8').trim() await configureHandlerFunctions({ netlifyConfig, ignore, publish: relative(process.cwd(), publish) }) - const apiRoutes = await getExtendedApiRouteConfigs(publish, appDir) + const apiRoutes = await getExtendedApiRouteConfigs(publish, appDir, pageExtensions) await generateFunctions(constants, appDir, apiRoutes) await generatePagesResolver(constants) From 1e00caa912df08f5c3420c5e929ebf403d673073 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Thu, 27 Apr 2023 08:56:42 +0200 Subject: [PATCH 2/5] chore: smaller edits --- packages/runtime/src/helpers/files.ts | 5 ++++- packages/runtime/src/index.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/runtime/src/helpers/files.ts b/packages/runtime/src/helpers/files.ts index aca93aa06b..8ac45aabce 100644 --- a/packages/runtime/src/helpers/files.ts +++ b/packages/runtime/src/helpers/files.ts @@ -18,7 +18,6 @@ import { Rewrites, RoutesManifest } from './types' import { findModuleFromBase } from './utils' const TEST_ROUTE = /(|\/)\[[^/]+?](\/|\.html|$)/ -const SOURCE_FILE_EXTENSIONS = ['js', 'jsx', 'ts', 'tsx'] export const isDynamicRoute = (route) => TEST_ROUTE.test(route) @@ -341,6 +340,10 @@ const getServerFile = (root: string, includeBase = true) => { return findModuleFromBase({ candidates, paths: [root] }) } +// Next.js already defines a default `pageExtensions` array in its `required-server-files.json` file +// In case it gets `undefined`, this is a fallback +const SOURCE_FILE_EXTENSIONS = ['js', 'jsx', 'ts', 'tsx'] + /** * Find the source file for a given page route */ diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 41f5329c78..178aeb01cb 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -79,7 +79,7 @@ const plugin: NetlifyPlugin = { checkNextSiteHasBuilt({ publish, failBuild }) - const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental, routesManifest, pageExtensions} = + const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental, routesManifest, pageExtensions } = await getNextConfig({ publish, failBuild, From 42a079e5d10aba9fb6feadd3bb67111391dae74a Mon Sep 17 00:00:00 2001 From: LekoArts Date: Thu, 27 Apr 2023 08:57:03 +0200 Subject: [PATCH 3/5] chore: add test --- .../custom/pages/api/custom.api.js | 1 + .../default/pages/api/default.js | 1 + test/helpers/files.spec.ts | 25 ++++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/page-extensions/custom/pages/api/custom.api.js create mode 100644 test/fixtures/page-extensions/default/pages/api/default.js diff --git a/test/fixtures/page-extensions/custom/pages/api/custom.api.js b/test/fixtures/page-extensions/custom/pages/api/custom.api.js new file mode 100644 index 0000000000..625c0891b2 --- /dev/null +++ b/test/fixtures/page-extensions/custom/pages/api/custom.api.js @@ -0,0 +1 @@ +// noop \ No newline at end of file diff --git a/test/fixtures/page-extensions/default/pages/api/default.js b/test/fixtures/page-extensions/default/pages/api/default.js new file mode 100644 index 0000000000..625c0891b2 --- /dev/null +++ b/test/fixtures/page-extensions/default/pages/api/default.js @@ -0,0 +1 @@ +// noop \ No newline at end of file diff --git a/test/helpers/files.spec.ts b/test/helpers/files.spec.ts index 4e1cb3f596..02e77e3e5b 100644 --- a/test/helpers/files.spec.ts +++ b/test/helpers/files.spec.ts @@ -6,6 +6,7 @@ import { patchNextFiles, unpatchNextFiles, getDependenciesOfFile, + getSourceFileForPage, } from "../../packages/runtime/src/helpers/files" import { readFileSync, @@ -19,6 +20,8 @@ import { join } from "pathe" import { Rewrites } from "../../packages/runtime/src/helpers/types" import { describeCwdTmpDir, moveNextDist } from "../test-utils" +const TEST_DIR = resolve(__dirname, '..') + const REDIRECTS: Rewrites = [ { source: '/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/', @@ -215,7 +218,27 @@ describe('dependency tracing', () => { it('generates dependency list from a source file', async () => { const dependencies = await getDependenciesOfFile(resolve(__dirname, '../fixtures/analysis/background.js')) expect(dependencies).toEqual( - ['test/webpack-api-runtime.js', 'package.json'].map((dep) => resolve(dirname(resolve(__dirname, '..')), dep)), + ['test/webpack-api-runtime.js', 'package.json'].map((dep) => resolve(dirname(TEST_DIR), dep)), ) }) +}) + +describe('getSourceFileForPage', () => { + it('handles default pageExtensions', () => { + const pagesDir = resolve(__dirname, '../fixtures/page-extensions/default/pages') + const apiRoute = '/api/default' + + const filePath = getSourceFileForPage(apiRoute, [pagesDir]) + + expect(filePath.replace(TEST_DIR, '')).toBe('/fixtures/page-extensions/default/pages/api/default.js') + }) + + it('handles custom pageExtensions', () => { + const pagesDir = resolve(__dirname, '../fixtures/page-extensions/custom/pages') + const apiRoute = '/api/custom' + + const filePath = getSourceFileForPage(apiRoute, [pagesDir], ['api.js']) + + expect(filePath.replace(TEST_DIR, '')).toBe('/fixtures/page-extensions/custom/pages/api/custom.api.js') + }) }) \ No newline at end of file From 2bda75ae179ddb42cd7f254f7a1cd7f97c83c1a7 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Thu, 27 Apr 2023 08:57:27 +0200 Subject: [PATCH 4/5] chore: linting --- packages/runtime/src/helpers/functions.ts | 12 ++++++++++-- packages/runtime/src/index.ts | 21 ++++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/packages/runtime/src/helpers/functions.ts b/packages/runtime/src/helpers/functions.ts index cfe9edc577..aa2423922e 100644 --- a/packages/runtime/src/helpers/functions.ts +++ b/packages/runtime/src/helpers/functions.ts @@ -199,7 +199,11 @@ export const setupImageFunction = async ({ /** * Look for API routes, and extract the config from the source file. */ -export const getApiRouteConfigs = async (publish: string, baseDir: string, pageExtensions: string[]): Promise> => { +export const getApiRouteConfigs = async ( + publish: string, + baseDir: string, + pageExtensions: string[], +): Promise> => { const pages = await readJSON(join(publish, 'server', 'pages-manifest.json')) const apiRoutes = Object.keys(pages).filter((page) => page.startsWith('/api/')) // two possible places @@ -218,7 +222,11 @@ export const getApiRouteConfigs = async (publish: string, baseDir: string, pageE /** * Looks for extended API routes (background and scheduled functions) and extract the config from the source file. */ -export const getExtendedApiRouteConfigs = async (publish: string, baseDir: string, pageExtensions: string[]): Promise> => { +export const getExtendedApiRouteConfigs = async ( + publish: string, + baseDir: string, + pageExtensions: string[], +): Promise> => { const settledApiRoutes = await getApiRouteConfigs(publish, baseDir, pageExtensions) // We only want to return the API routes that are background or scheduled functions diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 178aeb01cb..9c3794159c 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -79,11 +79,22 @@ const plugin: NetlifyPlugin = { checkNextSiteHasBuilt({ publish, failBuild }) - const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental, routesManifest, pageExtensions } = - await getNextConfig({ - publish, - failBuild, - }) + const { + appDir, + basePath, + i18n, + images, + target, + ignore, + trailingSlash, + outdir, + experimental, + routesManifest, + pageExtensions, + } = await getNextConfig({ + publish, + failBuild, + }) await cleanupEdgeFunctions(constants) const middlewareManifest = await loadMiddlewareManifest(netlifyConfig) From c9a377b0e381f799fae7bdb4b153a6abe1dfedba Mon Sep 17 00:00:00 2001 From: LekoArts Date: Thu, 27 Apr 2023 08:58:14 +0200 Subject: [PATCH 5/5] chore: linting --- packages/runtime/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 9c3794159c..58a3e3604e 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -65,6 +65,7 @@ const plugin: NetlifyPlugin = { netlifyConfig.build.environment.NEXT_PRIVATE_TARGET = 'server' }, + // eslint-disable-next-line max-lines-per-function async onBuild({ constants, netlifyConfig,