Next.js Discord

Discord Forum

Pages Router: Use of require causes build to scan parent folders.

Unanswered
goodsie posted this in #help-forum
Open in Discord
I created an endpoint to remotely execute scripts in a folder.
However when time to build the linting process is scanning parent folders of this endpoint, finding files it shouldnt and failing.
Why is the build process trying to compile anything in this destination ?

Code:
import type { NextApiRequest, NextApiResponse } from 'next';
import { exec } from 'child_process';

export default function Endpoints(req: NextApiRequest, res: NextApiResponse) {
  const ep = req.query;
  const uuid = ep.endpoint;
  const auid = req.headers.auth
  const file = req.body.file;
  const endpoint = require(`../../../processes/data/${auid}/${uuid}/${file}`);

  exec(`node ${endpoint}`, (error, stdout, stderr) => {
    if (error) {
      res.status(500).json({ error: error.message });
      return;
    }

    if (stderr) {
      res.status(500).json({ error: stderr });
      return;
    }

    res.status(200).json({ result: stdout });
  });
}


and Error:
> next build

 ✓ Linting and checking validity of types
Failed to compile.

./processes/data/05b3fc7d-b9d6-40d0-955b-befcb285d9ef/b09ccf66-4925-4050-b5c3-e6e246f502e7/functions/oauth/oauth2.js
Module not found: Can't resolve '../../../../../../src/Api'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./processes/data/ sync ^\.\/.*\/.*\/.*$
./pages/api/ep/[endpoint].ts

./processes/data/05b3fc7d-b9d6-40d0-955b-befcb285d9ef/d36c30e5-e1a8-4eec-b577-e6308d7aa5cd/_system/utils.js
Module not found: Can't resolve 'tweetnacl'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./processes/data/ sync ^\.\/.*\/.*\/.*$
./pages/api/ep/[endpoint].ts


> Build failed because of webpack errors
   Creating an optimized production build  .

I can't think of any reason the build process would look in any of these other folders.

51 Replies

@goodsie I created an endpoint to remotely execute scripts in a folder. However when time to build the linting process is scanning parent folders of this endpoint, finding files it shouldnt and failing. Why is the build process trying to compile anything in this destination ? Code: js import type { NextApiRequest, NextApiResponse } from 'next'; import { exec } from 'child_process'; export default function Endpoints(req: NextApiRequest, res: NextApiResponse) { const ep = req.query; const uuid = ep.endpoint; const auid = req.headers.auth const file = req.body.file; const endpoint = require(`../../../processes/data/${auid}/${uuid}/${file}`); exec(`node ${endpoint}`, (error, stdout, stderr) => { if (error) { res.status(500).json({ error: error.message }); return; } if (stderr) { res.status(500).json({ error: stderr }); return; } res.status(200).json({ result: stdout }); }); } and Error: > next build ✓ Linting and checking validity of types Failed to compile. ./processes/data/05b3fc7d-b9d6-40d0-955b-befcb285d9ef/b09ccf66-4925-4050-b5c3-e6e246f502e7/functions/oauth/oauth2.js Module not found: Can't resolve '../../../../../../src/Api' https://nextjs.org/docs/messages/module-not-found Import trace for requested module: ./processes/data/ sync ^\.\/.*\/.*\/.*$ ./pages/api/ep/[endpoint].ts ./processes/data/05b3fc7d-b9d6-40d0-955b-befcb285d9ef/d36c30e5-e1a8-4eec-b577-e6308d7aa5cd/_system/utils.js Module not found: Can't resolve 'tweetnacl' https://nextjs.org/docs/messages/module-not-found Import trace for requested module: ./processes/data/ sync ^\.\/.*\/.*\/.*$ ./pages/api/ep/[endpoint].ts > Build failed because of webpack errors Creating an optimized production build . I can't think of any reason the build process would look in any of these other folders.
try this
import fs from 'fs/promises'

