Skip to content

Commit 99ca449

Browse files
committed
feat: first draft for no bundler
1 parent bb4d4cf commit 99ca449

File tree

8 files changed

+136
-26
lines changed

8 files changed

+136
-26
lines changed

packages/runtime/src/helpers/config.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import slash from 'slash'
99
import { HANDLER_FUNCTION_NAME, IMAGE_FUNCTION_NAME, ODB_FUNCTION_NAME } from '../constants'
1010

1111
import { splitApiRoutes } from './flags'
12-
import type { APILambda } from './functions'
12+
import type { APILambda, SSRLambda } from './functions'
1313
import type { RoutesManifest } from './types'
1414
import { escapeStringRegexp } from './utils'
1515

@@ -101,12 +101,14 @@ export const configureHandlerFunctions = async ({
101101
publish,
102102
ignore = [],
103103
apiLambdas,
104+
ssrLambdas,
104105
featureFlags,
105106
}: {
106107
netlifyConfig: NetlifyConfig
107108
publish: string
108109
ignore: Array<string>
109110
apiLambdas: APILambda[]
111+
ssrLambdas: SSRLambda[]
110112
featureFlags: Record<string, unknown>
111113
}) => {
112114
const config = await getRequiredServerFiles(publish)
@@ -164,8 +166,18 @@ export const configureHandlerFunctions = async ({
164166
})
165167
}
166168

167-
configureFunction(HANDLER_FUNCTION_NAME)
168-
configureFunction(ODB_FUNCTION_NAME)
169+
if (ssrLambdas.length === 0) {
170+
configureFunction(HANDLER_FUNCTION_NAME)
171+
configureFunction(ODB_FUNCTION_NAME)
172+
} else {
173+
for (const ssrLambda of ssrLambdas) {
174+
const { functionName, includedFiles } = ssrLambda
175+
// TODO: add all the nextRoot/wasm/excludedModules stuff from above
176+
netlifyConfig.functions[functionName] ||= { included_files: [] }
177+
netlifyConfig.functions[functionName].node_bundler = 'none'
178+
netlifyConfig.functions[functionName].included_files = includedFiles
179+
}
180+
}
169181

