@netlify/plugin-nextjs 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -76,6 +76,15 @@ If you are using Nx, then you will need to point `publish` to the folder inside
76
76
  The Essential Next.js plugin now fully supports ISR on Netlify. For more details see
77
77
  [the ISR docs](https://github.com/netlify/netlify-plugin-nextjs/blob/main/docs/isr.md).
78
78
 
79
+ ## Use with `next export`
80
+
81
+ If you are using `next export` to generate a static site, you do not need most of the functionality of this plugin and
82
+ you can remove it. Alternatively you can
83
+ [set the environment variable](https://docs.netlify.com/configure-builds/environment-variables/)
84
+ `NETLIFY_NEXT_PLUGIN_SKIP` to `true` and the plugin will handle caching but won't generate any functions for SSR
85
+ support. See [`demos/next-export`](https://github.com/netlify/netlify-plugin-nextjs/tree/main/demos/next-export) for an
86
+ example.
87
+
79
88
  ## Feedback
80
89
 
81
90
  If you think you have found a bug in the plugin,
@@ -75,7 +75,7 @@ const moveStaticPages = async ({ netlifyConfig, target, i18n, }) => {
75
75
  Object.entries(prerenderManifest.routes).forEach(([route, { initialRevalidateSeconds }]) => {
76
76
  if (initialRevalidateSeconds) {
77
77
  // Find all files used by ISR routes
78
- const trimmedPath = route.slice(1);
78
+ const trimmedPath = route === '/' ? 'index' : route.slice(1);
79
79
  isrFiles.add(`${trimmedPath}.html`);
80
80
  isrFiles.add(`${trimmedPath}.json`);
81
81
  if (initialRevalidateSeconds < constants_1.MINIMUM_REVALIDATE_SECONDS) {
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generateRedirects = void 0;
6
+ exports.generateRedirects = exports.generateStaticRedirects = void 0;
7
7
  const fs_extra_1 = require("fs-extra");
8
8
  const globby_1 = __importDefault(require("globby"));
9
9
  const pathe_1 = require("pathe");
@@ -37,6 +37,14 @@ const generateLocaleRedirects = ({ i18n, basePath, trailingSlash, }) => {
37
37
  });
38
38
  return redirects;
39
39
  };
40
+ const generateStaticRedirects = ({ netlifyConfig, nextConfig: { i18n, basePath }, }) => {
41
+ // Static files are in `static`
42
+ netlifyConfig.redirects.push({ from: `${basePath}/_next/static/*`, to: `/static/:splat`, status: 200 });
43
+ if (i18n) {
44
+ netlifyConfig.redirects.push({ from: `${basePath}/:locale/_next/static/*`, to: `/static/:splat`, status: 200 });
45
+ }
46
+ };
47
+ exports.generateStaticRedirects = generateStaticRedirects;
40
48
  const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath, trailingSlash, appDir }, }) => {
41
49
  const { dynamicRoutes, routes: staticRoutes } = await fs_extra_1.readJSON(pathe_1.join(netlifyConfig.build.publish, 'prerender-manifest.json'));
42
50
  netlifyConfig.redirects.push(...constants_1.HIDDEN_PATHS.map((path) => ({
@@ -72,14 +80,9 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
72
80
  pageRedirects.push(...utils_1.netlifyRoutesForNextRoute(route));
73
81
  dataRedirects.push(...utils_1.netlifyRoutesForNextRoute(dataRoute));
74
82
  });
75
- if (i18n) {
76
- netlifyConfig.redirects.push({ from: `${basePath}/:locale/_next/static/*`, to: `/static/:splat`, status: 200 });
77
- }
78
83
  const publicFiles = await globby_1.default('**/*', { cwd: pathe_1.join(appDir, 'public') });
79
84
  // This is only used in prod, so dev uses `next dev` directly
80
85
  netlifyConfig.redirects.push(
81
- // Static files are in `static`
82
- { from: `${basePath}/_next/static/*`, to: `/static/:splat`, status: 200 },
83
86
  // API routes always need to be served from the regular function
84
87
  {
85
88
  from: `${basePath}/api`,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.netlifyRoutesForNextRoute = void 0;
3
+ exports.shouldSkip = exports.netlifyRoutesForNextRoute = void 0;
4
4
  const constants_1 = require("../constants");
5
5
  const netlifyRoutesForNextRoute = (nextRoute) => {
6
6
  const netlifyRoutes = [nextRoute];
@@ -30,3 +30,8 @@ const netlifyRoutesForNextRoute = (nextRoute) => {
30
30
  .replace(constants_1.DYNAMIC_PARAMETER_REGEX, '/:$1'));
31
31
  };
32
32
  exports.netlifyRoutesForNextRoute = netlifyRoutesForNextRoute;
33
+ const shouldSkip = () => process.env.NEXT_PLUGIN_FORCE_RUN === 'false' ||
34
+ process.env.NEXT_PLUGIN_FORCE_RUN === '0' ||
35
+ process.env.NETLIFY_NEXT_PLUGIN_SKIP === 'true' ||
36
+ process.env.NETLIFY_NEXT_PLUGIN_SKIP === '1';
37
+ exports.shouldSkip = shouldSkip;
@@ -22,7 +22,8 @@ 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.checkZipSize = exports.checkForRootPublish = exports.checkNextSiteHasBuilt = exports.checkForOldFunctions = exports.verifyNetlifyBuildVersion = void 0;
25
+ exports.warnForProblematicUserRewrites = exports.getProblematicUserRewrites = exports.checkZipSize = exports.checkForRootPublish = exports.checkNextSiteHasBuilt = exports.checkForOldFunctions = exports.verifyNetlifyBuildVersion = void 0;
26
+ /* eslint-disable max-lines */
26
27
  const fs_1 = require("fs");
27
28
  const path_1 = __importStar(require("path"));
28
29
  const chalk_1 = require("chalk");
@@ -67,14 +68,13 @@ const checkNextSiteHasBuilt = ({ publish, failBuild, }) => {
67
68
  return failBuild(outdent_1.outdent `
68
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.
69
70
  ${outWarning}
70
- If you are using "next export" then the Essential Next.js plugin should be removed. See https://ntl.fyi/remove-plugin for details.
71
+ If you are using "next export" then you should set the environment variable NETLIFY_NEXT_PLUGIN_SKIP to "true".
71
72
  `);
72
73
  }
73
74
  if (fs_1.existsSync(path_1.default.join(publish, 'export-detail.json'))) {
74
75
  failBuild(outdent_1.outdent `
75
76
  Detected that "next export" was run, but site is incorrectly publishing the ".next" directory.
76
- This plugin is not needed for "next export" so should be removed, and publish directory set to "out".
77
- See https://ntl.fyi/remove-plugin for more details on how to remove this plugin.
77
+ The publish directory should be set to "out", and you should set the environment variable NETLIFY_NEXT_PLUGIN_SKIP to "true".
78
78
  `);
79
79
  }
80
80
  };
@@ -119,3 +119,38 @@ const checkZipSize = async (file, maxSize = constants_1.LAMBDA_MAX_SIZE) => {
119
119
  console.log(chalk_1.greenBright `\n\nFor more information on fixing this, see ${chalk_1.blueBright `https://ntl.fyi/large-next-functions`}`);
120
120
  };
121
121
  exports.checkZipSize = checkZipSize;
122
+ const getProblematicUserRewrites = ({ redirects, basePath, }) => {
123
+ const userRewrites = [];
124
+ for (const redirect of redirects) {
125
+ // This is the first of the plugin-generated redirects so we can stop checking
126
+ if (redirect.from === `${basePath}/_next/static/*` && redirect.to === `/static/:splat` && redirect.status === 200) {
127
+ break;
128
+ }
129
+ if (
130
+ // Redirects are fine
131
+ (redirect.status === 200 || redirect.status === 404) &&
132
+ // Rewriting to a function is also fine
133
+ !redirect.to.startsWith('/.netlify/') &&
134
+ // ...so is proxying
135
+ !redirect.to.startsWith('http')) {
136
+ userRewrites.push(redirect);
137
+ }
138
+ }
139
+ return userRewrites;
140
+ };
141
+ exports.getProblematicUserRewrites = getProblematicUserRewrites;
142
+ const warnForProblematicUserRewrites = ({ redirects, basePath, }) => {
143
+ const userRewrites = exports.getProblematicUserRewrites({ redirects, basePath });
144
+ if (userRewrites.length === 0) {
145
+ return;
146
+ }
147
+ console.log(chalk_1.yellowBright(outdent_1.outdent `
148
+ You have the following Netlify rewrite${userRewrites.length === 1 ? '' : 's'} that might cause conflicts with the Next.js plugin:
149
+
150
+ ${chalk_1.reset(userRewrites.map(({ from, to, status }) => `- ${from} ${to} ${status}`).join('\n'))}
151
+
152
+ For more information, see https://ntl.fyi/next-rewrites
153
+ `));
154
+ };
155
+ exports.warnForProblematicUserRewrites = warnForProblematicUserRewrites;
156
+ /* eslint-enable max-lines */
package/lib/index.js CHANGED
@@ -1,17 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const path_1 = require("path");
4
+ const fs_extra_1 = require("fs-extra");
4
5
  const constants_1 = require("./constants");
5
6
  const cache_1 = require("./helpers/cache");
6
7
  const config_1 = require("./helpers/config");
7
8
  const files_1 = require("./helpers/files");
8
9
  const functions_1 = require("./helpers/functions");
9
10
  const redirects_1 = require("./helpers/redirects");
11
+ const utils_1 = require("./helpers/utils");
10
12
  const verification_1 = require("./helpers/verification");
11
13
  const plugin = {
12
14
  async onPreBuild({ constants, netlifyConfig, utils: { build: { failBuild }, cache, }, }) {
13
15
  var _a;
14
16
  const { publish } = netlifyConfig.build;
17
+ if (utils_1.shouldSkip()) {
18
+ await cache_1.restoreCache({ cache, publish });
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))) {
21
+ console.log(`Please ensure you remove any generated functions from ${constants.INTERNAL_FUNCTIONS_SRC}`);
22
+ }
23
+ return;
24
+ }
15
25
  verification_1.checkForRootPublish({ publish, failBuild });
16
26
  verification_1.verifyNetlifyBuildVersion({ failBuild, ...constants });
17
27
  await cache_1.restoreCache({ cache, publish });
@@ -20,6 +30,9 @@ const plugin = {
20
30
  netlifyConfig.build.environment.NEXT_PRIVATE_TARGET = 'server';
21
31
  },
22
32
  async onBuild({ constants, netlifyConfig, utils: { build: { failBuild }, }, }) {
33
+ if (utils_1.shouldSkip()) {
34
+ return;
35
+ }
23
36
  const { publish } = netlifyConfig.build;
24
37
  verification_1.checkNextSiteHasBuilt({ publish, failBuild });
25
38
  const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir } = await config_1.getNextConfig({
@@ -39,17 +52,31 @@ const plugin = {
39
52
  if (!process.env.SERVE_STATIC_FILES_FROM_ORIGIN) {
40
53
  await files_1.moveStaticPages({ target, netlifyConfig, i18n });
41
54
  }
55
+ await redirects_1.generateStaticRedirects({
56
+ netlifyConfig,
57
+ nextConfig: { basePath, i18n },
58
+ });
42
59
  await functions_1.setupImageFunction({ constants, imageconfig: images, netlifyConfig, basePath });
43
60
  await redirects_1.generateRedirects({
44
61
  netlifyConfig,
45
62
  nextConfig: { basePath, i18n, trailingSlash, appDir },
46
63
  });
47
64
  },
48
- async onPostBuild({ netlifyConfig, utils: { cache, functions, build: { failBuild }, }, constants: { FUNCTIONS_DIST }, }) {
49
- await cache_1.saveCache({ cache, publish: netlifyConfig.build.publish });
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()) {
68
+ status.show({
69
+ title: 'Essential Next.js plugin did not run',
70
+ summary: `Next cache was stored, but all other functions were skipped because ${process.env.NETLIFY_NEXT_PLUGIN_SKIP
71
+ ? `NETLIFY_NEXT_PLUGIN_SKIP is set`
72
+ : `NEXT_PLUGIN_FORCE_RUN is set to ${process.env.NEXT_PLUGIN_FORCE_RUN}`}`,
73
+ });
74
+ return;
75
+ }
50
76
  await verification_1.checkForOldFunctions({ functions });
51
77
  await verification_1.checkZipSize(path_1.join(FUNCTIONS_DIST, `${constants_1.ODB_FUNCTION_NAME}.zip`));
52
- const { basePath } = await config_1.getNextConfig({ publish: netlifyConfig.build.publish, failBuild });
78
+ const { basePath } = await config_1.getNextConfig({ publish, failBuild });
79
+ verification_1.warnForProblematicUserRewrites({ basePath, redirects });
53
80
  await files_1.unpatchNextFiles(basePath);
54
81
  },
55
82
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -74,8 +74,8 @@
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": "^20.3.0",
78
- "@netlify/eslint-config-node": "^4.0.0",
77
+ "@netlify/build": "^25.0.1",
78
+ "@netlify/eslint-config-node": "^4.0.1",
79
79
  "@testing-library/cypress": "^8.0.1",
80
80
  "@types/fs-extra": "^9.0.13",
81
81
  "@types/jest": "^27.0.2",