Skip to content

Commit e819e00

Browse files
authored
Add required server files manifest (#20035)
This keeps track of required server files in a manifest file
1 parent 5d5383b commit e819e00

File tree

10 files changed

+705
-157
lines changed

10 files changed

+705
-157
lines changed

packages/next/build/index.ts

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,21 @@ import { loadEnvConfig } from '@next/env'
2525
import { recursiveDelete } from '../lib/recursive-delete'
2626
import { verifyTypeScriptSetup } from '../lib/verifyTypeScriptSetup'
2727
import {
28+
BUILD_ID_FILE,
2829
BUILD_MANIFEST,
2930
CLIENT_STATIC_FILES_PATH,
3031
EXPORT_DETAIL,
3132
EXPORT_MARKER,
33+
FONT_MANIFEST,
3234
IMAGES_MANIFEST,
3335
PAGES_MANIFEST,
3436
PHASE_PRODUCTION_BUILD,
3537
PRERENDER_MANIFEST,
38+
REACT_LOADABLE_MANIFEST,
3639
ROUTES_MANIFEST,
3740
SERVERLESS_DIRECTORY,
3841
SERVER_DIRECTORY,
42+
SERVER_FILES_MANIFEST,
3943
} from '../next-server/lib/constants'
4044
import {
4145
getRouteRegex,
@@ -343,6 +347,39 @@ export default async function build(
343347
'utf8'
344348
)
345349

350+
const manifestPath = path.join(
351+
distDir,
352+
isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY,
353+
PAGES_MANIFEST
354+
)
355+
356+
const requiredServerFiles = {
357+
version: 1,
358+
config: {
359+
...config,
360+
compress: false,
361+
configFile: undefined,
362+
},
363+
files: [
364+
ROUTES_MANIFEST,
365+
path.relative(distDir, manifestPath),
366+
BUILD_MANIFEST,
367+
PRERENDER_MANIFEST,
368+
REACT_LOADABLE_MANIFEST,
369+
path.join(
370+
isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY,
371+
FONT_MANIFEST
372+
),
373+
BUILD_ID_FILE,
374+
].map((file) => path.join(config.distDir, file)),
375+
ignore: [
376+
path.relative(
377+
dir,
378+
path.join(path.dirname(require.resolve('sharp')), '**/*')
379+
),
380+
],
381+
}
382+
346383
const configs = await Promise.all([
347384
getBaseWebpackConfig(dir, {
348385
tracer,
@@ -465,11 +502,6 @@ export default async function build(
465502
prefixText: `${Log.prefixes.info} Collecting page data`,
466503
})
467504

468-
const manifestPath = path.join(
469-
distDir,
470-
isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY,
471-
PAGES_MANIFEST
472-
)
473505
const buildManifestPath = path.join(distDir, BUILD_MANIFEST)
474506

475507
const ssgPages = new Set<string>()
@@ -515,6 +547,8 @@ export default async function build(
515547
false
516548
))
517549

550+
let hasSsrAmpPages = false
551+
518552
const analysisBegin = process.hrtime()
519553
await Promise.all(
520554
pageKeys.map(async (page) => {
@@ -575,6 +609,13 @@ export default async function build(
575609
config.i18n?.defaultLocale
576610
)
577611

612+
if (
613+
workerResult.isStatic === false &&
614+
(workerResult.isHybridAmp || workerResult.isAmpOnly)
615+
) {
616+
hasSsrAmpPages = true
617+
}
618+
578619
if (workerResult.isHybridAmp) {
579620
isHybridAmp = true
580621
hybridAmpPages.add(page)
@@ -637,6 +678,18 @@ export default async function build(
637678
)
638679
staticCheckWorkers.end()
639680

681+
if (!hasSsrAmpPages) {
682+
requiredServerFiles.ignore.push(
683+
path.relative(
684+
dir,
685+
path.join(
686+
path.dirname(require.resolve('@ampproject/toolbox-optimizer')),
687+
'**/*'
688+
)
689+
)
690+
)
691+
}
692+
640693
if (serverPropsPages.size > 0 || ssgPages.size > 0) {
641694
// We update the routes manifest after the build with the
642695
// data routes since we can't determine these until after build
@@ -712,6 +765,12 @@ export default async function build(
712765

713766
await writeBuildId(distDir, buildId)
714767

768+
await promises.writeFile(
769+
path.join(distDir, SERVER_FILES_MANIFEST),
770+
JSON.stringify(requiredServerFiles),
771+
'utf8'
772+
)
773+
715774
const finalPrerenderRoutes: { [route: string]: SsgRoute } = {}
716775
const tbdPrerenderRoutes: string[] = []
717776
let ssgNotFoundPaths: string[] = []

packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts

Lines changed: 9 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { parse as parseQs } from 'querystring'
21
import { IncomingMessage, ServerResponse } from 'http'
32
import { parse as parseUrl, format as formatUrl, UrlWithParsedQuery } from 'url'
43
import { isResSent } from '../../../../next-server/lib/utils'
@@ -14,7 +13,6 @@ import {
1413
} from '../../../../next-server/server/api-utils'
1514
import { getRedirectStatus } from '../../../../lib/load-custom-routes'
1615
import getRouteNoAssetPath from '../../../../next-server/lib/router/utils/get-route-from-asset-path'
17-
import { getRouteMatcher } from '../../../../next-server/lib/router/utils/route-matcher'
1816
import { PERMANENT_REDIRECT_STATUS } from '../../../../next-server/lib/constants'
1917

2018
export function getPageHandler(ctx: ServerlessHandlerCtx) {
@@ -56,7 +54,10 @@ export function getPageHandler(ctx: ServerlessHandlerCtx) {
5654
handleBasePath,
5755
defaultRouteRegex,
5856
dynamicRouteMatcher,
57+
interpolateDynamicPath,
58+
getParamsFromRouteMatches,
5959
normalizeDynamicRouteParams,
60+
normalizeVercelUrl,
6061
} = getUtils(ctx)
6162

6263
async function renderReqToHTML(
@@ -222,116 +223,24 @@ export function getPageHandler(ctx: ServerlessHandlerCtx) {
222223
!hasValidParams &&
223224
req.headers?.['x-now-route-matches']
224225
) {
225-
nowParams = getRouteMatcher(
226-
(function () {
227-
const { groups, routeKeys } = defaultRouteRegex!
228-
229-
return {
230-
re: {
231-
// Simulate a RegExp match from the \`req.url\` input
232-
exec: (str: string) => {
233-
const obj = parseQs(str)
234-
235-
// favor named matches if available
236-
const routeKeyNames = Object.keys(routeKeys || {})
237-
238-
const filterLocaleItem = (val: string | string[]) => {
239-
if (i18n) {
240-
// locale items can be included in route-matches
241-
// for fallback SSG pages so ensure they are
242-
// filtered
243-
const isCatchAll = Array.isArray(val)
244-
const _val = isCatchAll ? val[0] : val
245-
246-
if (
247-
typeof _val === 'string' &&
248-
i18n.locales.some((item) => {
249-
if (item.toLowerCase() === _val.toLowerCase()) {
250-
detectedLocale = item
251-
renderOpts.locale = detectedLocale
252-
return true
253-
}
254-
return false
255-
})
256-
) {
257-
// remove the locale item from the match
258-
if (isCatchAll) {
259-
;(val as string[]).splice(0, 1)
260-
}
261-
262-
// the value is only a locale item and
263-
// shouldn't be added
264-
return isCatchAll ? val.length === 0 : true
265-
}
266-
}
267-
return false
268-
}
269-
270-
if (routeKeyNames.every((name) => obj[name])) {
271-
return routeKeyNames.reduce((prev, keyName) => {
272-
const paramName = routeKeys?.[keyName]
273-
274-
if (paramName && !filterLocaleItem(obj[keyName])) {
275-
prev[groups[paramName].pos] = obj[keyName]
276-
}
277-
return prev
278-
}, {} as any)
279-
}
280-
281-
return Object.keys(obj).reduce((prev, key) => {
282-
if (!filterLocaleItem(obj[key])) {
283-
return Object.assign(prev, {
284-
[key]: obj[key],
285-
})
286-
}
287-
return prev
288-
}, {})
289-
},
290-
},
291-
groups,
292-
}
293-
})() as any
294-
)(req.headers['x-now-route-matches'] as string)
226+
nowParams = getParamsFromRouteMatches(req, renderOpts, detectedLocale)
295227
}
296228

297229
// make sure to set renderOpts to the correct params e.g. _params
298230
// if provided from worker or params if we're parsing them here
299231
renderOpts.params = _params || params
300232

301-
// make sure to normalize req.url on Vercel to strip dynamic params
302-
// from the query which are added during routing
303-
if (pageIsDynamic && trustQuery && defaultRouteRegex) {
304-
const _parsedUrl = parseUrl(req.url!, true)
305-
delete (_parsedUrl as any).search
306-
307-
for (const param of Object.keys(defaultRouteRegex.groups)) {
308-
delete _parsedUrl.query[param]
309-
}
310-
req.url = formatUrl(_parsedUrl)
311-
}
233+
normalizeVercelUrl(req, !!trustQuery)
312234

313235
// normalize request URL/asPath for fallback/revalidate pages since the
314236
// proxy sets the request URL to the output's path for fallback pages
315237
if (pageIsDynamic && nowParams && defaultRouteRegex) {
316238
const _parsedUrl = parseUrl(req.url!)
317239

318-
for (const param of Object.keys(defaultRouteRegex.groups)) {
319-
const { optional, repeat } = defaultRouteRegex.groups[param]
320-
let builtParam = `[${repeat ? '...' : ''}${param}]`
321-
322-
if (optional) {
323-
builtParam = `[${builtParam}]`
324-
}
325-
326-
const paramIdx = _parsedUrl.pathname!.indexOf(builtParam)
327-
328-
if (paramIdx > -1) {
329-
_parsedUrl.pathname =
330-
_parsedUrl.pathname!.substr(0, paramIdx) +
331-
encodeURI((nowParams as any)[param] || '') +
332-
_parsedUrl.pathname!.substr(paramIdx + builtParam.length)
333-
}
334-
}
240+
_parsedUrl.pathname = interpolateDynamicPath(
241+
_parsedUrl.pathname!,
242+
nowParams
243+
)
335244
parsedUrl.pathname = _parsedUrl.pathname
336245
req.url = formatUrl(_parsedUrl)
337246
}

0 commit comments

Comments
 (0)