const endpoint = fs.readFile(path.join(process.cwd(), 'processes', 'data', auid, uuid, file), 'utf8');
@Ray try this ts import fs from 'fs/promises' const endpoint = fs.readFile(path.join(process.cwd(), 'processes', 'data', auid, uuid, file), 'utf8');
Thank you Ray, This indeed fixes compile. Same as if I were to set a value.
Why would the use of requiring a file in the endpoint code cause parent folder scanning ?
hmm i'm not sure
This has me wondering:
Import trace for requested module:
./processes/data/ sync ^\.\/.*\/.*\/.*$
./pages/api/ep/[endpoint].ts
https://vercel.com/guides/how-can-i-use-files-in-serverless-functions#next.js
this is the recommended way to read file on vercel/nextjs
Yes thank you.
However, The idea here is to process a script and return a result from said script.
Previously I used a custom handler but with the switch from php to React/NextJs it seemed redundant to have 2.
In my previous solution I would require the file with no issue.
Using NextJs Pages Router Dynamic API Routing the require cause the build to scan parent folders of that required file. <-- I do not understand this.(kinda a pain in the backside too)
I'd like to add that this behaviour ONLY happens when building a path as I did in the above block:
require(`../../../processes/data/${auid}/${uuid}/${file}`);
why you need to use require?
using require gives me a return as I would expect:
    const
      endpoint = require(filePath),
      result = await endpoint();
    res.status(200).json(result);
The image above is the ideal response, if I type in the path manually I can achieve results.
what is inside the file
I have been doing as much reading as possible on this, and the reason(that I can munch together) is that the webpack, due to the path being dynamic cannot compile a singular item at compile time(makes sense). so the webpack is checking all possibles at compile time
@Ray what is inside the file
module.exports = async () => {
  let body_ = `Hello Guest :)`;
  return {
    statusCode: 200,
    headers: {
      'Content-Type': 'text/html',
    },
    body: body_,
  };
};
its basic, test usage.
eval returns a 200 as it does not fail, however it does NOT return content (that Ive noticed)
the eval implement i used:
const result = await eval(await readFile(filePath, 'utf-8'));
    res.status(200).json(result);
