@netlify/plugin-nextjs 4.1.2 → 4.2.2
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 +22 -1
- package/lib/helpers/cache.js +3 -3
- package/lib/helpers/config.js +3 -3
- package/lib/helpers/files.js +53 -53
- package/lib/helpers/functions.js +18 -15
- package/lib/helpers/redirects.js +16 -16
- package/lib/helpers/utils.js +7 -7
- package/lib/helpers/verification.js +27 -21
- package/lib/index.js +27 -26
- package/lib/templates/getHandler.js +60 -46
- package/lib/templates/getPageResolver.js +4 -4
- package/lib/templates/handlerUtils.js +21 -8
- package/lib/templates/ipx.js +1 -1
- package/package.json +10 -15
package/README.md
CHANGED
|
@@ -31,6 +31,12 @@ npm install -D @netlify/plugin-nextjs
|
|
|
31
31
|
package = "@netlify/plugin-nextjs"
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## Deploying
|
|
35
|
+
|
|
36
|
+
If you build on Netlify, this plugin will work with no additional configuration. However if you are building and
|
|
37
|
+
deploying locally using the Netlify CLI, you must deploy using `netlify deploy --build`. Running the
|
|
38
|
+
build and deploy commands separately will not work, because the plugin will not generate the required configuration.
|
|
39
|
+
|
|
34
40
|
## Migrating from an older version of the plugin
|
|
35
41
|
|
|
36
42
|
You can manually upgrade from the previous version of the plugin by running the following command:
|
|
@@ -58,7 +64,8 @@ it.
|
|
|
58
64
|
|
|
59
65
|
If you currently use redirects or rewrites on your site, see
|
|
60
66
|
[the Rewrites and Redirects guide](https://github.com/netlify/netlify-plugin-nextjs/blob/main/docs/redirects-rewrites.md)
|
|
61
|
-
for information on changes to how they are handled in this version.
|
|
67
|
+
for information on changes to how they are handled in this version. In particular, note that `_redirects` and `_headers`
|
|
68
|
+
files must be placed in `public`, not in the root of the site.
|
|
62
69
|
|
|
63
70
|
If you want to use Next 12's beta Middleware feature, this will mostly work as expected but please
|
|
64
71
|
[read the docs on some caveats and workarounds](https://github.com/netlify/netlify-plugin-nextjs/blob/main/docs/middleware.md)
|
|
@@ -85,6 +92,20 @@ you can remove it. Alternatively you can
|
|
|
85
92
|
support. See [`demos/next-export`](https://github.com/netlify/netlify-plugin-nextjs/tree/main/demos/next-export) for an
|
|
86
93
|
example.
|
|
87
94
|
|
|
95
|
+
## Asset optimization
|
|
96
|
+
|
|
97
|
+
Netlify [asset optimization](https://docs.netlify.com/site-deploys/post-processing/) should not be used with Next.js
|
|
98
|
+
sites. Assets are already optimized by Next.js at build time, and doing further optimization can break your site. Ensure
|
|
99
|
+
that it is not enabled at **Site settings > Build & deploy > Post processing > Asset optimization**.
|
|
100
|
+
|
|
101
|
+
## Generated functions
|
|
102
|
+
|
|
103
|
+
This plugin works by generating three Netlify functions that handle requests that haven't been pre-rendered. These are
|
|
104
|
+
`___netlify-handler` (for SSR and API routes), `___netlify-odb-handler` (for ISR and fallback routes), and `_ipx` (for
|
|
105
|
+
images). You can see the requests for these in [the function logs](https://docs.netlify.com/functions/logs/). For ISR
|
|
106
|
+
and fallback routes you will not see any requests that are served from the edge cache, just actual rendering requests.
|
|
107
|
+
These are all internal functions, so you won't find them in your site's own functions directory.
|
|
108
|
+
|
|
88
109
|
## Feedback
|
|
89
110
|
|
|
90
111
|
If you think you have found a bug in the plugin,
|
package/lib/helpers/cache.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.saveCache = exports.restoreCache = void 0;
|
|
4
4
|
const path_1 = require("path");
|
|
5
5
|
const restoreCache = async ({ cache, publish }) => {
|
|
6
|
-
const cacheDir = path_1.join(publish, 'cache');
|
|
6
|
+
const cacheDir = (0, path_1.join)(publish, 'cache');
|
|
7
7
|
if (await cache.restore(cacheDir)) {
|
|
8
8
|
console.log('Next.js cache restored.');
|
|
9
9
|
}
|
|
@@ -13,8 +13,8 @@ const restoreCache = async ({ cache, publish }) => {
|
|
|
13
13
|
};
|
|
14
14
|
exports.restoreCache = restoreCache;
|
|
15
15
|
const saveCache = async ({ cache, publish }) => {
|
|
16
|
-
const cacheDir = path_1.join(publish, 'cache');
|
|
17
|
-
const buildManifest = path_1.join(publish, 'build-manifest.json');
|
|
16
|
+
const cacheDir = (0, path_1.join)(publish, 'cache');
|
|
17
|
+
const buildManifest = (0, path_1.join)(publish, 'build-manifest.json');
|
|
18
18
|
if (await cache.save(cacheDir, { digests: [buildManifest] })) {
|
|
19
19
|
console.log('Next.js cache saved.');
|
|
20
20
|
}
|
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 fs_extra_1.readJSON(pathe_1.join(publish, 'required-server-files.json'));
|
|
16
|
+
const { config, appDir, ignore } = await (0, fs_extra_1.readJSON)((0, 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 pathe_1.dirname(pathe_1.relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] })));
|
|
31
|
+
return (0, pathe_1.dirname)((0, pathe_1.relative)(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] })));
|
|
32
32
|
}
|
|
33
33
|
catch {
|
|
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) => `!${slash_1.default(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) => `!${(0, 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
|
@@ -43,33 +43,33 @@ const matchesRedirect = (file, redirects) => {
|
|
|
43
43
|
exports.matchesRedirect = matchesRedirect;
|
|
44
44
|
const matchesRewrite = (file, rewrites) => {
|
|
45
45
|
if (Array.isArray(rewrites)) {
|
|
46
|
-
return exports.matchesRedirect(file, rewrites);
|
|
46
|
+
return (0, exports.matchesRedirect)(file, rewrites);
|
|
47
47
|
}
|
|
48
48
|
if (!Array.isArray(rewrites === null || rewrites === void 0 ? void 0 : rewrites.beforeFiles)) {
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
51
|
-
return exports.matchesRedirect(file, rewrites.beforeFiles);
|
|
51
|
+
return (0, exports.matchesRedirect)(file, rewrites.beforeFiles);
|
|
52
52
|
};
|
|
53
53
|
exports.matchesRewrite = matchesRewrite;
|
|
54
54
|
// eslint-disable-next-line max-lines-per-function
|
|
55
55
|
const moveStaticPages = async ({ netlifyConfig, target, i18n, }) => {
|
|
56
56
|
console.log('Moving static page files to serve from CDN...');
|
|
57
|
-
const outputDir = pathe_1.join(netlifyConfig.build.publish, target === 'server' ? 'server' : 'serverless');
|
|
58
|
-
const root = pathe_1.join(outputDir, 'pages');
|
|
59
|
-
const buildId = fs_extra_1.readFileSync(pathe_1.join(netlifyConfig.build.publish, 'BUILD_ID'), 'utf8').trim();
|
|
60
|
-
const dataDir = pathe_1.join('_next', 'data', buildId);
|
|
61
|
-
await fs_extra_1.ensureDir(dataDir);
|
|
57
|
+
const outputDir = (0, pathe_1.join)(netlifyConfig.build.publish, target === 'server' ? 'server' : 'serverless');
|
|
58
|
+
const root = (0, pathe_1.join)(outputDir, 'pages');
|
|
59
|
+
const buildId = (0, fs_extra_1.readFileSync)((0, pathe_1.join)(netlifyConfig.build.publish, 'BUILD_ID'), 'utf8').trim();
|
|
60
|
+
const dataDir = (0, pathe_1.join)('_next', 'data', buildId);
|
|
61
|
+
await (0, fs_extra_1.ensureDir)(dataDir);
|
|
62
62
|
// Load the middleware manifest so we can check if a file matches it before moving
|
|
63
63
|
let middleware;
|
|
64
|
-
const manifestPath = pathe_1.join(outputDir, 'middleware-manifest.json');
|
|
65
|
-
if (fs_extra_1.existsSync(manifestPath)) {
|
|
66
|
-
const manifest = await fs_extra_1.readJson(manifestPath);
|
|
64
|
+
const manifestPath = (0, pathe_1.join)(outputDir, 'middleware-manifest.json');
|
|
65
|
+
if ((0, fs_extra_1.existsSync)(manifestPath)) {
|
|
66
|
+
const manifest = await (0, fs_extra_1.readJson)(manifestPath);
|
|
67
67
|
if (manifest === null || manifest === void 0 ? void 0 : manifest.middleware) {
|
|
68
68
|
middleware = Object.keys(manifest.middleware).map((path) => path.slice(1));
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
const prerenderManifest = await fs_extra_1.readJson(pathe_1.join(netlifyConfig.build.publish, 'prerender-manifest.json'));
|
|
72
|
-
const { redirects, rewrites } = await fs_extra_1.readJson(pathe_1.join(netlifyConfig.build.publish, 'routes-manifest.json'));
|
|
71
|
+
const prerenderManifest = await (0, fs_extra_1.readJson)((0, pathe_1.join)(netlifyConfig.build.publish, 'prerender-manifest.json'));
|
|
72
|
+
const { redirects, rewrites } = await (0, fs_extra_1.readJson)((0, pathe_1.join)(netlifyConfig.build.publish, 'routes-manifest.json'));
|
|
73
73
|
const isrFiles = new Set();
|
|
74
74
|
const shortRevalidateRoutes = [];
|
|
75
75
|
Object.entries(prerenderManifest.routes).forEach(([route, { initialRevalidateSeconds }]) => {
|
|
@@ -87,20 +87,20 @@ const moveStaticPages = async ({ netlifyConfig, target, i18n, }) => {
|
|
|
87
87
|
const filesManifest = {};
|
|
88
88
|
const moveFile = async (file) => {
|
|
89
89
|
const isData = file.endsWith('.json');
|
|
90
|
-
const source = pathe_1.join(root, file);
|
|
91
|
-
const targetFile = isData ? pathe_1.join(dataDir, file) : file;
|
|
90
|
+
const source = (0, pathe_1.join)(root, file);
|
|
91
|
+
const targetFile = isData ? (0, pathe_1.join)(dataDir, file) : file;
|
|
92
92
|
files.push(file);
|
|
93
93
|
filesManifest[file] = targetFile;
|
|
94
|
-
const dest = pathe_1.join(netlifyConfig.build.publish, targetFile);
|
|
94
|
+
const dest = (0, pathe_1.join)(netlifyConfig.build.publish, targetFile);
|
|
95
95
|
try {
|
|
96
|
-
await fs_extra_1.move(source, dest);
|
|
96
|
+
await (0, fs_extra_1.move)(source, dest);
|
|
97
97
|
}
|
|
98
98
|
catch (error) {
|
|
99
99
|
console.warn('Error moving file', source, error);
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
102
|
// Move all static files, except error documents and nft manifests
|
|
103
|
-
const pages = await globby_1.default(['**/*.{html,json}', '!**/(500|404|*.js.nft).{html,json}'], {
|
|
103
|
+
const pages = await (0, globby_1.default)(['**/*.{html,json}', '!**/(500|404|*.js.nft).{html,json}'], {
|
|
104
104
|
cwd: root,
|
|
105
105
|
dot: true,
|
|
106
106
|
});
|
|
@@ -109,27 +109,27 @@ const moveStaticPages = async ({ netlifyConfig, target, i18n, }) => {
|
|
|
109
109
|
const matchedRedirects = new Set();
|
|
110
110
|
const matchedRewrites = new Set();
|
|
111
111
|
// Limit concurrent file moves to number of cpus or 2 if there is only 1
|
|
112
|
-
const limit = p_limit_1.default(Math.max(2, os_1.cpus().length));
|
|
112
|
+
const limit = (0, p_limit_1.default)(Math.max(2, (0, os_1.cpus)().length));
|
|
113
113
|
const promises = pages.map((rawPath) => {
|
|
114
|
-
const filePath = slash_1.default(rawPath);
|
|
114
|
+
const filePath = (0, slash_1.default)(rawPath);
|
|
115
115
|
// Don't move ISR files, as they're used for the first request
|
|
116
116
|
if (isrFiles.has(filePath)) {
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
119
|
-
if (exports.isDynamicRoute(filePath)) {
|
|
119
|
+
if ((0, exports.isDynamicRoute)(filePath)) {
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
|
-
if (exports.matchesRedirect(filePath, redirects)) {
|
|
122
|
+
if ((0, exports.matchesRedirect)(filePath, redirects)) {
|
|
123
123
|
matchedRedirects.add(filePath);
|
|
124
124
|
return;
|
|
125
125
|
}
|
|
126
|
-
if (exports.matchesRewrite(filePath, rewrites)) {
|
|
126
|
+
if ((0, exports.matchesRewrite)(filePath, rewrites)) {
|
|
127
127
|
matchedRewrites.add(filePath);
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
130
130
|
// Middleware matches against the unlocalised path
|
|
131
|
-
const unlocalizedPath = exports.stripLocale(rawPath, i18n === null || i18n === void 0 ? void 0 : i18n.locales);
|
|
132
|
-
const middlewarePath = exports.matchMiddleware(middleware, unlocalizedPath);
|
|
131
|
+
const unlocalizedPath = (0, exports.stripLocale)(rawPath, i18n === null || i18n === void 0 ? void 0 : i18n.locales);
|
|
132
|
+
const middlewarePath = (0, exports.matchMiddleware)(middleware, unlocalizedPath);
|
|
133
133
|
// If a file matches middleware it can't be offloaded to the CDN, and needs to stay at the origin to be served by next/server
|
|
134
134
|
if (middlewarePath) {
|
|
135
135
|
matchingMiddleware.add(middlewarePath);
|
|
@@ -141,67 +141,67 @@ const moveStaticPages = async ({ netlifyConfig, target, i18n, }) => {
|
|
|
141
141
|
await Promise.all(promises);
|
|
142
142
|
console.log(`Moved ${files.length} files`);
|
|
143
143
|
if (matchedPages.size !== 0) {
|
|
144
|
-
console.log(chalk_1.yellowBright(outdent_1.outdent `
|
|
144
|
+
console.log((0, chalk_1.yellowBright)((0, outdent_1.outdent) `
|
|
145
145
|
Skipped moving ${matchedPages.size} ${matchedPages.size === 1 ? 'file because it matches' : 'files because they match'} middleware, so cannot be deployed to the CDN and will be served from the origin instead.
|
|
146
146
|
This is fine, but we're letting you know because it may not be what you expect.
|
|
147
147
|
`));
|
|
148
|
-
console.log(outdent_1.outdent `
|
|
148
|
+
console.log((0, outdent_1.outdent) `
|
|
149
149
|
The following middleware matched statically-rendered pages:
|
|
150
150
|
|
|
151
|
-
${chalk_1.yellowBright([...matchingMiddleware].map((mid) => `- /${mid}/_middleware`).join('\n'))}
|
|
151
|
+
${(0, chalk_1.yellowBright)([...matchingMiddleware].map((mid) => `- /${mid}/_middleware`).join('\n'))}
|
|
152
152
|
${constants_1.DIVIDER}
|
|
153
153
|
`);
|
|
154
154
|
// There could potentially be thousands of matching pages, so we don't want to spam the console with this
|
|
155
155
|
if (matchedPages.size < 50) {
|
|
156
|
-
console.log(outdent_1.outdent `
|
|
156
|
+
console.log((0, outdent_1.outdent) `
|
|
157
157
|
The following files matched middleware and were not moved to the CDN:
|
|
158
158
|
|
|
159
|
-
${chalk_1.yellowBright([...matchedPages].map((mid) => `- ${mid}`).join('\n'))}
|
|
159
|
+
${(0, chalk_1.yellowBright)([...matchedPages].map((mid) => `- ${mid}`).join('\n'))}
|
|
160
160
|
${constants_1.DIVIDER}
|
|
161
161
|
`);
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
if (matchedRedirects.size !== 0 || matchedRewrites.size !== 0) {
|
|
165
|
-
console.log(chalk_1.yellowBright(outdent_1.outdent `
|
|
165
|
+
console.log((0, chalk_1.yellowBright)((0, outdent_1.outdent) `
|
|
166
166
|
Skipped moving ${matchedRedirects.size + matchedRewrites.size} files because they match redirects or beforeFiles rewrites, so cannot be deployed to the CDN and will be served from the origin instead.
|
|
167
167
|
`));
|
|
168
168
|
if (matchedRedirects.size < 50 && matchedRedirects.size !== 0) {
|
|
169
|
-
console.log(outdent_1.outdent `
|
|
169
|
+
console.log((0, outdent_1.outdent) `
|
|
170
170
|
The following files matched redirects and were not moved to the CDN:
|
|
171
171
|
|
|
172
|
-
${chalk_1.yellowBright([...matchedRedirects].map((mid) => `- ${mid}`).join('\n'))}
|
|
172
|
+
${(0, chalk_1.yellowBright)([...matchedRedirects].map((mid) => `- ${mid}`).join('\n'))}
|
|
173
173
|
${constants_1.DIVIDER}
|
|
174
174
|
`);
|
|
175
175
|
}
|
|
176
176
|
if (matchedRewrites.size < 50 && matchedRewrites.size !== 0) {
|
|
177
|
-
console.log(outdent_1.outdent `
|
|
177
|
+
console.log((0, outdent_1.outdent) `
|
|
178
178
|
The following files matched beforeFiles rewrites and were not moved to the CDN:
|
|
179
179
|
|
|
180
|
-
${chalk_1.yellowBright([...matchedRewrites].map((mid) => `- ${mid}`).join('\n'))}
|
|
180
|
+
${(0, chalk_1.yellowBright)([...matchedRewrites].map((mid) => `- ${mid}`).join('\n'))}
|
|
181
181
|
${constants_1.DIVIDER}
|
|
182
182
|
`);
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
// Write the manifest for use in the serverless functions
|
|
186
|
-
await fs_extra_1.writeJson(pathe_1.join(netlifyConfig.build.publish, 'static-manifest.json'), Object.entries(filesManifest));
|
|
186
|
+
await (0, fs_extra_1.writeJson)((0, pathe_1.join)(netlifyConfig.build.publish, 'static-manifest.json'), Object.entries(filesManifest));
|
|
187
187
|
if (i18n === null || i18n === void 0 ? void 0 : i18n.defaultLocale) {
|
|
188
188
|
// Copy the default locale into the root
|
|
189
|
-
const defaultLocaleDir = pathe_1.join(netlifyConfig.build.publish, i18n.defaultLocale);
|
|
190
|
-
if (fs_extra_1.existsSync(defaultLocaleDir)) {
|
|
191
|
-
await fs_extra_1.copy(defaultLocaleDir, `${netlifyConfig.build.publish}/`);
|
|
189
|
+
const defaultLocaleDir = (0, pathe_1.join)(netlifyConfig.build.publish, i18n.defaultLocale);
|
|
190
|
+
if ((0, fs_extra_1.existsSync)(defaultLocaleDir)) {
|
|
191
|
+
await (0, fs_extra_1.copy)(defaultLocaleDir, `${netlifyConfig.build.publish}/`);
|
|
192
192
|
}
|
|
193
|
-
const defaultLocaleIndex = pathe_1.join(netlifyConfig.build.publish, `${i18n.defaultLocale}.html`);
|
|
194
|
-
const indexHtml = pathe_1.join(netlifyConfig.build.publish, 'index.html');
|
|
195
|
-
if (fs_extra_1.existsSync(defaultLocaleIndex) && !fs_extra_1.existsSync(indexHtml)) {
|
|
193
|
+
const defaultLocaleIndex = (0, pathe_1.join)(netlifyConfig.build.publish, `${i18n.defaultLocale}.html`);
|
|
194
|
+
const indexHtml = (0, pathe_1.join)(netlifyConfig.build.publish, 'index.html');
|
|
195
|
+
if ((0, fs_extra_1.existsSync)(defaultLocaleIndex) && !(0, fs_extra_1.existsSync)(indexHtml)) {
|
|
196
196
|
try {
|
|
197
|
-
await fs_extra_1.copy(defaultLocaleIndex, indexHtml, { overwrite: false });
|
|
198
|
-
await fs_extra_1.copy(pathe_1.join(netlifyConfig.build.publish, `${i18n.defaultLocale}.json`), pathe_1.join(netlifyConfig.build.publish, 'index.json'), { overwrite: false });
|
|
197
|
+
await (0, fs_extra_1.copy)(defaultLocaleIndex, indexHtml, { overwrite: false });
|
|
198
|
+
await (0, fs_extra_1.copy)((0, pathe_1.join)(netlifyConfig.build.publish, `${i18n.defaultLocale}.json`), (0, pathe_1.join)(netlifyConfig.build.publish, 'index.json'), { overwrite: false });
|
|
199
199
|
}
|
|
200
200
|
catch { }
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
if (shortRevalidateRoutes.length !== 0) {
|
|
204
|
-
console.log(outdent_1.outdent `
|
|
204
|
+
console.log((0, outdent_1.outdent) `
|
|
205
205
|
The following routes use "revalidate" values of under ${constants_1.MINIMUM_REVALIDATE_SECONDS} seconds, which is not supported.
|
|
206
206
|
They will use a revalidate time of ${constants_1.MINIMUM_REVALIDATE_SECONDS} seconds instead.
|
|
207
207
|
`);
|
|
@@ -217,16 +217,16 @@ const moveStaticPages = async ({ netlifyConfig, target, i18n, }) => {
|
|
|
217
217
|
};
|
|
218
218
|
exports.moveStaticPages = moveStaticPages;
|
|
219
219
|
const patchFile = async ({ file, from, to }) => {
|
|
220
|
-
if (!fs_extra_1.existsSync(file)) {
|
|
220
|
+
if (!(0, fs_extra_1.existsSync)(file)) {
|
|
221
221
|
return;
|
|
222
222
|
}
|
|
223
|
-
const content = await fs_extra_1.readFile(file, 'utf8');
|
|
223
|
+
const content = await (0, fs_extra_1.readFile)(file, 'utf8');
|
|
224
224
|
if (content.includes(to)) {
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
227
|
const newContent = content.replace(from, to);
|
|
228
|
-
await fs_extra_1.writeFile(`${file}.orig`, content);
|
|
229
|
-
await fs_extra_1.writeFile(file, newContent);
|
|
228
|
+
await (0, fs_extra_1.writeFile)(`${file}.orig`, content);
|
|
229
|
+
await (0, fs_extra_1.writeFile)(file, newContent);
|
|
230
230
|
};
|
|
231
231
|
const getServerFile = (root) => {
|
|
232
232
|
let serverFile;
|
|
@@ -262,8 +262,8 @@ exports.patchNextFiles = patchNextFiles;
|
|
|
262
262
|
const unpatchNextFiles = async (root) => {
|
|
263
263
|
const serverFile = getServerFile(root);
|
|
264
264
|
const origFile = `${serverFile}.orig`;
|
|
265
|
-
if (fs_extra_1.existsSync(origFile)) {
|
|
266
|
-
await fs_extra_1.move(origFile, serverFile, { overwrite: true });
|
|
265
|
+
if ((0, fs_extra_1.existsSync)(origFile)) {
|
|
266
|
+
await (0, fs_extra_1.move)(origFile, serverFile, { overwrite: true });
|
|
267
267
|
}
|
|
268
268
|
};
|
|
269
269
|
exports.unpatchNextFiles = unpatchNextFiles;
|
|
@@ -274,9 +274,9 @@ const movePublicFiles = async ({ appDir, outdir, publish, }) => {
|
|
|
274
274
|
// If it exists, copy the files from the public folder there in order to include
|
|
275
275
|
// any files that were generated during the build. Otherwise, copy the public
|
|
276
276
|
// directory from the original app directory.
|
|
277
|
-
const publicDir = outdir ? pathe_1.join(appDir, outdir, 'public') : pathe_1.join(appDir, 'public');
|
|
278
|
-
if (fs_extra_1.existsSync(publicDir)) {
|
|
279
|
-
await fs_extra_1.copy(publicDir, `${publish}/`);
|
|
277
|
+
const publicDir = outdir ? (0, pathe_1.join)(appDir, outdir, 'public') : (0, pathe_1.join)(appDir, 'public');
|
|
278
|
+
if ((0, fs_extra_1.existsSync)(publicDir)) {
|
|
279
|
+
await (0, fs_extra_1.copy)(publicDir, `${publish}/`);
|
|
280
280
|
}
|
|
281
281
|
};
|
|
282
282
|
exports.movePublicFiles = movePublicFiles;
|
package/lib/helpers/functions.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
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.setupImageFunction = exports.generatePagesResolver = exports.generateFunctions = void 0;
|
|
7
|
+
const node_bridge_1 = __importDefault(require("@vercel/node-bridge"));
|
|
4
8
|
const fs_extra_1 = require("fs-extra");
|
|
5
9
|
const pathe_1 = require("pathe");
|
|
6
10
|
const constants_1 = require("../constants");
|
|
@@ -8,15 +12,14 @@ const getHandler_1 = require("../templates/getHandler");
|
|
|
8
12
|
const getPageResolver_1 = require("../templates/getPageResolver");
|
|
9
13
|
const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC, PUBLISH_DIR }, appDir) => {
|
|
10
14
|
const functionsDir = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const publishDir = pathe_1.relative(functionDir, pathe_1.join(process.cwd(), PUBLISH_DIR));
|
|
15
|
+
const functionDir = (0, pathe_1.join)(process.cwd(), functionsDir, constants_1.HANDLER_FUNCTION_NAME);
|
|
16
|
+
const publishDir = (0, pathe_1.relative)(functionDir, (0, pathe_1.join)(process.cwd(), PUBLISH_DIR));
|
|
14
17
|
const writeHandler = async (func, isODB) => {
|
|
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(
|
|
19
|
-
await fs_extra_1.copyFile(pathe_1.join(__dirname, '..', '..', 'lib', 'templates', 'handlerUtils.js'), pathe_1.join(functionsDir, func, 'handlerUtils.js'));
|
|
18
|
+
const handlerSource = await (0, getHandler_1.getHandler)({ isODB, publishDir, appDir: (0, pathe_1.relative)(functionDir, appDir) });
|
|
19
|
+
await (0, fs_extra_1.ensureDir)((0, pathe_1.join)(functionsDir, func));
|
|
20
|
+
await (0, fs_extra_1.writeFile)((0, pathe_1.join)(functionsDir, func, `${func}.js`), handlerSource);
|
|
21
|
+
await (0, fs_extra_1.copyFile)(node_bridge_1.default, (0, pathe_1.join)(functionsDir, func, 'bridge.js'));
|
|
22
|
+
await (0, fs_extra_1.copyFile)((0, pathe_1.join)(__dirname, '..', '..', 'lib', 'templates', 'handlerUtils.js'), (0, pathe_1.join)(functionsDir, func, 'handlerUtils.js'));
|
|
20
23
|
};
|
|
21
24
|
await writeHandler(constants_1.HANDLER_FUNCTION_NAME, false);
|
|
22
25
|
await writeHandler(constants_1.ODB_FUNCTION_NAME, true);
|
|
@@ -28,25 +31,25 @@ exports.generateFunctions = generateFunctions;
|
|
|
28
31
|
*/
|
|
29
32
|
const generatePagesResolver = async ({ constants: { INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC, PUBLISH_DIR }, target, }) => {
|
|
30
33
|
const functionsPath = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
31
|
-
const jsSource = await getPageResolver_1.getPageResolver({
|
|
34
|
+
const jsSource = await (0, getPageResolver_1.getPageResolver)({
|
|
32
35
|
publish: PUBLISH_DIR,
|
|
33
36
|
target,
|
|
34
37
|
});
|
|
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);
|
|
38
|
+
await (0, fs_extra_1.writeFile)((0, pathe_1.join)(functionsPath, constants_1.ODB_FUNCTION_NAME, 'pages.js'), jsSource);
|
|
39
|
+
await (0, fs_extra_1.writeFile)((0, pathe_1.join)(functionsPath, constants_1.HANDLER_FUNCTION_NAME, 'pages.js'), jsSource);
|
|
37
40
|
};
|
|
38
41
|
exports.generatePagesResolver = generatePagesResolver;
|
|
39
42
|
// Move our next/image function into the correct functions directory
|
|
40
43
|
const setupImageFunction = async ({ constants: { INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS_SRC }, imageconfig = {}, netlifyConfig, basePath, }) => {
|
|
41
44
|
const functionsPath = INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC;
|
|
42
45
|
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'), {
|
|
46
|
+
const functionDirectory = (0, pathe_1.join)(functionsPath, constants_1.IMAGE_FUNCTION_NAME);
|
|
47
|
+
await (0, fs_extra_1.ensureDir)(functionDirectory);
|
|
48
|
+
await (0, fs_extra_1.writeJSON)((0, pathe_1.join)(functionDirectory, 'imageconfig.json'), {
|
|
46
49
|
...imageconfig,
|
|
47
50
|
basePath: [basePath, constants_1.IMAGE_FUNCTION_NAME].join('/'),
|
|
48
51
|
});
|
|
49
|
-
await fs_extra_1.copyFile(pathe_1.join(__dirname, '..', '..', 'lib', 'templates', 'ipx.js'), pathe_1.join(functionDirectory, functionName));
|
|
52
|
+
await (0, fs_extra_1.copyFile)((0, pathe_1.join)(__dirname, '..', '..', 'lib', 'templates', 'ipx.js'), (0, pathe_1.join)(functionDirectory, functionName));
|
|
50
53
|
const imagePath = imageconfig.path || '/_next/image';
|
|
51
54
|
netlifyConfig.redirects.push({
|
|
52
55
|
from: `${imagePath}*`,
|
package/lib/helpers/redirects.js
CHANGED
|
@@ -42,8 +42,8 @@ const generateStaticRedirects = ({ netlifyConfig, nextConfig: { i18n, basePath }
|
|
|
42
42
|
};
|
|
43
43
|
exports.generateStaticRedirects = generateStaticRedirects;
|
|
44
44
|
const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath, trailingSlash, appDir }, buildId, }) => {
|
|
45
|
-
const { dynamicRoutes: prerenderedDynamicRoutes, routes: prerenderedStaticRoutes } = await fs_extra_1.readJSON(pathe_1.join(netlifyConfig.build.publish, 'prerender-manifest.json'));
|
|
46
|
-
const { dynamicRoutes, staticRoutes } = await fs_extra_1.readJSON(pathe_1.join(netlifyConfig.build.publish, 'routes-manifest.json'));
|
|
45
|
+
const { dynamicRoutes: prerenderedDynamicRoutes, routes: prerenderedStaticRoutes } = await (0, fs_extra_1.readJSON)((0, pathe_1.join)(netlifyConfig.build.publish, 'prerender-manifest.json'));
|
|
46
|
+
const { dynamicRoutes, staticRoutes } = await (0, fs_extra_1.readJSON)((0, pathe_1.join)(netlifyConfig.build.publish, 'routes-manifest.json'));
|
|
47
47
|
netlifyConfig.redirects.push(...constants_1.HIDDEN_PATHS.map((path) => ({
|
|
48
48
|
from: `${basePath}${path}`,
|
|
49
49
|
to: '/404.html',
|
|
@@ -56,14 +56,14 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
|
|
|
56
56
|
// This is only used in prod, so dev uses `next dev` directly
|
|
57
57
|
netlifyConfig.redirects.push(
|
|
58
58
|
// API routes always need to be served from the regular function
|
|
59
|
-
...utils_1.getApiRewrites(basePath),
|
|
59
|
+
...(0, utils_1.getApiRewrites)(basePath),
|
|
60
60
|
// Preview mode gets forced to the function, to bypass pre-rendered pages, but static files need to be skipped
|
|
61
|
-
...(await utils_1.getPreviewRewrites({ basePath, appDir })));
|
|
61
|
+
...(await (0, utils_1.getPreviewRewrites)({ basePath, appDir })));
|
|
62
62
|
const staticRouteEntries = Object.entries(prerenderedStaticRoutes);
|
|
63
63
|
const staticRoutePaths = new Set();
|
|
64
64
|
// First add all static ISR routes
|
|
65
65
|
staticRouteEntries.forEach(([route, { initialRevalidateSeconds }]) => {
|
|
66
|
-
if (utils_1.isApiRoute(route)) {
|
|
66
|
+
if ((0, utils_1.isApiRoute)(route)) {
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
staticRoutePaths.add(route);
|
|
@@ -75,9 +75,9 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
|
|
|
75
75
|
if ((i18n === null || i18n === void 0 ? void 0 : i18n.defaultLocale) && route.startsWith(`/${i18n.defaultLocale}/`)) {
|
|
76
76
|
route = route.slice(i18n.defaultLocale.length + 1);
|
|
77
77
|
staticRoutePaths.add(route);
|
|
78
|
-
netlifyConfig.redirects.push(...utils_1.redirectsForNextRouteWithData({
|
|
78
|
+
netlifyConfig.redirects.push(...(0, utils_1.redirectsForNextRouteWithData)({
|
|
79
79
|
route,
|
|
80
|
-
dataRoute: utils_1.routeToDataRoute(route, buildId, i18n.defaultLocale),
|
|
80
|
+
dataRoute: (0, utils_1.routeToDataRoute)(route, buildId, i18n.defaultLocale),
|
|
81
81
|
basePath,
|
|
82
82
|
to: constants_1.ODB_FUNCTION_PATH,
|
|
83
83
|
force: true,
|
|
@@ -87,30 +87,30 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
|
|
|
87
87
|
// ISR routes use the ODB handler
|
|
88
88
|
netlifyConfig.redirects.push(
|
|
89
89
|
// No i18n, because the route is already localized
|
|
90
|
-
...utils_1.redirectsForNextRoute({ route, basePath, to: constants_1.ODB_FUNCTION_PATH, force: true, buildId, i18n: null }));
|
|
90
|
+
...(0, utils_1.redirectsForNextRoute)({ route, basePath, to: constants_1.ODB_FUNCTION_PATH, force: true, buildId, i18n: null }));
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
|
-
// Add rewrites for all static SSR routes
|
|
94
|
-
staticRoutes.forEach((route) => {
|
|
95
|
-
if (staticRoutePaths.has(route.page) || utils_1.isApiRoute(route.page)) {
|
|
93
|
+
// Add rewrites for all static SSR routes. This is Next 12+
|
|
94
|
+
staticRoutes === null || staticRoutes === void 0 ? void 0 : staticRoutes.forEach((route) => {
|
|
95
|
+
if (staticRoutePaths.has(route.page) || (0, utils_1.isApiRoute)(route.page)) {
|
|
96
96
|
// Prerendered static routes are either handled by the CDN or are ISR
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
|
-
netlifyConfig.redirects.push(...utils_1.redirectsForNextRoute({ route: route.page, buildId, basePath, to: constants_1.HANDLER_FUNCTION_PATH, i18n }));
|
|
99
|
+
netlifyConfig.redirects.push(...(0, utils_1.redirectsForNextRoute)({ route: route.page, buildId, basePath, to: constants_1.HANDLER_FUNCTION_PATH, i18n }));
|
|
100
100
|
});
|
|
101
101
|
// Add rewrites for all dynamic routes (both SSR and ISR)
|
|
102
102
|
dynamicRoutes.forEach((route) => {
|
|
103
|
-
if (utils_1.isApiRoute(route.page)) {
|
|
103
|
+
if ((0, utils_1.isApiRoute)(route.page)) {
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
106
|
if (route.page in prerenderedDynamicRoutes) {
|
|
107
107
|
const { fallback } = prerenderedDynamicRoutes[route.page];
|
|
108
|
-
const { to, status } = utils_1.targetForFallback(fallback);
|
|
109
|
-
netlifyConfig.redirects.push(...utils_1.redirectsForNextRoute({ buildId, route: route.page, basePath, to, status, i18n }));
|
|
108
|
+
const { to, status } = (0, utils_1.targetForFallback)(fallback);
|
|
109
|
+
netlifyConfig.redirects.push(...(0, utils_1.redirectsForNextRoute)({ buildId, route: route.page, basePath, to, status, i18n }));
|
|
110
110
|
}
|
|
111
111
|
else {
|
|
112
112
|
// If the route isn't prerendered, it's SSR
|
|
113
|
-
netlifyConfig.redirects.push(...utils_1.redirectsForNextRoute({ route: route.page, buildId, basePath, to: constants_1.HANDLER_FUNCTION_PATH, i18n }));
|
|
113
|
+
netlifyConfig.redirects.push(...(0, utils_1.redirectsForNextRoute)({ route: route.page, buildId, basePath, to: constants_1.HANDLER_FUNCTION_PATH, i18n }));
|
|
114
114
|
}
|
|
115
115
|
});
|
|
116
116
|
// Final fallback
|
package/lib/helpers/utils.js
CHANGED
|
@@ -36,8 +36,8 @@ const toNetlifyRoute = (nextRoute) => {
|
|
|
36
36
|
};
|
|
37
37
|
exports.toNetlifyRoute = toNetlifyRoute;
|
|
38
38
|
const netlifyRoutesForNextRouteWithData = ({ route, dataRoute }) => [
|
|
39
|
-
...exports.toNetlifyRoute(dataRoute),
|
|
40
|
-
...exports.toNetlifyRoute(route),
|
|
39
|
+
...(0, exports.toNetlifyRoute)(dataRoute),
|
|
40
|
+
...(0, exports.toNetlifyRoute)(route),
|
|
41
41
|
];
|
|
42
42
|
exports.netlifyRoutesForNextRouteWithData = netlifyRoutesForNextRouteWithData;
|
|
43
43
|
const routeToDataRoute = (route, buildId, locale) => `/_next/data/${buildId}${locale ? `/${locale}` : ''}${route === '/' ? '/index' : route}.json`;
|
|
@@ -45,16 +45,16 @@ exports.routeToDataRoute = routeToDataRoute;
|
|
|
45
45
|
const netlifyRoutesForNextRoute = (route, buildId, i18n) => {
|
|
46
46
|
var _a;
|
|
47
47
|
if (!((_a = i18n === null || i18n === void 0 ? void 0 : i18n.locales) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
48
|
-
return exports.netlifyRoutesForNextRouteWithData({ route, dataRoute: exports.routeToDataRoute(route, buildId) });
|
|
48
|
+
return (0, exports.netlifyRoutesForNextRouteWithData)({ route, dataRoute: (0, exports.routeToDataRoute)(route, buildId) });
|
|
49
49
|
}
|
|
50
50
|
const { locales, defaultLocale } = i18n;
|
|
51
51
|
const routes = [];
|
|
52
52
|
locales.forEach((locale) => {
|
|
53
53
|
// Data route is always localized
|
|
54
|
-
const dataRoute = exports.routeToDataRoute(route, buildId, locale);
|
|
54
|
+
const dataRoute = (0, exports.routeToDataRoute)(route, buildId, locale);
|
|
55
55
|
routes.push(
|
|
56
56
|
// Default locale is served from root, not localized
|
|
57
|
-
...exports.netlifyRoutesForNextRouteWithData({
|
|
57
|
+
...(0, exports.netlifyRoutesForNextRouteWithData)({
|
|
58
58
|
route: locale === defaultLocale ? route : `/${locale}${route}`,
|
|
59
59
|
dataRoute,
|
|
60
60
|
}));
|
|
@@ -80,7 +80,7 @@ const redirectsForNextRoute = ({ route, buildId, basePath, to, i18n, status = 20
|
|
|
80
80
|
force,
|
|
81
81
|
}));
|
|
82
82
|
exports.redirectsForNextRoute = redirectsForNextRoute;
|
|
83
|
-
const redirectsForNextRouteWithData = ({ route, dataRoute, basePath, to, status = 200, force = false, }) => exports.netlifyRoutesForNextRouteWithData({ route, dataRoute }).map((redirect) => ({
|
|
83
|
+
const redirectsForNextRouteWithData = ({ route, dataRoute, basePath, to, status = 200, force = false, }) => (0, exports.netlifyRoutesForNextRouteWithData)({ route, dataRoute }).map((redirect) => ({
|
|
84
84
|
from: `${basePath}${redirect}`,
|
|
85
85
|
to,
|
|
86
86
|
status,
|
|
@@ -101,7 +101,7 @@ const getApiRewrites = (basePath) => [
|
|
|
101
101
|
];
|
|
102
102
|
exports.getApiRewrites = getApiRewrites;
|
|
103
103
|
const getPreviewRewrites = async ({ basePath, appDir }) => {
|
|
104
|
-
const publicFiles = await globby_1.default('**/*', { cwd: pathe_1.join(appDir, 'public') });
|
|
104
|
+
const publicFiles = await (0, globby_1.default)('**/*', { cwd: (0, pathe_1.join)(appDir, 'public') });
|
|
105
105
|
// Preview mode gets forced to the function, to bypass pre-rendered pages, but static files need to be skipped
|
|
106
106
|
return [
|
|
107
107
|
...publicFiles.map((file) => ({
|
|
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.warnForProblematicUserRewrites = exports.getProblematicUserRewrites = exports.checkZipSize = exports.checkForRootPublish = exports.checkNextSiteHasBuilt = exports.checkForOldFunctions = exports.verifyNetlifyBuildVersion = void 0;
|
|
25
|
+
exports.warnForRootRedirects = exports.warnForProblematicUserRewrites = exports.getProblematicUserRewrites = exports.checkZipSize = exports.checkForRootPublish = exports.checkNextSiteHasBuilt = exports.checkForOldFunctions = exports.verifyNetlifyBuildVersion = void 0;
|
|
26
26
|
/* eslint-disable max-lines */
|
|
27
27
|
const fs_1 = require("fs");
|
|
28
28
|
const path_1 = __importStar(require("path"));
|
|
@@ -36,8 +36,8 @@ const constants_1 = require("../constants");
|
|
|
36
36
|
const REQUIRED_BUILD_VERSION = '>=18.16.0';
|
|
37
37
|
const verifyNetlifyBuildVersion = ({ IS_LOCAL, NETLIFY_BUILD_VERSION, failBuild, }) => {
|
|
38
38
|
// 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
|
|
39
|
-
if (IS_LOCAL && !semver_1.satisfies(NETLIFY_BUILD_VERSION, REQUIRED_BUILD_VERSION, { includePrerelease: true })) {
|
|
40
|
-
return failBuild(outdent_1.outdent `
|
|
39
|
+
if (IS_LOCAL && !(0, semver_1.satisfies)(NETLIFY_BUILD_VERSION, REQUIRED_BUILD_VERSION, { includePrerelease: true })) {
|
|
40
|
+
return failBuild((0, outdent_1.outdent) `
|
|
41
41
|
This version of the Essential Next.js plugin requires netlify-cli@6.12.4 or higher. Please upgrade and try again.
|
|
42
42
|
You can do this by running: "npm install -g netlify-cli@latest" or "yarn global add netlify-cli@latest"
|
|
43
43
|
`);
|
|
@@ -48,31 +48,31 @@ const checkForOldFunctions = async ({ functions }) => {
|
|
|
48
48
|
const allOldFunctions = await functions.list();
|
|
49
49
|
const oldFunctions = allOldFunctions.filter(({ name }) => name.startsWith('next_'));
|
|
50
50
|
if (oldFunctions.length !== 0) {
|
|
51
|
-
console.log(chalk_1.yellowBright(outdent_1.outdent `
|
|
51
|
+
console.log((0, chalk_1.yellowBright)((0, outdent_1.outdent) `
|
|
52
52
|
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_".
|
|
53
53
|
|
|
54
|
-
${chalk_1.reset(oldFunctions.map(({ name }) => `- ${name}`).join('\n'))}
|
|
54
|
+
${(0, chalk_1.reset)(oldFunctions.map(({ name }) => `- ${name}`).join('\n'))}
|
|
55
55
|
|
|
56
56
|
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:
|
|
57
57
|
|
|
58
|
-
${chalk_1.reset(oldFunctions.map(({ mainFile }) => `- ${path_1.default.relative(process.cwd(), path_1.default.dirname(mainFile))}`).join('\n'))}
|
|
58
|
+
${(0, chalk_1.reset)(oldFunctions.map(({ mainFile }) => `- ${path_1.default.relative(process.cwd(), path_1.default.dirname(mainFile))}`).join('\n'))}
|
|
59
59
|
`));
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
exports.checkForOldFunctions = checkForOldFunctions;
|
|
63
63
|
const checkNextSiteHasBuilt = ({ publish, failBuild, }) => {
|
|
64
|
-
if (!fs_1.existsSync(path_1.default.join(publish, 'BUILD_ID'))) {
|
|
64
|
+
if (!(0, fs_1.existsSync)(path_1.default.join(publish, 'BUILD_ID'))) {
|
|
65
65
|
const outWarning = path_1.default.basename(publish) === 'out'
|
|
66
66
|
? `Your publish directory is set to "out", but in most cases it should be ".next".`
|
|
67
67
|
: `In most cases it should be set to ".next", unless you have chosen a custom "distDir" in your Next config.`;
|
|
68
|
-
return failBuild(outdent_1.outdent `
|
|
68
|
+
return failBuild((0, outdent_1.outdent) `
|
|
69
69
|
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.
|
|
70
70
|
${outWarning}
|
|
71
71
|
If you are using "next export" then you should set the environment variable NETLIFY_NEXT_PLUGIN_SKIP to "true".
|
|
72
72
|
`);
|
|
73
73
|
}
|
|
74
|
-
if (fs_1.existsSync(path_1.default.join(publish, 'export-detail.json'))) {
|
|
75
|
-
failBuild(outdent_1.outdent `
|
|
74
|
+
if ((0, fs_1.existsSync)(path_1.default.join(publish, 'export-detail.json'))) {
|
|
75
|
+
failBuild((0, outdent_1.outdent) `
|
|
76
76
|
Detected that "next export" was run, but site is incorrectly publishing the ".next" directory.
|
|
77
77
|
The publish directory should be set to "out", and you should set the environment variable NETLIFY_NEXT_PLUGIN_SKIP to "true".
|
|
78
78
|
`);
|
|
@@ -81,7 +81,7 @@ const checkNextSiteHasBuilt = ({ publish, failBuild, }) => {
|
|
|
81
81
|
exports.checkNextSiteHasBuilt = checkNextSiteHasBuilt;
|
|
82
82
|
const checkForRootPublish = ({ publish, failBuild, }) => {
|
|
83
83
|
if (path_1.default.resolve(publish) === path_1.default.resolve('.')) {
|
|
84
|
-
failBuild(outdent_1.outdent `
|
|
84
|
+
failBuild((0, outdent_1.outdent) `
|
|
85
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.
|
|
86
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.
|
|
87
87
|
`);
|
|
@@ -89,7 +89,7 @@ const checkForRootPublish = ({ publish, failBuild, }) => {
|
|
|
89
89
|
};
|
|
90
90
|
exports.checkForRootPublish = checkForRootPublish;
|
|
91
91
|
const checkZipSize = async (file, maxSize = constants_1.LAMBDA_MAX_SIZE) => {
|
|
92
|
-
if (!fs_1.existsSync(file)) {
|
|
92
|
+
if (!(0, fs_1.existsSync)(file)) {
|
|
93
93
|
console.warn(`Could not check zip size because ${file} does not exist`);
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
@@ -98,8 +98,8 @@ const checkZipSize = async (file, maxSize = constants_1.LAMBDA_MAX_SIZE) => {
|
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
100
|
// We don't fail the build, because the actual hard max size is larger so it might still succeed
|
|
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)}.
|
|
101
|
+
console.log((0, chalk_1.redBright)((0, outdent_1.outdent) `
|
|
102
|
+
The function zip ${(0, chalk_1.yellowBright)((0, path_1.relative)(process.cwd(), file))} size is ${(0, pretty_bytes_1.default)(fileSize)}, which is larger than the maximum supported size of ${(0, pretty_bytes_1.default)(maxSize)}.
|
|
103
103
|
There are a few reasons this could happen. You may have accidentally bundled a large dependency, or you might have a
|
|
104
104
|
large number of pre-rendered pages included.
|
|
105
105
|
`));
|
|
@@ -110,13 +110,13 @@ const checkZipSize = async (file, maxSize = constants_1.LAMBDA_MAX_SIZE) => {
|
|
|
110
110
|
for (let i = 0; i < 10 && i < sortedFiles.length; i++) {
|
|
111
111
|
largest[`${i + 1}`] = {
|
|
112
112
|
File: sortedFiles[i].name,
|
|
113
|
-
'Compressed Size': pretty_bytes_1.default(sortedFiles[i].compressedSize),
|
|
114
|
-
'Uncompressed Size': pretty_bytes_1.default(sortedFiles[i].size),
|
|
113
|
+
'Compressed Size': (0, pretty_bytes_1.default)(sortedFiles[i].compressedSize),
|
|
114
|
+
'Uncompressed Size': (0, pretty_bytes_1.default)(sortedFiles[i].size),
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
|
-
console.log(chalk_1.yellowBright `\n\nThese are the largest files in the zip:`);
|
|
117
|
+
console.log((0, chalk_1.yellowBright) `\n\nThese are the largest files in the zip:`);
|
|
118
118
|
console.table(largest);
|
|
119
|
-
console.log(chalk_1.greenBright `\n\nFor more information on fixing this, see ${chalk_1.blueBright `https://ntl.fyi/large-next-functions`}`);
|
|
119
|
+
console.log((0, chalk_1.greenBright) `\n\nFor more information on fixing this, see ${(0, chalk_1.blueBright) `https://ntl.fyi/large-next-functions`}`);
|
|
120
120
|
};
|
|
121
121
|
exports.checkZipSize = checkZipSize;
|
|
122
122
|
const getProblematicUserRewrites = ({ redirects, basePath, }) => {
|
|
@@ -140,17 +140,23 @@ const getProblematicUserRewrites = ({ redirects, basePath, }) => {
|
|
|
140
140
|
};
|
|
141
141
|
exports.getProblematicUserRewrites = getProblematicUserRewrites;
|
|
142
142
|
const warnForProblematicUserRewrites = ({ redirects, basePath, }) => {
|
|
143
|
-
const userRewrites = exports.getProblematicUserRewrites({ redirects, basePath });
|
|
143
|
+
const userRewrites = (0, exports.getProblematicUserRewrites)({ redirects, basePath });
|
|
144
144
|
if (userRewrites.length === 0) {
|
|
145
145
|
return;
|
|
146
146
|
}
|
|
147
|
-
console.log(chalk_1.yellowBright(outdent_1.outdent `
|
|
147
|
+
console.log((0, chalk_1.yellowBright)((0, outdent_1.outdent) `
|
|
148
148
|
You have the following Netlify rewrite${userRewrites.length === 1 ? '' : 's'} that might cause conflicts with the Next.js plugin:
|
|
149
149
|
|
|
150
|
-
${chalk_1.reset(userRewrites.map(({ from, to, status }) => `- ${from} ${to} ${status}`).join('\n'))}
|
|
150
|
+
${(0, chalk_1.reset)(userRewrites.map(({ from, to, status }) => `- ${from} ${to} ${status}`).join('\n'))}
|
|
151
151
|
|
|
152
152
|
For more information, see https://ntl.fyi/next-rewrites
|
|
153
153
|
`));
|
|
154
154
|
};
|
|
155
155
|
exports.warnForProblematicUserRewrites = warnForProblematicUserRewrites;
|
|
156
|
+
const warnForRootRedirects = ({ appDir }) => {
|
|
157
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(appDir, '_redirects'))) {
|
|
158
|
+
console.log((0, chalk_1.yellowBright)(`You have a "_redirects" file in your root directory, which is not deployed and will be ignored. If you want it to be used, please move it into "public".`));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
exports.warnForRootRedirects = warnForRootRedirects;
|
|
156
162
|
/* eslint-enable max-lines */
|
package/lib/index.js
CHANGED
|
@@ -14,57 +14,57 @@ const plugin = {
|
|
|
14
14
|
async onPreBuild({ constants, netlifyConfig, utils: { build: { failBuild }, cache, }, }) {
|
|
15
15
|
var _a;
|
|
16
16
|
const { publish } = netlifyConfig.build;
|
|
17
|
-
if (utils_1.shouldSkip()) {
|
|
18
|
-
await cache_1.restoreCache({ cache, publish });
|
|
17
|
+
if ((0, utils_1.shouldSkip)()) {
|
|
18
|
+
await (0, cache_1.restoreCache)({ cache, publish });
|
|
19
19
|
console.log('Not running Essential Next.js plugin');
|
|
20
|
-
if (fs_extra_1.existsSync(path_1.join(constants.INTERNAL_FUNCTIONS_SRC, constants_1.HANDLER_FUNCTION_NAME))) {
|
|
20
|
+
if ((0, fs_extra_1.existsSync)((0, path_1.join)(constants.INTERNAL_FUNCTIONS_SRC, constants_1.HANDLER_FUNCTION_NAME))) {
|
|
21
21
|
console.log(`Please ensure you remove any generated functions from ${constants.INTERNAL_FUNCTIONS_SRC}`);
|
|
22
22
|
}
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
-
verification_1.checkForRootPublish({ publish, failBuild });
|
|
26
|
-
verification_1.verifyNetlifyBuildVersion({ failBuild, ...constants });
|
|
27
|
-
await cache_1.restoreCache({ cache, publish });
|
|
25
|
+
(0, verification_1.checkForRootPublish)({ publish, failBuild });
|
|
26
|
+
(0, verification_1.verifyNetlifyBuildVersion)({ failBuild, ...constants });
|
|
27
|
+
await (0, cache_1.restoreCache)({ cache, publish });
|
|
28
28
|
(_a = netlifyConfig.build).environment || (_a.environment = {});
|
|
29
29
|
// eslint-disable-next-line unicorn/consistent-destructuring
|
|
30
30
|
netlifyConfig.build.environment.NEXT_PRIVATE_TARGET = 'server';
|
|
31
31
|
},
|
|
32
32
|
async onBuild({ constants, netlifyConfig, utils: { build: { failBuild }, }, }) {
|
|
33
|
-
if (utils_1.shouldSkip()) {
|
|
33
|
+
if ((0, utils_1.shouldSkip)()) {
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
const { publish } = netlifyConfig.build;
|
|
37
|
-
verification_1.checkNextSiteHasBuilt({ publish, failBuild });
|
|
38
|
-
const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir } = await config_1.getNextConfig({
|
|
37
|
+
(0, verification_1.checkNextSiteHasBuilt)({ publish, failBuild });
|
|
38
|
+
const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir } = await (0, config_1.getNextConfig)({
|
|
39
39
|
publish,
|
|
40
40
|
failBuild,
|
|
41
41
|
});
|
|
42
|
-
const buildId = fs_extra_1.readFileSync(path_1.join(publish, 'BUILD_ID'), 'utf8').trim();
|
|
43
|
-
config_1.configureHandlerFunctions({ netlifyConfig, ignore, publish: path_1.relative(process.cwd(), publish) });
|
|
44
|
-
await functions_1.generateFunctions(constants, appDir);
|
|
45
|
-
await functions_1.generatePagesResolver({ target, constants });
|
|
46
|
-
await files_1.movePublicFiles({ appDir, outdir, publish });
|
|
47
|
-
await files_1.patchNextFiles(basePath);
|
|
42
|
+
const buildId = (0, fs_extra_1.readFileSync)((0, path_1.join)(publish, 'BUILD_ID'), 'utf8').trim();
|
|
43
|
+
(0, config_1.configureHandlerFunctions)({ netlifyConfig, ignore, publish: (0, path_1.relative)(process.cwd(), publish) });
|
|
44
|
+
await (0, functions_1.generateFunctions)(constants, appDir);
|
|
45
|
+
await (0, functions_1.generatePagesResolver)({ target, constants });
|
|
46
|
+
await (0, files_1.movePublicFiles)({ appDir, outdir, publish });
|
|
47
|
+
await (0, files_1.patchNextFiles)(basePath);
|
|
48
48
|
if (process.env.EXPERIMENTAL_MOVE_STATIC_PAGES) {
|
|
49
49
|
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'");
|
|
50
50
|
}
|
|
51
51
|
if (!process.env.SERVE_STATIC_FILES_FROM_ORIGIN) {
|
|
52
|
-
await files_1.moveStaticPages({ target, netlifyConfig, i18n });
|
|
52
|
+
await (0, files_1.moveStaticPages)({ target, netlifyConfig, i18n });
|
|
53
53
|
}
|
|
54
|
-
await redirects_1.generateStaticRedirects({
|
|
54
|
+
await (0, redirects_1.generateStaticRedirects)({
|
|
55
55
|
netlifyConfig,
|
|
56
56
|
nextConfig: { basePath, i18n },
|
|
57
57
|
});
|
|
58
|
-
await functions_1.setupImageFunction({ constants, imageconfig: images, netlifyConfig, basePath });
|
|
59
|
-
await redirects_1.generateRedirects({
|
|
58
|
+
await (0, functions_1.setupImageFunction)({ constants, imageconfig: images, netlifyConfig, basePath });
|
|
59
|
+
await (0, redirects_1.generateRedirects)({
|
|
60
60
|
netlifyConfig,
|
|
61
61
|
nextConfig: { basePath, i18n, trailingSlash, appDir },
|
|
62
62
|
buildId,
|
|
63
63
|
});
|
|
64
64
|
},
|
|
65
65
|
async onPostBuild({ netlifyConfig: { build: { publish }, redirects, }, utils: { status, cache, functions, build: { failBuild }, }, constants: { FUNCTIONS_DIST }, }) {
|
|
66
|
-
await cache_1.saveCache({ cache, publish });
|
|
67
|
-
if (utils_1.shouldSkip()) {
|
|
66
|
+
await (0, cache_1.saveCache)({ cache, publish });
|
|
67
|
+
if ((0, utils_1.shouldSkip)()) {
|
|
68
68
|
status.show({
|
|
69
69
|
title: 'Essential Next.js plugin did not run',
|
|
70
70
|
summary: `Next cache was stored, but all other functions were skipped because ${process.env.NETLIFY_NEXT_PLUGIN_SKIP
|
|
@@ -73,11 +73,12 @@ const plugin = {
|
|
|
73
73
|
});
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
|
-
await verification_1.checkForOldFunctions({ functions });
|
|
77
|
-
await verification_1.checkZipSize(path_1.join(FUNCTIONS_DIST, `${constants_1.ODB_FUNCTION_NAME}.zip`));
|
|
78
|
-
const { basePath } = await config_1.getNextConfig({ publish, failBuild });
|
|
79
|
-
verification_1.warnForProblematicUserRewrites({ basePath, redirects });
|
|
80
|
-
|
|
76
|
+
await (0, verification_1.checkForOldFunctions)({ functions });
|
|
77
|
+
await (0, verification_1.checkZipSize)((0, path_1.join)(FUNCTIONS_DIST, `${constants_1.ODB_FUNCTION_NAME}.zip`));
|
|
78
|
+
const { basePath, appDir } = await (0, config_1.getNextConfig)({ publish, failBuild });
|
|
79
|
+
(0, verification_1.warnForProblematicUserRewrites)({ basePath, redirects });
|
|
80
|
+
(0, verification_1.warnForRootRedirects)({ appDir });
|
|
81
|
+
await (0, files_1.unpatchNextFiles)(basePath);
|
|
81
82
|
},
|
|
82
83
|
};
|
|
83
84
|
module.exports = plugin;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getHandler = void 0;
|
|
4
|
+
const outdent_1 = require("outdent");
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
4
6
|
const { promises } = require('fs');
|
|
5
7
|
const { Server } = require('http');
|
|
6
8
|
const path = require('path');
|
|
7
9
|
// eslint-disable-next-line node/prefer-global/url, node/prefer-global/url-search-params
|
|
8
10
|
const { URLSearchParams, URL } = require('url');
|
|
9
|
-
const { Bridge } = require('@vercel/node/
|
|
11
|
+
const { Bridge } = require('@vercel/node-bridge/bridge');
|
|
10
12
|
const { augmentFsModule, getMaxAge, getMultiValueHeaders, getNextServer } = require('./handlerUtils');
|
|
11
|
-
const makeHandler = () =>
|
|
12
13
|
// We return a function and then call `toString()` on it to serialise it as the launcher function
|
|
13
14
|
// eslint-disable-next-line max-params
|
|
14
|
-
(conf, app, pageRoot, staticManifest = [], mode = 'ssr') => {
|
|
15
|
+
const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') => {
|
|
15
16
|
var _a;
|
|
16
17
|
// This is just so nft knows about the page entrypoints. It's not actually used
|
|
17
18
|
try {
|
|
@@ -23,30 +24,47 @@ const makeHandler = () =>
|
|
|
23
24
|
(_a = process.env).NODE_ENV || (_a.NODE_ENV = 'production');
|
|
24
25
|
// We don't want to write ISR files to disk in the lambda environment
|
|
25
26
|
conf.experimental.isrFlushToDisk = false;
|
|
27
|
+
// This is our flag that we use when patching the source
|
|
26
28
|
// eslint-disable-next-line no-underscore-dangle
|
|
27
29
|
process.env._BYPASS_SSG = 'true';
|
|
28
30
|
// Set during the request as it needs the host header. Hoisted so we can define the function once
|
|
29
31
|
let base;
|
|
30
32
|
augmentFsModule({ promises, staticManifest, pageRoot, getBase: () => base });
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const requestHandler = nextServer.getRequestHandler();
|
|
38
|
-
const server = new Server(async (req, res) => {
|
|
39
|
-
try {
|
|
40
|
-
await requestHandler(req, res);
|
|
33
|
+
// We memoize this because it can be shared between requests, but don't instantiate it until
|
|
34
|
+
// the first request because we need the host and port.
|
|
35
|
+
let bridge;
|
|
36
|
+
const getBridge = (event) => {
|
|
37
|
+
if (bridge) {
|
|
38
|
+
return bridge;
|
|
41
39
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
const url = new URL(event.rawUrl);
|
|
41
|
+
const port = Number.parseInt(url.port) || 80;
|
|
42
|
+
const { host } = event.headers;
|
|
43
|
+
const protocol = event.headers['x-forwarded-proto'] || 'http';
|
|
44
|
+
base = `${protocol}://${host}`;
|
|
45
|
+
const NextServer = getNextServer();
|
|
46
|
+
const nextServer = new NextServer({
|
|
47
|
+
conf,
|
|
48
|
+
dir: path.resolve(__dirname, app),
|
|
49
|
+
customServer: false,
|
|
50
|
+
hostname: url.hostname,
|
|
51
|
+
port,
|
|
52
|
+
});
|
|
53
|
+
const requestHandler = nextServer.getRequestHandler();
|
|
54
|
+
const server = new Server(async (req, res) => {
|
|
55
|
+
try {
|
|
56
|
+
await requestHandler(req, res);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error(error);
|
|
60
|
+
throw new Error('Error handling request. See function logs for details.');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
bridge = new Bridge(server);
|
|
64
|
+
bridge.listen();
|
|
65
|
+
return bridge;
|
|
66
|
+
};
|
|
67
|
+
return async function handler(event, context) {
|
|
50
68
|
var _a, _b, _c;
|
|
51
69
|
let requestMode = mode;
|
|
52
70
|
// Ensure that paths are encoded - but don't double-encode them
|
|
@@ -54,13 +72,7 @@ const makeHandler = () =>
|
|
|
54
72
|
// Next expects to be able to parse the query from the URL
|
|
55
73
|
const query = new URLSearchParams(event.queryStringParameters).toString();
|
|
56
74
|
event.path = query ? `${event.path}?${query}` : event.path;
|
|
57
|
-
|
|
58
|
-
if (staticManifest.length !== 0) {
|
|
59
|
-
const { host } = event.headers;
|
|
60
|
-
const protocol = event.headers['x-forwarded-proto'] || 'http';
|
|
61
|
-
base = `${protocol}://${host}`;
|
|
62
|
-
}
|
|
63
|
-
const { headers, ...result } = await bridge.launcher(event, context);
|
|
75
|
+
const { headers, ...result } = await getBridge(event).launcher(event, context);
|
|
64
76
|
// Convert all headers to multiValueHeaders
|
|
65
77
|
const multiValueHeaders = getMultiValueHeaders(headers);
|
|
66
78
|
if ((_b = (_a = multiValueHeaders['set-cookie']) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.includes('__prerender_bypass')) {
|
|
@@ -81,6 +93,7 @@ const makeHandler = () =>
|
|
|
81
93
|
multiValueHeaders['cache-control'] = ['public, max-age=0, must-revalidate'];
|
|
82
94
|
}
|
|
83
95
|
multiValueHeaders['x-render-mode'] = [requestMode];
|
|
96
|
+
console.log(`[${event.httpMethod}] ${event.path} (${requestMode === null || requestMode === void 0 ? void 0 : requestMode.toUpperCase()})`);
|
|
84
97
|
return {
|
|
85
98
|
...result,
|
|
86
99
|
multiValueHeaders,
|
|
@@ -88,24 +101,25 @@ const makeHandler = () =>
|
|
|
88
101
|
};
|
|
89
102
|
};
|
|
90
103
|
};
|
|
91
|
-
const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '../../..' }) =>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const {
|
|
96
|
-
|
|
104
|
+
const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '../../..' }) =>
|
|
105
|
+
// This is a string, but if you have the right editor plugin it should format as js
|
|
106
|
+
(0, outdent_1.outdent) `
|
|
107
|
+
const { Server } = require("http");
|
|
108
|
+
const { promises } = require("fs");
|
|
109
|
+
// We copy the file here rather than requiring from the node module
|
|
110
|
+
const { Bridge } = require("./bridge");
|
|
111
|
+
const { augmentFsModule, getMaxAge, getMultiValueHeaders, getNextServer } = require('./handlerUtils')
|
|
97
112
|
|
|
98
|
-
const { builder } = require("@netlify/functions");
|
|
99
|
-
const { config } = require("${publishDir}/required-server-files.json")
|
|
100
|
-
let staticManifest
|
|
101
|
-
try {
|
|
102
|
-
|
|
103
|
-
} catch {}
|
|
104
|
-
const path = require("path");
|
|
105
|
-
const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", config.target === "server" ? "server" : "serverless", "pages"));
|
|
106
|
-
exports.handler = ${isODB
|
|
107
|
-
? `builder((${makeHandler
|
|
108
|
-
: `(${makeHandler
|
|
113
|
+
const { builder } = require("@netlify/functions");
|
|
114
|
+
const { config } = require("${publishDir}/required-server-files.json")
|
|
115
|
+
let staticManifest
|
|
116
|
+
try {
|
|
117
|
+
staticManifest = require("${publishDir}/static-manifest.json")
|
|
118
|
+
} catch {}
|
|
119
|
+
const path = require("path");
|
|
120
|
+
const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", config.target === "server" ? "server" : "serverless", "pages"));
|
|
121
|
+
exports.handler = ${isODB
|
|
122
|
+
? `builder((${makeHandler.toString()})(config, "${appDir}", pageRoot, staticManifest, 'odb'));`
|
|
123
|
+
: `(${makeHandler.toString()})(config, "${appDir}", pageRoot, staticManifest, 'ssr');`}
|
|
109
124
|
`;
|
|
110
125
|
exports.getHandler = getHandler;
|
|
111
|
-
/* eslint-enable @typescript-eslint/no-var-requires */
|
|
@@ -13,15 +13,15 @@ const constants_1 = require("../constants");
|
|
|
13
13
|
// build. This is used by the nft bundler to find all the pages.
|
|
14
14
|
const getPageResolver = async ({ publish, target }) => {
|
|
15
15
|
const functionDir = path_1.posix.resolve(path_1.posix.join('.netlify', 'functions', constants_1.HANDLER_FUNCTION_NAME));
|
|
16
|
-
const root = path_1.posix.resolve(slash_1.default(publish), target === 'server' ? 'server' : 'serverless', 'pages');
|
|
17
|
-
const pages = await tiny_glob_1.default('**/*.js', {
|
|
16
|
+
const root = path_1.posix.resolve((0, slash_1.default)(publish), target === 'server' ? 'server' : 'serverless', 'pages');
|
|
17
|
+
const pages = await (0, 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, slash_1.default(page)))}')`)
|
|
22
|
+
.map((page) => `require.resolve('${path_1.posix.relative(functionDir, path_1.posix.join(root, (0, slash_1.default)(page)))}')`)
|
|
23
23
|
.sort();
|
|
24
|
-
return outdent_1.outdent `
|
|
24
|
+
return (0, 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,11 @@ 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 = util_1.promisify(stream_1.pipeline);
|
|
14
|
+
const streamPipeline = (0, util_1.promisify)(stream_1.pipeline);
|
|
15
|
+
/**
|
|
16
|
+
* Downloads a file from the CDN to the local aliased filesystem. This is a fallback, because in most cases we'd expect
|
|
17
|
+
* files required at runtime to not be sent to the CDN.
|
|
18
|
+
*/
|
|
15
19
|
const downloadFile = async (url, destination) => {
|
|
16
20
|
console.log(`Downloading ${url} to ${destination}`);
|
|
17
21
|
const httpx = url.startsWith('https') ? https_1.default : http_1.default;
|
|
@@ -21,7 +25,7 @@ const downloadFile = async (url, destination) => {
|
|
|
21
25
|
reject(new Error(`Failed to download ${url}: ${response.statusCode} ${response.statusMessage || ''}`));
|
|
22
26
|
return;
|
|
23
27
|
}
|
|
24
|
-
const fileStream = fs_1.createWriteStream(destination);
|
|
28
|
+
const fileStream = (0, fs_1.createWriteStream)(destination);
|
|
25
29
|
streamPipeline(response, fileStream)
|
|
26
30
|
.then(resolve)
|
|
27
31
|
.catch((error) => {
|
|
@@ -36,6 +40,9 @@ const downloadFile = async (url, destination) => {
|
|
|
36
40
|
});
|
|
37
41
|
};
|
|
38
42
|
exports.downloadFile = downloadFile;
|
|
43
|
+
/**
|
|
44
|
+
* Parse maxage from a cache-control header
|
|
45
|
+
*/
|
|
39
46
|
const getMaxAge = (header) => {
|
|
40
47
|
const parts = header.split(',');
|
|
41
48
|
let maxAge;
|
|
@@ -66,6 +73,9 @@ const getMultiValueHeaders = (headers) => {
|
|
|
66
73
|
return multiValueHeaders;
|
|
67
74
|
};
|
|
68
75
|
exports.getMultiValueHeaders = getMultiValueHeaders;
|
|
76
|
+
/**
|
|
77
|
+
* Monkey-patch the fs module to download missing files from the CDN
|
|
78
|
+
*/
|
|
69
79
|
const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
70
80
|
// Only do this if we have some static files moved to the CDN
|
|
71
81
|
if (staticManifest.length === 0) {
|
|
@@ -78,7 +88,7 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
78
88
|
const staticFiles = new Map(staticManifest);
|
|
79
89
|
const downloadPromises = new Map();
|
|
80
90
|
// Yes, you can cache stuff locally in a Lambda
|
|
81
|
-
const cacheDir = path_1.default.join(os_1.tmpdir(), 'next-static-cache');
|
|
91
|
+
const cacheDir = path_1.default.join((0, os_1.tmpdir)(), 'next-static-cache');
|
|
82
92
|
// Grab the real fs.promises.readFile...
|
|
83
93
|
const readfileOrig = promises.readFile;
|
|
84
94
|
const statsOrig = promises.stat;
|
|
@@ -90,7 +100,7 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
90
100
|
// We only want the part after `pages/`
|
|
91
101
|
const filePath = file.slice(pageRoot.length + 1);
|
|
92
102
|
// Is it in the CDN and not local?
|
|
93
|
-
if (staticFiles.has(filePath) && !fs_1.existsSync(file)) {
|
|
103
|
+
if (staticFiles.has(filePath) && !(0, fs_1.existsSync)(file)) {
|
|
94
104
|
// This name is safe to use, because it's one that was already created by Next
|
|
95
105
|
const cacheFile = path_1.default.join(cacheDir, filePath);
|
|
96
106
|
const url = `${base}/${staticFiles.get(filePath)}`;
|
|
@@ -99,11 +109,11 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
99
109
|
await downloadPromises.get(url);
|
|
100
110
|
}
|
|
101
111
|
// Have we already cached it? We download every time if running locally to avoid staleness
|
|
102
|
-
if ((!fs_1.existsSync(cacheFile) || process.env.NETLIFY_DEV) && base) {
|
|
112
|
+
if ((!(0, fs_1.existsSync)(cacheFile) || process.env.NETLIFY_DEV) && base) {
|
|
103
113
|
await promises.mkdir(path_1.default.dirname(cacheFile), { recursive: true });
|
|
104
114
|
try {
|
|
105
115
|
// Append the path to our host and we can load it like a regular page
|
|
106
|
-
const downloadPromise = exports.downloadFile(url, cacheFile);
|
|
116
|
+
const downloadPromise = (0, exports.downloadFile)(url, cacheFile);
|
|
107
117
|
downloadPromises.set(url, downloadPromise);
|
|
108
118
|
await downloadPromise;
|
|
109
119
|
}
|
|
@@ -122,7 +132,7 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
122
132
|
if (file.startsWith(pageRoot)) {
|
|
123
133
|
// We only want the part after `pages/`
|
|
124
134
|
const cacheFile = path_1.default.join(cacheDir, file.slice(pageRoot.length + 1));
|
|
125
|
-
if (fs_1.existsSync(cacheFile)) {
|
|
135
|
+
if ((0, fs_1.existsSync)(cacheFile)) {
|
|
126
136
|
return statsOrig(cacheFile, options);
|
|
127
137
|
}
|
|
128
138
|
}
|
|
@@ -130,6 +140,9 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
|
130
140
|
});
|
|
131
141
|
};
|
|
132
142
|
exports.augmentFsModule = augmentFsModule;
|
|
143
|
+
/**
|
|
144
|
+
* Next.js has an annoying habit of needing deep imports, but then moving those in patch releases. This is our abstraction.
|
|
145
|
+
*/
|
|
133
146
|
const getNextServer = () => {
|
|
134
147
|
let NextServer;
|
|
135
148
|
try {
|
|
@@ -142,7 +155,7 @@ const getNextServer = () => {
|
|
|
142
155
|
// A different error, so rethrow it
|
|
143
156
|
throw error;
|
|
144
157
|
}
|
|
145
|
-
// Probably an old version of next
|
|
158
|
+
// Probably an old version of next, so fall through and find it elsewhere.
|
|
146
159
|
}
|
|
147
160
|
if (!NextServer) {
|
|
148
161
|
try {
|
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 = ipx_1.createIPXHandler({
|
|
8
|
+
exports.handler = (0, 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.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "Run Next.js seamlessly on Netlify",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"test:jest": "jest",
|
|
28
28
|
"test:jest:update": "jest --updateSnapshot",
|
|
29
29
|
"test:update": "run-s build build:demo test:jest:update",
|
|
30
|
-
"prepare": "npm run build",
|
|
30
|
+
"prepare": "husky install node_modules/@netlify/eslint-config-node/.husky/ && npm run build",
|
|
31
31
|
"clean": "rimraf lib",
|
|
32
32
|
"build": "tsc",
|
|
33
33
|
"watch": "tsc --watch"
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
},
|
|
54
54
|
"homepage": "https://github.com/netlify/netlify-plugin-nextjs#readme",
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@netlify/functions": "^0.
|
|
57
|
-
"@netlify/ipx": "^0.0.
|
|
58
|
-
"@vercel/node": "^1.
|
|
56
|
+
"@netlify/functions": "^0.11.0",
|
|
57
|
+
"@netlify/ipx": "^0.0.9",
|
|
58
|
+
"@vercel/node-bridge": "^2.1.0",
|
|
59
59
|
"chalk": "^4.1.2",
|
|
60
60
|
"fs-extra": "^10.0.0",
|
|
61
61
|
"globby": "^11.0.4",
|
|
@@ -74,20 +74,21 @@
|
|
|
74
74
|
"@babel/core": "^7.15.8",
|
|
75
75
|
"@babel/preset-env": "^7.15.8",
|
|
76
76
|
"@babel/preset-typescript": "^7.16.0",
|
|
77
|
-
"@netlify/build": "^26.
|
|
78
|
-
"@netlify/eslint-config-node": "^4.1.
|
|
77
|
+
"@netlify/build": "^26.2.2",
|
|
78
|
+
"@netlify/eslint-config-node": "^4.1.7",
|
|
79
79
|
"@testing-library/cypress": "^8.0.1",
|
|
80
80
|
"@types/fs-extra": "^9.0.13",
|
|
81
81
|
"@types/jest": "^27.0.2",
|
|
82
82
|
"@types/mocha": "^9.0.0",
|
|
83
|
+
"@types/node": "^17.0.10",
|
|
83
84
|
"babel-jest": "^27.2.5",
|
|
84
85
|
"cpy": "^8.1.2",
|
|
85
86
|
"cypress": "^9.0.0",
|
|
86
87
|
"eslint-config-next": "^12.0.0",
|
|
87
|
-
"husky": "^
|
|
88
|
+
"husky": "^7.0.4",
|
|
88
89
|
"jest": "^27.0.0",
|
|
89
90
|
"netlify-plugin-cypress": "^2.2.0",
|
|
90
|
-
"next": "^12.0.
|
|
91
|
+
"next": "^12.0.8",
|
|
91
92
|
"npm-run-all": "^4.1.5",
|
|
92
93
|
"prettier": "^2.1.2",
|
|
93
94
|
"react": "^17.0.1",
|
|
@@ -96,12 +97,6 @@
|
|
|
96
97
|
"tmp-promise": "^3.0.2",
|
|
97
98
|
"typescript": "^4.3.4"
|
|
98
99
|
},
|
|
99
|
-
"husky": {
|
|
100
|
-
"hooks": {
|
|
101
|
-
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
|
|
102
|
-
"pre-push": "npm run format"
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
100
|
"engines": {
|
|
106
101
|
"node": ">=12.0.0"
|
|
107
102
|
},
|