diff --git a/demos/default/hello.txt b/demos/default/hello.txt new file mode 100644 index 0000000000..04fea06420 --- /dev/null +++ b/demos/default/hello.txt @@ -0,0 +1 @@ +world \ No newline at end of file diff --git a/demos/default/pages/getServerSideProps/file.js b/demos/default/pages/getServerSideProps/file.js new file mode 100644 index 0000000000..a9f1581a94 --- /dev/null +++ b/demos/default/pages/getServerSideProps/file.js @@ -0,0 +1,29 @@ +import Link from 'next/link' +import path from 'path' +import fs from 'fs' + +export async function getServerSideProps() { + const text = fs.readFileSync(path.join(process.cwd(), 'hello.txt'), 'utf8').trim() + + return { + props: { + world: text, + time: new Date().getTime(), + }, + } +} + +const File = ({ world, time }) => ( + <> +

hello {world}

+ time: {time} + + to home + +
+ + to something + + +) +export default File diff --git a/packages/runtime/src/helpers/functions.ts b/packages/runtime/src/helpers/functions.ts index 052e0b5efc..88c7b07886 100644 --- a/packages/runtime/src/helpers/functions.ts +++ b/packages/runtime/src/helpers/functions.ts @@ -11,7 +11,7 @@ import { join, relative, resolve } from 'pathe' import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, IMAGE_FUNCTION_NAME, DEFAULT_FUNCTIONS_SRC } from '../constants' import { getApiHandler } from '../templates/getApiHandler' import { getHandler } from '../templates/getHandler' -import { getPageResolver, getSinglePageResolver } from '../templates/getPageResolver' +import { getResolverForPages, getResolverForSourceFiles } from '../templates/getPageResolver' import { ApiConfig, ApiRouteType, extractConfigFromFile } from './analysis' import { getSourceFileForPage } from './files' @@ -52,7 +52,7 @@ export const generateFunctions = async ( const resolveSourceFile = (file: string) => join(publish, 'server', file) - const resolverSource = await getSinglePageResolver({ + const resolverSource = await getResolverForSourceFiles({ functionsDir, // These extra pages are always included by Next.js sourceFiles: [compiled, 'pages/_app.js', 'pages/_document.js', 'pages/_error.js'].map(resolveSourceFile), @@ -80,18 +80,13 @@ export const generateFunctions = async ( * This is just so that the nft bundler knows about them. We'll eventually do this better. */ export const generatePagesResolver = async ({ - constants: { INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC = DEFAULT_FUNCTIONS_SRC, PUBLISH_DIR }, - target, -}: { - constants: NetlifyPluginConstants - target: string -}): Promise => { + INTERNAL_FUNCTIONS_SRC, + FUNCTIONS_SRC = DEFAULT_FUNCTIONS_SRC, + PUBLISH_DIR, +}: NetlifyPluginConstants): Promise => { const functionsPath = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC - const jsSource = await getPageResolver({ - publish: PUBLISH_DIR, - target, - }) + const jsSource = await getResolverForPages(PUBLISH_DIR) await writeFile(join(functionsPath, ODB_FUNCTION_NAME, 'pages.js'), jsSource) await writeFile(join(functionsPath, HANDLER_FUNCTION_NAME, 'pages.js'), jsSource) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 90ffcb43b6..05f9eb0b2d 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -155,7 +155,7 @@ const plugin: NetlifyPlugin = { const apiRoutes = await getExtendedApiRouteConfigs(publish, appDir) await generateFunctions(constants, appDir, apiRoutes) - await generatePagesResolver({ target, constants }) + await generatePagesResolver(constants) await movePublicFiles({ appDir, outdir, publish }) diff --git a/packages/runtime/src/templates/getPageResolver.ts b/packages/runtime/src/templates/getPageResolver.ts index 6ca5165858..1326558c30 100644 --- a/packages/runtime/src/templates/getPageResolver.ts +++ b/packages/runtime/src/templates/getPageResolver.ts @@ -1,9 +1,6 @@ -import { posix } from 'path' - +import glob from 'globby' import { outdent } from 'outdent' -import { relative, resolve } from 'pathe' -import slash from 'slash' -import glob from 'tiny-glob' +import { join, relative, resolve } from 'pathe' import { HANDLER_FUNCTION_NAME } from '../constants' import { getDependenciesOfFile } from '../helpers/files' @@ -11,20 +8,33 @@ import { getDependenciesOfFile } from '../helpers/files' // Generate a file full of require.resolve() calls for all the pages in the // build. This is used by the nft bundler to find all the pages. -export const getPageResolver = async ({ publish, target }: { publish: string; target: string }) => { - const functionDir = posix.resolve(posix.join('.netlify', 'functions', HANDLER_FUNCTION_NAME)) - const root = posix.resolve(slash(publish), target === 'server' ? 'server' : 'serverless', 'pages') +export const getUniqueDependencies = async (sourceFiles: Array) => { + const dependencies = await Promise.all(sourceFiles.map((sourceFile) => getDependenciesOfFile(sourceFile))) + return [...new Set([...sourceFiles, ...dependencies.flat()])].sort() +} - const pages = await glob('**/*.js', { +export const getAllPageDependencies = async (publish: string) => { + const root = resolve(publish, 'server') + + const pageFiles = await glob('{pages,app}/**/*.js', { cwd: root, dot: true, }) - const pageFiles = pages - .map((page) => `require.resolve('${posix.relative(functionDir, posix.join(root, slash(page)))}')`) - .sort() + // We don't use `absolute: true` because that returns Windows paths on Windows. + // Instead we use pathe to normalize the paths. + return getUniqueDependencies(pageFiles.map((pageFile) => join(root, pageFile))) +} - return outdent` - // This file is purely to allow nft to know about these pages. It should be temporary. +export const getResolverForDependencies = ({ + dependencies, + functionDir, +}: { + dependencies: string[] + functionDir: string +}) => { + const pageFiles = dependencies.map((file) => `require.resolve('${relative(functionDir, file)}')`) + return outdent/* javascript */ ` + // This file is purely to allow nft to know about these pages. exports.resolvePages = () => { try { ${pageFiles.join('\n ')} @@ -33,31 +43,21 @@ export const getPageResolver = async ({ publish, target }: { publish: string; ta ` } -/** - * API routes only need the dependencies for a single entrypoint, so we use the - * NFT trace file to get the dependencies. - */ -export const getSinglePageResolver = async ({ +export const getResolverForPages = async (publish: string) => { + const functionDir = resolve('.netlify', 'functions', HANDLER_FUNCTION_NAME) + const dependencies = await getAllPageDependencies(publish) + return getResolverForDependencies({ dependencies, functionDir }) +} + +export const getResolverForSourceFiles = async ({ functionsDir, sourceFiles, }: { functionsDir: string sourceFiles: Array }) => { - const dependencies = await Promise.all(sourceFiles.map((sourceFile) => getDependenciesOfFile(sourceFile))) // We don't need the actual name, just the relative path. const functionDir = resolve(functionsDir, 'functionName') - - const deduped = [...new Set(dependencies.flat())] - - const pageFiles = [...sourceFiles, ...deduped] - .map((file) => `require.resolve('${relative(functionDir, file)}')`) - .sort() - - return outdent/* javascript */ ` - // This file is purely to allow nft to know about these pages. - try { - ${pageFiles.join('\n ')} - } catch {} - ` + const dependencies = await getUniqueDependencies(sourceFiles) + return getResolverForDependencies({ dependencies, functionDir }) } diff --git a/test/__snapshots__/index.js.snap b/test/__snapshots__/index.js.snap index 78b56bbeb2..e9bea87306 100644 --- a/test/__snapshots__/index.js.snap +++ b/test/__snapshots__/index.js.snap @@ -1,9 +1,97 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`onBuild() generates a file referencing all API route sources 1`] = ` -"// This file is purely to allow nft to know about these pages. It should be temporary. +exports[`function helpers config dependency tracing extracts a list of all dependencies 1`] = ` +Array [ + ".next/package.json", + ".next/server/chunks/1987.js", + ".next/server/chunks/274.js", + ".next/server/chunks/4271.js", + ".next/server/chunks/5237.js", + ".next/server/chunks/9097.js", + ".next/server/chunks/header.js", + ".next/server/pages/_app.js", + ".next/server/pages/_document.js", + ".next/server/pages/_error.js", + ".next/server/pages/api/enterPreview.js", + ".next/server/pages/api/exitPreview.js", + ".next/server/pages/api/hello-background.js", + ".next/server/pages/api/hello-scheduled.js", + ".next/server/pages/api/hello.js", + ".next/server/pages/api/og.js", + ".next/server/pages/api/shows/[...params].js", + ".next/server/pages/api/shows/[id].js", + ".next/server/pages/deep/import.js", + ".next/server/pages/getServerSideProps/[id].js", + ".next/server/pages/getServerSideProps/all/[[...slug]].js", + ".next/server/pages/getServerSideProps/file.js", + ".next/server/pages/getServerSideProps/static.js", + ".next/server/pages/getStaticProps/[id].js", + ".next/server/pages/getStaticProps/env.js", + ".next/server/pages/getStaticProps/static.js", + ".next/server/pages/getStaticProps/with-revalidate-404.js", + ".next/server/pages/getStaticProps/with-revalidate.js", + ".next/server/pages/getStaticProps/withFallback/[...slug].js", + ".next/server/pages/getStaticProps/withFallback/[id].js", + ".next/server/pages/getStaticProps/withFallbackBlocking/[id].js", + ".next/server/pages/getStaticProps/withRevalidate/[id].js", + ".next/server/pages/getStaticProps/withRevalidate/withFallback/[id].js", + ".next/server/pages/getStaticProps/withRevalidate/withFallbackBlocking/[id].js", + ".next/server/pages/index.js", + ".next/server/pages/previewTest.js", + ".next/server/pages/shows/[...params].js", + ".next/server/pages/shows/[id].js", + ".next/server/webpack-api-runtime.js", + ".next/server/webpack-runtime.js", + "components/Header.js", + "hello.txt", + "package.json", +] +`; + +exports[`onBuild() generates a file referencing all API route sources: for _api_hello-background-background 1`] = ` +"// This file is purely to allow nft to know about these pages. +exports.resolvePages = () => { + try { + require.resolve('../../../.next/package.json') + require.resolve('../../../.next/server/chunks/274.js') + require.resolve('../../../.next/server/pages/_app.js') + require.resolve('../../../.next/server/pages/_document.js') + require.resolve('../../../.next/server/pages/_error.js') + require.resolve('../../../.next/server/pages/api/hello-background.js') + require.resolve('../../../.next/server/webpack-api-runtime.js') + require.resolve('../../../.next/server/webpack-runtime.js') + } catch {} +}" +`; + +exports[`onBuild() generates a file referencing all API route sources: for _api_hello-scheduled-handler 1`] = ` +"// This file is purely to allow nft to know about these pages. +exports.resolvePages = () => { + try { + require.resolve('../../../.next/package.json') + require.resolve('../../../.next/server/chunks/274.js') + require.resolve('../../../.next/server/pages/_app.js') + require.resolve('../../../.next/server/pages/_document.js') + require.resolve('../../../.next/server/pages/_error.js') + require.resolve('../../../.next/server/pages/api/hello-scheduled.js') + require.resolve('../../../.next/server/webpack-api-runtime.js') + require.resolve('../../../.next/server/webpack-runtime.js') + require.resolve('../../../package.json') + } catch {} +}" +`; + +exports[`onBuild() generates a file referencing all page sources 1`] = ` +"// This file is purely to allow nft to know about these pages. exports.resolvePages = () => { try { + require.resolve('../../../.next/package.json') + require.resolve('../../../.next/server/chunks/1987.js') + require.resolve('../../../.next/server/chunks/274.js') + require.resolve('../../../.next/server/chunks/4271.js') + require.resolve('../../../.next/server/chunks/5237.js') + require.resolve('../../../.next/server/chunks/9097.js') + require.resolve('../../../.next/server/chunks/header.js') require.resolve('../../../.next/server/pages/_app.js') require.resolve('../../../.next/server/pages/_document.js') require.resolve('../../../.next/server/pages/_error.js') @@ -18,6 +106,7 @@ exports.resolvePages = () => { require.resolve('../../../.next/server/pages/deep/import.js') require.resolve('../../../.next/server/pages/getServerSideProps/[id].js') require.resolve('../../../.next/server/pages/getServerSideProps/all/[[...slug]].js') + require.resolve('../../../.next/server/pages/getServerSideProps/file.js') require.resolve('../../../.next/server/pages/getServerSideProps/static.js') require.resolve('../../../.next/server/pages/getStaticProps/[id].js') require.resolve('../../../.next/server/pages/getStaticProps/env.js') @@ -34,14 +123,26 @@ exports.resolvePages = () => { require.resolve('../../../.next/server/pages/previewTest.js') require.resolve('../../../.next/server/pages/shows/[...params].js') require.resolve('../../../.next/server/pages/shows/[id].js') + require.resolve('../../../.next/server/webpack-api-runtime.js') + require.resolve('../../../.next/server/webpack-runtime.js') + require.resolve('../../../components/Header.js') + require.resolve('../../../hello.txt') + require.resolve('../../../package.json') } catch {} }" `; -exports[`onBuild() generates a file referencing all API route sources 2`] = ` -"// This file is purely to allow nft to know about these pages. It should be temporary. +exports[`onBuild() generates a file referencing all page sources 2`] = ` +"// This file is purely to allow nft to know about these pages. exports.resolvePages = () => { try { + require.resolve('../../../.next/package.json') + require.resolve('../../../.next/server/chunks/1987.js') + require.resolve('../../../.next/server/chunks/274.js') + require.resolve('../../../.next/server/chunks/4271.js') + require.resolve('../../../.next/server/chunks/5237.js') + require.resolve('../../../.next/server/chunks/9097.js') + require.resolve('../../../.next/server/chunks/header.js') require.resolve('../../../.next/server/pages/_app.js') require.resolve('../../../.next/server/pages/_document.js') require.resolve('../../../.next/server/pages/_error.js') @@ -56,6 +157,7 @@ exports.resolvePages = () => { require.resolve('../../../.next/server/pages/deep/import.js') require.resolve('../../../.next/server/pages/getServerSideProps/[id].js') require.resolve('../../../.next/server/pages/getServerSideProps/all/[[...slug]].js') + require.resolve('../../../.next/server/pages/getServerSideProps/file.js') require.resolve('../../../.next/server/pages/getServerSideProps/static.js') require.resolve('../../../.next/server/pages/getStaticProps/[id].js') require.resolve('../../../.next/server/pages/getStaticProps/env.js') @@ -72,43 +174,26 @@ exports.resolvePages = () => { require.resolve('../../../.next/server/pages/previewTest.js') require.resolve('../../../.next/server/pages/shows/[...params].js') require.resolve('../../../.next/server/pages/shows/[id].js') - } catch {} -}" -`; - -exports[`onBuild() generates a file referencing all page sources: for _api_hello-background-background 1`] = ` -"// This file is purely to allow nft to know about these pages. - try { - require.resolve('../../../.next/package.json') - require.resolve('../../../.next/server/chunks/274.js') - require.resolve('../../../.next/server/pages/_app.js') - require.resolve('../../../.next/server/pages/_document.js') - require.resolve('../../../.next/server/pages/_error.js') - require.resolve('../../../.next/server/pages/api/hello-background.js') - require.resolve('../../../.next/server/webpack-api-runtime.js') - require.resolve('../../../.next/server/webpack-runtime.js') - } catch {}" -`; - -exports[`onBuild() generates a file referencing all page sources: for _api_hello-scheduled-handler 1`] = ` -"// This file is purely to allow nft to know about these pages. - try { - require.resolve('../../../.next/package.json') - require.resolve('../../../.next/server/chunks/274.js') - require.resolve('../../../.next/server/pages/_app.js') - require.resolve('../../../.next/server/pages/_document.js') - require.resolve('../../../.next/server/pages/_error.js') - require.resolve('../../../.next/server/pages/api/hello-scheduled.js') require.resolve('../../../.next/server/webpack-api-runtime.js') require.resolve('../../../.next/server/webpack-runtime.js') + require.resolve('../../../components/Header.js') + require.resolve('../../../hello.txt') require.resolve('../../../package.json') - } catch {}" + } catch {} +}" `; exports[`onBuild() generates a file referencing all when publish dir is a subdirectory 1`] = ` -"// This file is purely to allow nft to know about these pages. It should be temporary. +"// This file is purely to allow nft to know about these pages. exports.resolvePages = () => { try { + require.resolve('../../../web/.next/package.json') + require.resolve('../../../web/.next/server/chunks/1987.js') + require.resolve('../../../web/.next/server/chunks/274.js') + require.resolve('../../../web/.next/server/chunks/4271.js') + require.resolve('../../../web/.next/server/chunks/5237.js') + require.resolve('../../../web/.next/server/chunks/9097.js') + require.resolve('../../../web/.next/server/chunks/header.js') require.resolve('../../../web/.next/server/pages/_app.js') require.resolve('../../../web/.next/server/pages/_document.js') require.resolve('../../../web/.next/server/pages/_error.js') @@ -123,6 +208,7 @@ exports.resolvePages = () => { require.resolve('../../../web/.next/server/pages/deep/import.js') require.resolve('../../../web/.next/server/pages/getServerSideProps/[id].js') require.resolve('../../../web/.next/server/pages/getServerSideProps/all/[[...slug]].js') + require.resolve('../../../web/.next/server/pages/getServerSideProps/file.js') require.resolve('../../../web/.next/server/pages/getServerSideProps/static.js') require.resolve('../../../web/.next/server/pages/getStaticProps/[id].js') require.resolve('../../../web/.next/server/pages/getStaticProps/env.js') @@ -139,14 +225,26 @@ exports.resolvePages = () => { require.resolve('../../../web/.next/server/pages/previewTest.js') require.resolve('../../../web/.next/server/pages/shows/[...params].js') require.resolve('../../../web/.next/server/pages/shows/[id].js') + require.resolve('../../../web/.next/server/webpack-api-runtime.js') + require.resolve('../../../web/.next/server/webpack-runtime.js') + require.resolve('../../../web/components/Header.js') + require.resolve('../../../web/hello.txt') + require.resolve('../../../web/package.json') } catch {} }" `; exports[`onBuild() generates a file referencing all when publish dir is a subdirectory 2`] = ` -"// This file is purely to allow nft to know about these pages. It should be temporary. +"// This file is purely to allow nft to know about these pages. exports.resolvePages = () => { try { + require.resolve('../../../web/.next/package.json') + require.resolve('../../../web/.next/server/chunks/1987.js') + require.resolve('../../../web/.next/server/chunks/274.js') + require.resolve('../../../web/.next/server/chunks/4271.js') + require.resolve('../../../web/.next/server/chunks/5237.js') + require.resolve('../../../web/.next/server/chunks/9097.js') + require.resolve('../../../web/.next/server/chunks/header.js') require.resolve('../../../web/.next/server/pages/_app.js') require.resolve('../../../web/.next/server/pages/_document.js') require.resolve('../../../web/.next/server/pages/_error.js') @@ -161,6 +259,7 @@ exports.resolvePages = () => { require.resolve('../../../web/.next/server/pages/deep/import.js') require.resolve('../../../web/.next/server/pages/getServerSideProps/[id].js') require.resolve('../../../web/.next/server/pages/getServerSideProps/all/[[...slug]].js') + require.resolve('../../../web/.next/server/pages/getServerSideProps/file.js') require.resolve('../../../web/.next/server/pages/getServerSideProps/static.js') require.resolve('../../../web/.next/server/pages/getStaticProps/[id].js') require.resolve('../../../web/.next/server/pages/getStaticProps/env.js') @@ -177,6 +276,11 @@ exports.resolvePages = () => { require.resolve('../../../web/.next/server/pages/previewTest.js') require.resolve('../../../web/.next/server/pages/shows/[...params].js') require.resolve('../../../web/.next/server/pages/shows/[id].js') + require.resolve('../../../web/.next/server/webpack-api-runtime.js') + require.resolve('../../../web/.next/server/webpack-runtime.js') + require.resolve('../../../web/components/Header.js') + require.resolve('../../../web/hello.txt') + require.resolve('../../../web/package.json') } catch {} }" `; @@ -518,6 +622,12 @@ Array [ "status": 200, "to": "/.netlify/functions/___netlify-handler", }, + Object { + "force": false, + "from": "/_next/data/build-id/en/getServerSideProps/file.json", + "status": 200, + "to": "/.netlify/functions/___netlify-handler", + }, Object { "force": false, "from": "/_next/data/build-id/en/getServerSideProps/static.json", @@ -746,6 +856,12 @@ Array [ "status": 200, "to": "/.netlify/functions/___netlify-handler", }, + Object { + "force": false, + "from": "/_next/data/build-id/es/getServerSideProps/file.json", + "status": 200, + "to": "/.netlify/functions/___netlify-handler", + }, Object { "force": false, "from": "/_next/data/build-id/es/getServerSideProps/static.json", @@ -938,6 +1054,12 @@ Array [ "status": 200, "to": "/.netlify/functions/___netlify-handler", }, + Object { + "force": false, + "from": "/_next/data/build-id/fr/getServerSideProps/file.json", + "status": 200, + "to": "/.netlify/functions/___netlify-handler", + }, Object { "force": false, "from": "/_next/data/build-id/fr/getServerSideProps/static.json", @@ -1236,6 +1358,12 @@ Array [ "status": 200, "to": "/.netlify/functions/___netlify-handler", }, + Object { + "force": false, + "from": "/es/getServerSideProps/file", + "status": 200, + "to": "/.netlify/functions/___netlify-handler", + }, Object { "force": false, "from": "/es/getServerSideProps/static", @@ -1434,6 +1562,12 @@ Array [ "status": 200, "to": "/.netlify/functions/___netlify-handler", }, + Object { + "force": false, + "from": "/fr/getServerSideProps/file", + "status": 200, + "to": "/.netlify/functions/___netlify-handler", + }, Object { "force": false, "from": "/fr/getServerSideProps/static", @@ -1584,6 +1718,12 @@ Array [ "status": 200, "to": "/.netlify/functions/___netlify-handler", }, + Object { + "force": false, + "from": "/getServerSideProps/file", + "status": 200, + "to": "/.netlify/functions/___netlify-handler", + }, Object { "force": false, "from": "/getServerSideProps/static", @@ -1704,6 +1844,17 @@ Array [ "status": 200, "to": "/.netlify/functions/___netlify-handler", }, + Object { + "conditions": Object { + "Cookie": Array [ + "__prerender_bypass", + "__next_preview_data", + ], + }, + "from": "/next-on-netlify.png", + "status": 200, + "to": "/next-on-netlify.png", + }, Object { "force": false, "from": "/old/image", @@ -1770,6 +1921,17 @@ Array [ "status": 200, "to": "/.netlify/functions/___netlify-handler", }, + Object { + "conditions": Object { + "Cookie": Array [ + "__prerender_bypass", + "__next_preview_data", + ], + }, + "from": "/shows1.json", + "status": 200, + "to": "/shows1.json", + }, Object { "force": false, "from": "/static", diff --git a/test/index.js b/test/index.js index 538787a1a7..c442be43ce 100644 --- a/test/index.js +++ b/test/index.js @@ -1,3 +1,6 @@ +import { relative } from 'pathe' +import { getAllPageDependencies } from '../packages/runtime/src/templates/getPageResolver' + jest.mock('../packages/runtime/src/helpers/utils', () => { return { ...jest.requireActual('../packages/runtime/src/helpers/utils'), @@ -6,7 +9,7 @@ jest.mock('../packages/runtime/src/helpers/utils', () => { }) const Chance = require('chance') -const { writeJSON, unlink, existsSync, readFileSync, copy, ensureDir, readJson } = require('fs-extra') +const { writeJSON, unlink, existsSync, readFileSync, copy, ensureDir, readJson, pathExists } = require('fs-extra') const path = require('path') const process = require('process') const os = require('os') @@ -120,7 +123,14 @@ export const moveNextDist = async function (dir = '.next') { await stubModules(['next', 'sharp']) await ensureDir(dirname(dir)) await copy(path.join(SAMPLE_PROJECT_DIR, '.next'), path.join(process.cwd(), dir)) - await copy(path.join(SAMPLE_PROJECT_DIR, 'pages'), path.join(process.cwd(), 'pages')) + + for (const file of ['pages', 'app', 'src', 'components', 'public', 'components', 'hello.txt', 'package.json']) { + const source = path.join(SAMPLE_PROJECT_DIR, file) + if (existsSync(source)) { + await copy(source, path.join(process.cwd(), file)) + } + } + await rewriteAppDir(dir) } @@ -573,7 +583,7 @@ describe('onBuild()', () => { } }) - it('generates a file referencing all page sources', async () => { + it('generates a file referencing all API route sources', async () => { await moveNextDist() await nextRuntime.onBuild(defaultArgs) @@ -584,7 +594,7 @@ describe('onBuild()', () => { } }) - test('generates a file referencing all API route sources', async () => { + test('generates a file referencing all page sources', async () => { await moveNextDist() await nextRuntime.onBuild(defaultArgs) const handlerPagesFile = path.join(constants.INTERNAL_FUNCTIONS_SRC, HANDLER_FUNCTION_NAME, 'pages.js') @@ -1247,6 +1257,23 @@ describe('function helpers', () => { }) describe('config', () => { + describe('dependency tracing', () => { + it('extracts a list of all dependencies', async () => { + await moveNextDist() + await nextRuntime.onBuild(defaultArgs) + const dependencies = await getAllPageDependencies(constants.PUBLISH_DIR) + expect(dependencies.map((dep) => relative(process.cwd(), dep))).toMatchSnapshot() + }) + + it('extracts dependencies that exist', async () => { + await moveNextDist() + await nextRuntime.onBuild(defaultArgs) + const dependencies = await getAllPageDependencies(constants.PUBLISH_DIR) + const filesExist = await Promise.all(dependencies.map((dep) => pathExists(dep))) + expect(filesExist.every((exists) => exists)).toBeTruthy() + }) + }) + describe('generateCustomHeaders', () => { // The routesManifest is the contents of the routes-manifest.json file which will already contain the generated // header paths which take locales and base path into account which is why you'll see them in the paths already