@sitecore-jss/sitecore-jss-nextjs 22.2.0-canary.3 → 22.2.0-canary.30

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.
@@ -24,7 +24,9 @@ const sitecore_jss_react_2 = require("@sitecore-jss/sitecore-jss-react");
24
24
  const sitecore_jss_react_3 = require("@sitecore-jss/sitecore-jss-react");
25
25
  const layout_1 = require("@sitecore-jss/sitecore-jss/layout");
26
26
  exports.NextImage = (0, sitecore_jss_react_1.withFieldMetadata)((0, sitecore_jss_react_2.withEmptyFieldEditingComponent)((_a) => {
27
+ var _b;
27
28
  var { editable = true, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
29
+ const sitecoreContext = react_1.default.useContext(sitecore_jss_react_1.SitecoreContextReactContext);
28
30
  // next handles src and we use a custom loader,
29
31
  // throw error if these are present
30
32
  if (otherProps.src) {
@@ -46,8 +48,11 @@ exports.NextImage = (0, sitecore_jss_react_1.withFieldMetadata)((0, sitecore_jss
46
48
  if (!img) {
47
49
  return null;
48
50
  }
51
+ // disable image optimization for Edit and Preview, but preserve original value if true
52
+ const unoptimized = otherProps.unoptimized ||
53
+ ((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.pageState) !== layout_1.LayoutServicePageState.Normal;
49
54
  const attrs = Object.assign(Object.assign(Object.assign({}, img), otherProps), { fill,
50
- priority, src: media_1.mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix) });
55
+ priority, src: media_1.mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix), unoptimized });
51
56
  const imageProps = Object.assign(Object.assign({}, attrs), {
52
57
  // force replace /media with /jssmedia in src since we _know_ we will be adding a 'mw' query string parameter
53
58
  // this is required for Sitecore media API resizing to work properly
@@ -12,4 +12,4 @@ exports.EDITING_PASS_THROUGH_HEADERS = ['authorization', 'cookie'];
12
12
  /**
13
13
  * Default allowed origins for editing requests. This is used to enforce CORS, CSP headers.
14
14
  */
15
- exports.EDITING_ALLOWED_ORIGINS = ['https://pages*.cloud', 'https://pages.sitecorecloud.io'];
15
+ exports.EDITING_ALLOWED_ORIGINS = ['https://pages.sitecorecloud.io'];
@@ -224,7 +224,6 @@ class MetadataHandler {
224
224
  },
225
225
  // Cache the preview data for 3 seconds to ensure the page is rendered with the correct preview data not the cached one
226
226
  {
227
- path: query.route,
228
227
  maxAge: 3,
229
228
  });
230
229
  // Cookies with the SameSite=Lax policy set by Next.js setPreviewData function causes CORS issue
@@ -43,6 +43,7 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
43
43
  hostname,
44
44
  });
45
45
  const createResponse = () => __awaiter(this, void 0, void 0, function* () {
46
+ var _a;
46
47
  if (this.config.disabled && this.config.disabled(req, res || server_1.NextResponse.next())) {
47
48
  sitecore_jss_1.debug.redirects('skipped (redirects middleware is disabled)');
48
49
  return res || server_1.NextResponse.next();
@@ -64,47 +65,44 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
64
65
  existsRedirect.target.includes(hostname))) {
65
66
  existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
66
67
  }
67
- const url = req.nextUrl.clone();
68
+ const url = this.normalizeUrl(req.nextUrl.clone());
68
69
  if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
69
70
  url.href = existsRedirect.target;
70
71
  }
71
72
  else {
72
- const source = `${url.pathname}${url.search}`;
73
- url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
73
+ const source = `${url.pathname.replace(/\/*$/gi, '')}${url.search}`;
74
74
  const urlFirstPart = existsRedirect.target.split('/')[1];
75
75
  if (this.locales.includes(urlFirstPart)) {
76
- url.locale = urlFirstPart;
76
+ req.nextUrl.locale = urlFirstPart;
77
77
  existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
78
78
  }
79
79
  const target = source
80
80
  .replace((0, regex_parser_1.default)(existsRedirect.pattern), existsRedirect.target)
81
81
  .replace(/^\/\//, '/')
82
82
  .split('?');
83
- url.pathname = target[0];
84
- if (target[1]) {
85
- const newParams = new URLSearchParams(target[1]);
86
- for (const [key, val] of newParams.entries()) {
87
- url.searchParams.append(key, val);
88
- }
83
+ if (url.search && existsRedirect.isQueryStringPreserved) {
84
+ const targetQueryString = (_a = target[1]) !== null && _a !== void 0 ? _a : '';
85
+ url.search = '?' + new URLSearchParams(`${url.search}&${targetQueryString}`).toString();
89
86
  }
87
+ else if (target[1]) {
88
+ url.search = '?' + target[1];
89
+ }
90
+ else {
91
+ url.search = '';
92
+ }
93
+ const prepareNewURL = new URL(`${target[0]}${url.search}`, url.origin);
94
+ url.href = prepareNewURL.href;
90
95
  }
91
96
  const redirectUrl = decodeURIComponent(url.href);
92
97
  /** return Response redirect with http code of redirect type **/
93
98
  switch (existsRedirect.redirectType) {
94
99
  case site_1.REDIRECT_TYPE_301:
95
- return server_1.NextResponse.redirect(redirectUrl, {
96
- status: 301,
97
- statusText: 'Moved Permanently',
98
- headers: res === null || res === void 0 ? void 0 : res.headers,
99
- });
100
+ return server_1.NextResponse.redirect(redirectUrl, Object.assign(Object.assign({}, res), { status: 301, statusText: 'Moved Permanently', headers: res === null || res === void 0 ? void 0 : res.headers }));
100
101
  case site_1.REDIRECT_TYPE_302:
101
- return server_1.NextResponse.redirect(redirectUrl, {
102
- status: 302,
103
- statusText: 'Found',
104
- headers: res === null || res === void 0 ? void 0 : res.headers,
105
- });
106
- case site_1.REDIRECT_TYPE_SERVER_TRANSFER:
107
- return server_1.NextResponse.rewrite(redirectUrl, res);
102
+ return server_1.NextResponse.redirect(redirectUrl, Object.assign(Object.assign({}, res), { status: 302, statusText: 'Found', headers: res === null || res === void 0 ? void 0 : res.headers }));
103
+ case site_1.REDIRECT_TYPE_SERVER_TRANSFER: {
104
+ return this.rewrite(redirectUrl, req, res || server_1.NextResponse.next());
105
+ }
108
106
  default:
109
107
  return res || server_1.NextResponse.next();
110
108
  }
@@ -149,8 +147,9 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
149
147
  getExistsRedirect(req, siteName) {
150
148
  return __awaiter(this, void 0, void 0, function* () {
151
149
  const redirects = yield this.redirectsService.fetchRedirects(siteName);
152
- const tragetURL = req.nextUrl.pathname;
153
- const targetQS = req.nextUrl.search || '';
150
+ const normalizedUrl = this.normalizeUrl(req.nextUrl.clone());
151
+ const tragetURL = normalizedUrl.pathname;
152
+ const targetQS = normalizedUrl.search || '';
154
153
  const language = this.getLanguage(req);
155
154
  const modifyRedirects = structuredClone(redirects);
156
155
  return modifyRedirects.length
@@ -163,7 +162,7 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
163
162
  .replace(/(?<!\\)\?/g, '\\?')
164
163
  .replace(/\$\/gi$/g, '')}[\/]?$/gi`;
165
164
  return (((0, regex_parser_1.default)(redirect.pattern).test(tragetURL) ||
166
- (0, regex_parser_1.default)(redirect.pattern).test(`${tragetURL}${targetQS}`) ||
165
+ (0, regex_parser_1.default)(redirect.pattern).test(`${tragetURL.replace(/\/*$/gi, '')}${targetQS}`) ||
167
166
  (0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${tragetURL}`) ||
168
167
  (0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${tragetURL}${targetQS}`)) &&
169
168
  (redirect.locale
@@ -173,5 +172,44 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
173
172
  : undefined;
174
173
  });
175
174
  }
175
+ /**
176
+ * When a user clicks on a link generated by the Link component from next/link,
177
+ * Next.js adds special parameters in the route called path.
178
+ * This method removes these special parameters.
179
+ * @param {URL} url
180
+ * @returns {string} normalize url
181
+ */
182
+ normalizeUrl(url) {
183
+ if (!url.search) {
184
+ return url;
185
+ }
186
+ /**
187
+ * Prepare special parameters for exclusion.
188
+ */
189
+ const splittedPathname = url.pathname
190
+ .split('/')
191
+ .filter((route) => route)
192
+ .map((route) => `path=${route}`);
193
+ /**
194
+ * Remove special parameters(Next.JS)
195
+ * Example: /about/contact/us
196
+ * When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
197
+ * http://host/about/contact/us?path=about&path=contact&path=us
198
+ */
199
+ const newQueryString = url.search
200
+ .replace(/^\?/, '')
201
+ .split('&')
202
+ .filter((param) => {
203
+ if (!splittedPathname.includes(param)) {
204
+ return param;
205
+ }
206
+ return false;
207
+ })
208
+ .join('&');
209
+ if (newQueryString) {
210
+ return new URL(`${url.pathname}?${newQueryString}`, url.origin);
211
+ }
212
+ return new URL(`${url.pathname}`, url.origin);
213
+ }
176
214
  }
177
215
  exports.RedirectsMiddleware = RedirectsMiddleware;
@@ -12,13 +12,15 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import { mediaApi } from '@sitecore-jss/sitecore-jss/media';
13
13
  import PropTypes from 'prop-types';
14
14
  import React from 'react';
15
- import { getEEMarkup, withFieldMetadata, } from '@sitecore-jss/sitecore-jss-react';
15
+ import { getEEMarkup, withFieldMetadata, SitecoreContextReactContext, } from '@sitecore-jss/sitecore-jss-react';
16
16
  import Image from 'next/image';
17
17
  import { withEmptyFieldEditingComponent } from '@sitecore-jss/sitecore-jss-react';
18
18
  import { DefaultEmptyFieldEditingComponentImage } from '@sitecore-jss/sitecore-jss-react';
19
- import { isFieldValueEmpty } from '@sitecore-jss/sitecore-jss/layout';
19
+ import { isFieldValueEmpty, LayoutServicePageState } from '@sitecore-jss/sitecore-jss/layout';
20
20
  export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
21
+ var _b;
21
22
  var { editable = true, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
23
+ const sitecoreContext = React.useContext(SitecoreContextReactContext);
22
24
  // next handles src and we use a custom loader,
23
25
  // throw error if these are present
24
26
  if (otherProps.src) {
@@ -40,8 +42,11 @@ export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) =
40
42
  if (!img) {
41
43
  return null;
42
44
  }
45
+ // disable image optimization for Edit and Preview, but preserve original value if true
46
+ const unoptimized = otherProps.unoptimized ||
47
+ ((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.pageState) !== LayoutServicePageState.Normal;
43
48
  const attrs = Object.assign(Object.assign(Object.assign({}, img), otherProps), { fill,
44
- priority, src: mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix) });
49
+ priority, src: mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix), unoptimized });
45
50
  const imageProps = Object.assign(Object.assign({}, attrs), {
46
51
  // force replace /media with /jssmedia in src since we _know_ we will be adding a 'mw' query string parameter
47
52
  // this is required for Sitecore media API resizing to work properly
@@ -9,4 +9,4 @@ export const EDITING_PASS_THROUGH_HEADERS = ['authorization', 'cookie'];
9
9
  /**
10
10
  * Default allowed origins for editing requests. This is used to enforce CORS, CSP headers.
11
11
  */
12
- export const EDITING_ALLOWED_ORIGINS = ['https://pages*.cloud', 'https://pages.sitecorecloud.io'];
12
+ export const EDITING_ALLOWED_ORIGINS = ['https://pages.sitecorecloud.io'];
@@ -219,7 +219,6 @@ export class MetadataHandler {
219
219
  },
220
220
  // Cache the preview data for 3 seconds to ensure the page is rendered with the correct preview data not the cached one
221
221
  {
222
- path: query.route,
223
222
  maxAge: 3,
224
223
  });
225
224
  // Cookies with the SameSite=Lax policy set by Next.js setPreviewData function causes CORS issue
@@ -37,6 +37,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
37
37
  hostname,
38
38
  });
39
39
  const createResponse = () => __awaiter(this, void 0, void 0, function* () {
40
+ var _a;
40
41
  if (this.config.disabled && this.config.disabled(req, res || NextResponse.next())) {
41
42
  debug.redirects('skipped (redirects middleware is disabled)');
42
43
  return res || NextResponse.next();
@@ -58,47 +59,44 @@ export class RedirectsMiddleware extends MiddlewareBase {
58
59
  existsRedirect.target.includes(hostname))) {
59
60
  existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
60
61
  }
61
- const url = req.nextUrl.clone();
62
+ const url = this.normalizeUrl(req.nextUrl.clone());
62
63
  if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
63
64
  url.href = existsRedirect.target;
64
65
  }
65
66
  else {
66
- const source = `${url.pathname}${url.search}`;
67
- url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
67
+ const source = `${url.pathname.replace(/\/*$/gi, '')}${url.search}`;
68
68
  const urlFirstPart = existsRedirect.target.split('/')[1];
69
69
  if (this.locales.includes(urlFirstPart)) {
70
- url.locale = urlFirstPart;
70
+ req.nextUrl.locale = urlFirstPart;
71
71
  existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
72
72
  }
73
73
  const target = source
74
74
  .replace(regexParser(existsRedirect.pattern), existsRedirect.target)
75
75
  .replace(/^\/\//, '/')
76
76
  .split('?');
77
- url.pathname = target[0];
78
- if (target[1]) {
79
- const newParams = new URLSearchParams(target[1]);
80
- for (const [key, val] of newParams.entries()) {
81
- url.searchParams.append(key, val);
82
- }
77
+ if (url.search && existsRedirect.isQueryStringPreserved) {
78
+ const targetQueryString = (_a = target[1]) !== null && _a !== void 0 ? _a : '';
79
+ url.search = '?' + new URLSearchParams(`${url.search}&${targetQueryString}`).toString();
83
80
  }
81
+ else if (target[1]) {
82
+ url.search = '?' + target[1];
83
+ }
84
+ else {
85
+ url.search = '';
86
+ }
87
+ const prepareNewURL = new URL(`${target[0]}${url.search}`, url.origin);
88
+ url.href = prepareNewURL.href;
84
89
  }
85
90
  const redirectUrl = decodeURIComponent(url.href);
86
91
  /** return Response redirect with http code of redirect type **/
87
92
  switch (existsRedirect.redirectType) {
88
93
  case REDIRECT_TYPE_301:
89
- return NextResponse.redirect(redirectUrl, {
90
- status: 301,
91
- statusText: 'Moved Permanently',
92
- headers: res === null || res === void 0 ? void 0 : res.headers,
93
- });
94
+ return NextResponse.redirect(redirectUrl, Object.assign(Object.assign({}, res), { status: 301, statusText: 'Moved Permanently', headers: res === null || res === void 0 ? void 0 : res.headers }));
94
95
  case REDIRECT_TYPE_302:
95
- return NextResponse.redirect(redirectUrl, {
96
- status: 302,
97
- statusText: 'Found',
98
- headers: res === null || res === void 0 ? void 0 : res.headers,
99
- });
100
- case REDIRECT_TYPE_SERVER_TRANSFER:
101
- return NextResponse.rewrite(redirectUrl, res);
96
+ return NextResponse.redirect(redirectUrl, Object.assign(Object.assign({}, res), { status: 302, statusText: 'Found', headers: res === null || res === void 0 ? void 0 : res.headers }));
97
+ case REDIRECT_TYPE_SERVER_TRANSFER: {
98
+ return this.rewrite(redirectUrl, req, res || NextResponse.next());
99
+ }
102
100
  default:
103
101
  return res || NextResponse.next();
104
102
  }
@@ -143,8 +141,9 @@ export class RedirectsMiddleware extends MiddlewareBase {
143
141
  getExistsRedirect(req, siteName) {
144
142
  return __awaiter(this, void 0, void 0, function* () {
145
143
  const redirects = yield this.redirectsService.fetchRedirects(siteName);
146
- const tragetURL = req.nextUrl.pathname;
147
- const targetQS = req.nextUrl.search || '';
144
+ const normalizedUrl = this.normalizeUrl(req.nextUrl.clone());
145
+ const tragetURL = normalizedUrl.pathname;
146
+ const targetQS = normalizedUrl.search || '';
148
147
  const language = this.getLanguage(req);
149
148
  const modifyRedirects = structuredClone(redirects);
150
149
  return modifyRedirects.length
@@ -157,7 +156,7 @@ export class RedirectsMiddleware extends MiddlewareBase {
157
156
  .replace(/(?<!\\)\?/g, '\\?')
158
157
  .replace(/\$\/gi$/g, '')}[\/]?$/gi`;
159
158
  return ((regexParser(redirect.pattern).test(tragetURL) ||
160
- regexParser(redirect.pattern).test(`${tragetURL}${targetQS}`) ||
159
+ regexParser(redirect.pattern).test(`${tragetURL.replace(/\/*$/gi, '')}${targetQS}`) ||
161
160
  regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${tragetURL}`) ||
162
161
  regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${tragetURL}${targetQS}`)) &&
163
162
  (redirect.locale
@@ -167,4 +166,43 @@ export class RedirectsMiddleware extends MiddlewareBase {
167
166
  : undefined;
168
167
  });
169
168
  }
169
+ /**
170
+ * When a user clicks on a link generated by the Link component from next/link,
171
+ * Next.js adds special parameters in the route called path.
172
+ * This method removes these special parameters.
173
+ * @param {URL} url
174
+ * @returns {string} normalize url
175
+ */
176
+ normalizeUrl(url) {
177
+ if (!url.search) {
178
+ return url;
179
+ }
180
+ /**
181
+ * Prepare special parameters for exclusion.
182
+ */
183
+ const splittedPathname = url.pathname
184
+ .split('/')
185
+ .filter((route) => route)
186
+ .map((route) => `path=${route}`);
187
+ /**
188
+ * Remove special parameters(Next.JS)
189
+ * Example: /about/contact/us
190
+ * When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
191
+ * http://host/about/contact/us?path=about&path=contact&path=us
192
+ */
193
+ const newQueryString = url.search
194
+ .replace(/^\?/, '')
195
+ .split('&')
196
+ .filter((param) => {
197
+ if (!splittedPathname.includes(param)) {
198
+ return param;
199
+ }
200
+ return false;
201
+ })
202
+ .join('&');
203
+ if (newQueryString) {
204
+ return new URL(`${url.pathname}?${newQueryString}`, url.origin);
205
+ }
206
+ return new URL(`${url.pathname}`, url.origin);
207
+ }
170
208
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sitecore-jss/sitecore-jss-nextjs",
3
- "version": "22.2.0-canary.3",
3
+ "version": "22.2.0-canary.30",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "sideEffects": false,
@@ -72,9 +72,9 @@
72
72
  "react-dom": "^18.2.0"
73
73
  },
74
74
  "dependencies": {
75
- "@sitecore-jss/sitecore-jss": "^22.2.0-canary.3",
76
- "@sitecore-jss/sitecore-jss-dev-tools": "^22.2.0-canary.3",
77
- "@sitecore-jss/sitecore-jss-react": "^22.2.0-canary.3",
75
+ "@sitecore-jss/sitecore-jss": "^22.2.0-canary.30",
76
+ "@sitecore-jss/sitecore-jss-dev-tools": "^22.2.0-canary.30",
77
+ "@sitecore-jss/sitecore-jss-react": "^22.2.0-canary.30",
78
78
  "@vercel/kv": "^0.2.1",
79
79
  "prop-types": "^15.8.1",
80
80
  "regex-parser": "^2.2.11",
@@ -82,7 +82,7 @@
82
82
  },
83
83
  "description": "",
84
84
  "types": "types/index.d.ts",
85
- "gitHead": "04f6c9e59f3ca0b31f4f575c9cf3ee93d6b88f61",
85
+ "gitHead": "d08b137fb78470b40970e3d7210806f1c954d0e7",
86
86
  "files": [
87
87
  "dist",
88
88
  "types",
@@ -37,4 +37,12 @@ export declare class RedirectsMiddleware extends MiddlewareBase {
37
37
  * @private
38
38
  */
39
39
  private getExistsRedirect;
40
+ /**
41
+ * When a user clicks on a link generated by the Link component from next/link,
42
+ * Next.js adds special parameters in the route called path.
43
+ * This method removes these special parameters.
44
+ * @param {URL} url
45
+ * @returns {string} normalize url
46
+ */
47
+ private normalizeUrl;
40
48
  }