@netlify/plugin-nextjs 4.0.0-beta.2 → 4.0.0-beta.3
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 +7 -1
- package/lib/helpers/config.js +3 -2
- package/lib/helpers/files.js +48 -0
- package/lib/helpers/functions.js +1 -1
- package/lib/helpers/verification.js +40 -8
- package/lib/index.js +13 -7
- package/lib/templates/getHandler.js +75 -5
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -31,7 +31,13 @@ publish = ".next"
|
|
|
31
31
|
package = "@netlify/plugin-nextjs"
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
If you previously set `target: "serverless"` in your `next.config.js`
|
|
34
|
+
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.
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
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.
|
|
39
|
+
|
|
40
|
+
If you are using Nx, then you will need to point `publish` to the folder inside `dist`, e.g. `dist/apps/myapp/.next`.
|
|
35
41
|
|
|
36
42
|
## Beta feedback
|
|
37
43
|
|
package/lib/helpers/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
const { readJSON } = 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
|
};
|
|
@@ -95,13 +96,13 @@ exports.configureHandlerFunctions = ({ netlifyConfig, publish, ignore = [] }) =>
|
|
|
95
96
|
var _a;
|
|
96
97
|
/* eslint-disable no-underscore-dangle */
|
|
97
98
|
(_a = netlifyConfig.functions)._ipx || (_a._ipx = {});
|
|
98
|
-
netlifyConfig.functions._ipx.node_bundler = '
|
|
99
|
+
netlifyConfig.functions._ipx.node_bundler = 'nft';
|
|
99
100
|
[HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME].forEach((functionName) => {
|
|
100
101
|
var _a, _b;
|
|
101
102
|
(_a = netlifyConfig.functions)[functionName] || (_a[functionName] = { included_files: [], external_node_modules: [] });
|
|
102
103
|
netlifyConfig.functions[functionName].node_bundler = 'nft';
|
|
103
104
|
(_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}`));
|
|
105
|
+
netlifyConfig.functions[functionName].included_files.push(`${publish}/server/**`, `${publish}/serverless/**`, `${publish}/*.json`, `${publish}/BUILD_ID`, ...ignore.map((path) => `!${slash(path)}`));
|
|
105
106
|
const nextRoot = resolveModuleRoot('next');
|
|
106
107
|
if (nextRoot) {
|
|
107
108
|
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`);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const { existsSync, readJson, move, cpSync, copy, writeJson } = require('fs-extra');
|
|
3
|
+
const pLimit = require('p-limit');
|
|
4
|
+
const { join } = require('pathe');
|
|
5
|
+
const TEST_ROUTE = /\/\[[^/]+?](?=\/|$)/;
|
|
6
|
+
const isDynamicRoute = (route) => TEST_ROUTE.test(route);
|
|
7
|
+
exports.moveStaticPages = async ({ netlifyConfig, target, i18n, failBuild }) => {
|
|
8
|
+
const root = join(netlifyConfig.build.publish, target === 'server' ? 'server' : 'serverless');
|
|
9
|
+
const pagesManifestPath = join(root, 'pages-manifest.json');
|
|
10
|
+
if (!existsSync(pagesManifestPath)) {
|
|
11
|
+
failBuild(`Could not find pages manifest at ${pagesManifestPath}`);
|
|
12
|
+
}
|
|
13
|
+
const files = [];
|
|
14
|
+
const moveFile = async (file) => {
|
|
15
|
+
const source = join(root, file);
|
|
16
|
+
// Trim the initial "pages"
|
|
17
|
+
const filePath = file.slice(6);
|
|
18
|
+
files.push(filePath);
|
|
19
|
+
const dest = join(netlifyConfig.build.publish, filePath);
|
|
20
|
+
await move(source, dest);
|
|
21
|
+
};
|
|
22
|
+
const pagesManifest = await readJson(pagesManifestPath);
|
|
23
|
+
// Arbitrary limit of 10 concurrent file moves
|
|
24
|
+
const limit = pLimit(10);
|
|
25
|
+
const promises = Object.entries(pagesManifest).map(async ([route, filePath]) => {
|
|
26
|
+
if (isDynamicRoute(route) ||
|
|
27
|
+
!(filePath.endsWith('.html') || filePath.endsWith('.json')) ||
|
|
28
|
+
filePath.endsWith('/404.html') ||
|
|
29
|
+
filePath.endsWith('/500.html')) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
return limit(moveFile, filePath);
|
|
33
|
+
});
|
|
34
|
+
await Promise.all(promises);
|
|
35
|
+
console.log(`Moved ${files.length} page 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
|
+
await copy(join(netlifyConfig.build.publish, i18n.defaultLocale), `${netlifyConfig.build.publish}/`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
exports.movePublicFiles = async ({ appDir, publish }) => {
|
|
44
|
+
const publicDir = join(appDir, 'public');
|
|
45
|
+
if (existsSync(publicDir)) {
|
|
46
|
+
await copy(publicDir, `${publish}/`);
|
|
47
|
+
}
|
|
48
|
+
};
|
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,34 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
const { join, relative } = require('path');
|
|
3
3
|
const { copy, existsSync } = require('fs-extra');
|
|
4
|
+
const { ODB_FUNCTION_NAME } = require('./constants');
|
|
4
5
|
const { restoreCache, saveCache } = require('./helpers/cache');
|
|
5
6
|
const { getNextConfig, configureHandlerFunctions, generateRedirects } = require('./helpers/config');
|
|
7
|
+
const { moveStaticPages, movePublicFiles } = require('./helpers/files');
|
|
6
8
|
const { generateFunctions, setupImageFunction, generatePagesResolver } = require('./helpers/functions');
|
|
7
|
-
const { verifyNetlifyBuildVersion, checkNextSiteHasBuilt,
|
|
9
|
+
const { verifyNetlifyBuildVersion, checkNextSiteHasBuilt, checkForRootPublish, logBetaMessage, checkZipSize, } = require('./helpers/verification');
|
|
8
10
|
module.exports = {
|
|
9
11
|
async onPreBuild({ constants, netlifyConfig, utils: { build: { failBuild }, cache, }, }) {
|
|
12
|
+
var _a;
|
|
10
13
|
logBetaMessage();
|
|
11
14
|
const { publish } = netlifyConfig.build;
|
|
12
15
|
checkForRootPublish({ publish, failBuild });
|
|
13
16
|
verifyNetlifyBuildVersion({ failBuild, ...constants });
|
|
14
17
|
await restoreCache({ cache, publish });
|
|
18
|
+
(_a = netlifyConfig.build).environment || (_a.environment = {});
|
|
19
|
+
// eslint-disable-next-line unicorn/consistent-destructuring
|
|
20
|
+
netlifyConfig.build.environment.NEXT_PRIVATE_TARGET = 'server';
|
|
15
21
|
},
|
|
16
22
|
async onBuild({ constants, netlifyConfig, utils: { build: { failBuild }, }, }) {
|
|
17
23
|
const { publish } = netlifyConfig.build;
|
|
18
24
|
checkNextSiteHasBuilt({ publish, failBuild });
|
|
19
25
|
const { appDir, basePath, i18n, images, target, ignore } = await getNextConfig({ publish, failBuild });
|
|
20
|
-
verifyBuildTarget(target);
|
|
21
26
|
configureHandlerFunctions({ netlifyConfig, ignore, publish: relative(process.cwd(), publish) });
|
|
22
27
|
await generateFunctions(constants, appDir);
|
|
23
28
|
await generatePagesResolver({ netlifyConfig, target, constants });
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
await
|
|
29
|
+
await movePublicFiles({ appDir, publish });
|
|
30
|
+
if (process.env.EXPERIMENTAL_MOVE_STATIC_PAGES) {
|
|
31
|
+
await moveStaticPages({ target, failBuild, netlifyConfig, i18n });
|
|
27
32
|
}
|
|
28
33
|
await setupImageFunction({ constants, imageconfig: images, netlifyConfig, basePath });
|
|
29
34
|
await generateRedirects({
|
|
@@ -32,8 +37,9 @@ module.exports = {
|
|
|
32
37
|
i18n,
|
|
33
38
|
});
|
|
34
39
|
},
|
|
35
|
-
async onPostBuild({ netlifyConfig, utils: { cache } }) {
|
|
36
|
-
|
|
40
|
+
async onPostBuild({ netlifyConfig, utils: { cache }, constants }) {
|
|
41
|
+
await saveCache({ cache, publish: netlifyConfig.build.publish });
|
|
42
|
+
await checkZipSize(join(process.cwd(), constants.FUNCTIONS_DIST, `${ODB_FUNCTION_NAME}.zip`));
|
|
37
43
|
},
|
|
38
44
|
onEnd() {
|
|
39
45
|
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.3",
|
|
4
4
|
"description": "Run Next.js seamlessly on Netlify",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -58,8 +58,12 @@
|
|
|
58
58
|
"chalk": "^4.1.2",
|
|
59
59
|
"fs-extra": "^10.0.0",
|
|
60
60
|
"moize": "^6.1.0",
|
|
61
|
+
"node-fetch": "^2.6.5",
|
|
62
|
+
"node-stream-zip": "^1.15.0",
|
|
61
63
|
"outdent": "^0.8.0",
|
|
64
|
+
"p-limit": "^3.1.0",
|
|
62
65
|
"pathe": "^0.2.0",
|
|
66
|
+
"pretty-bytes": "^5.6.0",
|
|
63
67
|
"semver": "^7.3.5",
|
|
64
68
|
"slash": "^3.0.0",
|
|
65
69
|
"tiny-glob": "^0.2.9"
|
|
@@ -69,6 +73,7 @@
|
|
|
69
73
|
"@babel/preset-env": "^7.15.8",
|
|
70
74
|
"@netlify/eslint-config-node": "^3.3.0",
|
|
71
75
|
"@testing-library/cypress": "^8.0.1",
|
|
76
|
+
"@types/fs-extra": "^9.0.13",
|
|
72
77
|
"@types/jest": "^27.0.2",
|
|
73
78
|
"@types/mocha": "^9.0.0",
|
|
74
79
|
"babel-jest": "^27.2.5",
|