@netlify/plugin-nextjs 4.0.0-beta.2 → 4.0.0-beta.6
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 +10 -1
- package/lib/helpers/config.js +23 -8
- package/lib/helpers/files.js +51 -0
- package/lib/helpers/functions.js +1 -1
- package/lib/helpers/verification.js +40 -8
- package/lib/index.js +13 -8
- package/lib/templates/getHandler.js +75 -5
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
</a>
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
|
+
## What's new in this version
|
|
18
|
+
|
|
19
|
+
Version 4 is a complete rewrite of the Essential Next.js plugin. For full details of everything that's new, see [the v4 release notes](https://github.com/netlify/netlify-plugin-nextjs/blob/main/docs/release-notes/v4.md)
|
|
17
20
|
|
|
18
21
|
## Installing the beta
|
|
19
22
|
|
|
@@ -31,7 +34,13 @@ publish = ".next"
|
|
|
31
34
|
package = "@netlify/plugin-nextjs"
|
|
32
35
|
```
|
|
33
36
|
|
|
34
|
-
If you previously set `target: "serverless"` in your `next.config.js`
|
|
37
|
+
If you previously set `target: "serverless"` or a custom `distDir` in your `next.config.js`, or set `node_bundler` or `external_node_modules` in your `netlify.toml` these are no longer needed and can be removed.
|
|
38
|
+
|
|
39
|
+
The `serverless` and `experimental-serverless-trace` targets are deprecated in Next 12, and all builds with this plugin will now use the default `server` target.
|
|
40
|
+
|
|
41
|
+
If you are using a monorepo you will need to change `publish` to point to the full path to the built `.next` directory, which may be in a subdirectory. If you have changed your `distDir` then it will need to match that.
|
|
42
|
+
|
|
43
|
+
If you are using Nx, then you will need to point `publish` to the folder inside `dist`, e.g. `dist/apps/myapp/.next`.
|
|
35
44
|
|
|
36
45
|
## Beta feedback
|
|
37
46
|
|
package/lib/helpers/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
const { readJSON } = require('fs-extra');
|
|
2
|
+
const { readJSON, existsSync } = require('fs-extra');
|
|
3
3
|
const { join, dirname, relative } = require('pathe');
|
|
4
|
+
const slash = require('slash');
|
|
4
5
|
const defaultFailBuild = (message, { error }) => {
|
|
5
6
|
throw new Error(`${message}\n${error && error.stack}`);
|
|
6
7
|
};
|
|
@@ -58,12 +59,23 @@ exports.generateRedirects = async ({ netlifyConfig, basePath, i18n }) => {
|
|
|
58
59
|
if (i18n) {
|
|
59
60
|
netlifyConfig.redirects.push({ from: `${basePath}/:locale/_next/static/*`, to: `/static/:splat`, status: 200 });
|
|
60
61
|
}
|
|
62
|
+
const staticManifest = join(netlifyConfig.build.publish, 'static-manifest.json');
|
|
63
|
+
if (process.env.EXPERIMENTAL_MOVE_STATIC_PAGES && existsSync(staticManifest)) {
|
|
64
|
+
// Static page files need to have a forced redirect for preview mode. Otherwise it's non-forced
|
|
65
|
+
const staticFiles = await readJSON(staticManifest);
|
|
66
|
+
netlifyConfig.redirects.push(...staticFiles.map((file) => ({
|
|
67
|
+
from: `${basePath}/${file}`,
|
|
68
|
+
to: HANDLER_FUNCTION_PATH,
|
|
69
|
+
status: 200,
|
|
70
|
+
force: true,
|
|
71
|
+
conditions: { Cookie: ['__prerender_bypass', '__next_preview_data'] },
|
|
72
|
+
})));
|
|
73
|
+
}
|
|
61
74
|
// This is only used in prod, so dev uses `next dev` directly
|
|
62
75
|
netlifyConfig.redirects.push({ from: `${basePath}/_next/static/*`, to: `/static/:splat`, status: 200 }, {
|
|
63
76
|
from: `${basePath}/*`,
|
|
64
77
|
to: HANDLER_FUNCTION_PATH,
|
|
65
78
|
status: 200,
|
|
66
|
-
force: true,
|
|
67
79
|
conditions: { Cookie: ['__prerender_bypass', '__next_preview_data'] },
|
|
68
80
|
}, ...redirects.map((redirect) => ({
|
|
69
81
|
from: `${basePath}${redirect}`,
|
|
@@ -91,24 +103,27 @@ const resolveModuleRoot = (moduleName) => {
|
|
|
91
103
|
return null;
|
|
92
104
|
}
|
|
93
105
|
};
|
|
106
|
+
const DEFAULT_EXCLUDED_MODULES = ['sharp', 'electron'];
|
|
94
107
|
exports.configureHandlerFunctions = ({ netlifyConfig, publish, ignore = [] }) => {
|
|
95
108
|
var _a;
|
|
96
109
|
/* eslint-disable no-underscore-dangle */
|
|
97
110
|
(_a = netlifyConfig.functions)._ipx || (_a._ipx = {});
|
|
98
|
-
netlifyConfig.functions._ipx.node_bundler = '
|
|
111
|
+
netlifyConfig.functions._ipx.node_bundler = 'nft';
|
|
99
112
|
[HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME].forEach((functionName) => {
|
|
100
113
|
var _a, _b;
|
|
101
114
|
(_a = netlifyConfig.functions)[functionName] || (_a[functionName] = { included_files: [], external_node_modules: [] });
|
|
102
115
|
netlifyConfig.functions[functionName].node_bundler = 'nft';
|
|
103
116
|
(_b = netlifyConfig.functions[functionName]).included_files || (_b.included_files = []);
|
|
104
|
-
netlifyConfig.functions[functionName].included_files.push(`${publish}/server/**`, `${publish}/serverless/**`, `${publish}/*.json`, `${publish}/BUILD_ID`, ...ignore.map((path) => `!${path}`));
|
|
117
|
+
netlifyConfig.functions[functionName].included_files.push(`${publish}/server/**`, `${publish}/serverless/**`, `${publish}/*.json`, `${publish}/BUILD_ID`, `${publish}/static/chunks/webpack-middleware*.js`, `!${publish}/server/**/*.js.nft.json`, ...ignore.map((path) => `!${slash(path)}`));
|
|
105
118
|
const nextRoot = resolveModuleRoot('next');
|
|
106
119
|
if (nextRoot) {
|
|
107
120
|
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`);
|
|
108
121
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
122
|
+
DEFAULT_EXCLUDED_MODULES.forEach((moduleName) => {
|
|
123
|
+
const moduleRoot = resolveModuleRoot(moduleName);
|
|
124
|
+
if (moduleRoot) {
|
|
125
|
+
netlifyConfig.functions[functionName].included_files.push(`!${moduleRoot}/**/*`);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
113
128
|
});
|
|
114
129
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const { cpus } = require('os');
|
|
3
|
+
const { existsSync, readJson, move, cpSync, copy, writeJson } = require('fs-extra');
|
|
4
|
+
const globby = require('globby');
|
|
5
|
+
const pLimit = require('p-limit');
|
|
6
|
+
const { join } = require('pathe');
|
|
7
|
+
const slash = require('slash');
|
|
8
|
+
const TEST_ROUTE = /(|\/)\[[^/]+?](\/|\.html|$)/;
|
|
9
|
+
const isDynamicRoute = (route) => TEST_ROUTE.test(route);
|
|
10
|
+
exports.moveStaticPages = async ({ netlifyConfig, target, i18n, failBuild }) => {
|
|
11
|
+
console.log('Moving static page files to serve from CDN...');
|
|
12
|
+
const root = join(netlifyConfig.build.publish, target === 'server' ? 'server' : 'serverless', 'pages');
|
|
13
|
+
const files = [];
|
|
14
|
+
const moveFile = async (file) => {
|
|
15
|
+
const source = join(root, file);
|
|
16
|
+
files.push(file);
|
|
17
|
+
const dest = join(netlifyConfig.build.publish, file);
|
|
18
|
+
await move(source, dest);
|
|
19
|
+
};
|
|
20
|
+
// Move all static files, except error documents and nft manifests
|
|
21
|
+
const pages = await globby(['**/*.{html,json}', '!**/(500|404|*.js.nft).{html,json}'], {
|
|
22
|
+
cwd: root,
|
|
23
|
+
dot: true,
|
|
24
|
+
});
|
|
25
|
+
// Limit concurrent file moves to number of cpus or 2 if there is only 1
|
|
26
|
+
const limit = pLimit(Math.max(2, cpus().length));
|
|
27
|
+
const promises = pages.map(async (rawPath) => {
|
|
28
|
+
const filePath = slash(rawPath);
|
|
29
|
+
if (isDynamicRoute(filePath)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
return limit(moveFile, filePath);
|
|
33
|
+
});
|
|
34
|
+
await Promise.all(promises);
|
|
35
|
+
console.log(`Moved ${files.length} files`);
|
|
36
|
+
// Write the manifest for use in the serverless functions
|
|
37
|
+
await writeJson(join(netlifyConfig.build.publish, 'static-manifest.json'), files);
|
|
38
|
+
if (i18n === null || i18n === void 0 ? void 0 : i18n.defaultLocale) {
|
|
39
|
+
// Copy the default locale into the root
|
|
40
|
+
const defaultLocaleDir = join(netlifyConfig.build.publish, i18n.defaultLocale);
|
|
41
|
+
if (existsSync(defaultLocaleDir)) {
|
|
42
|
+
await copy(defaultLocaleDir, `${netlifyConfig.build.publish}/`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
exports.movePublicFiles = async ({ appDir, publish }) => {
|
|
47
|
+
const publicDir = join(appDir, 'public');
|
|
48
|
+
if (existsSync(publicDir)) {
|
|
49
|
+
await copy(publicDir, `${publish}/`);
|
|
50
|
+
}
|
|
51
|
+
};
|
package/lib/helpers/functions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { join, relative } = require('path');
|
|
2
1
|
const { copyFile, ensureDir, writeFile, writeJSON } = require('fs-extra');
|
|
2
|
+
const { join, relative } = require('pathe');
|
|
3
3
|
const { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, IMAGE_FUNCTION_NAME } = require('../constants');
|
|
4
4
|
const getHandler = require('../templates/getHandler');
|
|
5
5
|
const { getPageResolver } = require('../templates/getPageResolver');
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
+
const { existsSync, promises } = require('fs');
|
|
1
2
|
const path = require('path');
|
|
2
|
-
const {
|
|
3
|
-
const {
|
|
3
|
+
const { relative } = require('path');
|
|
4
|
+
const { yellowBright, greenBright, blueBright, redBright } = require('chalk');
|
|
5
|
+
const { async: StreamZip } = require('node-stream-zip');
|
|
4
6
|
const outdent = require('outdent');
|
|
7
|
+
const prettyBytes = require('pretty-bytes');
|
|
5
8
|
const { satisfies } = require('semver');
|
|
6
|
-
exports.verifyBuildTarget = (target) => {
|
|
7
|
-
if (target !== 'server') {
|
|
8
|
-
console.log(yellowBright `Setting target to ${target} is no longer required. You should check if target=server works for you.`);
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
9
|
// This is when nft support was added
|
|
12
10
|
const REQUIRED_BUILD_VERSION = '>=18.16.0';
|
|
13
11
|
exports.verifyNetlifyBuildVersion = ({ IS_LOCAL, NETLIFY_BUILD_VERSION, failBuild }) => {
|
|
@@ -41,7 +39,41 @@ exports.checkForRootPublish = ({ publish, failBuild }) => {
|
|
|
41
39
|
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.`);
|
|
42
40
|
}
|
|
43
41
|
};
|
|
42
|
+
// 50MB, which is the documented max, though the hard max seems to be higher
|
|
43
|
+
const LAMBDA_MAX_SIZE = 1024 * 1024 * 50;
|
|
44
|
+
exports.checkZipSize = async (file, maxSize = LAMBDA_MAX_SIZE) => {
|
|
45
|
+
if (!existsSync(file)) {
|
|
46
|
+
console.warn(`Could not check zip size because ${file} does not exist`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const size = await promises.stat(file).then(({ size }) => size);
|
|
50
|
+
if (size < maxSize) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// We don't fail the build, because the actual hard max size is larger so it might still succeed
|
|
54
|
+
console.log(redBright(outdent `
|
|
55
|
+
|
|
56
|
+
The function zip ${yellowBright(relative(process.cwd(), file))} size is ${prettyBytes(size)}, which is larger than the maximum supported size of ${prettyBytes(maxSize)}.
|
|
57
|
+
There are a few reasons this could happen. You may have accidentally bundled a large dependency, or you might have a
|
|
58
|
+
large number of pre-rendered pages included.
|
|
59
|
+
|
|
60
|
+
`));
|
|
61
|
+
const zip = new StreamZip({ file });
|
|
62
|
+
console.log(`Contains ${await zip.entriesCount} files`);
|
|
63
|
+
const sortedFiles = Object.values(await zip.entries()).sort((a, b) => b.size - a.size);
|
|
64
|
+
const largest = {};
|
|
65
|
+
for (let i = 0; i < 10 && i < sortedFiles.length; i++) {
|
|
66
|
+
largest[`${i + 1}`] = {
|
|
67
|
+
File: sortedFiles[i].name,
|
|
68
|
+
'Compressed Size': prettyBytes(sortedFiles[i].compressedSize),
|
|
69
|
+
'Uncompressed Size': prettyBytes(sortedFiles[i].size),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
console.log(yellowBright `\n\nThese are the largest files in the zip:`);
|
|
73
|
+
console.table(largest);
|
|
74
|
+
console.log(greenBright `\n\nFor more information on fixing this, see ${blueBright `https://ntl.fyi/large-next-functions`}`);
|
|
75
|
+
};
|
|
44
76
|
exports.logBetaMessage = () => console.log(greenBright(outdent `
|
|
45
77
|
Thank you for trying the Essential Next.js beta plugin.
|
|
46
|
-
Please share feedback at ${blueBright `https://ntl.fyi/next-beta-feedback`}
|
|
78
|
+
Please share feedback (both good and bad) at ${blueBright `https://ntl.fyi/next-beta-feedback`}
|
|
47
79
|
`));
|
package/lib/index.js
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
const { join, relative } = require('path');
|
|
3
|
-
const {
|
|
3
|
+
const { ODB_FUNCTION_NAME } = require('./constants');
|
|
4
4
|
const { restoreCache, saveCache } = require('./helpers/cache');
|
|
5
5
|
const { getNextConfig, configureHandlerFunctions, generateRedirects } = require('./helpers/config');
|
|
6
|
+
const { moveStaticPages, movePublicFiles } = require('./helpers/files');
|
|
6
7
|
const { generateFunctions, setupImageFunction, generatePagesResolver } = require('./helpers/functions');
|
|
7
|
-
const { verifyNetlifyBuildVersion, checkNextSiteHasBuilt,
|
|
8
|
+
const { verifyNetlifyBuildVersion, checkNextSiteHasBuilt, checkForRootPublish, logBetaMessage, checkZipSize, } = require('./helpers/verification');
|
|
8
9
|
module.exports = {
|
|
9
10
|
async onPreBuild({ constants, netlifyConfig, utils: { build: { failBuild }, cache, }, }) {
|
|
11
|
+
var _a;
|
|
10
12
|
logBetaMessage();
|
|
11
13
|
const { publish } = netlifyConfig.build;
|
|
12
14
|
checkForRootPublish({ publish, failBuild });
|
|
13
15
|
verifyNetlifyBuildVersion({ failBuild, ...constants });
|
|
14
16
|
await restoreCache({ cache, publish });
|
|
17
|
+
(_a = netlifyConfig.build).environment || (_a.environment = {});
|
|
18
|
+
// eslint-disable-next-line unicorn/consistent-destructuring
|
|
19
|
+
netlifyConfig.build.environment.NEXT_PRIVATE_TARGET = 'server';
|
|
15
20
|
},
|
|
16
21
|
async onBuild({ constants, netlifyConfig, utils: { build: { failBuild }, }, }) {
|
|
17
22
|
const { publish } = netlifyConfig.build;
|
|
18
23
|
checkNextSiteHasBuilt({ publish, failBuild });
|
|
19
24
|
const { appDir, basePath, i18n, images, target, ignore } = await getNextConfig({ publish, failBuild });
|
|
20
|
-
verifyBuildTarget(target);
|
|
21
25
|
configureHandlerFunctions({ netlifyConfig, ignore, publish: relative(process.cwd(), publish) });
|
|
22
26
|
await generateFunctions(constants, appDir);
|
|
23
27
|
await generatePagesResolver({ netlifyConfig, target, constants });
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
await
|
|
28
|
+
await movePublicFiles({ appDir, publish });
|
|
29
|
+
if (process.env.EXPERIMENTAL_MOVE_STATIC_PAGES) {
|
|
30
|
+
await moveStaticPages({ target, failBuild, netlifyConfig, i18n });
|
|
27
31
|
}
|
|
28
32
|
await setupImageFunction({ constants, imageconfig: images, netlifyConfig, basePath });
|
|
29
33
|
await generateRedirects({
|
|
@@ -32,8 +36,9 @@ module.exports = {
|
|
|
32
36
|
i18n,
|
|
33
37
|
});
|
|
34
38
|
},
|
|
35
|
-
async onPostBuild({ netlifyConfig, utils: { cache } }) {
|
|
36
|
-
|
|
39
|
+
async onPostBuild({ netlifyConfig, utils: { cache }, constants: { FUNCTIONS_DIST } }) {
|
|
40
|
+
await saveCache({ cache, publish: netlifyConfig.build.publish });
|
|
41
|
+
await checkZipSize(join(FUNCTIONS_DIST, `${ODB_FUNCTION_NAME}.zip`));
|
|
37
42
|
},
|
|
38
43
|
onEnd() {
|
|
39
44
|
logBetaMessage();
|
|
@@ -1,15 +1,64 @@
|
|
|
1
|
+
const { promises, createWriteStream, existsSync } = require('fs');
|
|
1
2
|
const { Server } = require('http');
|
|
3
|
+
const { tmpdir } = require('os');
|
|
2
4
|
const path = require('path');
|
|
5
|
+
const { promisify } = require('util');
|
|
6
|
+
const streamPipeline = promisify(require('stream').pipeline);
|
|
3
7
|
const { Bridge } = require('@vercel/node/dist/bridge');
|
|
8
|
+
const fetch = require('node-fetch');
|
|
4
9
|
const makeHandler = () =>
|
|
5
10
|
// We return a function and then call `toString()` on it to serialise it as the launcher function
|
|
6
|
-
(conf, app) => {
|
|
7
|
-
// This is just so nft knows about the page entrypoints
|
|
11
|
+
(conf, app, pageRoot, staticManifest = []) => {
|
|
12
|
+
// This is just so nft knows about the page entrypoints. It's not actually used
|
|
8
13
|
try {
|
|
9
14
|
// eslint-disable-next-line node/no-missing-require
|
|
10
15
|
require.resolve('./pages.js');
|
|
11
16
|
}
|
|
12
17
|
catch { }
|
|
18
|
+
// Set during the request as it needs the host header. Hoisted so we can define the function once
|
|
19
|
+
let base;
|
|
20
|
+
// Only do this if we have some static files moved to the CDN
|
|
21
|
+
if (staticManifest.length !== 0) {
|
|
22
|
+
// These are static page files that have been removed from the function bundle
|
|
23
|
+
// In most cases these are served from the CDN, but for rewrites Next may try to read them
|
|
24
|
+
// from disk. We need to intercept these and load them from the CDN instead
|
|
25
|
+
// Sadly the only way to do this is to monkey-patch fs.promises. Yeah, I know.
|
|
26
|
+
const staticFiles = new Set(staticManifest);
|
|
27
|
+
// Yes, you can cache stuff locally in a Lambda
|
|
28
|
+
const cacheDir = path.join(tmpdir(), 'next-static-cache');
|
|
29
|
+
// Grab the real fs.promises.readFile...
|
|
30
|
+
const readfileOrig = promises.readFile;
|
|
31
|
+
// ...then money-patch it to see if it's requesting a CDN file
|
|
32
|
+
promises.readFile = async (file, options) => {
|
|
33
|
+
// We only care about page files
|
|
34
|
+
if (file.startsWith(pageRoot)) {
|
|
35
|
+
// We only want the part after `pages/`
|
|
36
|
+
const filePath = file.slice(pageRoot.length + 1);
|
|
37
|
+
// Is it in the CDN and not local?
|
|
38
|
+
if (staticFiles.has(filePath) && !existsSync(file)) {
|
|
39
|
+
// This name is safe to use, because it's one that was already created by Next
|
|
40
|
+
const cacheFile = path.join(cacheDir, filePath);
|
|
41
|
+
// Have we already cached it? We ignore the cache if running locally to avoid staleness
|
|
42
|
+
if ((!existsSync(cacheFile) || process.env.NETLIFY_DEV) && base) {
|
|
43
|
+
await promises.mkdir(path.dirname(cacheFile), { recursive: true });
|
|
44
|
+
// Append the path to our host and we can load it like a regular page
|
|
45
|
+
const url = `${base}/${filePath}`;
|
|
46
|
+
console.log(`Downloading ${url} to ${cacheFile}`);
|
|
47
|
+
const response = await fetch(url);
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
// Next catches this and returns it as a not found file
|
|
50
|
+
throw new Error(`Failed to fetch ${url}`);
|
|
51
|
+
}
|
|
52
|
+
// Stream it to disk
|
|
53
|
+
await streamPipeline(response.body, createWriteStream(cacheFile));
|
|
54
|
+
}
|
|
55
|
+
// Return the cache file
|
|
56
|
+
return readfileOrig(cacheFile, options);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return readfileOrig(file, options);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
13
62
|
let NextServer;
|
|
14
63
|
try {
|
|
15
64
|
// next >= 11.0.1. Yay breaking changes in patch releases!
|
|
@@ -53,10 +102,16 @@ const makeHandler = () =>
|
|
|
53
102
|
const bridge = new Bridge(server);
|
|
54
103
|
bridge.listen();
|
|
55
104
|
return async (event, context) => {
|
|
56
|
-
var _a, _b;
|
|
105
|
+
var _a, _b, _c, _d;
|
|
57
106
|
// Next expects to be able to parse the query from the URL
|
|
58
107
|
const query = new URLSearchParams(event.queryStringParameters).toString();
|
|
59
108
|
event.path = query ? `${event.path}?${query}` : event.path;
|
|
109
|
+
// Only needed if we're intercepting static files
|
|
110
|
+
if (staticManifest.length !== 0) {
|
|
111
|
+
const { host } = event.headers;
|
|
112
|
+
const protocol = event.headers['x-forwarded-proto'] || 'http';
|
|
113
|
+
base = `${protocol}://${host}`;
|
|
114
|
+
}
|
|
60
115
|
const { headers, ...result } = await bridge.launcher(event, context);
|
|
61
116
|
/** @type import("@netlify/functions").HandlerResponse */
|
|
62
117
|
// Convert all headers to multiValueHeaders
|
|
@@ -73,6 +128,10 @@ const makeHandler = () =>
|
|
|
73
128
|
delete multiValueHeaders.etag;
|
|
74
129
|
multiValueHeaders['cache-control'] = ['no-cache'];
|
|
75
130
|
}
|
|
131
|
+
// Sending SWR headers causes undefined behaviour with the Netlify CDN
|
|
132
|
+
if ((_d = (_c = multiValueHeaders['cache-control']) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.includes('stale-while-revalidate')) {
|
|
133
|
+
multiValueHeaders['cache-control'] = ['public, max-age=0, must-revalidate'];
|
|
134
|
+
}
|
|
76
135
|
return {
|
|
77
136
|
...result,
|
|
78
137
|
multiValueHeaders,
|
|
@@ -82,13 +141,24 @@ const makeHandler = () =>
|
|
|
82
141
|
};
|
|
83
142
|
const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '../../..' }) => `
|
|
84
143
|
const { Server } = require("http");
|
|
144
|
+
const { tmpdir } = require('os')
|
|
145
|
+
const { promises, createWriteStream, existsSync } = require("fs");
|
|
146
|
+
const { promisify } = require('util')
|
|
147
|
+
const streamPipeline = promisify(require('stream').pipeline)
|
|
85
148
|
// We copy the file here rather than requiring from the node module
|
|
86
149
|
const { Bridge } = require("./bridge");
|
|
150
|
+
const fetch = require('node-fetch')
|
|
151
|
+
|
|
87
152
|
const { builder } = require("@netlify/functions");
|
|
88
153
|
const { config } = require("${publishDir}/required-server-files.json")
|
|
154
|
+
let staticManifest
|
|
155
|
+
try {
|
|
156
|
+
staticManifest = require("${publishDir}/static-manifest.json")
|
|
157
|
+
} catch {}
|
|
89
158
|
const path = require("path");
|
|
159
|
+
const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", config.target === "server" ? "server" : "serverless", "pages"));
|
|
90
160
|
exports.handler = ${isODB
|
|
91
|
-
? `builder((${makeHandler().toString()})(config, "${appDir}"));`
|
|
92
|
-
: `(${makeHandler().toString()})(config, "${appDir}");`}
|
|
161
|
+
? `builder((${makeHandler().toString()})(config, "${appDir}", pageRoot, staticManifest));`
|
|
162
|
+
: `(${makeHandler().toString()})(config, "${appDir}", pageRoot, staticManifest);`}
|
|
93
163
|
`;
|
|
94
164
|
module.exports = getHandler;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/plugin-nextjs",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.6",
|
|
4
4
|
"description": "Run Next.js seamlessly on Netlify",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build:demo": "next build demo",
|
|
12
12
|
"cy:open": "cypress open --config-file cypress/config/all.json",
|
|
13
|
-
"cy:run": "cypress run --config-file cypress/config/
|
|
13
|
+
"cy:run": "cypress run --config-file ../cypress/config/ci.json",
|
|
14
14
|
"dev:demo": "next dev demo",
|
|
15
15
|
"format": "run-s format:check-fix:*",
|
|
16
16
|
"format:ci": "run-s format:check:*",
|
|
@@ -52,14 +52,19 @@
|
|
|
52
52
|
},
|
|
53
53
|
"homepage": "https://github.com/netlify/netlify-plugin-nextjs#readme",
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@netlify/functions": "^0.
|
|
55
|
+
"@netlify/functions": "^0.8.0",
|
|
56
56
|
"@netlify/ipx": "^0.0.7",
|
|
57
57
|
"@vercel/node": "^1.11.2-canary.4",
|
|
58
58
|
"chalk": "^4.1.2",
|
|
59
59
|
"fs-extra": "^10.0.0",
|
|
60
|
+
"globby": "^11.0.4",
|
|
60
61
|
"moize": "^6.1.0",
|
|
62
|
+
"node-fetch": "^2.6.6",
|
|
63
|
+
"node-stream-zip": "^1.15.0",
|
|
61
64
|
"outdent": "^0.8.0",
|
|
65
|
+
"p-limit": "^3.1.0",
|
|
62
66
|
"pathe": "^0.2.0",
|
|
67
|
+
"pretty-bytes": "^5.6.0",
|
|
63
68
|
"semver": "^7.3.5",
|
|
64
69
|
"slash": "^3.0.0",
|
|
65
70
|
"tiny-glob": "^0.2.9"
|
|
@@ -69,6 +74,7 @@
|
|
|
69
74
|
"@babel/preset-env": "^7.15.8",
|
|
70
75
|
"@netlify/eslint-config-node": "^3.3.0",
|
|
71
76
|
"@testing-library/cypress": "^8.0.1",
|
|
77
|
+
"@types/fs-extra": "^9.0.13",
|
|
72
78
|
"@types/jest": "^27.0.2",
|
|
73
79
|
"@types/mocha": "^9.0.0",
|
|
74
80
|
"babel-jest": "^27.2.5",
|
|
@@ -78,7 +84,7 @@
|
|
|
78
84
|
"husky": "^4.3.0",
|
|
79
85
|
"jest": "^27.0.0",
|
|
80
86
|
"netlify-plugin-cypress": "^2.2.0",
|
|
81
|
-
"next": "^
|
|
87
|
+
"next": "^12.0.2",
|
|
82
88
|
"npm-run-all": "^4.1.5",
|
|
83
89
|
"prettier": "^2.1.2",
|
|
84
90
|
"react": "^17.0.1",
|