From 8a6e08ac7949dd9eb91e2fc922325e08bd4e77ef Mon Sep 17 00:00:00 2001 From: Lindsay Levine Date: Fri, 19 Feb 2021 04:29:32 -0500 Subject: [PATCH 1/2] add support for background functions in api pages only --- lib/helpers/getNetlifyFunctionName.js | 13 +++++++++++-- lib/helpers/setupNetlifyFunctionForPage.js | 8 ++++++-- lib/pages/api/redirects.js | 2 +- lib/pages/api/setup.js | 2 +- tests/__snapshots__/defaults.test.js.snap | 1 + tests/__snapshots__/i18n.test.js.snap | 1 + tests/fixtures/pages/api/hello-background.js | 5 +++++ 7 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 tests/fixtures/pages/api/hello-background.js diff --git a/lib/helpers/getNetlifyFunctionName.js b/lib/helpers/getNetlifyFunctionName.js index a8c62e6..ed6de5f 100644 --- a/lib/helpers/getNetlifyFunctionName.js +++ b/lib/helpers/getNetlifyFunctionName.js @@ -5,7 +5,7 @@ const path = require("path"); // The function name will be next_directory_subdirectory_[id] // We do this because every function needs to be at the top-level, i.e. // nested functions are not allowed. -const getNetlifyFunctionName = (filePath) => { +const getNetlifyFunctionName = (filePath, isApiPage) => { // Remove pages/ from file path: // pages/directory/page.js > directory/page.js const relativeFilePath = path.relative("pages", filePath); @@ -24,7 +24,16 @@ const getNetlifyFunctionName = (filePath) => { // Netlify Function names may not contain periods or square brackets. // To be safe, we keep only alphanumeric characters and underscores. // See: https://community.netlify.com/t/failed-to-upload-functions-file-function-too-large/3549/8 - functionName = functionName.replace(/[^\w\d]/g, ""); + const cleanNameRegex = new RegExp(/[^\w\d]/g); + // Allow users to use background functions for /api pages *only* + // Note: this means that there is a chance a Next on Netlify user could + // unknowingly create a background function if they're not familiar with them + // and their syntax + const allowBackgroundRegex = new RegExp(/[^\w\d-]|-(?!background$)/g); + functionName = functionName.replace( + isApiPage ? allowBackgroundRegex : cleanNameRegex, + "" + ); return functionName; }; diff --git a/lib/helpers/setupNetlifyFunctionForPage.js b/lib/helpers/setupNetlifyFunctionForPage.js index a5f4cb4..abc1420 100644 --- a/lib/helpers/setupNetlifyFunctionForPage.js +++ b/lib/helpers/setupNetlifyFunctionForPage.js @@ -8,9 +8,13 @@ const { const getNetlifyFunctionName = require("./getNetlifyFunctionName"); // Create a Netlify Function for the page with the given file path -const setupNetlifyFunctionForPage = ({ filePath, functionsPath }) => { +const setupNetlifyFunctionForPage = ({ + filePath, + functionsPath, + isApiPage, +}) => { // Set function name based on file path - const functionName = getNetlifyFunctionName(filePath); + const functionName = getNetlifyFunctionName(filePath, isApiPage); const functionDirectory = join(functionsPath, functionName); // Copy function template diff --git a/lib/pages/api/redirects.js b/lib/pages/api/redirects.js index 5552070..cad355c 100644 --- a/lib/pages/api/redirects.js +++ b/lib/pages/api/redirects.js @@ -3,7 +3,7 @@ const pages = require("./pages"); const redirects = pages.map(({ route, filePath }) => ({ route, - target: `/.netlify/functions/${getNetlifyFunctionName(filePath)}`, + target: `/.netlify/functions/${getNetlifyFunctionName(filePath, true)}`, })); module.exports = redirects; diff --git a/lib/pages/api/setup.js b/lib/pages/api/setup.js index a9fd4ab..b6f88f2 100644 --- a/lib/pages/api/setup.js +++ b/lib/pages/api/setup.js @@ -12,7 +12,7 @@ const setup = (functionsPath) => { // Create Netlify Function for every page pages.forEach(({ filePath }) => { logItem(filePath); - setupNetlifyFunctionForPage({ filePath, functionsPath }); + setupNetlifyFunctionForPage({ filePath, functionsPath, isApiPage: true }); }); }; diff --git a/tests/__snapshots__/defaults.test.js.snap b/tests/__snapshots__/defaults.test.js.snap index 62f0900..913bb10 100644 --- a/tests/__snapshots__/defaults.test.js.snap +++ b/tests/__snapshots__/defaults.test.js.snap @@ -24,6 +24,7 @@ exports[`Routing creates Netlify redirects 1`] = ` /_next/data/%BUILD_ID%/getStaticProps/withFallbackBlocking/4.json /.netlify/functions/next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data /_next/data/%BUILD_ID%/getStaticProps/withRevalidate/1.json /.netlify/functions/next_getStaticProps_withRevalidate_id 200 /_next/data/%BUILD_ID%/getStaticProps/withRevalidate/2.json /.netlify/functions/next_getStaticProps_withRevalidate_id 200 +/api/hello-background /.netlify/functions/next_api_hello-background 200 /api/static /.netlify/functions/next_api_static 200 /getServerSideProps/static /.netlify/functions/next_getServerSideProps_static 200 /getStaticProps/1 /.netlify/functions/next_getStaticProps_id 200! Cookie=__prerender_bypass,__next_preview_data diff --git a/tests/__snapshots__/i18n.test.js.snap b/tests/__snapshots__/i18n.test.js.snap index b1f4d55..e491048 100644 --- a/tests/__snapshots__/i18n.test.js.snap +++ b/tests/__snapshots__/i18n.test.js.snap @@ -23,6 +23,7 @@ exports[`Routing creates Netlify redirects 1`] = ` /_next/data/%BUILD_ID%/es/getStaticProps/static.json /.netlify/functions/next_getStaticProps_static 200! Cookie=__prerender_bypass,__next_preview_data /_next/data/%BUILD_ID%/es/getStaticProps/with-revalidate.json /.netlify/functions/next_getStaticProps_withrevalidate 200 /_next/data/%BUILD_ID%/getServerSideProps/static.json /.netlify/functions/next_getServerSideProps_static 200 +/api/hello-background /.netlify/functions/next_api_hello-background 200 /api/static /.netlify/functions/next_api_static 200 /en /.netlify/functions/next_index 200 /en/getServerSideProps/static /.netlify/functions/next_getServerSideProps_static 200 diff --git a/tests/fixtures/pages/api/hello-background.js b/tests/fixtures/pages/api/hello-background.js new file mode 100644 index 0000000..06b2d9d --- /dev/null +++ b/tests/fixtures/pages/api/hello-background.js @@ -0,0 +1,5 @@ +export default (req, res) => { + res.setHeader("Content-Type", "application/json"); + res.status(200); + res.json({ message: "hello world :)" }); +}; From 086ecd17119ebf5fa4e22acd31bb3d694a3ad16a Mon Sep 17 00:00:00 2001 From: Lindsay Levine Date: Wed, 24 Feb 2021 22:00:22 -0500 Subject: [PATCH 2/2] logItem when bground func is set up + README + test --- README.md | 8 +++++++- lib/helpers/setupNetlifyFunctionForPage.js | 9 ++++++++- tests/defaults.test.js | 9 +++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 28a8501..b4cd666 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ The plugin can be found on [npm here](https://www.npmjs.com/package/@netlify/plu - [Preview Locally](#preview-locally) - [Custom Netlify Redirects](#custom-netlify-redirects) - [Custom Netlify Functions](#custom-netlify-functions) + - [Background Functions](#background-functions) - [Using Netlify Identity](#using-netlify-identity) - [Caveats](#caveats) - [Fallbacks for Pages with `getStaticPaths`](#fallbacks-for-pages-with-getstaticpaths) @@ -223,7 +224,12 @@ Currently, there is no support for redirects set in your `netlify.toml` file. ### Custom Netlify Functions `next-on-netlify` creates one Netlify Function for each of your -SSR pages and API endpoints. It is currently not possible to create custom Netlify Functions. This feature is on our list to do. +SSR pages and API endpoints. Currently, you can only create custom Netlify functions using [@netlify/plugin-nextjs](https://github.com/netlify/netlify-plugin-nextjs#custom-netlify-functions). + +### Background Functions + +If your Next.js API page/route ends in `-background`, it will be treated as a [Netlify background function](https://docs.netlify.com/functions/background-functions/). +Note: background functions are only available on certain plans. ### Using Netlify Identity diff --git a/lib/helpers/setupNetlifyFunctionForPage.js b/lib/helpers/setupNetlifyFunctionForPage.js index abc1420..967d135 100644 --- a/lib/helpers/setupNetlifyFunctionForPage.js +++ b/lib/helpers/setupNetlifyFunctionForPage.js @@ -6,6 +6,7 @@ const { FUNCTION_TEMPLATE_PATH, } = require("../config"); const getNetlifyFunctionName = require("./getNetlifyFunctionName"); +const { logItem } = require("./logger"); // Create a Netlify Function for the page with the given file path const setupNetlifyFunctionForPage = ({ @@ -17,7 +18,13 @@ const setupNetlifyFunctionForPage = ({ const functionName = getNetlifyFunctionName(filePath, isApiPage); const functionDirectory = join(functionsPath, functionName); - // Copy function template + if (isApiPage && functionName.endsWith("-background")) { + logItem( + `👁 Setting up API page ${functionName} as a Netlify background function` + ); + } + + // Copy function templates const functionTemplateCopyPath = join( functionDirectory, `${functionName}.js` diff --git a/tests/defaults.test.js b/tests/defaults.test.js index 8652214..7e6f911 100644 --- a/tests/defaults.test.js +++ b/tests/defaults.test.js @@ -129,6 +129,15 @@ describe("API Pages", () => { join(functionsDir, "next_api_shows_params", "next_api_shows_params.js") ) ).toBe(true); + expect( + existsSync( + join( + functionsDir, + "next_api_hello-background", + "next_api_hello-background.js" + ) + ) + ).toBe(true); }); });