Skip to content

Commit

Permalink
support prerendered api functions (#826)
Browse files Browse the repository at this point in the history
  • Loading branch information
james-elicx authored Jul 10, 2024
1 parent f684bc9 commit 97d7981
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/quiet-glasses-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cloudflare/next-on-pages': patch
---

Support for pre-rendered `API` functions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { join, relative } from 'node:path';
import type { PathInfo } from '../../utils';
import {
addLeadingSlash,
formatRoutePath,
getRouteOverrides,
normalizePath,
readDirectories,
readFilesAndDirectories,
readJsonFile,
} from '../../utils';

Expand All @@ -25,7 +26,9 @@ export async function collectFunctionConfigsRecursively(
ignoredFunctions: new Map(),
},
): Promise<CollectedFunctions> {
const dirs = await readDirectories(baseDir);
const paths = await readFilesAndDirectories(baseDir);
const dirs = paths.filter(path => path.isDirectory);
const files = paths.filter(path => !path.isDirectory);

for (const { path } of dirs) {
if (path.endsWith('.func')) {
Expand All @@ -36,10 +39,14 @@ export async function collectFunctionConfigsRecursively(
normalizePath(relative(configs.functionsDir, path)),
);

if (
const isPrerenderedIsrFunc =
config?.operationType?.toLowerCase() === 'isr' &&
!path.endsWith('.action.func')
) {
!path.endsWith('.action.func');
const isPrerenderedApiFunc =
config?.operationType?.toLowerCase() === 'api' &&
checkPrerenderConfigExists(path, files);

if (isPrerenderedIsrFunc || isPrerenderedApiFunc) {
configs.prerenderedFunctions.set(path, { relativePath, config });
} else if (config?.runtime?.toLowerCase() === 'edge') {
const formattedPathName = formatRoutePath(relativePath);
Expand All @@ -61,6 +68,15 @@ export async function collectFunctionConfigsRecursively(
return configs;
}

function checkPrerenderConfigExists(funcPath: string, files: PathInfo[]) {
const prerenderConfigPath = funcPath.replace(
/\.func$/,
'.prerender-config.json',
);

return files.find(({ path }) => path === prerenderConfigPath);
}

export type CollectedFunctions = {
functionsDir: string;
edgeFunctions: Map<string, FunctionInfo>;
Expand Down
16 changes: 8 additions & 8 deletions packages/next-on-pages/src/utils/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,18 @@ export async function copyFileWithDir(sourceFile: string, destFile: string) {
}

/**
* Reads all directories in a directory.
* Reads all files and directories in a directory.
*
* @param path Path to read directories from.
* @returns Array of all directories in a directory.
* @param path Path to read from.
* @returns Array of all files and directories in a directory.
*/
export async function readDirectories(
export async function readFilesAndDirectories(
basePath: string,
): Promise<DirectoryInfo[]> {
): Promise<PathInfo[]> {
try {
const files = await readdir(basePath, { withFileTypes: true });

const dirs = await Promise.all(
const paths = await Promise.all(
files.map(async file => {
const path = normalizePath(join(basePath, file.name));
const isSymbolicLink = file.isSymbolicLink();
Expand All @@ -151,13 +151,13 @@ export async function readDirectories(
}),
);

return dirs.filter(file => file.isDirectory);
return paths;
} catch {
return [];
}
}

type DirectoryInfo = {
export type PathInfo = {
name: string;
path: string;
isDirectory: boolean;
Expand Down
56 changes: 32 additions & 24 deletions packages/next-on-pages/tests/src/utils/fs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
copyFileWithDir,
getFileHash,
normalizePath,
readDirectories,
readFilesAndDirectories,
readJsonFile,
readPathsRecursively,
validateDir,
Expand Down Expand Up @@ -183,7 +183,7 @@ describe('copyFileWithDir', () => {
});
});

describe('readDirectories', () => {
describe('readFilesAndDirectories', () => {
beforeAll(() => {
mockFs({
root: {
Expand All @@ -193,39 +193,47 @@ describe('readDirectories', () => {
},
'index.func': { 'index.js': 'index-js-code' },
'home.func': { 'index.js': 'home-js-code' },
'test.js': 'test-js-code',
},
},
});
});

afterAll(() => mockFs.restore());

test('should read all directories inside the provided folder', async () => {
const dirs = await readDirectories('root/functions');
test('should read all files and directories inside the provided folder', async () => {
const dirs = await readFilesAndDirectories('root/functions');

expect(dirs.length).toEqual(3);
expect(dirs[0]).toEqual({
name: '(route-group)',
path: 'root/functions/(route-group)',
isDirectory: true,
isSymbolicLink: false,
});
expect(dirs[1]).toEqual({
name: 'home.func',
path: 'root/functions/home.func',
isDirectory: true,
isSymbolicLink: false,
});
expect(dirs[2]).toEqual({
name: 'index.func',
path: 'root/functions/index.func',
isDirectory: true,
isSymbolicLink: false,
});
expect(dirs).toEqual([
{
name: '(route-group)',
path: 'root/functions/(route-group)',
isDirectory: true,
isSymbolicLink: false,
},
{
name: 'home.func',
path: 'root/functions/home.func',
isDirectory: true,
isSymbolicLink: false,
},
{
name: 'index.func',
path: 'root/functions/index.func',
isDirectory: true,
isSymbolicLink: false,
},
{
name: 'test.js',
path: 'root/functions/test.js',
isDirectory: false,
isSymbolicLink: false,
},
]);
});

test('invalid directory returns empty array', async () => {
const dirs = await readDirectories('invalid-root/functions');
const dirs = await readFilesAndDirectories('invalid-root/functions');

expect(dirs).toEqual([]);
});
Expand Down

0 comments on commit 97d7981

Please sign in to comment.