|
1 |
| -/* eslint-disable max-lines */ |
2 |
| - |
3 |
| -import { NetlifyConfig } from '@netlify/build' |
4 |
| -import { yellowBright } from 'chalk' |
5 | 1 | import { readJSON } from 'fs-extra'
|
6 |
| -import { PrerenderManifest } from 'next/dist/build' |
7 |
| -import { outdent } from 'outdent' |
8 | 2 | import { join, dirname, relative } from 'pathe'
|
9 | 3 | import slash from 'slash'
|
10 | 4 |
|
11 |
| -import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, HIDDEN_PATHS } from '../constants' |
| 5 | +import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME } from '../constants' |
12 | 6 |
|
13 | 7 | import { RequiredServerFiles } from './requiredServerFilesType'
|
14 | 8 |
|
15 |
| -const defaultFailBuild = (message: string, { error } ): never => { |
| 9 | +const defaultFailBuild = (message: string, { error }): never => { |
16 | 10 | throw new Error(`${message}\n${error && error.stack}`)
|
17 | 11 | }
|
18 | 12 |
|
19 |
| -const ODB_FUNCTION_PATH = `/.netlify/builders/${ODB_FUNCTION_NAME}` |
20 |
| -const HANDLER_FUNCTION_PATH = `/.netlify/functions/${HANDLER_FUNCTION_NAME}` |
21 |
| - |
22 |
| -const CATCH_ALL_REGEX = /\/\[\.{3}(.*)](.json)?$/ |
23 |
| -const OPTIONAL_CATCH_ALL_REGEX = /\/\[{2}\.{3}(.*)]{2}(.json)?$/ |
24 |
| -const DYNAMIC_PARAMETER_REGEX = /\/\[(.*?)]/g |
25 |
| - |
26 |
| -const getNetlifyRoutes = (nextRoute: string): Array<string> => { |
27 |
| - let netlifyRoutes = [nextRoute] |
28 |
| - |
29 |
| - // If the route is an optional catch-all route, we need to add a second |
30 |
| - // Netlify route for the base path (when no parameters are present). |
31 |
| - // The file ending must be present! |
32 |
| - if (OPTIONAL_CATCH_ALL_REGEX.test(nextRoute)) { |
33 |
| - let netlifyRoute = nextRoute.replace(OPTIONAL_CATCH_ALL_REGEX, '$2') |
34 |
| - |
35 |
| - // When optional catch-all route is at top-level, the regex on line 19 will |
36 |
| - // create an empty string, but actually needs to be a forward slash |
37 |
| - if (netlifyRoute === '') netlifyRoute = '/' |
38 |
| - |
39 |
| - // When optional catch-all route is at top-level, the regex on line 19 will |
40 |
| - // create an incorrect route for the data route. For example, it creates |
41 |
| - // /_next/data/%BUILDID%.json, but NextJS looks for |
42 |
| - // /_next/data/%BUILDID%/index.json |
43 |
| - netlifyRoute = netlifyRoute.replace(/(\/_next\/data\/[^/]+).json/, '$1/index.json') |
44 |
| - |
45 |
| - // Add second route to the front of the array |
46 |
| - netlifyRoutes.unshift(netlifyRoute) |
47 |
| - } |
48 |
| - |
49 |
| - // Replace catch-all, e.g., [...slug] |
50 |
| - netlifyRoutes = netlifyRoutes.map((route) => route.replace(CATCH_ALL_REGEX, '/:$1/*')) |
51 |
| - |
52 |
| - // Replace optional catch-all, e.g., [[...slug]] |
53 |
| - netlifyRoutes = netlifyRoutes.map((route) => route.replace(OPTIONAL_CATCH_ALL_REGEX, '/*')) |
54 |
| - |
55 |
| - // Replace dynamic parameters, e.g., [id] |
56 |
| - netlifyRoutes = netlifyRoutes.map((route) => route.replace(DYNAMIC_PARAMETER_REGEX, '/:$1')) |
57 |
| - |
58 |
| - return netlifyRoutes |
59 |
| -} |
60 |
| - |
61 |
| -export const generateRedirects = async ({ netlifyConfig, basePath, i18n }: { |
62 |
| - netlifyConfig: NetlifyConfig, |
63 |
| - basePath: string, |
64 |
| - i18n |
65 |
| -}) => { |
66 |
| - const { dynamicRoutes, routes: staticRoutes }: PrerenderManifest = await readJSON( |
67 |
| - join(netlifyConfig.build.publish, 'prerender-manifest.json'), |
68 |
| - ) |
69 |
| - |
70 |
| - netlifyConfig.redirects.push( |
71 |
| - ...HIDDEN_PATHS.map((path) => ({ |
72 |
| - from: `${basePath}${path}`, |
73 |
| - to: '/404.html', |
74 |
| - status: 404, |
75 |
| - force: true, |
76 |
| - })), |
77 |
| - ) |
78 |
| - |
79 |
| - const dataRedirects = [] |
80 |
| - const pageRedirects = [] |
81 |
| - const isrRedirects = [] |
82 |
| - let hasIsr = false |
83 |
| - |
84 |
| - const dynamicRouteEntries = Object.entries(dynamicRoutes) |
85 |
| - |
86 |
| - const staticRouteEntries = Object.entries(staticRoutes) |
87 |
| - |
88 |
| - staticRouteEntries.forEach(([route, { dataRoute, initialRevalidateSeconds }]) => { |
89 |
| - // Only look for revalidate as we need to rewrite these to SSR rather than ODB |
90 |
| - if (initialRevalidateSeconds === false) { |
91 |
| - // These can be ignored, as they're static files handled by the CDN |
92 |
| - return |
93 |
| - } |
94 |
| - if (i18n?.defaultLocale && route.startsWith(`/${i18n.defaultLocale}/`)) { |
95 |
| - route = route.slice(i18n.defaultLocale.length + 1) |
96 |
| - } |
97 |
| - hasIsr = true |
98 |
| - isrRedirects.push(...getNetlifyRoutes(dataRoute), ...getNetlifyRoutes(route)) |
99 |
| - }) |
100 |
| - |
101 |
| - dynamicRouteEntries.forEach(([route, { dataRoute, fallback }]) => { |
102 |
| - // Add redirects if fallback is "null" (aka blocking) or true/a string |
103 |
| - if (fallback === false) { |
104 |
| - return |
105 |
| - } |
106 |
| - pageRedirects.push(...getNetlifyRoutes(route)) |
107 |
| - dataRedirects.push(...getNetlifyRoutes(dataRoute)) |
108 |
| - }) |
109 |
| - |
110 |
| - if (i18n) { |
111 |
| - netlifyConfig.redirects.push({ from: `${basePath}/:locale/_next/static/*`, to: `/static/:splat`, status: 200 }) |
112 |
| - } |
113 |
| - |
114 |
| - // This is only used in prod, so dev uses `next dev` directly |
115 |
| - netlifyConfig.redirects.push( |
116 |
| - // Static files are in `static` |
117 |
| - { from: `${basePath}/_next/static/*`, to: `/static/:splat`, status: 200 }, |
118 |
| - // API routes always need to be served from the regular function |
119 |
| - { |
120 |
| - from: `${basePath}/api`, |
121 |
| - to: HANDLER_FUNCTION_PATH, |
122 |
| - status: 200, |
123 |
| - }, |
124 |
| - { |
125 |
| - from: `${basePath}/api/*`, |
126 |
| - to: HANDLER_FUNCTION_PATH, |
127 |
| - status: 200, |
128 |
| - }, |
129 |
| - // Preview mode gets forced to the function, to bypess pre-rendered pages |
130 |
| - { |
131 |
| - from: `${basePath}/*`, |
132 |
| - to: HANDLER_FUNCTION_PATH, |
133 |
| - status: 200, |
134 |
| - // eslint-disable-next-line @typescript-eslint/ban-ts-comment |
135 |
| - // @ts-ignore |
136 |
| - conditions: { Cookie: ['__prerender_bypass', '__next_preview_data'] }, |
137 |
| - force: true, |
138 |
| - }, |
139 |
| - // ISR redirects are handled by the regular function. Forced to avoid pre-rendered pages |
140 |
| - ...isrRedirects.map((redirect) => ({ |
141 |
| - from: `${basePath}${redirect}`, |
142 |
| - to: process.env.EXPERIMENTAL_ODB_TTL ? ODB_FUNCTION_PATH : HANDLER_FUNCTION_PATH, |
143 |
| - status: 200, |
144 |
| - force: true, |
145 |
| - })), |
146 |
| - // These are pages with fallback set, which need an ODB |
147 |
| - // Data redirects go first, to avoid conflict with splat redirects |
148 |
| - ...dataRedirects.map((redirect) => ({ |
149 |
| - from: `${basePath}${redirect}`, |
150 |
| - to: ODB_FUNCTION_PATH, |
151 |
| - status: 200, |
152 |
| - })), |
153 |
| - // ...then all the other fallback pages |
154 |
| - ...pageRedirects.map((redirect) => ({ |
155 |
| - from: `${basePath}${redirect}`, |
156 |
| - to: ODB_FUNCTION_PATH, |
157 |
| - status: 200, |
158 |
| - })), |
159 |
| - // Everything else is handled by the regular function |
160 |
| - { from: `${basePath}/*`, to: HANDLER_FUNCTION_PATH, status: 200 }, |
161 |
| - ) |
162 |
| - if (hasIsr) { |
163 |
| - console.log( |
164 |
| - yellowBright(outdent` |
165 |
| - You have some pages that use ISR (pages that use getStaticProps with revalidate set), which is not currently fully-supported by this plugin. Be aware that results may be unreliable. |
166 |
| - `), |
167 |
| - ) |
168 |
| - } |
169 |
| -} |
170 |
| - |
171 | 13 | export const getNextConfig = async function getNextConfig({ publish, failBuild = defaultFailBuild }) {
|
172 | 14 | try {
|
173 | 15 | const { config, appDir, ignore }: RequiredServerFiles = await readJSON(join(publish, 'required-server-files.json'))
|
@@ -234,4 +76,3 @@ export const configureHandlerFunctions = ({ netlifyConfig, publish, ignore = []
|
234 | 76 | })
|
235 | 77 | })
|
236 | 78 | }
|
237 |
| -/* eslint-enable max-lines */ |
0 commit comments