@netlify/plugin-nextjs 4.0.0-rc.0 → 4.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/constants.js +5 -1
- package/lib/helpers/config.js +3 -3
- package/lib/helpers/files.js +27 -12
- package/lib/helpers/functions.js +14 -14
- package/lib/helpers/redirects.js +19 -19
- package/lib/helpers/verification.js +3 -3
- package/lib/index.js +1 -1
- package/lib/templates/getHandler.js +1 -1
- package/lib/templates/getPageResolver.js +3 -3
- package/lib/templates/handlerUtils.js +7 -7
- package/lib/templates/ipx.js +1 -1
- package/package.json +4 -4
package/lib/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DYNAMIC_PARAMETER_REGEX = exports.OPTIONAL_CATCH_ALL_REGEX = exports.CATCH_ALL_REGEX = exports.DEFAULT_FUNCTIONS_SRC = exports.HANDLER_FUNCTION_PATH = exports.ODB_FUNCTION_PATH = exports.HIDDEN_PATHS = exports.IMAGE_FUNCTION_NAME = exports.ODB_FUNCTION_NAME = exports.HANDLER_FUNCTION_NAME = void 0;
|
|
3
|
+
exports.DIVIDER = exports.MINIMUM_REVALIDATE_SECONDS = exports.DYNAMIC_PARAMETER_REGEX = exports.OPTIONAL_CATCH_ALL_REGEX = exports.CATCH_ALL_REGEX = exports.DEFAULT_FUNCTIONS_SRC = exports.HANDLER_FUNCTION_PATH = exports.ODB_FUNCTION_PATH = exports.HIDDEN_PATHS = exports.IMAGE_FUNCTION_NAME = exports.ODB_FUNCTION_NAME = exports.HANDLER_FUNCTION_NAME = void 0;
|
|
4
4
|
exports.HANDLER_FUNCTION_NAME = '___netlify-handler';
|
|
5
5
|
exports.ODB_FUNCTION_NAME = '___netlify-odb-handler';
|
|
6
6
|
exports.IMAGE_FUNCTION_NAME = '_ipx';
|
|
@@ -22,3 +22,7 @@ exports.DEFAULT_FUNCTIONS_SRC = 'netlify/functions';
|
|
|
22
22
|
exports.CATCH_ALL_REGEX = /\/\[\.{3}(.*)](.json)?$/;
|
|
23
23
|
exports.OPTIONAL_CATCH_ALL_REGEX = /\/\[{2}\.{3}(.*)]{2}(.json)?$/;
|
|
24
24
|
exports.DYNAMIC_PARAMETER_REGEX = /\/\[(.*?)]/g;
|
|
25
|
+
exports.MINIMUM_REVALIDATE_SECONDS = 60;
|
|
26
|
+
exports.DIVIDER = `
|
|
27
|
+
────────────────────────────────────────────────────────────────
|
|
28
|
+
`;
|
package/lib/helpers/config.js
CHANGED
|
@@ -13,7 +13,7 @@ const defaultFailBuild = (message, { error }) => {
|
|
|
13
13
|
};
|
|
14
14
|
const getNextConfig = async function getNextConfig({ publish, failBuild = defaultFailBuild, }) {
|
|
15
15
|
try {
|
|
16
|
-
const { config, appDir, ignore } = await
|
|
16
|
+
const { config, appDir, ignore } = await fs_extra_1.readJSON(pathe_1.join(publish, 'required-server-files.json'));
|
|
17
17
|
if (!config) {
|
|
18
18
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
19
19
|
// @ts-ignore
|
|
@@ -28,7 +28,7 @@ const getNextConfig = async function getNextConfig({ publish, failBuild = defaul
|
|
|
28
28
|
exports.getNextConfig = getNextConfig;
|
|
29
29
|
const resolveModuleRoot = (moduleName) => {
|
|
30
30
|
try {
|
|
31
|
-
return
|
|
31
|
+
return pathe_1.dirname(pathe_1.relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] })));
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
34
34
|
return null;
|
|
@@ -45,7 +45,7 @@ const configureHandlerFunctions = ({ netlifyConfig, publish, ignore = [] }) => {
|
|
|
45
45
|
(_a = netlifyConfig.functions)[functionName] || (_a[functionName] = { included_files: [], external_node_modules: [] });
|
|
46
46
|
netlifyConfig.functions[functionName].node_bundler = 'nft';
|
|
47
47
|
(_b = netlifyConfig.functions[functionName]).included_files || (_b.included_files = []);
|
|
48
|
-
netlifyConfig.functions[functionName].included_files.push('.env', '.env.local', '.env.production', '.env.production.local', `${publish}/server/**`, `${publish}/serverless/**`, `${publish}/*.json`, `${publish}/BUILD_ID`, `${publish}/static/chunks/webpack-middleware*.js`, `!${publish}/server/**/*.js.nft.json`, ...ignore.map((path) => `!${
|
|
48
|
+
netlifyConfig.functions[functionName].included_files.push('.env', '.env.local', '.env.production', '.env.production.local', `${publish}/server/**`, `${publish}/serverless/**`, `${publish}/*.json`, `${publish}/BUILD_ID`, `${publish}/static/chunks/webpack-middleware*.js`, `!${publish}/server/**/*.js.nft.json`, ...ignore.map((path) => `!${slash_1.default(path)}`));
|
|
49
49
|
const nextRoot = resolveModuleRoot('next');
|
|
50
50
|
if (nextRoot) {
|
|
51
51
|
netlifyConfig.functions[functionName].included_files.push(`!${nextRoot}/dist/server/lib/squoosh/**/*.wasm`, `!${nextRoot}/dist/next-server/server/lib/squoosh/**/*.wasm`, `!${nextRoot}/dist/compiled/webpack/bundle4.js`, `!${nextRoot}/dist/compiled/webpack/bundle5.js`, `!${nextRoot}/dist/compiled/terser/bundle.min.js`);
|
package/lib/helpers/files.js
CHANGED
|
@@ -7,6 +7,7 @@ const { outdent } = require('outdent');
|
|
|
7
7
|
const pLimit = require('p-limit');
|
|
8
8
|
const { join } = require('pathe');
|
|
9
9
|
const slash = require('slash');
|
|
10
|
+
const { MINIMUM_REVALIDATE_SECONDS, DIVIDER } = require('../constants');
|
|
10
11
|
const TEST_ROUTE = /(|\/)\[[^/]+?](\/|\.html|$)/;
|
|
11
12
|
const isDynamicRoute = (route) => TEST_ROUTE.test(route);
|
|
12
13
|
const stripLocale = (rawPath, locales = []) => {
|
|
@@ -64,12 +65,16 @@ exports.moveStaticPages = async ({ netlifyConfig, target, i18n }) => {
|
|
|
64
65
|
const prerenderManifest = await readJson(join(netlifyConfig.build.publish, 'prerender-manifest.json'));
|
|
65
66
|
const { redirects, rewrites } = await readJson(join(netlifyConfig.build.publish, 'routes-manifest.json'));
|
|
66
67
|
const isrFiles = new Set();
|
|
68
|
+
const shortRevalidateRoutes = [];
|
|
67
69
|
Object.entries(prerenderManifest.routes).forEach(([route, { initialRevalidateSeconds }]) => {
|
|
68
70
|
if (initialRevalidateSeconds) {
|
|
69
71
|
// Find all files used by ISR routes
|
|
70
72
|
const trimmedPath = route.slice(1);
|
|
71
73
|
isrFiles.add(`${trimmedPath}.html`);
|
|
72
74
|
isrFiles.add(`${trimmedPath}.json`);
|
|
75
|
+
if (initialRevalidateSeconds < MINIMUM_REVALIDATE_SECONDS) {
|
|
76
|
+
shortRevalidateRoutes.push({ Route: route, Revalidate: initialRevalidateSeconds });
|
|
77
|
+
}
|
|
73
78
|
}
|
|
74
79
|
});
|
|
75
80
|
const files = [];
|
|
@@ -77,10 +82,10 @@ exports.moveStaticPages = async ({ netlifyConfig, target, i18n }) => {
|
|
|
77
82
|
const moveFile = async (file) => {
|
|
78
83
|
const isData = file.endsWith('.json');
|
|
79
84
|
const source = join(root, file);
|
|
80
|
-
const
|
|
85
|
+
const targetFile = isData ? join(dataDir, file) : file;
|
|
81
86
|
files.push(file);
|
|
82
|
-
filesManifest[file] =
|
|
83
|
-
const dest = join(netlifyConfig.build.publish,
|
|
87
|
+
filesManifest[file] = targetFile;
|
|
88
|
+
const dest = join(netlifyConfig.build.publish, targetFile);
|
|
84
89
|
try {
|
|
85
90
|
await move(source, dest);
|
|
86
91
|
}
|
|
@@ -99,7 +104,7 @@ exports.moveStaticPages = async ({ netlifyConfig, target, i18n }) => {
|
|
|
99
104
|
const matchedRewrites = new Set();
|
|
100
105
|
// Limit concurrent file moves to number of cpus or 2 if there is only 1
|
|
101
106
|
const limit = pLimit(Math.max(2, cpus().length));
|
|
102
|
-
const promises = pages.map(
|
|
107
|
+
const promises = pages.map((rawPath) => {
|
|
103
108
|
const filePath = slash(rawPath);
|
|
104
109
|
// Don't move ISR files, as they're used for the first request
|
|
105
110
|
if (isrFiles.has(filePath)) {
|
|
@@ -138,8 +143,7 @@ exports.moveStaticPages = async ({ netlifyConfig, target, i18n }) => {
|
|
|
138
143
|
The following middleware matched statically-rendered pages:
|
|
139
144
|
|
|
140
145
|
${yellowBright([...matchingMiddleware].map((mid) => `- /${mid}/_middleware`).join('\n'))}
|
|
141
|
-
|
|
142
|
-
────────────────────────────────────────────────────────────────
|
|
146
|
+
${DIVIDER}
|
|
143
147
|
`);
|
|
144
148
|
// There could potentially be thousands of matching pages, so we don't want to spam the console with this
|
|
145
149
|
if (matchedPages.size < 50) {
|
|
@@ -147,8 +151,7 @@ exports.moveStaticPages = async ({ netlifyConfig, target, i18n }) => {
|
|
|
147
151
|
The following files matched middleware and were not moved to the CDN:
|
|
148
152
|
|
|
149
153
|
${yellowBright([...matchedPages].map((mid) => `- ${mid}`).join('\n'))}
|
|
150
|
-
|
|
151
|
-
────────────────────────────────────────────────────────────────
|
|
154
|
+
${DIVIDER}
|
|
152
155
|
`);
|
|
153
156
|
}
|
|
154
157
|
}
|
|
@@ -161,8 +164,7 @@ exports.moveStaticPages = async ({ netlifyConfig, target, i18n }) => {
|
|
|
161
164
|
The following files matched redirects and were not moved to the CDN:
|
|
162
165
|
|
|
163
166
|
${yellowBright([...matchedRedirects].map((mid) => `- ${mid}`).join('\n'))}
|
|
164
|
-
|
|
165
|
-
────────────────────────────────────────────────────────────────
|
|
167
|
+
${DIVIDER}
|
|
166
168
|
`);
|
|
167
169
|
}
|
|
168
170
|
if (matchedRewrites.size < 50 && matchedRewrites.size !== 0) {
|
|
@@ -170,8 +172,7 @@ exports.moveStaticPages = async ({ netlifyConfig, target, i18n }) => {
|
|
|
170
172
|
The following files matched beforeFiles rewrites and were not moved to the CDN:
|
|
171
173
|
|
|
172
174
|
${yellowBright([...matchedRewrites].map((mid) => `- ${mid}`).join('\n'))}
|
|
173
|
-
|
|
174
|
-
────────────────────────────────────────────────────────────────
|
|
175
|
+
${DIVIDER}
|
|
175
176
|
`);
|
|
176
177
|
}
|
|
177
178
|
}
|
|
@@ -193,6 +194,20 @@ exports.moveStaticPages = async ({ netlifyConfig, target, i18n }) => {
|
|
|
193
194
|
catch { }
|
|
194
195
|
}
|
|
195
196
|
}
|
|
197
|
+
if (shortRevalidateRoutes.length !== 0) {
|
|
198
|
+
console.log(outdent `
|
|
199
|
+
The following routes use "revalidate" values of under ${MINIMUM_REVALIDATE_SECONDS} seconds, which is not supported.
|
|
200
|
+
They will use a revalidate time of ${MINIMUM_REVALIDATE_SECONDS} seconds instead.
|
|
201
|
+
`);
|
|
202
|
+
console.table(shortRevalidateRoutes);
|
|
203
|
+
// TODO: add these docs
|
|
204
|
+
// console.log(
|
|
205
|
+
// outdent`
|
|
206
|
+
// For more information, see https://ntl.fyi/next-revalidate-time
|
|
207
|
+
// ${DIVIDER}
|
|
208
|
+
// `,
|
|
209
|
+
// )
|
|
210
|
+
}
|
|
196
211
|
};
|
|
197
212
|
const patchFile = async ({ file, from, to }) => {
|
|
198
213
|
if (!existsSync(file)) {
|
package/lib/helpers/functions.js
CHANGED
|
@@ -9,14 +9,14 @@ const getPageResolver_1 = require("../templates/getPageResolver");
|
|
|
9
9
|
const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC, PUBLISH_DIR }, appDir) => {
|
|
10
10
|
const functionsDir = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
11
11
|
const bridgeFile = require.resolve('@vercel/node/dist/bridge');
|
|
12
|
-
const functionDir =
|
|
13
|
-
const publishDir =
|
|
12
|
+
const functionDir = pathe_1.join(process.cwd(), functionsDir, constants_1.HANDLER_FUNCTION_NAME);
|
|
13
|
+
const publishDir = pathe_1.relative(functionDir, pathe_1.join(process.cwd(), PUBLISH_DIR));
|
|
14
14
|
const writeHandler = async (func, isODB) => {
|
|
15
|
-
const handlerSource = await
|
|
16
|
-
await
|
|
17
|
-
await
|
|
18
|
-
await
|
|
19
|
-
await
|
|
15
|
+
const handlerSource = await getHandler_1.getHandler({ isODB, publishDir, appDir: pathe_1.relative(functionDir, appDir) });
|
|
16
|
+
await fs_extra_1.ensureDir(pathe_1.join(functionsDir, func));
|
|
17
|
+
await fs_extra_1.writeFile(pathe_1.join(functionsDir, func, `${func}.js`), handlerSource);
|
|
18
|
+
await fs_extra_1.copyFile(bridgeFile, pathe_1.join(functionsDir, func, 'bridge.js'));
|
|
19
|
+
await fs_extra_1.copyFile(pathe_1.join(__dirname, '..', '..', 'lib', 'templates', 'handlerUtils.js'), pathe_1.join(functionsDir, func, 'handlerUtils.js'));
|
|
20
20
|
};
|
|
21
21
|
await writeHandler(constants_1.HANDLER_FUNCTION_NAME, false);
|
|
22
22
|
await writeHandler(constants_1.ODB_FUNCTION_NAME, true);
|
|
@@ -28,25 +28,25 @@ exports.generateFunctions = generateFunctions;
|
|
|
28
28
|
*/
|
|
29
29
|
const generatePagesResolver = async ({ constants: { INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC }, netlifyConfig, target, }) => {
|
|
30
30
|
const functionsPath = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
31
|
-
const jsSource = await
|
|
31
|
+
const jsSource = await getPageResolver_1.getPageResolver({
|
|
32
32
|
netlifyConfig,
|
|
33
33
|
target,
|
|
34
34
|
});
|
|
35
|
-
await
|
|
36
|
-
await
|
|
35
|
+
await fs_extra_1.writeFile(pathe_1.join(functionsPath, constants_1.ODB_FUNCTION_NAME, 'pages.js'), jsSource);
|
|
36
|
+
await fs_extra_1.writeFile(pathe_1.join(functionsPath, constants_1.HANDLER_FUNCTION_NAME, 'pages.js'), jsSource);
|
|
37
37
|
};
|
|
38
38
|
exports.generatePagesResolver = generatePagesResolver;
|
|
39
39
|
// Move our next/image function into the correct functions directory
|
|
40
40
|
const setupImageFunction = async ({ constants: { INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC }, imageconfig = {}, netlifyConfig, basePath, }) => {
|
|
41
41
|
const functionsPath = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
42
42
|
const functionName = `${constants_1.IMAGE_FUNCTION_NAME}.js`;
|
|
43
|
-
const functionDirectory =
|
|
44
|
-
await
|
|
45
|
-
await
|
|
43
|
+
const functionDirectory = pathe_1.join(functionsPath, constants_1.IMAGE_FUNCTION_NAME);
|
|
44
|
+
await fs_extra_1.ensureDir(functionDirectory);
|
|
45
|
+
await fs_extra_1.writeJSON(pathe_1.join(functionDirectory, 'imageconfig.json'), {
|
|
46
46
|
...imageconfig,
|
|
47
47
|
basePath: [basePath, constants_1.IMAGE_FUNCTION_NAME].join('/'),
|
|
48
48
|
});
|
|
49
|
-
await
|
|
49
|
+
await fs_extra_1.copyFile(pathe_1.join(__dirname, '..', '..', 'lib', 'templates', 'ipx.js'), pathe_1.join(functionDirectory, functionName));
|
|
50
50
|
const imagePath = imageconfig.path || '/_next/image';
|
|
51
51
|
netlifyConfig.redirects.push({
|
|
52
52
|
from: `${imagePath}*`,
|
package/lib/helpers/redirects.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.generateRedirects = void 0;
|
|
4
|
-
const chalk_1 = require("chalk");
|
|
5
7
|
const fs_extra_1 = require("fs-extra");
|
|
6
|
-
const
|
|
8
|
+
const globby_1 = __importDefault(require("globby"));
|
|
7
9
|
const pathe_1 = require("pathe");
|
|
8
10
|
const constants_1 = require("../constants");
|
|
9
11
|
const utils_1 = require("./utils");
|
|
@@ -35,8 +37,8 @@ const generateLocaleRedirects = ({ i18n, basePath, trailingSlash, }) => {
|
|
|
35
37
|
});
|
|
36
38
|
return redirects;
|
|
37
39
|
};
|
|
38
|
-
const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath, trailingSlash }, }) => {
|
|
39
|
-
const { dynamicRoutes, routes: staticRoutes } = await
|
|
40
|
+
const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath, trailingSlash, appDir }, }) => {
|
|
41
|
+
const { dynamicRoutes, routes: staticRoutes } = await fs_extra_1.readJSON(pathe_1.join(netlifyConfig.build.publish, 'prerender-manifest.json'));
|
|
40
42
|
netlifyConfig.redirects.push(...constants_1.HIDDEN_PATHS.map((path) => ({
|
|
41
43
|
from: `${basePath}${path}`,
|
|
42
44
|
to: '/404.html',
|
|
@@ -49,7 +51,6 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
|
|
|
49
51
|
const dataRedirects = [];
|
|
50
52
|
const pageRedirects = [];
|
|
51
53
|
const isrRedirects = [];
|
|
52
|
-
let hasIsr = false;
|
|
53
54
|
const dynamicRouteEntries = Object.entries(dynamicRoutes);
|
|
54
55
|
const staticRouteEntries = Object.entries(staticRoutes);
|
|
55
56
|
staticRouteEntries.forEach(([route, { dataRoute, initialRevalidateSeconds }]) => {
|
|
@@ -61,20 +62,20 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
|
|
|
61
62
|
if ((i18n === null || i18n === void 0 ? void 0 : i18n.defaultLocale) && route.startsWith(`/${i18n.defaultLocale}/`)) {
|
|
62
63
|
route = route.slice(i18n.defaultLocale.length + 1);
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
isrRedirects.push(...(0, utils_1.netlifyRoutesForNextRoute)(dataRoute), ...(0, utils_1.netlifyRoutesForNextRoute)(route));
|
|
65
|
+
isrRedirects.push(...utils_1.netlifyRoutesForNextRoute(dataRoute), ...utils_1.netlifyRoutesForNextRoute(route));
|
|
66
66
|
});
|
|
67
67
|
dynamicRouteEntries.forEach(([route, { dataRoute, fallback }]) => {
|
|
68
68
|
// Add redirects if fallback is "null" (aka blocking) or true/a string
|
|
69
69
|
if (fallback === false) {
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
-
pageRedirects.push(...
|
|
73
|
-
dataRedirects.push(...
|
|
72
|
+
pageRedirects.push(...utils_1.netlifyRoutesForNextRoute(route));
|
|
73
|
+
dataRedirects.push(...utils_1.netlifyRoutesForNextRoute(dataRoute));
|
|
74
74
|
});
|
|
75
75
|
if (i18n) {
|
|
76
76
|
netlifyConfig.redirects.push({ from: `${basePath}/:locale/_next/static/*`, to: `/static/:splat`, status: 200 });
|
|
77
77
|
}
|
|
78
|
+
const publicFiles = await globby_1.default('**/*', { cwd: pathe_1.join(appDir, 'public') });
|
|
78
79
|
// This is only used in prod, so dev uses `next dev` directly
|
|
79
80
|
netlifyConfig.redirects.push(
|
|
80
81
|
// Static files are in `static`
|
|
@@ -89,20 +90,24 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
|
|
|
89
90
|
to: constants_1.HANDLER_FUNCTION_PATH,
|
|
90
91
|
status: 200,
|
|
91
92
|
},
|
|
92
|
-
// Preview mode gets forced to the function, to
|
|
93
|
-
{
|
|
93
|
+
// Preview mode gets forced to the function, to bypass pre-rendered pages, but static files need to be skipped
|
|
94
|
+
...publicFiles.map((file) => ({
|
|
95
|
+
from: `${basePath}/${file}`,
|
|
96
|
+
// This is a no-op, but we do it to stop it matching the following rule
|
|
97
|
+
to: `${basePath}/${file}`,
|
|
98
|
+
conditions: { Cookie: ['__prerender_bypass', '__next_preview_data'] },
|
|
99
|
+
status: 200,
|
|
100
|
+
})), {
|
|
94
101
|
from: `${basePath}/*`,
|
|
95
102
|
to: constants_1.HANDLER_FUNCTION_PATH,
|
|
96
103
|
status: 200,
|
|
97
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
98
|
-
// @ts-ignore The conditions type is incorrect
|
|
99
104
|
conditions: { Cookie: ['__prerender_bypass', '__next_preview_data'] },
|
|
100
105
|
force: true,
|
|
101
106
|
},
|
|
102
107
|
// ISR redirects are handled by the regular function. Forced to avoid pre-rendered pages
|
|
103
108
|
...isrRedirects.map((redirect) => ({
|
|
104
109
|
from: `${basePath}${redirect}`,
|
|
105
|
-
to:
|
|
110
|
+
to: constants_1.ODB_FUNCTION_PATH,
|
|
106
111
|
status: 200,
|
|
107
112
|
force: true,
|
|
108
113
|
})),
|
|
@@ -121,10 +126,5 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
|
|
|
121
126
|
})),
|
|
122
127
|
// Everything else is handled by the regular function
|
|
123
128
|
{ from: `${basePath}/*`, to: constants_1.HANDLER_FUNCTION_PATH, status: 200 });
|
|
124
|
-
if (hasIsr) {
|
|
125
|
-
console.log((0, chalk_1.yellowBright)((0, outdent_1.outdent) `
|
|
126
|
-
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.
|
|
127
|
-
`));
|
|
128
|
-
}
|
|
129
129
|
};
|
|
130
130
|
exports.generateRedirects = generateRedirects;
|
|
@@ -63,13 +63,13 @@ exports.checkZipSize = async (file, maxSize = LAMBDA_MAX_SIZE) => {
|
|
|
63
63
|
console.warn(`Could not check zip size because ${file} does not exist`);
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
66
|
+
const fileSize = await promises.stat(file).then(({ size }) => size);
|
|
67
|
+
if (fileSize < maxSize) {
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
// We don't fail the build, because the actual hard max size is larger so it might still succeed
|
|
71
71
|
console.log(redBright(outdent `
|
|
72
|
-
The function zip ${yellowBright(relative(process.cwd(), file))} size is ${prettyBytes(
|
|
72
|
+
The function zip ${yellowBright(relative(process.cwd(), file))} size is ${prettyBytes(fileSize)}, which is larger than the maximum supported size of ${prettyBytes(maxSize)}.
|
|
73
73
|
There are a few reasons this could happen. You may have accidentally bundled a large dependency, or you might have a
|
|
74
74
|
large number of pre-rendered pages included.
|
|
75
75
|
`));
|
package/lib/index.js
CHANGED
|
@@ -42,7 +42,7 @@ module.exports = {
|
|
|
42
42
|
await setupImageFunction({ constants, imageconfig: images, netlifyConfig, basePath });
|
|
43
43
|
await generateRedirects({
|
|
44
44
|
netlifyConfig,
|
|
45
|
-
nextConfig: { basePath, i18n, trailingSlash },
|
|
45
|
+
nextConfig: { basePath, i18n, trailingSlash, appDir },
|
|
46
46
|
});
|
|
47
47
|
},
|
|
48
48
|
async onPostBuild({ netlifyConfig, utils: { cache, functions, failBuild }, constants: { FUNCTIONS_DIST } }) {
|
|
@@ -68,7 +68,7 @@ const makeHandler = () =>
|
|
|
68
68
|
// Sending SWR headers causes undefined behaviour with the Netlify CDN
|
|
69
69
|
const cacheHeader = (_c = multiValueHeaders['cache-control']) === null || _c === void 0 ? void 0 : _c[0];
|
|
70
70
|
if (cacheHeader === null || cacheHeader === void 0 ? void 0 : cacheHeader.includes('stale-while-revalidate')) {
|
|
71
|
-
if (requestMode === 'odb'
|
|
71
|
+
if (requestMode === 'odb') {
|
|
72
72
|
requestMode = 'isr';
|
|
73
73
|
const ttl = getMaxAge(cacheHeader);
|
|
74
74
|
// Long-expiry TTL is basically no TTL
|
|
@@ -14,14 +14,14 @@ const constants_1 = require("../constants");
|
|
|
14
14
|
const getPageResolver = async ({ netlifyConfig, target }) => {
|
|
15
15
|
const functionDir = path_1.posix.resolve(path_1.posix.join('.netlify', 'functions', constants_1.HANDLER_FUNCTION_NAME));
|
|
16
16
|
const root = path_1.posix.join(netlifyConfig.build.publish, target === 'server' ? 'server' : 'serverless', 'pages');
|
|
17
|
-
const pages = await
|
|
17
|
+
const pages = await tiny_glob_1.default('**/*.js', {
|
|
18
18
|
cwd: root,
|
|
19
19
|
dot: true,
|
|
20
20
|
});
|
|
21
21
|
const pageFiles = pages
|
|
22
|
-
.map((page) => `require.resolve('${path_1.posix.relative(functionDir, path_1.posix.join(root,
|
|
22
|
+
.map((page) => `require.resolve('${path_1.posix.relative(functionDir, path_1.posix.join(root, slash_1.default(page)))}')`)
|
|
23
23
|
.sort();
|
|
24
|
-
return
|
|
24
|
+
return outdent_1.outdent `
|
|
25
25
|
// This file is purely to allow nft to know about these pages. It should be temporary.
|
|
26
26
|
exports.resolvePages = () => {
|
|
27
27
|
try {
|
|
@@ -11,7 +11,7 @@ const os_1 = require("os");
|
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const stream_1 = require("stream");
|
|
13
13
|
const util_1 = require("util");
|
|
14
|
-
const streamPipeline =
|
|
14
|
+
const streamPipeline = util_1.promisify(stream_1.pipeline);
|
|
15
15
|
const downloadFile = async (url, destination) => {
|
|
16
16
|
console.log(`Downloading ${url} to ${destination}`);
|
|
17
17
|
const httpx = url.startsWith('https') ? https_1.default : http_1.default;
|
|
@@ -21,7 +21,7 @@ const downloadFile = async (url, destination) => {
|
|
|
21
21
|
reject(new Error(`Failed to download ${url}: ${response.statusCode} ${response.statusMessage || ''}`));
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
const fileStream =
|
|
24
|
+
const fileStream = fs_1.createWriteStream(destination);
|
|
25
25
|
streamPipeline(response, fileStream)
|
|
26
26
|
.then(resolve)
|
|
27
27
|
.catch((error) => {
|
|
@@ -78,7 +78,7 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
78
78
|
const staticFiles = new Map(staticManifest);
|
|
79
79
|
const downloadPromises = new Map();
|
|
80
80
|
// Yes, you can cache stuff locally in a Lambda
|
|
81
|
-
const cacheDir = path_1.default.join(
|
|
81
|
+
const cacheDir = path_1.default.join(os_1.tmpdir(), 'next-static-cache');
|
|
82
82
|
// Grab the real fs.promises.readFile...
|
|
83
83
|
const readfileOrig = promises.readFile;
|
|
84
84
|
const statsOrig = promises.stat;
|
|
@@ -90,7 +90,7 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
90
90
|
// We only want the part after `pages/`
|
|
91
91
|
const filePath = file.slice(pageRoot.length + 1);
|
|
92
92
|
// Is it in the CDN and not local?
|
|
93
|
-
if (staticFiles.has(filePath) && !
|
|
93
|
+
if (staticFiles.has(filePath) && !fs_1.existsSync(file)) {
|
|
94
94
|
// This name is safe to use, because it's one that was already created by Next
|
|
95
95
|
const cacheFile = path_1.default.join(cacheDir, filePath);
|
|
96
96
|
const url = `${base}/${staticFiles.get(filePath)}`;
|
|
@@ -99,11 +99,11 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
99
99
|
await downloadPromises.get(url);
|
|
100
100
|
}
|
|
101
101
|
// Have we already cached it? We download every time if running locally to avoid staleness
|
|
102
|
-
if ((!
|
|
102
|
+
if ((!fs_1.existsSync(cacheFile) || process.env.NETLIFY_DEV) && base) {
|
|
103
103
|
await promises.mkdir(path_1.default.dirname(cacheFile), { recursive: true });
|
|
104
104
|
try {
|
|
105
105
|
// Append the path to our host and we can load it like a regular page
|
|
106
|
-
const downloadPromise =
|
|
106
|
+
const downloadPromise = exports.downloadFile(url, cacheFile);
|
|
107
107
|
downloadPromises.set(url, downloadPromise);
|
|
108
108
|
await downloadPromise;
|
|
109
109
|
}
|
|
@@ -122,7 +122,7 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
122
122
|
if (file.startsWith(pageRoot)) {
|
|
123
123
|
// We only want the part after `pages/`
|
|
124
124
|
const cacheFile = path_1.default.join(cacheDir, file.slice(pageRoot.length + 1));
|
|
125
|
-
if (
|
|
125
|
+
if (fs_1.existsSync(cacheFile)) {
|
|
126
126
|
return statsOrig(cacheFile, options);
|
|
127
127
|
}
|
|
128
128
|
}
|
package/lib/templates/ipx.js
CHANGED
|
@@ -5,7 +5,7 @@ exports.handler = void 0;
|
|
|
5
5
|
const ipx_1 = require("@netlify/ipx");
|
|
6
6
|
// @ts-ignore Injected at build time
|
|
7
7
|
const imageconfig_json_1 = require("./imageconfig.json");
|
|
8
|
-
exports.handler =
|
|
8
|
+
exports.handler = ipx_1.createIPXHandler({
|
|
9
9
|
basePath: imageconfig_json_1.basePath,
|
|
10
10
|
domains: imageconfig_json_1.domains,
|
|
11
11
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/plugin-nextjs",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.1",
|
|
4
4
|
"description": "Run Next.js seamlessly on Netlify",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"homepage": "https://github.com/netlify/netlify-plugin-nextjs#readme",
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@netlify/functions": "^0.10.0",
|
|
58
|
-
"@netlify/ipx": "^0.0.
|
|
58
|
+
"@netlify/ipx": "^0.0.8",
|
|
59
59
|
"@vercel/node": "^1.11.2-canary.4",
|
|
60
60
|
"chalk": "^4.1.2",
|
|
61
61
|
"fs-extra": "^10.0.0",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"@babel/core": "^7.15.8",
|
|
76
76
|
"@babel/preset-env": "^7.15.8",
|
|
77
77
|
"@babel/preset-typescript": "^7.16.0",
|
|
78
|
-
"@netlify/build": "^20.0
|
|
79
|
-
"@netlify/eslint-config-node": "^
|
|
78
|
+
"@netlify/build": "^20.1.0",
|
|
79
|
+
"@netlify/eslint-config-node": "^4.0.0",
|
|
80
80
|
"@testing-library/cypress": "^8.0.1",
|
|
81
81
|
"@types/fs-extra": "^9.0.13",
|
|
82
82
|
"@types/jest": "^27.0.2",
|