can you use await import()
hmm
did not try
@goodsie hmm
 const endpoint = await import(`../../../processes/data/${auid}/${uuid}/${file}`);

  exec(`node ${endpoint.default}`, (error, stdout, stderr) => {

look like this work
@Ray ts const endpoint = await import(`../../../processes/data/${auid}/${uuid}/${file}`); exec(`node ${endpoint.default}`, (error, stdout, stderr) => { look like this work
I almost had this combination earlier.. Will give it a try.
fyi: using import like require gave the same 'module not found' error(on a perfectly fine path I might add..(...require..):lolsob: )
@Ray ts const endpoint = await import(`../../../processes/data/${auid}/${uuid}/${file}`); exec(`node ${endpoint.default}`, (error, stdout, stderr) => { look like this work
No jive;
When performing build it is looking in all folders that can be found inside ../../../processes/data/.
This is the webpack compile doing this as mentioned up above.
I'm going to play with the use of exec some more.
I do not think it is of use for me on my end however
@Ray where are you gonna deploy? I think "../../.." won't work on vercel
I was thinking the ../ would be an issue aswell.
I am deployed over aws using my own implementation of https
I think its better to use process.cwd() in serverless env
I will try in 5 mins.
btw, Thank you for your time Ray.
I went back to the filePath with path.join:
import type { NextApiRequest, NextApiResponse } from 'next';
import { access, readFile } from 'fs/promises';
import { exec } from 'child_process';
import path from 'path';

export default async function Endpoints(req: NextApiRequest, res: NextApiResponse) {
  const ep = req.query;
  const uuid = ep.endpoint as string;
  const auid = req.headers.auth as string;
  const file = req.body.file as string;
  const filePath = path.join(process.cwd(), 'processes', 'data', auid, uuid, file);
  const exists = await access(filePath).then(() => true).catch(() => false);
  if (!exists) {
    res.status(404).json({ error: 'File not found.' });
    return;
  }
  if (req.method === 'GET') {
    getEndpoint(req, res, filePath);
  } else if (req.method === 'POST') {
    postEndpoint(res, filePath);
  } else {
    res.status(405).json({ error: 'Method not allowed.' });
  }
}

async function postEndpoint(res: NextApiResponse, filePath: string) {
  try {
    const endpoint = await import(filePath);

    exec(`node ${endpoint}`, (error, stdout, stderr) => {
      if (error) {
        res.status(500).json({ error: error.message });
        return;
      }
      if (stderr) {
        res.status(500).json({ error: stderr });
        return;
      }
      res.status(200).json(stdout);
    });

  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
};
{
    "success": false,
    "error": "Cannot find module 'C:\\Os\\processes\\data\\05b3fc7d-b9d6-40d0-955b-befcb285d9ef\\b09ccf66-4925-4050-b5c3-e6e246f502e7\\endpoints\\hello.js'",
    "filePath": "C:\\Os\\processes\\data\\05b3fc7d-b9d6-40d0-955b-befcb285d9ef\\b09ccf66-4925-4050-b5c3-e6e246f502e7\\endpoints\\hello.js"
}
Paths are correct, can be pasted to file browser to achieve file( I also doubt this every 20 seconds 😅 )
@goodsie I went back to the filePath with path.join: js import type { NextApiRequest, NextApiResponse } from 'next'; import { access, readFile } from 'fs/promises'; import { exec } from 'child_process'; import path from 'path'; export default async function Endpoints(req: NextApiRequest, res: NextApiResponse) { const ep = req.query; const uuid = ep.endpoint as string; const auid = req.headers.auth as string; const file = req.body.file as string; const filePath = path.join(process.cwd(), 'processes', 'data', auid, uuid, file); const exists = await access(filePath).then(() => true).catch(() => false); if (!exists) { res.status(404).json({ error: 'File not found.' }); return; } if (req.method === 'GET') { getEndpoint(req, res, filePath); } else if (req.method === 'POST') { postEndpoint(res, filePath); } else { res.status(405).json({ error: 'Method not allowed.' }); } } async function postEndpoint(res: NextApiResponse, filePath: string) { try { const endpoint = await import(filePath); exec(`node ${endpoint}`, (error, stdout, stderr) => { if (error) { res.status(500).json({ error: error.message }); return; } if (stderr) { res.status(500).json({ error: stderr }); return; } res.status(200).json(stdout); }); } catch (error) { res.status(500).json({ success: false, error: error.message }); } };
import { exec } from "child_process";
import { access } from "fs/promises";
import { NextApiRequest, NextApiResponse } from "next";
import path from "path";
import { promisify } from "util";
import JSON5 from "json5";

const execPromise = promisify(exec);

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const filePath = path.join(process.cwd(), "data.js");
    const exists = await access(filePath)
      .then(() => true)
      .catch(() => false);
    if (!exists) {
      res.status(404).json({ error: "File not found." });
      return;
    }

    const out = await execPromise(`node ${filePath}`);
    const data = JSON5.parse(out.stdout);

    res.status(200).json({ success: true, data });
  } catch (error) {
    res.status(500).json({ success: false, error: (error as Error).message });
  }
}
// data.js
(() => {
  let body_ = `Hello Guest :)`;

  console.log({
    statusCode: 200,
    headers: {
      "Content-Type": "text/html",
    },
    body: body_,
  });
})();
got it work
@Ray got it work
:partywizard:
@Ray js // data.js (() => { let body_ = `Hello Guest :)`; console.log({ statusCode: 200, headers: { "Content-Type": "text/html", }, body: body_, }); })();
I think this was biggest of changes. i was getting empty strings before. both exec methods. Changing file on endpoint gave the return
I think there is not necessary to import :lolsob:
Thank You, Very Much
used to be module.exports = async () =>{}
so yes
yes this would not work lol
it does with require 😉
lol
ow :lolsob:
I now have a method and pre-determined environment for user endpoint handlers.
+100