170182
if (splitApiRoutes(featureFlags)) {
171183
for (const apiLambda of apiLambdas) {

packages/runtime/src/helpers/flags.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,16 @@ export const splitApiRoutes = (featureFlags: Record<string, unknown>): boolean =
1616
// default to true during testing, swap to false before merging
1717
return typeof featureFlags.next_split_api_routes === 'boolean' ? featureFlags.next_split_api_routes : true
1818
}
19+
20+
/**
21+
* TODO: write comment
22+
*/
23+
export const useNFTFilesForBundling = (featureFlags: Record<string, unknown>): boolean => {
24+
if (process.env.NEXT_USE_NFT_FILES_FOR_BUNDLING) {
25+
return process.env.NEXT_USE_NFT_FILES_FOR_BUNDLING === 'true'
26+
}
27+
// default to true during testing, swap to false before merging
28+
return typeof featureFlags.next_use_nft_files_for_bundling === 'boolean'
29+
? featureFlags.next_use_nft_files_for_bundling
30+
: true
31+
}

packages/runtime/src/helpers/functions.ts

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,36 @@ import { getFunctionNameForPage } from './utils'
2929

3030
// TODO, for reviewer: I added my properties here because that was the easiest way,
3131
// but is it the right spot for it?
32-
export interface ApiRouteConfig {
32+
export interface RouteConfig {
3333
functionName: string
3434
route: string
35-
config: ApiConfig
3635
compiled: string
3736
includedFiles: string[]
3837
}
3938

39+
export interface ApiRouteConfig extends RouteConfig {
40+
config: ApiConfig
41+
}
42+
4043
export interface APILambda {
4144
functionName: string
4245
routes: ApiRouteConfig[]
4346
includedFiles: string[]
4447
type?: ApiRouteType
4548
}
4649

50+
export interface SSRLambda {
51+
functionName: string
52+
routes: RouteConfig[]
53+
includedFiles: string[]
54+
type?: ApiRouteType
55+
}
56+
4757
export const generateFunctions = async (
4858
{ FUNCTIONS_SRC = DEFAULT_FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC, PUBLISH_DIR }: NetlifyPluginConstants,
4959
appDir: string,
5060
apiLambdas: APILambda[],
61+
ssrLambdas: SSRLambda[],
5162
): Promise<void> => {
5263
const publish = resolve(PUBLISH_DIR)
5364
const functionsDir = resolve(INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC)
@@ -116,6 +127,12 @@ export const generateFunctions = async (
116127
join(functionsDir, functionName, 'handlerUtils.js'),
117128
)
118129
writeFunctionConfiguration({ functionName, functionTitle, functionsDir })
130+
131+
const nfInternalFiles = await glob(join(functionsDir, functionName, '**'))
132+
const lambda = ssrLambdas.find((l) => l.functionName === functionName)
133+
if (lambda) {
134+
lambda.includedFiles.push(...nfInternalFiles)
135+
}
119136
}
120137

121138
await writeHandler(HANDLER_FUNCTION_NAME, HANDLER_FUNCTION_TITLE, false)
@@ -245,16 +262,20 @@ const traceNextServer = async (publish: string, baseDir: string): Promise<string
245262
return filtered.map((file) => relative(baseDir, file))
246263
}
247264

248-
export const getAPIPRouteCommonDependencies = async (publish: string, baseDir: string) => {
249-
const [requiredServerFiles, nextServerFiles, followRedirectsFiles] = await Promise.all([
265+
export const getCommonDependencies = async (publish: string, baseDir: string) => {
266+
const deps = await Promise.all([
250267
traceRequiredServerFiles(publish),
251268
traceNextServer(publish, baseDir),
252269

253-
// used by our own bridge.js
254-
glob(join(dirname(require.resolve('follow-redirects', { paths: [publish] })), '**', '*')),
270+
...[
271+
'follow-redirects',
272+
// using package.json because otherwise, we'd find some /dist/... path
273+
'@netlify/functions/package.json',
274+
'is-promise',
275+
].map((dep) => glob(join(dirname(require.resolve(dep, { paths: [publish] })), '**', '*'))),
255276
])
256277

257-
return [...requiredServerFiles, ...nextServerFiles, ...followRedirectsFiles]
278+
return deps.flat(1)
258279
}
259280

260281
const sum = (arr: number[]) => arr.reduce((v, current) => v + current, 0)
@@ -279,6 +300,53 @@ const getBundleWeight = async (patterns: string[]) => {
279300
return sum(sizes.flat(1))
280301
}
281302

303+
export const getSSRLambdas = async (publish: string, baseDir: string): Promise<SSRLambda[]> => {
304+
const commonDependencies = await getCommonDependencies(publish, baseDir)
305+
const ssrRoutes = await getSSRRoutes(publish)
306+
307+
// TODO: for now, they're the same - but we should separate them
308+
const nonOdbRoutes = ssrRoutes
309+
const odbRoutes = ssrRoutes
310+
311+
return [
312+
{
313+
functionName: HANDLER_FUNCTION_NAME,
314+
includedFiles: [...commonDependencies, ...nonOdbRoutes.flatMap((route) => route.includedFiles)],
315+
routes: nonOdbRoutes,
316+
},
317+
{
318+
functionName: ODB_FUNCTION_NAME,
319+
includedFiles: [...commonDependencies, ...odbRoutes.flatMap((route) => route.includedFiles)],
320+
routes: odbRoutes,
321+
},
322+
]
323+
}
324+
325+
const getSSRRoutes = async (publish: string): Promise<RouteConfig[]> => {
326+
const pages = (await readJSON(join(publish, 'server', 'pages-manifest.json'))) as Record<string, string>
327+
const routes = Object.entries(pages).filter(
328+
([page, compiled]) => !page.startsWith('/api/') && !compiled.endsWith('.html'),
329+
)
330+
331+
return await Promise.all(
332+
routes.map(async ([route, compiled]) => {
333+
const functionName = getFunctionNameForPage(route)
334+
335+
const compiledPath = join(publish, 'server', compiled)
336+
337+
const routeDependencies = await getDependenciesOfFile(compiledPath)
338+
const includedFiles = [compiledPath, ...routeDependencies]
339+
340+
return {
341+
functionName,
342+
route,
343+
compiled,
344+
includedFiles,
345+
}
346+
}),
347+
)
348+
}
349+
282350
const MB = 1024 * 1024
283351

284352
export const getAPILambdas = async (
@@ -287,7 +355,7 @@ export const getAPILambdas = async (
287355
pageExtensions: string[],
288356
): Promise<APILambda[]> => {
289357
console.time('traceCommonDependencies')
290-
const commonDependencies = await getAPIPRouteCommonDependencies(publish, baseDir)
358+
const commonDependencies = await getCommonDependencies(publish, baseDir)
291359
console.timeEnd('traceCommonDependencies')
292360

293361
console.time('weighCommonDependencies')

packages/runtime/src/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { onPreDev } from './helpers/dev'
1919
import { writeEdgeFunctions, loadMiddlewareManifest, cleanupEdgeFunctions } from './helpers/edge'
2020
import { moveStaticPages, movePublicFiles, patchNextFiles } from './helpers/files'
21-
import { splitApiRoutes } from './helpers/flags'
21+
import { splitApiRoutes, useNFTFilesForBundling } from './helpers/flags'
2222
import {
2323
generateFunctions,
2424
setupImageFunction,
@@ -28,6 +28,7 @@ import {
2828
packSingleFunction,
2929
getExtendedApiRouteConfigs,
3030
APILambda,
31+
getSSRLambdas,
3132
} from './helpers/functions'
3233
import { generateRedirects, generateStaticRedirects } from './helpers/redirects'
3334
import { shouldSkip, isNextAuthInstalled, getCustomImageResponseHeaders, getRemotePatterns } from './helpers/utils'
@@ -172,15 +173,19 @@ const plugin: NetlifyPlugin = {
172173
extendedRoutes.map(packSingleFunction),
173174
)
174175

176+
const ssrLambdas = useNFTFilesForBundling(featureFlags) ? await getSSRLambdas(publish, appDir) : []
177+
178+
await generateFunctions(constants, appDir, apiLambdas, ssrLambdas)
179+
175180
await configureHandlerFunctions({
176181
netlifyConfig,
177182
ignore,
178183
publish: relative(process.cwd(), publish),
179184
apiLambdas,
185+
ssrLambdas,
180186
featureFlags,
181187
})
182188

183-
await generateFunctions(constants, appDir, apiLambdas)
184189
await generatePagesResolver(constants)
185190

186191
await movePublicFiles({ appDir, outdir, publish, basePath })

packages/runtime/src/templates/getHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ const makeHandler = (conf: NextConfig, app, pageRoot, staticManifest: Array<[str
172172
export const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '../../..' }): string =>
173173
// This is a string, but if you have the right editor plugin it should format as js (e.g. bierner.comment-tagged-templates in VS Code)
174174
javascript/* javascript */ `
175+
process.env.NODE_ENV = 'production';
175176
const { Server } = require("http");
176177
const { promises } = require("fs");
177178
// We copy the file here rather than requiring from the node module

test/helpers/functions.spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,57 +9,57 @@ describeCwdTmpDir('api route file analysis', () => {
99
expect(new Set(configs.map(({ includedFiles, ...rest }) => rest))).toEqual(
1010
new Set([
1111
{
12-
functionName: "_api_og-handler",
12+
functionName: '_api_og-handler',
1313
compiled: 'pages/api/og.js',
1414
config: {
1515
runtime: 'edge',
1616
},
1717
route: '/api/og',
1818
},
1919
{
20-
functionName: "_api_enterPreview-handler",
20+
functionName: '_api_enterPreview-handler',
2121
compiled: 'pages/api/enterPreview.js',
2222
config: {},
2323
route: '/api/enterPreview',
2424
},
2525
{
26-
functionName: "_api_exitPreview-handler",
26+
functionName: '_api_exitPreview-handler',
2727
compiled: 'pages/api/exitPreview.js',
2828
config: {},
2929
route: '/api/exitPreview',
3030
},
3131
{
32-
functionName: "_api_hello-handler",
32+
functionName: '_api_hello-handler',
3333
compiled: 'pages/api/hello.js',
3434
config: {},
3535
route: '/api/hello',
3636
},
3737
{
38-
functionName: "_api_shows_params-SPLAT-handler",
38+
functionName: '_api_shows_params-SPLAT-handler',
3939
compiled: 'pages/api/shows/[...params].js',
4040
config: {},
4141
route: '/api/shows/[...params]',
4242
},
4343
{
44-
functionName: "_api_shows_id-PARAM-handler",
44+
functionName: '_api_shows_id-PARAM-handler',
4545
compiled: 'pages/api/shows/[id].js',
4646
config: {},
4747
route: '/api/shows/[id]',
4848
},
4949
{
50-
functionName: "_api_hello-background-background",
50+
functionName: '_api_hello-background-background',
5151
compiled: 'pages/api/hello-background.js',
5252
config: { type: 'experimental-background' },
5353
route: '/api/hello-background',
5454
},
5555
{
56-
functionName: "_api_hello-scheduled-handler",
56+
functionName: '_api_hello-scheduled-handler',
5757
compiled: 'pages/api/hello-scheduled.js',
5858
config: { schedule: '@hourly', type: 'experimental-scheduled' },
5959
route: '/api/hello-scheduled',
6060
},
6161
{
62-
functionName: "_api_revalidate-handler",
62+
functionName: '_api_revalidate-handler',
6363
compiled: 'pages/api/revalidate.js',
6464
config: {},
6565
route: '/api/revalidate',
@@ -75,13 +75,13 @@ describeCwdTmpDir('api route file analysis', () => {
7575
expect(new Set(configs.map(({ includedFiles, ...rest }) => rest))).toEqual(
7676
new Set([
7777
{
78-
functionName: "_api_hello-background-background",
78+
functionName: '_api_hello-background-background',
7979
compiled: 'pages/api/hello-background.js',
8080
config: { type: 'experimental-background' },
8181
route: '/api/hello-background',
8282
},
8383
{
84-
functionName: "_api_hello-scheduled-handler",
84+
functionName: '_api_hello-scheduled-handler',
8585
compiled: 'pages/api/hello-scheduled.js',
8686
config: { schedule: '@hourly', type: 'experimental-scheduled' },
8787
route: '/api/hello-scheduled',

test/helpers/pack.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { pack } from '../../packages/runtime/src/helpers/pack'
22

3-
test('pack', () => {
3+
it('pack', () => {
44
expect(pack([], 0)).toEqual([])
55
expect(pack([{ value: '10', weight: 10 }], 100)).toEqual([['10']])
66
expect(

test/index.spec.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,18 @@ import process from 'process'
44

55
import type { NetlifyPluginOptions } from '@netlify/build'
66
import Chance from 'chance'
7-
import { writeJSON, unlink, existsSync, readFileSync, ensureDir, readJson, pathExists, writeFile, move, copy } from 'fs-extra'
7+
import {
8+
writeJSON,
9+
unlink,
10+
existsSync,
11+
readFileSync,
12+
ensureDir,
13+
readJson,
14+
pathExists,
15+
writeFile,
16+
move,
17+
copy,
18+
} from 'fs-extra'
819
import { join, relative } from 'pathe'
920
import { dir as getTmpDir } from 'tmp-promise'
1021

0 commit comments

Comments
 (0)