Skip to content

feat: move static pages by default #816

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 2 commits into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
33 changes: 20 additions & 13 deletions docs/large-functions.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
## Troubleshooting large functions

You may see an error about generated functions being too large. This is because when deploying your site it is packaged into a zipfile, which is limited by AWS to 50MB in size.
There are two possible causes for this, each with its own solution. The list of largest files shown in the build logs will help you see what the cause is.
You may see an error about generated functions being too large. This is because when deploying your site it is packaged
into a zipfile, which is limited by AWS to 50MB in size. There are two possible causes for this, each with its own
solution. The list of largest files shown in the build logs will help you see what the cause is.

- **Large dependencies**
This is the most common cause of the problem. Some node modules are very large, mostly those that include native modules. Examples include `electron` and `chromium`. The function bundler is usually able to find out which modules are actually used by your code, but sometimes it will incorrectly include unneeded modules. If this is the case, you can either remove the module from your dependencies if you installed it yourself, or exclude it manually by adding something like this to your `netlify.toml`, changing the value according to the problematic module. The `!` at the beginning of the module path indicates that it should be excluded:
- **Large dependencies** This is the most common cause of the problem. Some node modules are very large, mostly those
that include native modules. Examples include `electron` and `chromium`. The function bundler is usually able to find
out which modules are actually used by your code, but sometimes it will incorrectly include unneeded modules. If this
is the case, you can either remove the module from your dependencies if you installed it yourself, or exclude it
manually by adding something like this to your `netlify.toml`, changing the value according to the problematic module.
The `!` at the beginning of the module path indicates that it should be excluded:

```toml
[functions]
included_files = ["!node_modules/a-large-module/**/*"]
```
```toml
[functions]
included_files = ["!node_modules/a-large-module/**/*"]
```

If you do need large modules at runtime (e.g. if you are running Puppeteer in a Next API route), consider changing to a Netlify function which will have less overhead than the equivalent Next.js function.
If you do need large modules at runtime (e.g. if you are running Puppeteer in a Next API route), consider changing to
a Netlify function which will have less overhead than the equivalent Next.js function.

- **Large numbers of pre-rendered pages**
If you have a very large number of pre-rendered pages, these can take up a lot of space in the function. There are two approaches to fixing this. One is to consider deferring the building of the pages. If you return `fallback: "blocking"` from `getStaticPaths` the rendering will be deferred until the first user requests the page. This is a good choice for low-traffic pages. It reduces build and deploy time, and can make your bundle a lot smaller.

The other option is to enable an experimental feature that moves static files out of the function bundle. To do this, set the environment variable `EXPERIMENTAL_MOVE_STATIC_PAGES` to true.
- **Large numbers of pre-rendered pages** If you have a very large number of pre-rendered pages, these can take up a lot
of space in the function. There are two approaches to fixing this. One is to consider deferring the building of the
pages. If you return `fallback: "blocking"` from `getStaticPaths` the rendering will be deferred until the first user
requests the page. This is a good choice for low-traffic pages. It reduces build and deploy time, and can make your
bundle a lot smaller.
23 changes: 3 additions & 20 deletions docs/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,11 @@ export async function middleware(req: NextRequest) {

Because the middleware runs at the origin, it is run _after_ Netlify rewrites and redirects. If a static file is served
by the Netlify CDN then the middleware is never run, as middleware only runs when a page is served by Next.js. This
means that middleware should not be used with the `EXPERIMENTAL_MOVE_STATIC_FILES` option, as this causes
statically-generated pages to be served by the Netlify CDN before any middleware can be run.
means that any pages that match middleware routes are served from the origin rather than the CDN.

There is a bug in Next.js `<=12.0.3` that causes a proxy loop if you try to rewrite to a URL with a host other than
localhost. This bug is fixed in version `12.0.4`. If you are using Next.js `<=12.0.3`, you can work around the bug by
ensuring that when rewriting a request you either use a relative path, or manually set the host to `localhost` if
returning a URL object. For example:

```typescript
export function middleware(req: NextRequest) {
const rewrittenUrl = req.nextUrl

// Change the URL in some way
// ...

// Before returning the URL, set the host to localhost
rewrittenUrl.host = 'localhost'

// ...then rewrite to the changed URL
return NextResponse.rewrite(rewrittenUrl)
}
```
localhost. This bug is fixed in version `12.0.4`, so if you are using middleware you should upgrade to that version or
later.

If you have an issue with Next.js middleware on Netlify while it is beta, particularly if the issue cannot be reproduced
when running locally, then please add a comment to
Expand Down
6 changes: 6 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ module.exports = {
await movePublicFiles({ appDir, publish })

if (process.env.EXPERIMENTAL_MOVE_STATIC_PAGES) {
console.log(
"The flag 'EXPERIMENTAL_MOVE_STATIC_PAGES' is no longer required, as it is now the default. To disable this behavior, set the env var 'SERVE_STATIC_FILES_FROM_ORIGIN' to 'true'",
)
}

if (!process.env.SERVE_STATIC_FILES_FROM_ORIGIN) {
await moveStaticPages({ target, failBuild, netlifyConfig, i18n })
}

Expand Down
11 changes: 0 additions & 11 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,32 +207,26 @@ describe('onBuild()', () => {

test('generates static files manifest', async () => {
await moveNextDist()
process.env.EXPERIMENTAL_MOVE_STATIC_PAGES = 'true'
await plugin.onBuild(defaultArgs)
const manifestPath = path.resolve('.next/static-manifest.json')
expect(existsSync(manifestPath)).toBeTruthy()
const data = (await readJson(manifestPath)).sort()
expect(data).toMatchSnapshot()
delete process.env.EXPERIMENTAL_MOVE_STATIC_PAGES
})

test('moves static files to root', async () => {
await moveNextDist()
process.env.EXPERIMENTAL_MOVE_STATIC_PAGES = 'true'
await plugin.onBuild(defaultArgs)
const data = JSON.parse(readFileSync(path.resolve('.next/static-manifest.json'), 'utf8'))

data.forEach((file) => {
expect(existsSync(path.resolve(path.join('.next', file)))).toBeTruthy()
expect(existsSync(path.resolve(path.join('.next', 'server', 'pages', file)))).toBeFalsy()
})

delete process.env.EXPERIMENTAL_MOVE_STATIC_PAGES
})

test('copies default locale files to top level', async () => {
await moveNextDist()
process.env.EXPERIMENTAL_MOVE_STATIC_PAGES = 'true'
await plugin.onBuild(defaultArgs)
const data = JSON.parse(readFileSync(path.resolve('.next/static-manifest.json'), 'utf8'))

Expand All @@ -245,19 +239,14 @@ describe('onBuild()', () => {
const trimmed = file.substring(locale.length)
expect(existsSync(path.resolve(path.join('.next', trimmed)))).toBeTruthy()
})

delete process.env.EXPERIMENTAL_MOVE_STATIC_PAGES
})

test('skips static files that match middleware', async () => {
await moveNextDist()
process.env.EXPERIMENTAL_MOVE_STATIC_PAGES = 'true'
await plugin.onBuild(defaultArgs)

expect(existsSync(path.resolve(path.join('.next', 'en', 'middle.html')))).toBeFalsy()
expect(existsSync(path.resolve(path.join('.next', 'server', 'pages', 'en', 'middle.html')))).toBeTruthy()

delete process.env.EXPERIMENTAL_MOVE_STATIC_PAGES
})

test('sets correct config', async () => {
Expand Down