Skip to content
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

fix: resolve next-server from next app directory and not from plugin #2059

Merged

Conversation

pieh
Copy link
Contributor

@pieh pieh commented Apr 21, 2023

Summary

This PR adds functionality to resolve next/dist/server/next-server from the Next app directory (not to be confused with appDir inside pages), and not from the location of the next-runtime.

Test plan

A new integration test was added.

Relevant links (GitHub issues, Notion docs, etc.) or a picture of cute animal

Fixes #2024

Standard checks:

  • Check the Deploy Preview's Demo site for your PR's functionality
  • Add docs when necessary

🧪 Once merged, make sure to update the version if needed and that it was published correctly.

@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for netlify-plugin-nextjs-export-demo ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/netlify-plugin-nextjs-export-demo/deploys/6453591a93e37e00080052c2
😎 Deploy Preview https://deploy-preview-2059--netlify-plugin-nextjs-export-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for netlify-plugin-nextjs-nx-monorepo-demo ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/netlify-plugin-nextjs-nx-monorepo-demo/deploys/6453591a462884000873ffe4
😎 Deploy Preview https://deploy-preview-2059--netlify-plugin-nextjs-nx-monorepo-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for netlify-plugin-nextjs-static-root-demo ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/netlify-plugin-nextjs-static-root-demo/deploys/6453591ab23e810008f4a742
😎 Deploy Preview https://deploy-preview-2059--netlify-plugin-nextjs-static-root-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@github-actions github-actions bot added the type: bug code to address defects in shipped code label Apr 21, 2023
@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for next-i18next-demo ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/next-i18next-demo/deploys/6453591a08be3f0008db239d
😎 Deploy Preview https://deploy-preview-2059--next-i18next-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for next-plugin-edge-middleware ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/next-plugin-edge-middleware/deploys/6453591a340b9500071c5378
😎 Deploy Preview https://deploy-preview-2059--next-plugin-edge-middleware.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for nextjs-plugin-custom-routes-demo ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/nextjs-plugin-custom-routes-demo/deploys/6453591a93e37e00080052cd
😎 Deploy Preview https://deploy-preview-2059--nextjs-plugin-custom-routes-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for next-plugin-canary ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/next-plugin-canary/deploys/6453591a43993400085ab1f3
😎 Deploy Preview https://deploy-preview-2059--next-plugin-canary.netlify.app/
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for netlify-plugin-nextjs-next-auth-demo ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/netlify-plugin-nextjs-next-auth-demo/deploys/6453591adae6810008f86b05
😎 Deploy Preview https://deploy-preview-2059--netlify-plugin-nextjs-next-auth-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@netlify
Copy link

netlify bot commented Apr 21, 2023

Deploy Preview for netlify-plugin-nextjs-demo ready!

Name Link
🔨 Latest commit 40c6469
🔍 Latest deploy log https://app.netlify.com/sites/netlify-plugin-nextjs-demo/deploys/6453591ab51d9e0008d25652
😎 Deploy Preview https://deploy-preview-2059--netlify-plugin-nextjs-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

Comment on lines +45 to +48
const nextServerModuleRelativeLocation = nextServerModuleAbsoluteLocation
? relative(functionDir, nextServerModuleAbsoluteLocation)
: undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if nextServerModuleAbsoluteLocation is falsy we could warn here that non-SSG routes will fail (tho that might be noisy, so if we want warning like that maybe we add one once we know if we need lambdas for routes)

