@netlify/plugin-nextjs 4.0.0-beta.9 → 4.0.0
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/README.md +42 -22
- package/lib/constants.js +19 -10
- package/lib/helpers/cache.js +11 -6
- package/lib/helpers/config.js +20 -77
- package/lib/helpers/files.js +220 -41
- package/lib/helpers/functions.js +36 -30
- package/lib/helpers/redirects.js +130 -0
- package/lib/helpers/utils.js +32 -0
- package/lib/helpers/verification.js +71 -43
- package/lib/index.js +37 -31
- package/lib/templates/getHandler.js +38 -94
- package/lib/templates/getPageResolver.js +20 -11
- package/lib/templates/handlerUtils.js +162 -0
- package/lib/templates/ipx.js +11 -7
- package/package.json +14 -12
package/lib/helpers/functions.js
CHANGED
|
@@ -1,56 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setupImageFunction = exports.generatePagesResolver = exports.generateFunctions = void 0;
|
|
4
|
+
const fs_extra_1 = require("fs-extra");
|
|
5
|
+
const pathe_1 = require("pathe");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
7
|
+
const getHandler_1 = require("../templates/getHandler");
|
|
8
|
+
const getPageResolver_1 = require("../templates/getPageResolver");
|
|
9
|
+
const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC, PUBLISH_DIR }, appDir) => {
|
|
8
10
|
const functionsDir = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
9
11
|
const bridgeFile = require.resolve('@vercel/node/dist/bridge');
|
|
10
|
-
const functionDir = join(process.cwd(), functionsDir, HANDLER_FUNCTION_NAME);
|
|
11
|
-
const publishDir = relative(functionDir, join(process.cwd(), PUBLISH_DIR));
|
|
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));
|
|
12
14
|
const writeHandler = async (func, isODB) => {
|
|
13
|
-
const handlerSource = await getHandler({ isODB, publishDir, appDir: relative(functionDir, appDir) });
|
|
14
|
-
await ensureDir(join(functionsDir, func));
|
|
15
|
-
await writeFile(join(functionsDir, func, `${func}.js`), handlerSource);
|
|
16
|
-
await copyFile(bridgeFile, join(functionsDir, func, 'bridge.js'));
|
|
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'));
|
|
17
20
|
};
|
|
18
|
-
await writeHandler(HANDLER_FUNCTION_NAME, false);
|
|
19
|
-
await writeHandler(ODB_FUNCTION_NAME, true);
|
|
21
|
+
await writeHandler(constants_1.HANDLER_FUNCTION_NAME, false);
|
|
22
|
+
await writeHandler(constants_1.ODB_FUNCTION_NAME, true);
|
|
20
23
|
};
|
|
24
|
+
exports.generateFunctions = generateFunctions;
|
|
21
25
|
/**
|
|
22
26
|
* Writes a file in each function directory that contains references to every page entrypoint.
|
|
23
27
|
* This is just so that the nft bundler knows about them. We'll eventually do this better.
|
|
24
28
|
*/
|
|
25
|
-
|
|
29
|
+
const generatePagesResolver = async ({ constants: { INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC, PUBLISH_DIR }, target, }) => {
|
|
26
30
|
const functionsPath = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
27
|
-
const jsSource = await getPageResolver({
|
|
28
|
-
|
|
31
|
+
const jsSource = await getPageResolver_1.getPageResolver({
|
|
32
|
+
publish: PUBLISH_DIR,
|
|
29
33
|
target,
|
|
30
34
|
});
|
|
31
|
-
await writeFile(join(functionsPath, ODB_FUNCTION_NAME, 'pages.js'), jsSource);
|
|
32
|
-
await writeFile(join(functionsPath, HANDLER_FUNCTION_NAME, 'pages.js'), jsSource);
|
|
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);
|
|
33
37
|
};
|
|
38
|
+
exports.generatePagesResolver = generatePagesResolver;
|
|
34
39
|
// Move our next/image function into the correct functions directory
|
|
35
|
-
|
|
40
|
+
const setupImageFunction = async ({ constants: { INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC }, imageconfig = {}, netlifyConfig, basePath, }) => {
|
|
36
41
|
const functionsPath = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
37
|
-
const functionName = `${IMAGE_FUNCTION_NAME}.js`;
|
|
38
|
-
const functionDirectory = join(functionsPath, IMAGE_FUNCTION_NAME);
|
|
39
|
-
await ensureDir(functionDirectory);
|
|
40
|
-
await writeJSON(join(functionDirectory, 'imageconfig.json'), {
|
|
42
|
+
const functionName = `${constants_1.IMAGE_FUNCTION_NAME}.js`;
|
|
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'), {
|
|
41
46
|
...imageconfig,
|
|
42
|
-
basePath: [basePath, IMAGE_FUNCTION_NAME].join('/'),
|
|
47
|
+
basePath: [basePath, constants_1.IMAGE_FUNCTION_NAME].join('/'),
|
|
43
48
|
});
|
|
44
|
-
await copyFile(join(__dirname, '..', 'templates', 'ipx.js'), join(functionDirectory, functionName));
|
|
49
|
+
await fs_extra_1.copyFile(pathe_1.join(__dirname, '..', '..', 'lib', 'templates', 'ipx.js'), pathe_1.join(functionDirectory, functionName));
|
|
45
50
|
const imagePath = imageconfig.path || '/_next/image';
|
|
46
51
|
netlifyConfig.redirects.push({
|
|
47
52
|
from: `${imagePath}*`,
|
|
48
53
|
query: { url: ':url', w: ':width', q: ':quality' },
|
|
49
|
-
to: `${basePath}/${IMAGE_FUNCTION_NAME}/w_:width,q_:quality/:url`,
|
|
54
|
+
to: `${basePath}/${constants_1.IMAGE_FUNCTION_NAME}/w_:width,q_:quality/:url`,
|
|
50
55
|
status: 301,
|
|
51
56
|
}, {
|
|
52
|
-
from: `${basePath}/${IMAGE_FUNCTION_NAME}/*`,
|
|
53
|
-
to: `/.netlify/builders/${IMAGE_FUNCTION_NAME}`,
|
|
57
|
+
from: `${basePath}/${constants_1.IMAGE_FUNCTION_NAME}/*`,
|
|
58
|
+
to: `/.netlify/builders/${constants_1.IMAGE_FUNCTION_NAME}`,
|
|
54
59
|
status: 200,
|
|
55
60
|
});
|
|
56
61
|
if (basePath) {
|
|
@@ -62,3 +67,4 @@ exports.setupImageFunction = async ({ constants: { INTERNAL_FUNCTIONS_SRC, FUNCT
|
|
|
62
67
|
});
|
|
63
68
|
}
|
|
64
69
|
};
|
|
70
|
+
exports.setupImageFunction = setupImageFunction;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateRedirects = void 0;
|
|
7
|
+
const fs_extra_1 = require("fs-extra");
|
|
8
|
+
const globby_1 = __importDefault(require("globby"));
|
|
9
|
+
const pathe_1 = require("pathe");
|
|
10
|
+
const constants_1 = require("../constants");
|
|
11
|
+
const utils_1 = require("./utils");
|
|
12
|
+
const generateLocaleRedirects = ({ i18n, basePath, trailingSlash, }) => {
|
|
13
|
+
const redirects = [];
|
|
14
|
+
// If the cookie is set, we need to redirect at the origin
|
|
15
|
+
redirects.push({
|
|
16
|
+
from: `${basePath}/`,
|
|
17
|
+
to: constants_1.HANDLER_FUNCTION_PATH,
|
|
18
|
+
status: 200,
|
|
19
|
+
force: true,
|
|
20
|
+
conditions: {
|
|
21
|
+
Cookie: ['NEXT_LOCALE'],
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
i18n.locales.forEach((locale) => {
|
|
25
|
+
if (locale === i18n.defaultLocale) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
redirects.push({
|
|
29
|
+
from: `${basePath}/`,
|
|
30
|
+
to: `${basePath}/${locale}${trailingSlash ? '/' : ''}`,
|
|
31
|
+
status: 301,
|
|
32
|
+
conditions: {
|
|
33
|
+
Language: [locale],
|
|
34
|
+
},
|
|
35
|
+
force: true,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
return redirects;
|
|
39
|
+
};
|
|
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'));
|
|
42
|
+
netlifyConfig.redirects.push(...constants_1.HIDDEN_PATHS.map((path) => ({
|
|
43
|
+
from: `${basePath}${path}`,
|
|
44
|
+
to: '/404.html',
|
|
45
|
+
status: 404,
|
|
46
|
+
force: true,
|
|
47
|
+
})));
|
|
48
|
+
if (i18n && i18n.localeDetection !== false) {
|
|
49
|
+
netlifyConfig.redirects.push(...generateLocaleRedirects({ i18n, basePath, trailingSlash }));
|
|
50
|
+
}
|
|
51
|
+
const dataRedirects = [];
|
|
52
|
+
const pageRedirects = [];
|
|
53
|
+
const isrRedirects = [];
|
|
54
|
+
const dynamicRouteEntries = Object.entries(dynamicRoutes);
|
|
55
|
+
const staticRouteEntries = Object.entries(staticRoutes);
|
|
56
|
+
staticRouteEntries.forEach(([route, { dataRoute, initialRevalidateSeconds }]) => {
|
|
57
|
+
// Only look for revalidate as we need to rewrite these to SSR rather than ODB
|
|
58
|
+
if (initialRevalidateSeconds === false) {
|
|
59
|
+
// These can be ignored, as they're static files handled by the CDN
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if ((i18n === null || i18n === void 0 ? void 0 : i18n.defaultLocale) && route.startsWith(`/${i18n.defaultLocale}/`)) {
|
|
63
|
+
route = route.slice(i18n.defaultLocale.length + 1);
|
|
64
|
+
}
|
|
65
|
+
isrRedirects.push(...utils_1.netlifyRoutesForNextRoute(dataRoute), ...utils_1.netlifyRoutesForNextRoute(route));
|
|
66
|
+
});
|
|
67
|
+
dynamicRouteEntries.forEach(([route, { dataRoute, fallback }]) => {
|
|
68
|
+
// Add redirects if fallback is "null" (aka blocking) or true/a string
|
|
69
|
+
if (fallback === false) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
pageRedirects.push(...utils_1.netlifyRoutesForNextRoute(route));
|
|
73
|
+
dataRedirects.push(...utils_1.netlifyRoutesForNextRoute(dataRoute));
|
|
74
|
+
});
|
|
75
|
+
if (i18n) {
|
|
76
|
+
netlifyConfig.redirects.push({ from: `${basePath}/:locale/_next/static/*`, to: `/static/:splat`, status: 200 });
|
|
77
|
+
}
|
|
78
|
+
const publicFiles = await globby_1.default('**/*', { cwd: pathe_1.join(appDir, 'public') });
|
|
79
|
+
// This is only used in prod, so dev uses `next dev` directly
|
|
80
|
+
netlifyConfig.redirects.push(
|
|
81
|
+
// Static files are in `static`
|
|
82
|
+
{ from: `${basePath}/_next/static/*`, to: `/static/:splat`, status: 200 },
|
|
83
|
+
// API routes always need to be served from the regular function
|
|
84
|
+
{
|
|
85
|
+
from: `${basePath}/api`,
|
|
86
|
+
to: constants_1.HANDLER_FUNCTION_PATH,
|
|
87
|
+
status: 200,
|
|
88
|
+
}, {
|
|
89
|
+
from: `${basePath}/api/*`,
|
|
90
|
+
to: constants_1.HANDLER_FUNCTION_PATH,
|
|
91
|
+
status: 200,
|
|
92
|
+
},
|
|
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
|
+
})), {
|
|
101
|
+
from: `${basePath}/*`,
|
|
102
|
+
to: constants_1.HANDLER_FUNCTION_PATH,
|
|
103
|
+
status: 200,
|
|
104
|
+
conditions: { Cookie: ['__prerender_bypass', '__next_preview_data'] },
|
|
105
|
+
force: true,
|
|
106
|
+
},
|
|
107
|
+
// ISR redirects are handled by the regular function. Forced to avoid pre-rendered pages
|
|
108
|
+
...isrRedirects.map((redirect) => ({
|
|
109
|
+
from: `${basePath}${redirect}`,
|
|
110
|
+
to: constants_1.ODB_FUNCTION_PATH,
|
|
111
|
+
status: 200,
|
|
112
|
+
force: true,
|
|
113
|
+
})),
|
|
114
|
+
// These are pages with fallback set, which need an ODB
|
|
115
|
+
// Data redirects go first, to avoid conflict with splat redirects
|
|
116
|
+
...dataRedirects.map((redirect) => ({
|
|
117
|
+
from: `${basePath}${redirect}`,
|
|
118
|
+
to: constants_1.ODB_FUNCTION_PATH,
|
|
119
|
+
status: 200,
|
|
120
|
+
})),
|
|
121
|
+
// ...then all the other fallback pages
|
|
122
|
+
...pageRedirects.map((redirect) => ({
|
|
123
|
+
from: `${basePath}${redirect}`,
|
|
124
|
+
to: constants_1.ODB_FUNCTION_PATH,
|
|
125
|
+
status: 200,
|
|
126
|
+
})),
|
|
127
|
+
// Everything else is handled by the regular function
|
|
128
|
+
{ from: `${basePath}/*`, to: constants_1.HANDLER_FUNCTION_PATH, status: 200 });
|
|
129
|
+
};
|
|
130
|
+
exports.generateRedirects = generateRedirects;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.netlifyRoutesForNextRoute = void 0;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const netlifyRoutesForNextRoute = (nextRoute) => {
|
|
6
|
+
const netlifyRoutes = [nextRoute];
|
|
7
|
+
// If the route is an optional catch-all route, we need to add a second
|
|
8
|
+
// Netlify route for the base path (when no parameters are present).
|
|
9
|
+
// The file ending must be present!
|
|
10
|
+
if (constants_1.OPTIONAL_CATCH_ALL_REGEX.test(nextRoute)) {
|
|
11
|
+
let netlifyRoute = nextRoute.replace(constants_1.OPTIONAL_CATCH_ALL_REGEX, '$2');
|
|
12
|
+
// create an empty string, but actually needs to be a forward slash
|
|
13
|
+
if (netlifyRoute === '') {
|
|
14
|
+
netlifyRoute = '/';
|
|
15
|
+
}
|
|
16
|
+
// When optional catch-all route is at top-level, the regex on line 19 will
|
|
17
|
+
// create an incorrect route for the data route. For example, it creates
|
|
18
|
+
// /_next/data/%BUILDID%.json, but NextJS looks for
|
|
19
|
+
// /_next/data/%BUILDID%/index.json
|
|
20
|
+
netlifyRoute = netlifyRoute.replace(/(\/_next\/data\/[^/]+).json/, '$1/index.json');
|
|
21
|
+
// Add second route to the front of the array
|
|
22
|
+
netlifyRoutes.unshift(netlifyRoute);
|
|
23
|
+
}
|
|
24
|
+
return netlifyRoutes.map((route) => route
|
|
25
|
+
// Replace catch-all, e.g., [...slug]
|
|
26
|
+
.replace(constants_1.CATCH_ALL_REGEX, '/:$1/*')
|
|
27
|
+
// Replace optional catch-all, e.g., [[...slug]]
|
|
28
|
+
.replace(constants_1.OPTIONAL_CATCH_ALL_REGEX, '/*')
|
|
29
|
+
// Replace dynamic parameters, e.g., [id]
|
|
30
|
+
.replace(constants_1.DYNAMIC_PARAMETER_REGEX, '/:$1'));
|
|
31
|
+
};
|
|
32
|
+
exports.netlifyRoutesForNextRoute = netlifyRoutesForNextRoute;
|
|
@@ -1,93 +1,121 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.checkZipSize = exports.checkForRootPublish = exports.checkNextSiteHasBuilt = exports.checkForOldFunctions = exports.verifyNetlifyBuildVersion = void 0;
|
|
26
|
+
const fs_1 = require("fs");
|
|
27
|
+
const path_1 = __importStar(require("path"));
|
|
28
|
+
const chalk_1 = require("chalk");
|
|
29
|
+
const node_stream_zip_1 = require("node-stream-zip");
|
|
30
|
+
const outdent_1 = require("outdent");
|
|
31
|
+
const pretty_bytes_1 = __importDefault(require("pretty-bytes"));
|
|
32
|
+
const semver_1 = require("semver");
|
|
33
|
+
const constants_1 = require("../constants");
|
|
9
34
|
// This is when nft support was added
|
|
10
35
|
const REQUIRED_BUILD_VERSION = '>=18.16.0';
|
|
11
|
-
|
|
36
|
+
const verifyNetlifyBuildVersion = ({ IS_LOCAL, NETLIFY_BUILD_VERSION, failBuild, }) => {
|
|
12
37
|
// We check for build version because that's what's available to us, but prompt about the cli because that's what they can upgrade
|
|
13
|
-
if (IS_LOCAL && !satisfies(NETLIFY_BUILD_VERSION, REQUIRED_BUILD_VERSION, { includePrerelease: true })) {
|
|
14
|
-
return failBuild(outdent `
|
|
38
|
+
if (IS_LOCAL && !semver_1.satisfies(NETLIFY_BUILD_VERSION, REQUIRED_BUILD_VERSION, { includePrerelease: true })) {
|
|
39
|
+
return failBuild(outdent_1.outdent `
|
|
15
40
|
This version of the Essential Next.js plugin requires netlify-cli@6.12.4 or higher. Please upgrade and try again.
|
|
16
41
|
You can do this by running: "npm install -g netlify-cli@latest" or "yarn global add netlify-cli@latest"
|
|
17
42
|
`);
|
|
18
43
|
}
|
|
19
44
|
};
|
|
20
|
-
exports.
|
|
21
|
-
|
|
45
|
+
exports.verifyNetlifyBuildVersion = verifyNetlifyBuildVersion;
|
|
46
|
+
const checkForOldFunctions = async ({ functions }) => {
|
|
47
|
+
const allOldFunctions = await functions.list();
|
|
48
|
+
const oldFunctions = allOldFunctions.filter(({ name }) => name.startsWith('next_'));
|
|
22
49
|
if (oldFunctions.length !== 0) {
|
|
23
|
-
console.log(yellowBright(outdent `
|
|
50
|
+
console.log(chalk_1.yellowBright(outdent_1.outdent `
|
|
24
51
|
We have found the following functions in your site that seem to be left over from the old Next.js plugin (v3). We have guessed this because the name starts with "next_".
|
|
25
52
|
|
|
26
|
-
${reset(oldFunctions.map(({ name }) => `- ${name}`).join('\n'))}
|
|
53
|
+
${chalk_1.reset(oldFunctions.map(({ name }) => `- ${name}`).join('\n'))}
|
|
27
54
|
|
|
28
55
|
If they were created by the old plugin, these functions are likely to cause errors so should be removed. You can do this by deleting the following directories:
|
|
29
56
|
|
|
30
|
-
${reset(oldFunctions.map(({ mainFile }) => `- ${
|
|
57
|
+
${chalk_1.reset(oldFunctions.map(({ mainFile }) => `- ${path_1.default.relative(process.cwd(), path_1.default.dirname(mainFile))}`).join('\n'))}
|
|
31
58
|
`));
|
|
32
59
|
}
|
|
33
60
|
};
|
|
34
|
-
exports.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
exports.checkForOldFunctions = checkForOldFunctions;
|
|
62
|
+
const checkNextSiteHasBuilt = ({ publish, failBuild, }) => {
|
|
63
|
+
if (!fs_1.existsSync(path_1.default.join(publish, 'BUILD_ID'))) {
|
|
64
|
+
const outWarning = path_1.default.basename(publish) === 'out'
|
|
65
|
+
? `Your publish directory is set to "out", but in most cases it should be ".next".`
|
|
66
|
+
: `In most cases it should be set to ".next", unless you have chosen a custom "distDir" in your Next config.`;
|
|
67
|
+
return failBuild(outdent_1.outdent `
|
|
68
|
+
The directory "${path_1.default.relative(process.cwd(), publish)}" does not contain a Next.js production build. Perhaps the build command was not run, or you specified the wrong publish directory.
|
|
69
|
+
${outWarning}
|
|
39
70
|
If you are using "next export" then the Essential Next.js plugin should be removed. See https://ntl.fyi/remove-plugin for details.
|
|
40
71
|
`);
|
|
41
72
|
}
|
|
42
|
-
if (existsSync(
|
|
43
|
-
failBuild(outdent `
|
|
73
|
+
if (fs_1.existsSync(path_1.default.join(publish, 'export-detail.json'))) {
|
|
74
|
+
failBuild(outdent_1.outdent `
|
|
44
75
|
Detected that "next export" was run, but site is incorrectly publishing the ".next" directory.
|
|
45
76
|
This plugin is not needed for "next export" so should be removed, and publish directory set to "out".
|
|
46
77
|
See https://ntl.fyi/remove-plugin for more details on how to remove this plugin.
|
|
47
78
|
`);
|
|
48
79
|
}
|
|
49
80
|
};
|
|
50
|
-
exports.
|
|
51
|
-
|
|
52
|
-
|
|
81
|
+
exports.checkNextSiteHasBuilt = checkNextSiteHasBuilt;
|
|
82
|
+
const checkForRootPublish = ({ publish, failBuild, }) => {
|
|
83
|
+
if (path_1.default.resolve(publish) === path_1.default.resolve('.')) {
|
|
84
|
+
failBuild(outdent_1.outdent `
|
|
53
85
|
Your publish directory is pointing to the base directory of your site. This is not supported for Next.js sites, and is probably a mistake.
|
|
54
86
|
In most cases it should be set to ".next", unless you have chosen a custom "distDir" in your Next config, or the Next site is in a subdirectory.
|
|
55
87
|
`);
|
|
56
88
|
}
|
|
57
89
|
};
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
if (!existsSync(file)) {
|
|
90
|
+
exports.checkForRootPublish = checkForRootPublish;
|
|
91
|
+
const checkZipSize = async (file, maxSize = constants_1.LAMBDA_MAX_SIZE) => {
|
|
92
|
+
if (!fs_1.existsSync(file)) {
|
|
62
93
|
console.warn(`Could not check zip size because ${file} does not exist`);
|
|
63
94
|
return;
|
|
64
95
|
}
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
96
|
+
const fileSize = await fs_1.promises.stat(file).then(({ size }) => size);
|
|
97
|
+
if (fileSize < maxSize) {
|
|
67
98
|
return;
|
|
68
99
|
}
|
|
69
100
|
// We don't fail the build, because the actual hard max size is larger so it might still succeed
|
|
70
|
-
console.log(redBright(outdent `
|
|
71
|
-
The function zip ${yellowBright(relative(process.cwd(), file))} size is ${
|
|
101
|
+
console.log(chalk_1.redBright(outdent_1.outdent `
|
|
102
|
+
The function zip ${chalk_1.yellowBright(path_1.relative(process.cwd(), file))} size is ${pretty_bytes_1.default(fileSize)}, which is larger than the maximum supported size of ${pretty_bytes_1.default(maxSize)}.
|
|
72
103
|
There are a few reasons this could happen. You may have accidentally bundled a large dependency, or you might have a
|
|
73
104
|
large number of pre-rendered pages included.
|
|
74
105
|
`));
|
|
75
|
-
const zip = new
|
|
106
|
+
const zip = new node_stream_zip_1.async({ file });
|
|
76
107
|
console.log(`Contains ${await zip.entriesCount} files`);
|
|
77
108
|
const sortedFiles = Object.values(await zip.entries()).sort((a, b) => b.size - a.size);
|
|
78
109
|
const largest = {};
|
|
79
110
|
for (let i = 0; i < 10 && i < sortedFiles.length; i++) {
|
|
80
111
|
largest[`${i + 1}`] = {
|
|
81
112
|
File: sortedFiles[i].name,
|
|
82
|
-
'Compressed Size':
|
|
83
|
-
'Uncompressed Size':
|
|
113
|
+
'Compressed Size': pretty_bytes_1.default(sortedFiles[i].compressedSize),
|
|
114
|
+
'Uncompressed Size': pretty_bytes_1.default(sortedFiles[i].size),
|
|
84
115
|
};
|
|
85
116
|
}
|
|
86
|
-
console.log(yellowBright `\n\nThese are the largest files in the zip:`);
|
|
117
|
+
console.log(chalk_1.yellowBright `\n\nThese are the largest files in the zip:`);
|
|
87
118
|
console.table(largest);
|
|
88
|
-
console.log(greenBright `\n\nFor more information on fixing this, see ${blueBright `https://ntl.fyi/large-next-functions`}`);
|
|
119
|
+
console.log(chalk_1.greenBright `\n\nFor more information on fixing this, see ${chalk_1.blueBright `https://ntl.fyi/large-next-functions`}`);
|
|
89
120
|
};
|
|
90
|
-
exports.
|
|
91
|
-
Thank you for trying the Essential Next.js beta plugin.
|
|
92
|
-
Please share feedback (both good and bad) at ${blueBright `https://ntl.fyi/next-beta-feedback`}
|
|
93
|
-
`));
|
|
121
|
+
exports.checkZipSize = checkZipSize;
|
package/lib/index.js
CHANGED
|
@@ -1,50 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const path_1 = require("path");
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const cache_1 = require("./helpers/cache");
|
|
6
|
+
const config_1 = require("./helpers/config");
|
|
7
|
+
const files_1 = require("./helpers/files");
|
|
8
|
+
const functions_1 = require("./helpers/functions");
|
|
9
|
+
const redirects_1 = require("./helpers/redirects");
|
|
10
|
+
const verification_1 = require("./helpers/verification");
|
|
11
|
+
const plugin = {
|
|
10
12
|
async onPreBuild({ constants, netlifyConfig, utils: { build: { failBuild }, cache, }, }) {
|
|
11
13
|
var _a;
|
|
12
|
-
logBetaMessage();
|
|
13
14
|
const { publish } = netlifyConfig.build;
|
|
14
|
-
checkForRootPublish({ publish, failBuild });
|
|
15
|
-
verifyNetlifyBuildVersion({ failBuild, ...constants });
|
|
16
|
-
await restoreCache({ cache, publish });
|
|
15
|
+
verification_1.checkForRootPublish({ publish, failBuild });
|
|
16
|
+
verification_1.verifyNetlifyBuildVersion({ failBuild, ...constants });
|
|
17
|
+
await cache_1.restoreCache({ cache, publish });
|
|
17
18
|
(_a = netlifyConfig.build).environment || (_a.environment = {});
|
|
18
19
|
// eslint-disable-next-line unicorn/consistent-destructuring
|
|
19
20
|
netlifyConfig.build.environment.NEXT_PRIVATE_TARGET = 'server';
|
|
20
21
|
},
|
|
21
22
|
async onBuild({ constants, netlifyConfig, utils: { build: { failBuild }, }, }) {
|
|
22
23
|
const { publish } = netlifyConfig.build;
|
|
23
|
-
checkNextSiteHasBuilt({ publish, failBuild });
|
|
24
|
-
const { appDir, basePath, i18n, images, target, ignore } = await getNextConfig({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
verification_1.checkNextSiteHasBuilt({ publish, failBuild });
|
|
25
|
+
const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir } = await config_1.getNextConfig({
|
|
26
|
+
publish,
|
|
27
|
+
failBuild,
|
|
28
|
+
});
|
|
29
|
+
config_1.configureHandlerFunctions({ netlifyConfig, ignore, publish: path_1.relative(process.cwd(), publish) });
|
|
30
|
+
await functions_1.generateFunctions(constants, appDir);
|
|
31
|
+
await functions_1.generatePagesResolver({ target, constants });
|
|
32
|
+
await files_1.movePublicFiles({ appDir, outdir, publish });
|
|
33
|
+
if (process.env.EXPERIMENTAL_ODB_TTL) {
|
|
34
|
+
await files_1.patchNextFiles(basePath);
|
|
35
|
+
}
|
|
29
36
|
if (process.env.EXPERIMENTAL_MOVE_STATIC_PAGES) {
|
|
30
37
|
console.log("The flag 'EXPERIMENTAL_MOVE_STATIC_PAGES' is no longer required, as it is now the default. To disable this behavior, set the env var 'SERVE_STATIC_FILES_FROM_ORIGIN' to 'true'");
|
|
31
38
|
}
|
|
32
39
|
if (!process.env.SERVE_STATIC_FILES_FROM_ORIGIN) {
|
|
33
|
-
await moveStaticPages({ target,
|
|
40
|
+
await files_1.moveStaticPages({ target, netlifyConfig, i18n });
|
|
34
41
|
}
|
|
35
|
-
await setupImageFunction({ constants, imageconfig: images, netlifyConfig, basePath });
|
|
36
|
-
await generateRedirects({
|
|
42
|
+
await functions_1.setupImageFunction({ constants, imageconfig: images, netlifyConfig, basePath });
|
|
43
|
+
await redirects_1.generateRedirects({
|
|
37
44
|
netlifyConfig,
|
|
38
|
-
basePath,
|
|
39
|
-
i18n,
|
|
45
|
+
nextConfig: { basePath, i18n, trailingSlash, appDir },
|
|
40
46
|
});
|
|
41
47
|
},
|
|
42
|
-
async onPostBuild({ netlifyConfig, utils: { cache, functions }, constants: { FUNCTIONS_DIST } }) {
|
|
43
|
-
await saveCache({ cache, publish: netlifyConfig.build.publish });
|
|
44
|
-
await checkForOldFunctions({ functions });
|
|
45
|
-
await checkZipSize(join(FUNCTIONS_DIST, `${ODB_FUNCTION_NAME}.zip`));
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
logBetaMessage();
|
|
48
|
+
async onPostBuild({ netlifyConfig, utils: { cache, functions, build: { failBuild }, }, constants: { FUNCTIONS_DIST }, }) {
|
|
49
|
+
await cache_1.saveCache({ cache, publish: netlifyConfig.build.publish });
|
|
50
|
+
await verification_1.checkForOldFunctions({ functions });
|
|
51
|
+
await verification_1.checkZipSize(path_1.join(FUNCTIONS_DIST, `${constants_1.ODB_FUNCTION_NAME}.zip`));
|
|
52
|
+
const { basePath } = await config_1.getNextConfig({ publish: netlifyConfig.build.publish, failBuild });
|
|
53
|
+
await files_1.unpatchNextFiles(basePath);
|
|
49
54
|
},
|
|
50
55
|
};
|
|
56
|
+
module.exports = plugin;
|