Skip to content

Commit c0cc952

Browse files
committed
feat: revert to nft due to output issues with bundling
1 parent ab2ec38 commit c0cc952

File tree

10 files changed

+8307
-7496
lines changed

10 files changed

+8307
-7496
lines changed

package-lock.json

Lines changed: 8219 additions & 7347 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
},
3535
"homepage": "https://github.com/netlify/next-runtime-minimal#readme",
3636
"dependencies": {
37-
"@fastly/http-compute-js": "^1.1.0",
37+
"@fastly/http-compute-js": "github:orinokai/http-compute-js",
3838
"@netlify/blobs": "^1.6.0",
3939
"@netlify/build": "^29.20.6",
4040
"@netlify/functions": "^2.0.1",
41-
"esbuild": "^0.19.4",
41+
"@vercel/nft": "^0.24.3",
4242
"fs-extra": "^11.1.1"
4343
},
4444
"devDependencies": {

src/helpers/config.ts

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,23 @@
1-
import { readFileSync } from 'fs'
1+
import { readFile } from 'node:fs/promises'
22

3-
import type { NetlifyConfig } from '@netlify/build'
4-
import { copySync, moveSync } from 'fs-extra/esm'
5-
6-
import { MODULE_DIR, NETLIFY_PUBLISH_DIR, NETLIFY_TEMP_DIR } from './constants.js'
3+
import { RUNTIME_DIR } from './constants.js'
74

85
/**
9-
* Modify the user's next.config.js to use standalone mode and cache handler
6+
* Enable Next.js standalone mode
107
*/
11-
export const modifyNextConfig = () => {
12-
// revert any previous changes
13-
revertNextConfig()
14-
15-
// TODO: find a better way to do this because there's a ton of different ways
16-
// to configure next.config.js and the user could be using any of them
17-
// https://github.com/netlify/next-runtime-minimal/issues/12
18-
moveSync('next.config.js', `${NETLIFY_TEMP_DIR}/next.config.js`)
19-
copySync(`${MODULE_DIR}/../templates/next-config.cjs`, 'next.config.js')
8+
export const setBuildtimeConfig = () => {
9+
process.env.NEXT_PRIVATE_STANDALONE = 'true'
2010
}
2111

22-
export const revertNextConfig = () => {
23-
// check if modified, then revert
24-
if (readFileSync('next.config.js').includes('Netlify generated code')) {
25-
moveSync(`${NETLIFY_TEMP_DIR}/next.config.js`, 'next.config.js', { overwrite: true })
12+
export const setRuntimeConfig = async () => {
13+
const runtimeConfig = JSON.parse(await readFile('./.next/required-server-files.json', 'utf-8'))
14+
15+
// set the path to the cache handler
16+
runtimeConfig.config.experimental = {
17+
...runtimeConfig.config.experimental,
18+
incrementalCacheHandlerPath: `${RUNTIME_DIR}/dist/templates/cache-handler.cjs`,
2619
}
27-
}
2820

29-
/**
30-
* Modify the user's netlify.toml to use our new publish directory
31-
* @param config Netlify config
32-
*/
33-
export const modifyNetlifyConfig = (config: NetlifyConfig) => {
34-
// TODO: once onEnd is fixed, we can remove this
35-
// https://github.com/netlify/cli/issues/6050
36-
config.build.publish = NETLIFY_PUBLISH_DIR
21+
// set config from the build output
22+
process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(runtimeConfig.config)
3723
}

src/helpers/constants.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { resolve } from 'node:path'
22
import { fileURLToPath } from 'node:url'
33

4-
export const MODULE_DIR = fileURLToPath(new URL('.', import.meta.url))
4+
let dir
5+
try {
6+
dir = __dirname
7+
} catch {
8+
dir = fileURLToPath(new URL('.', import.meta.url))
9+
}
10+
11+
export const MODULE_DIR = dir
512
export const PLUGIN_DIR = resolve(`${MODULE_DIR}../..`)
13+
export const RUNTIME_DIR = resolve(`${MODULE_DIR}../..`)
614

7-
export const NETLIFY_PUBLISH_DIR = '.netlify/publish'
8-
export const NETLIFY_TEMP_DIR = '.netlify/temp'
15+
export const NEXT_BUILD_DIR = '.netlify/.next'
916

1017
export const FUNCTIONS_INTERNAL_DIR = '.netlify/functions-internal'
1118
export const FUNCTIONS_URL = '/.netlify/functions'

src/helpers/files.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
import { existsSync } from 'node:fs'
22

3-
import { copySync } from 'fs-extra/esm'
3+
import { NetlifyPluginConstants } from '@netlify/build'
4+
import { copySync, moveSync } from 'fs-extra/esm'
45

5-
import { NETLIFY_PUBLISH_DIR } from './constants.js'
6+
import { NEXT_BUILD_DIR } from './constants.js'
67

78
/**
8-
* Ensure static assets get uploaded to the Netlify CDN
9-
* @param publishDir The publish directory
9+
* Stash the Next.js build output in a temporary directory
1010
*/
11-
export const publishStaticAssets = (publishDir: string) => {
11+
export const stashBuildOutput = ({ PUBLISH_DIR }: NetlifyPluginConstants) => {
12+
moveSync(PUBLISH_DIR, NEXT_BUILD_DIR, { overwrite: true })
13+
}
14+
15+
/**
16+
* Move static assets so they are uploaded to the Netlify CDN
17+
*/
18+
export const moveStaticAssets = ({ PUBLISH_DIR }: NetlifyPluginConstants) => {
1219
if (existsSync('public')) {
13-
copySync('public', NETLIFY_PUBLISH_DIR, { overwrite: true })
20+
copySync('public', PUBLISH_DIR)
1421
}
15-
copySync(`${publishDir}/static/`, `${NETLIFY_PUBLISH_DIR}/_next/static`, { overwrite: true })
22+
copySync(`${NEXT_BUILD_DIR}/static/`, `${PUBLISH_DIR}/_next/static`)
1623
}

src/helpers/functions.ts

Lines changed: 23 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,53 @@
11
import { writeFileSync } from 'fs'
22

3-
import { NetlifyConfig } from '@netlify/build'
4-
import { build } from 'esbuild'
3+
import { nodeFileTrace } from '@vercel/nft'
54
import { copySync, emptyDirSync, readJsonSync, writeJSONSync } from 'fs-extra/esm'
65

7-
import {
8-
NETLIFY_TEMP_DIR,
9-
SERVER_HANDLER_DIR,
10-
SERVER_HANDLER_NAME,
11-
SERVER_HANDLER_URL,
12-
PLUGIN_DIR,
13-
} from './constants.js'
6+
import { NEXT_BUILD_DIR, SERVER_HANDLER_DIR, SERVER_HANDLER_NAME, PLUGIN_DIR } from './constants.js'
7+
import { write } from 'fs-extra'
8+
9+
const pkg = readJsonSync(`${PLUGIN_DIR}/package.json`)
1410

1511
/**
1612
* Create a Netlify function to run the Next.js server
1713
* @param publishDir The publish directory
1814
* @param config Netlify config
1915
*/
20-
export const createServerHandler = async (publishDir: string, config: NetlifyConfig) => {
16+
export const createServerHandler = async () => {
2117
// clear the server handler directory
2218
emptyDirSync(SERVER_HANDLER_DIR)
2319

24-
// copy the next.js standalone build output to the server handler directory
25-
copySync(`${publishDir}/standalone/.next`, `${SERVER_HANDLER_DIR}/.next`)
26-
copySync(`${publishDir}/standalone/node_modules`, `${SERVER_HANDLER_DIR}/node_modules`)
20+
// trace the server handler dependencies
21+
const { fileList } = await nodeFileTrace(
22+
[`${PLUGIN_DIR}/dist/templates/server-handler.js`, `${PLUGIN_DIR}/dist/templates/cache-handler.cjs`],
23+
{ base: PLUGIN_DIR, ignore: ['package.json', 'node_modules/next/**'] },
24+
)
2725

28-
const pkg = readJsonSync(`${PLUGIN_DIR}/package.json`)
26+
// copy the handler dependencies
27+
fileList.forEach((path) => {
28+
copySync(`${PLUGIN_DIR}/${path}`, `${SERVER_HANDLER_DIR}/${path}`)
29+
})
30+
31+
// copy the next.js standalone build output to the server handler directory
32+
copySync(`${NEXT_BUILD_DIR}/standalone/.next`, `${SERVER_HANDLER_DIR}/.next`)
33+
copySync(`${NEXT_BUILD_DIR}/standalone/node_modules`, `${SERVER_HANDLER_DIR}/node_modules`)
2934

3035
// create the server handler metadata file
3136
writeJSONSync(`${SERVER_HANDLER_DIR}/${SERVER_HANDLER_NAME}.json`, {
3237
config: {
3338
name: 'Next.js Server Handler',
3439
generator: `${pkg.name}@${pkg.version}`,
3540
nodeBundler: 'none',
36-
// TODO: remove the final include when Netlify Functions v2 fixes the default exports bug
37-
includedFiles: ['.next/**', 'node_modules/**', `${SERVER_HANDLER_NAME}*`],
41+
includedFiles: [`${SERVER_HANDLER_NAME}*`, 'package.json', 'dist/**', '.next/**', 'node_modules/**'],
3842
includedFilesBasePath: SERVER_HANDLER_DIR,
3943
},
4044
version: 1,
4145
})
4246

43-
// bundle the cache handler
44-
await build({
45-
entryPoints: [`${PLUGIN_DIR}/dist/templates/cache-handler.js`],
46-
bundle: true,
47-
platform: 'node',
48-
target: ['node18'],
49-
format: 'cjs',
50-
outfile: `${SERVER_HANDLER_DIR}/${SERVER_HANDLER_NAME}-cache.js`,
51-
})
47+
writeFileSync(`${SERVER_HANDLER_DIR}/package.json`, JSON.stringify({ type: 'module' }))
5248

53-
// bundle the server handler
54-
await build({
55-
entryPoints: [`${PLUGIN_DIR}/dist/templates/server-handler.js`],
56-
bundle: true,
57-
platform: 'node',
58-
target: ['node18'],
59-
format: 'esm',
60-
external: ['next'],
61-
outfile: `${SERVER_HANDLER_DIR}/${SERVER_HANDLER_NAME}-actual.mjs`,
62-
})
63-
64-
// TODO: remove when Netlify Functions v2 fixes the default exports bug
65-
// https://github.com/netlify/pod-dev-foundations/issues/599
6649
writeFileSync(
67-
`${SERVER_HANDLER_DIR}/${SERVER_HANDLER_NAME}.mjs`,
68-
`import handler from './${SERVER_HANDLER_NAME}-actual.mjs';export default (request) => handler(request);`,
50+
`${SERVER_HANDLER_DIR}/${SERVER_HANDLER_NAME}.js`,
51+
`import handler from './dist/templates/server-handler.js';export default (request) => handler;export const config = { path: '/*' }`,
6952
)
70-
71-
// TODO: remove this when we can use inline config
72-
// https://github.com/netlify/next-runtime-minimal/issues/13
73-
config.redirects ||= []
74-
config.redirects.push({ from: `/*`, to: SERVER_HANDLER_URL, status: 200 })
75-
}
76-
77-
export const createCacheHandler = async () => {
78-
await build({
79-
entryPoints: [`${PLUGIN_DIR}/dist/templates/cache-handler.js`],
80-
bundle: true,
81-
platform: 'node',
82-
target: ['node18'],
83-
format: 'cjs',
84-
outfile: `${NETLIFY_TEMP_DIR}/cache-handler.js`,
85-
})
8653
}

src/index.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
import type { NetlifyPluginOptions } from '@netlify/build'
22

3-
import { modifyNetlifyConfig, modifyNextConfig, revertNextConfig } from './helpers/config.js'
4-
import { publishStaticAssets } from './helpers/files.js'
5-
import { createServerHandler, createCacheHandler } from './helpers/functions.js'
3+
import { setBuildtimeConfig } from './helpers/config.js'
4+
import { moveStaticAssets, stashBuildOutput } from './helpers/files.js'
5+
import { createServerHandler } from './helpers/functions.js'
66

77
type NetlifyPluginOptionsWithFlags = NetlifyPluginOptions & { featureFlags?: Record<string, unknown> }
88

9-
export const onPreBuild = async () => {
10-
await createCacheHandler()
11-
modifyNextConfig()
9+
export const onPreBuild = () => {
10+
setBuildtimeConfig()
1211
}
1312

14-
export const onBuild = async ({ constants, netlifyConfig }: NetlifyPluginOptionsWithFlags) => {
15-
publishStaticAssets(constants.PUBLISH_DIR)
16-
await createServerHandler(constants.PUBLISH_DIR, netlifyConfig)
17-
modifyNetlifyConfig(netlifyConfig)
18-
}
19-
20-
export const onEnd = () => {
21-
revertNextConfig()
13+
export const onBuild = async ({ constants }: NetlifyPluginOptionsWithFlags) => {
14+
stashBuildOutput(constants)
15+
moveStaticAssets(constants)
16+
await createServerHandler()
2217
}
File renamed without changes.

src/templates/next-config.cts

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/templates/server-handler.ts

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,24 @@
1-
import { readFile } from 'node:fs/promises'
2-
import { fileURLToPath } from 'node:url'
3-
41
import { toComputeResponse, toReqRes } from '@fastly/http-compute-js'
52

6-
import { SERVER_HANDLER_NAME } from '../helpers/constants.js'
7-
8-
const dir = fileURLToPath(new URL('.', import.meta.url))
9-
10-
// json config generated at build time
11-
const requiredServerFiles = JSON.parse(await readFile('./.next/required-server-files.json', 'utf-8'))
3+
import { RUNTIME_DIR } from '../helpers/constants.js'
124

13-
// set the path to the cache handler
14-
requiredServerFiles.config.experimental = {
15-
...requiredServerFiles.config.experimental,
16-
incrementalCacheHandlerPath: `${dir}/${SERVER_HANDLER_NAME}-cache.js`,
17-
}
18-
19-
// set config from the build output
20-
process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(requiredServerFiles.config)
5+
let nextHandler: any
216

227
export default async (request: Request) => {
23-
const { getRequestHandlers } = await import('next/dist/server/lib/start-server.js')
24-
25-
// let Next.js initialize and create the request handler
26-
const [nextHandler] = await getRequestHandlers({
27-
port: 3000,
28-
hostname: 'localhost',
29-
dir,
30-
isDev: false,
31-
})
8+
if (!nextHandler) {
9+
// set the server config
10+
const { setRuntimeConfig } = await import('../helpers/config.js')
11+
await setRuntimeConfig()
12+
13+
// let Next.js initialize and create the request handler
14+
const { getRequestHandlers } = await import('next/dist/server/lib/start-server.js')
15+
;[nextHandler] = await getRequestHandlers({
16+
port: 3000,
17+
hostname: 'localhost',
18+
dir: RUNTIME_DIR,
19+
isDev: false,
20+
})
21+
}
3222

3323
const { req, res } = toReqRes(request)
3424

0 commit comments

Comments
 (0)