Skip to content

fix: resolve all pages using nft #1780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions demos/default/hello.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
world
29 changes: 29 additions & 0 deletions demos/default/pages/getServerSideProps/file.js
Original file line number Diff line number Diff line change
@@ -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 }) => (
<>
<p>hello {world}</p>
<span id="anotherTime">time: {time}</span>
<Link href="/" id="home">
to home
</Link>
<br />
<Link href="/something" id="something">
to something
</Link>
</>
)
export default File
19 changes: 7 additions & 12 deletions packages/runtime/src/helpers/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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<void> => {
INTERNAL_FUNCTIONS_SRC,
FUNCTIONS_SRC = DEFAULT_FUNCTIONS_SRC,
PUBLISH_DIR,
}: NetlifyPluginConstants): Promise<void> => {
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)
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 })

Expand Down
61 changes: 32 additions & 29 deletions packages/runtime/src/templates/getPageResolver.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
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 { HANDLER_FUNCTION_NAME } from '../constants'
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<string>) => {
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 = posix.resolve(slash(publish), 'server')

const pageFiles = await glob('{pages,app}/**/*.js', {
cwd: root,
absolute: true,
dot: true,
})
const pageFiles = pages
.map((page) => `require.resolve('${posix.relative(functionDir, posix.join(root, slash(page)))}')`)
.sort()

return outdent`
// This file is purely to allow nft to know about these pages. It should be temporary.
return getUniqueDependencies(pageFiles)
}

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 ')}
Expand All @@ -33,31 +46,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 = posix.resolve(posix.join('.netlify', 'functions', HANDLER_FUNCTION_NAME))
const dependencies = await getAllPageDependencies(publish)
return getResolverForDependencies({ dependencies, functionDir })
}

export const getResolverForSourceFiles = async ({
functionsDir,
sourceFiles,
}: {
functionsDir: string
sourceFiles: Array<string>
}) => {
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 })
}
Loading