// This is a string, but if you have the right editor plugin it should format as js
javascript/* javascript */ `
if (!${JSON.stringify(nextServerModuleRelativeLocation)}) {
throw new Error('Could not find Next.js server')
Copy link
Contributor Author

@pieh pieh Apr 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In current state of this PR, this is thrown on lambda invocation and not during the build, so it restores the that behavior prior to #1950 on when the error is thrown - not the hill I will die on and I can move it back to build time.

The other changes in this PR make it more likely to find next-server than before, but I don't have 100% confidence in it, so that's why I move this error throwing back here as that was behavior for a long time.

Without that move, builds for fully static sites that hit current error are just blocked until we implement logic to determine wether we need page route handlers at all and skip it altogether if not needed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, i think that makes sense tbh. We only need the server at runtime so let's go with this.

@pieh
Copy link
Contributor Author

pieh commented Apr 24, 2023

There are few more code paths that would need adjustment -

/**
* Uses Next's swc static analysis to extract the config values from a file.
*/
export const extractConfigFromFile = async (apiFilePath: string): Promise<ApiConfig> => {
if (!apiFilePath || !existsSync(apiFilePath)) {
return {}
}
try {
if (!extractConstValue) {
extractConstValue = require('next/dist/build/analysis/extract-const-value')
}
if (!parseModule) {
// eslint-disable-next-line prefer-destructuring, @typescript-eslint/no-var-requires
parseModule = require('next/dist/build/analysis/parse-module').parseModule
}
} catch (error) {
is another one that tries to import next internals that might not be resolvable (will need to grep for other attempts like that to get full list)

EDIT: this is now done

Comment on lines 28 to 29
// eslint-disable-next-line max-params
const makeHandler = (conf: NextConfig, app, pageRoot, page, NextServer: NextServerType) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since another parameter is being added, consider converting the parameters to one parameter object.

Suggested change
// eslint-disable-next-line max-params
const makeHandler = (conf: NextConfig, app, pageRoot, page, NextServer: NextServerType) => {
const makeHandler = ({ conf, app, pageRoot, page, NextServer }: MakeHandlerParams) => {

and update at the call sites.

Copy link
Contributor Author

@pieh pieh Apr 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in 610d73f

packages/runtime/src/helpers/files.ts Outdated Show resolved Hide resolved
@pieh pieh marked this pull request as ready for review April 27, 2023 07:19
@pieh pieh requested a review from a team April 27, 2023 07:19
Comment on lines 93 to 97
// eslint-disable-next-line import/no-dynamic-require
extractConstValue = require(findModuleFromBase({
paths: [appDir],
candidates: ['next/dist/build/analysis/extract-const-value'],
}) ?? 'next/dist/build/analysis/extract-const-value')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit messy - especially part after ?? - it's done because findModuleFromBase can return null, and calling require(null) won't result in MODULE_NOT_FOUND error that we handle below - so the reason for ?? part is to actually produce above error in case module can't be resolved

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sand] you could potentially move the try/catch from findModuleFromBase to getServerFile (which is the only place that findModuleFromBase is used) and then you don't have to the juggling with ??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to do 04f3164 instead - I looked at how throwing would look like in findModuleFromBase and realized that removing try/catch there would result in throwing in first missed candidate, and to make it somewhat work it would be quite messy with storing exceptions and rethrowing them when we completed iterating over candidates and still didn't find one.

Also thinking more - this would change semantics of findModuleFromBase which is not end of the world, but I prefer to avoid changing shared utils behavior if it can be addressed in single place that would benefit from it (and possibly cause problems in all the other places)

Comment on lines 55 to 60
// I have no idea what eslint is up to here but it gives an error
// eslint-disable-next-line no-shadow
export const enum ApiRouteType {
SCHEDULED = 'experimental-scheduled',
BACKGROUND = 'experimental-background',
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (including comment) was moved from another module due to cyclic imports problem

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled this locally and same thing. The error is weird because it says it's shadowing itself. 🙃

CleanShot 2023-04-28 at 15 53 08

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// I have no idea what eslint is up to here but it gives an error
// eslint-disable-next-line no-shadow

This is needed to be moved as well and not just type - same error happen in current location when you remove comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typescript-eslint/typescript-eslint#2483 is the reason - enum stuff is problematic with non typescript no-shadow rule - I can fix this either here or separately

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 66a9670

@@ -0,0 +1,29 @@
name: Next Runtime Integration Tests
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not really integration test - it's really more e2e, but "e2e" is somewhat taken by tests we have copied from Next - so I went with integration for now, but maybe we just need to agree on how to refer to e2e tests that are not copied from next and I would love to adjust naming in this PR to reflect that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to rename the Next tests 'upstream' tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it make sense, but at this point I'd rather ship those as-is and do renaming in separate standalone PR to not keep the fix hostage

orinokai
orinokai previously approved these changes Apr 28, 2023
Copy link
Contributor

@orinokai orinokai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor suggestions, but take them or leave them. Otherwise looks great, thanks @pieh !

@@ -0,0 +1,29 @@
name: Next Runtime Integration Tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to rename the Next tests 'upstream' tests?

} catch {
// Ignore the error
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sand] instead of an additional for loop, consider pushing the current directory '.' to the paths array

Copy link
Contributor Author

@pieh pieh Apr 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried, and it's actually not that simple. Using just ., __dirname etc in paths alone doesn't replicate behavior of require.resolve (when using default paths). We can get what default would use by calling require.resolve.paths(candidate) and we can push all that to our custom paths sure, but I never actually used that function before in production and I'm not sure of its sharp edges so would prefer to avoid going that route.

Just for some more details on that - here's example what default paths look like generally - it first starts with directory of current module + /node_modules and then go up directory tree, later seems to be checking some globals (??) etc:

> pwd
/Users/misiek/dev/next-runtime
> node -e "console.log(require.resolve.paths('next'))"
[
  '/Users/misiek/dev/next-runtime/node_modules',
  '/Users/misiek/dev/node_modules',
  '/Users/misiek/node_modules',
  '/Users/node_modules',
  '/node_modules',
  '/Users/misiek/.node_modules',
  '/Users/misiek/.node_libraries',
  '/Users/misiek/.nvm/versions/node/v18.14.0/lib/node'
]

So TL/DR it's not as simple as . and it might potentially introduce unknown problems that I would prefer to avoid

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation @pieh, got it 👍🏼

Comment on lines 93 to 97
// eslint-disable-next-line import/no-dynamic-require
extractConstValue = require(findModuleFromBase({
paths: [appDir],
candidates: ['next/dist/build/analysis/extract-const-value'],
}) ?? 'next/dist/build/analysis/extract-const-value')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sand] you could potentially move the try/catch from findModuleFromBase to getServerFile (which is the only place that findModuleFromBase is used) and then you don't have to the juggling with ??

packages/runtime/src/templates/server.ts Show resolved Hide resolved
// This is a string, but if you have the right editor plugin it should format as js
javascript/* javascript */ `
if (!${JSON.stringify(nextServerModuleRelativeLocation)}) {
throw new Error('Could not find Next.js server')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, i think that makes sense tbh. We only need the server at runtime so let's go with this.

Copy link
Contributor

@nickytonline nickytonline left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not too much to say. This is looking good.

Comment on lines 55 to 60
// I have no idea what eslint is up to here but it gives an error
// eslint-disable-next-line no-shadow
export const enum ApiRouteType {
SCHEDULED = 'experimental-scheduled',
BACKGROUND = 'experimental-background',
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled this locally and same thing. The error is weird because it says it's shadowing itself. 🙃

CleanShot 2023-04-28 at 15 53 08

packages/runtime/src/templates/server.ts Outdated Show resolved Hide resolved

module.exports = {
onPostBuild: async () => {
const movedDir = path.join(process.cwd(), `next-app`, `node_modules2`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this is coming from the tests pulled in, but where does node_modules2 get set? This appears to be the only place it's mentioned. 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry - this is not pulled in test - it's just new one that uses netlify serve and this "build plugin" is meant to get rid of node_modules after functions were bundled but before they started to be served. However when I just deleted node_modules it was problematic, sometimes getting ENOENT errors so instead of deleting it I just move node_modules to node_modules2 so it no longer is used. The reason to check if node_modules2 exist and deleting it if it does is because running the same test twice in a row would crash otherwise.

And the reason to remove node_modules are just functions bundling quirks I encountered:

I did use absolute path to next-server (so something like /Users/misiek/dev/repo/next-app/node_modules/next/<path-to-next-server>) when generating lambda handler at some point and it worked locally, but not when deployed, because functions bundler doesn't seem to handle absolute paths correctly and left those imports as they are instead of bundling it. Things appeared to be working locally because those node_modules still existed.

@LekoArts LekoArts merged commit 38f34c7 into main May 4, 2023
100 checks passed
@LekoArts LekoArts deleted the fix/use-next-directory-when-resolving-server-for-handler branch May 4, 2023 07:42
@pieh pieh added the automerge label May 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge type: bug code to address defects in shipped code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Build Error - Plugin "@netlify/plugin-nextjs" internal error
4 participants