Am incredibly stumped by this issue and have spent half a day troubleshooting it.
I am trying to dynamically generate menu items based on the directory structure in a Next.JS application. It works locally but not when building on netlify. I have tried changing the cases of all the files that are not being returned by the api so I don’t think it is that but who knows.
Two functions being used to generate the menuItems object.
getPages.js:
// scripts/getPages.js
import path from 'path';
import fs from 'fs';
export function getPages(dir, filelist = []) {
const files = fs.readdirSync(dir);
console.log("dir", dir)
console.log("files", files)
files.forEach(function (file) {
console.log("file", file)
if (fs.statSync(path.join(dir, file)).isDirectory()) {
filelist = getPages(path.join(dir, file), filelist);
}
else {
filelist.push(path.join(dir, file));
}
});
console.log("fileList", filelist)
return filelist;
}
/api/menuItems.ts
import { getPages } from '../../scripts/getPages';
export default async function handler(req, res) {
const pages = getPages('./pages').filter(page => !page.includes('api') && !page.includes('_app') && !page.includes('_document'))
.map(page => page.replace(/pages/, '')
.replace(/\.tsx$/, '')
.replace(/\.js$/, '')
.replace(/\.ts$/, '')
.replace(/index$/g, ''));
console.log("pages after", pages);
const menuItems = {};
for (const page of pages) {
const parts = page.split(/\\|\//).filter(part => part !== '');
let currentLevel = menuItems;
for (let i = 0; i < parts.length; i++) {
let part = parts[i];
// Replace underscores with spaces
part = part.replace(/_/g, ' ');
// Capitalize the first letter of the part if it's not already capitalized
if (part[0] !== part[0].toUpperCase()) {
part = part[0].toUpperCase() + part.slice(1);
}
if (!currentLevel[part]) {
currentLevel[part] = {
href: `/${parts.slice(0, i + 1).join('/')}`,
text: part,
subItems: {}
};
}
currentLevel = currentLevel[part].subItems;
}
}
console.log("menuItems", menuItems);
res.status(200).json(menuItems);
}
On my local environment I get this object at the /api/menuItems path:
{
"Hoa":{
"href":"/hoa",
"text":"Hoa",
"subItems":{
"About":{
"href":"/hoa/about",
"text":"About",
"subItems":{
}
},
"Calendar":{
"href":"/hoa/calendar",
"text":"Calendar",
"subItems":{
}
},
"Documents":{
"href":"/hoa/documents",
"text":"Documents",
"subItems":{
}
}
}
},
"Map":{
"href":"/map",
"text":"Map",
"subItems":{
}
},
"PageList":{
"href":"/PageList",
"text":"PageList",
"subItems":{
}
},
"Resources":{
"href":"/resources",
"text":"Resources",
"subItems":{
"FAQ":{
"href":"/resources/FAQ",
"text":"FAQ",
"subItems":{
}
},
"Utilities and links":{
"href":"/resources/utilities_and_links",
"text":"Utilities and links",
"subItems":{
}
}
}
}
}
You can see the truncated object being returned here: https://magenta-palmier-138a18.netlify.app/api/menuItems
{
"Resources":{
"href":"/resources",
"text":"Resources",
"subItems":{
"FAQ":{
"href":"/resources/FAQ",
"text":"FAQ",
"subItems":{
}
},
"Utilities and links":{
"href":"/resources/utilities_and_links",
"text":"Utilities and links",
"subItems":{
}
}
}
}
}
I have mainly been looking at the missing “hoa” folder that is not being returned in the menuItems object.
What has me confused is the console.logs I added all throughout both functions above does in fact show that the files/folders are being detected during the netlify build process but they are simply just not being returned by the API response.
Snippet of some netlify build logs here:
7:15:17 PM: Netlify Build
7:15:17 PM: ────────────────────────────────────────────────────────────────
7:15:17 PM:
7:15:17 PM: ❯ Version
7:15:17 PM: @netlify/build 29.12.1
7:15:17 PM:
7:15:17 PM: ❯ Flags
7:15:17 PM: baseRelDir: true
7:15:17 PM: buildId: 6498f494186bea0008009ea9
7:15:17 PM: config: /opt/build/repo/netlify.toml
7:15:17 PM: deployId: 6498f494186bea0008009eab
7:15:17 PM: tracing:
7:15:17 PM: enabled: 'false'
7:15:17 PM: host: 10.65.24.243
7:15:17 PM: parentSpanId: 11e1cc006633ebf1
7:15:17 PM: traceFlags: '01'
7:15:17 PM: traceId: 153a3ad95451be201043638fac6906fa
7:15:17 PM:
7:15:17 PM: ❯ Current directory
7:15:17 PM: /opt/build/repo
7:15:17 PM:
7:15:17 PM: ❯ Config file
7:15:17 PM: /opt/build/repo/netlify.toml
7:15:17 PM:
7:15:17 PM: ❯ Context
7:15:17 PM: production
7:15:17 PM:
7:15:17 PM: ❯ Using Next.js Runtime - v4.38.1
7:15:18 PM:
7:15:18 PM: @netlify/plugin-nextjs (onPreBuild event)
7:15:18 PM: ────────────────────────────────────────────────────────────────
7:15:18 PM:
7:15:18 PM: Next.js cache restored.
7:15:18 PM: Netlify configuration property build.environment.NEXT_PRIVATE_TARGET value changed.
7:15:18 PM:
7:15:18 PM: (@netlify/plugin-nextjs onPreBuild completed in 134ms)
7:15:18 PM:
7:15:18 PM: build.command from netlify.toml
7:15:18 PM: ────────────────────────────────────────────────────────────────
7:15:18 PM:
7:15:18 PM: $ next build
7:15:18 PM: info - Loaded env from /opt/build/repo/.env.production
7:15:18 PM: warn - Detected next.config.js, no exported configuration found. https://nextjs.org/docs/messages/empty-configuration
7:15:18 PM: info - Linting and checking validity of types...
7:15:23 PM: info - Creating an optimized production build...
7:15:49 PM: info - Compiled successfully
7:15:49 PM: info - Collecting page data...
7:16:04 PM: info - Generating static pages (0/12)
7:16:19 PM: dir ./pages
7:16:19 PM: files [
7:16:19 PM: 'PageList.tsx',
7:16:19 PM: '_app.tsx',
7:16:19 PM: '_document.js',
7:16:19 PM: 'api',
7:16:19 PM: 'hoa',
7:16:19 PM: 'index.tsx',
7:16:19 PM: 'map.tsx',
7:16:19 PM: 'resources'
7:16:19 PM: ]
7:16:19 PM: file PageList.tsx
7:16:19 PM: file _app.tsx
7:16:19 PM: file _document.js
7:16:19 PM: file api
7:16:19 PM: dir pages/api
7:16:19 PM: files [ 'auth', 'hello.js', 'menuItems.ts', 'pages.ts' ]
7:16:19 PM: file auth
7:16:19 PM: dir pages/api/auth
7:16:19 PM: files [ 'auth.tsx' ]
7:16:19 PM: file auth.tsx
7:16:19 PM: fileList [
7:16:19 PM: 'pages/PageList.tsx',
7:16:19 PM: 'pages/_app.tsx',
7:16:19 PM: 'pages/_document.js',
7:16:19 PM: 'pages/api/auth/auth.tsx'
7:16:19 PM: ]
7:16:19 PM: file hello.js
7:16:19 PM: file menuItems.ts
7:16:19 PM: file pages.ts
7:16:19 PM: fileList [
7:16:19 PM: 'pages/PageList.tsx',
7:16:19 PM: 'pages/_app.tsx',
7:16:19 PM: 'pages/_document.js',
7:16:19 PM: 'pages/api/auth/auth.tsx',
7:16:19 PM: 'pages/api/hello.js',
7:16:19 PM: 'pages/api/menuItems.ts',
7:16:19 PM: 'pages/api/pages.ts'
7:16:19 PM: ]
7:16:19 PM: file hoa
7:16:19 PM: dir pages/hoa
7:16:19 PM: files [ 'about.tsx', 'calendar', 'documents', 'index.tsx' ]
7:16:19 PM: file about.tsx
7:16:19 PM: file calendar
7:16:19 PM: dir pages/hoa/calendar
7:16:19 PM: files [ 'index.tsx' ]
7:16:19 PM: file index.tsx
7:16:19 PM: fileList [
7:16:19 PM: 'pages/PageList.tsx',
7:16:19 PM: 'pages/_app.tsx',
7:16:19 PM: 'pages/_document.js',
7:16:19 PM: 'pages/api/auth/auth.tsx',
7:16:19 PM: 'pages/api/hello.js',
7:16:19 PM: 'pages/api/menuItems.ts',
7:16:19 PM: 'pages/api/pages.ts',
7:16:19 PM: 'pages/hoa/about.tsx',
7:16:19 PM: 'pages/hoa/calendar/index.tsx'
7:16:19 PM: ]
7:16:19 PM: file documents
7:16:19 PM: dir pages/hoa/documents
7:16:19 PM: files [ 'index.tsx' ]
7:16:19 PM: file index.tsx
7:16:19 PM: fileList [
7:16:19 PM: 'pages/PageList.tsx',
7:16:19 PM: 'pages/_app.tsx',
7:16:19 PM: 'pages/_document.js',
7:16:19 PM: 'pages/api/auth/auth.tsx',
7:16:19 PM: 'pages/api/hello.js',
7:16:19 PM: 'pages/api/menuItems.ts',
7:16:19 PM: 'pages/api/pages.ts',
7:16:19 PM: 'pages/hoa/about.tsx',
7:16:19 PM: 'pages/hoa/calendar/index.tsx',
7:16:19 PM: 'pages/hoa/documents/index.tsx'
7:16:19 PM: ]
7:16:19 PM: file index.tsx
7:16:19 PM: fileList [
7:16:19 PM: 'pages/PageList.tsx',
7:16:19 PM: 'pages/_app.tsx',
7:16:19 PM: 'pages/_document.js',
7:16:19 PM: 'pages/api/auth/auth.tsx',
7:16:19 PM: 'pages/api/hello.js',
7:16:19 PM: 'pages/api/menuItems.ts',
7:16:19 PM: 'pages/api/pages.ts',
7:16:19 PM: 'pages/hoa/about.tsx',
7:16:19 PM: 'pages/hoa/calendar/index.tsx',
7:16:19 PM: 'pages/hoa/documents/index.tsx',
7:16:19 PM: 'pages/hoa/index.tsx'
7:16:19 PM: ]
7:16:19 PM: file index.tsx
7:16:19 PM: file map.tsx
7:16:19 PM: file resources
7:16:19 PM: dir pages/resources
7:16:19 PM: files [ 'FAQ.tsx', 'index.tsx', 'utilities_and_links.tsx' ]
7:16:19 PM: file FAQ.tsx
7:16:19 PM: file index.tsx
7:16:19 PM: file utilities_and_links.tsx
7:16:19 PM: fileList [
7:16:19 PM: 'pages/PageList.tsx',
7:16:19 PM: 'pages/_app.tsx',
7:16:19 PM: 'pages/_document.js',
7:16:19 PM: 'pages/api/auth/auth.tsx',
7:16:19 PM: 'pages/api/hello.js',
7:16:19 PM: 'pages/api/menuItems.ts',
7:16:19 PM: 'pages/api/pages.ts',
7:16:19 PM: 'pages/hoa/about.tsx',
7:16:19 PM: 'pages/hoa/calendar/index.tsx',
7:16:19 PM: 'pages/hoa/documents/index.tsx',
7:16:19 PM: 'pages/hoa/index.tsx',
7:16:19 PM: 'pages/index.tsx',
7:16:19 PM: 'pages/map.tsx',
7:16:19 PM: 'pages/resources/FAQ.tsx',
7:16:19 PM: 'pages/resources/index.tsx',
7:16:19 PM: 'pages/resources/utilities_and_links.tsx'
7:16:19 PM: ]
7:16:19 PM: fileList [
7:16:19 PM: 'pages/PageList.tsx',
7:16:19 PM: 'pages/_app.tsx',
7:16:19 PM: 'pages/_document.js',
7:16:19 PM: 'pages/api/auth/auth.tsx',
7:16:19 PM: 'pages/api/hello.js',
7:16:19 PM: 'pages/api/menuItems.ts',
7:16:19 PM: 'pages/api/pages.ts',
7:16:19 PM: 'pages/hoa/about.tsx',
7:16:19 PM: 'pages/hoa/calendar/index.tsx',
7:16:19 PM: 'pages/hoa/documents/index.tsx',
7:16:19 PM: 'pages/hoa/index.tsx',
7:16:19 PM: 'pages/index.tsx',
7:16:19 PM: 'pages/map.tsx',
7:16:19 PM: 'pages/resources/FAQ.tsx',
7:16:19 PM: 'pages/resources/index.tsx',
7:16:19 PM: 'pages/resources/utilities_and_links.tsx'
7:16:19 PM: ]
7:16:19 PM: [
7:16:19 PM: 'pages/PageList.tsx',
7:16:19 PM: 'pages/_app.tsx',
7:16:19 PM: 'pages/_document.js',
7:16:19 PM: 'pages/api/auth/auth.tsx',
7:16:19 PM: 'pages/api/hello.js',
7:16:19 PM: 'pages/api/menuItems.ts',
7:16:19 PM: 'pages/api/pages.ts',
7:16:19 PM: 'pages/hoa/about.tsx',
7:16:19 PM: 'pages/hoa/calendar/index.tsx',
7:16:19 PM: 'pages/hoa/documents/index.tsx',
7:16:19 PM: 'pages/hoa/index.tsx',
7:16:19 PM: 'pages/index.tsx',
7:16:19 PM: 'pages/map.tsx',
7:16:19 PM: 'pages/resources/FAQ.tsx',
7:16:19 PM: 'pages/resources/index.tsx',
7:16:19 PM: 'pages/resources/utilities_and_links.tsx'
7:16:19 PM: ]
7:16:19 PM: info - Generating static pages (3/12)
7:16:19 PM: info - Generating static pages (6/12)
7:16:20 PM: info - Generating static pages (9/12)
7:16:20 PM: info - Generating static pages (12/12)
7:16:20 PM: info - Finalizing page optimization...
Further confusing me is that the pages are in fact available to be accessed:
etc. they just don’t show up in the menuItems object.
At the point where I need a second pair of eyes on this as I am genuinely baffled by this.
Happy to supply any other info needed.