@sitecore-jss/sitecore-jss-nextjs 21.10.0 → 21.10.1-canary.1
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/LICENSE.txt +202 -202
- package/README.md +9 -9
- package/dist/cjs/middleware/middleware.js +28 -3
- package/dist/cjs/middleware/redirects-middleware.js +214 -92
- package/dist/esm/middleware/middleware.js +27 -2
- package/dist/esm/middleware/redirects-middleware.js +215 -93
- package/package.json +5 -5
- package/types/middleware/middleware.d.ts +9 -1
- package/types/middleware/redirects-middleware.d.ts +48 -8
|
@@ -11,7 +11,8 @@ import regexParser from 'regex-parser';
|
|
|
11
11
|
import { NextResponse } from 'next/server';
|
|
12
12
|
import { GraphQLRedirectsService, REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT_TYPE_SERVER_TRANSFER, } from '@sitecore-jss/sitecore-jss/site';
|
|
13
13
|
import { debug } from '@sitecore-jss/sitecore-jss';
|
|
14
|
-
import {
|
|
14
|
+
import { areURLSearchParamsEqual, escapeNonSpecialQuestionMarks, isRegexOrUrl, mergeURLSearchParams, } from '@sitecore-jss/sitecore-jss/utils';
|
|
15
|
+
import { MiddlewareBase, REWRITE_HEADER_NAME } from './middleware';
|
|
15
16
|
const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
|
|
16
17
|
const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
|
|
17
18
|
/**
|
|
@@ -19,13 +20,100 @@ const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
|
|
|
19
20
|
* compares with current url and redirects to target url
|
|
20
21
|
*/
|
|
21
22
|
export class RedirectsMiddleware extends MiddlewareBase {
|
|
22
|
-
/**
|
|
23
|
-
* @param {RedirectsMiddlewareConfig} [config] redirects middleware config
|
|
24
|
-
*/
|
|
25
23
|
constructor(config) {
|
|
26
24
|
super(config);
|
|
27
25
|
this.config = config;
|
|
28
|
-
|
|
26
|
+
// NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
|
|
27
|
+
// (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
|
|
28
|
+
this.redirectsService = new GraphQLRedirectsService(Object.assign(Object.assign({}, config), { fetch: fetch }));
|
|
29
|
+
this.locales = config.locales;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Gets the Next.js middleware handler with error handling
|
|
33
|
+
* @returns route handler
|
|
34
|
+
*/
|
|
35
|
+
getHandler() {
|
|
36
|
+
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
try {
|
|
38
|
+
return this.processRedirectRequest(req, res);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.log('Redirect middleware failed:');
|
|
42
|
+
console.log(error);
|
|
43
|
+
return res || NextResponse.next();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Method returns RedirectInfo when matches
|
|
49
|
+
* @param {NextRequest} req request
|
|
50
|
+
* @param {string} siteName site name
|
|
51
|
+
* @returns Promise<RedirectInfo | undefined> The redirect info or undefined if no redirect is found
|
|
52
|
+
* @protected
|
|
53
|
+
*/
|
|
54
|
+
getExistsRedirect(req, siteName) {
|
|
55
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
const { pathname: incomingURL, search: incomingQS = '' } = this.normalizeUrl(req.nextUrl.clone());
|
|
57
|
+
const locale = this.getLanguage(req);
|
|
58
|
+
const normalizedPath = incomingURL.replace(/\/*$/gi, '').toLowerCase();
|
|
59
|
+
const redirects = yield this.getRedirects(siteName);
|
|
60
|
+
const language = this.getLanguage(req);
|
|
61
|
+
const modifyRedirects = structuredClone(redirects);
|
|
62
|
+
let matchedQueryString;
|
|
63
|
+
const localePath = `/${locale.toLowerCase()}${normalizedPath}`;
|
|
64
|
+
return modifyRedirects.length
|
|
65
|
+
? modifyRedirects.find((redirect) => {
|
|
66
|
+
// process static URL (non-regex) rules
|
|
67
|
+
if (isRegexOrUrl(redirect.pattern) === 'url') {
|
|
68
|
+
const urlArray = redirect.pattern.endsWith('/')
|
|
69
|
+
? redirect.pattern.slice(0, -1).split('?')
|
|
70
|
+
: redirect.pattern.split('?');
|
|
71
|
+
const patternQS = urlArray[1];
|
|
72
|
+
let patternPath = urlArray[0].toLowerCase();
|
|
73
|
+
// nextjs routes are case-sensitive, but locales should be compared case-insensitively
|
|
74
|
+
const patternParts = patternPath.split('/');
|
|
75
|
+
const maybeLocale = patternParts[1].toLowerCase();
|
|
76
|
+
// case insensitive lookup of locales
|
|
77
|
+
if (new RegExp(this.locales.join('|'), 'i').test(maybeLocale)) {
|
|
78
|
+
patternPath = patternPath.replace(`/${patternParts[1]}`, `/${maybeLocale}`);
|
|
79
|
+
}
|
|
80
|
+
return ((patternPath === localePath || patternPath === normalizedPath) &&
|
|
81
|
+
(!patternQS ||
|
|
82
|
+
areURLSearchParamsEqual(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
|
|
83
|
+
}
|
|
84
|
+
// process regex rules
|
|
85
|
+
// Modify the redirect pattern to ignore the language prefix in the path
|
|
86
|
+
// And escapes non-special "?" characters in a string or regex.
|
|
87
|
+
redirect.pattern = escapeNonSpecialQuestionMarks(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
|
|
88
|
+
// Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
|
|
89
|
+
redirect.pattern = `/^\/${redirect.pattern
|
|
90
|
+
.replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
|
|
91
|
+
.replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
|
|
92
|
+
.replace(/^\^|\$$/g, '') // Further cleans up anchors
|
|
93
|
+
.replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
|
|
94
|
+
// Redirect pattern matches the full incoming URL with query string present
|
|
95
|
+
matchedQueryString = [
|
|
96
|
+
regexParser(redirect.pattern).test(`/${localePath}${incomingQS}`),
|
|
97
|
+
regexParser(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
|
|
98
|
+
].some(Boolean)
|
|
99
|
+
? incomingQS
|
|
100
|
+
: undefined;
|
|
101
|
+
// Save the matched query string (if found) into the redirect object
|
|
102
|
+
redirect.matchedQueryString = matchedQueryString || '';
|
|
103
|
+
return (!!(regexParser(redirect.pattern).test(`/${req.nextUrl.locale}${incomingURL}`) ||
|
|
104
|
+
regexParser(redirect.pattern).test(incomingURL) ||
|
|
105
|
+
matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
|
|
106
|
+
})
|
|
107
|
+
: undefined;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* @param {NextRequest} req request
|
|
112
|
+
* @param {Response} res response
|
|
113
|
+
* @returns {Promise<NextResponse>} The redirect response.
|
|
114
|
+
*/
|
|
115
|
+
processRedirectRequest(req, res) {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
29
117
|
const pathname = req.nextUrl.pathname;
|
|
30
118
|
const language = this.getLanguage(req);
|
|
31
119
|
const hostname = this.getHostHeader(req) || this.defaultHostname;
|
|
@@ -37,70 +125,69 @@ export class RedirectsMiddleware extends MiddlewareBase {
|
|
|
37
125
|
hostname,
|
|
38
126
|
});
|
|
39
127
|
const createResponse = () => __awaiter(this, void 0, void 0, function* () {
|
|
40
|
-
|
|
128
|
+
var _a;
|
|
129
|
+
const response = res || NextResponse.next();
|
|
130
|
+
if (this.config.disabled && this.config.disabled(req, response)) {
|
|
41
131
|
debug.redirects('skipped (redirects middleware is disabled)');
|
|
42
|
-
return
|
|
132
|
+
return response;
|
|
43
133
|
}
|
|
44
134
|
if (this.isPreview(req) || this.excludeRoute(pathname)) {
|
|
45
135
|
debug.redirects('skipped (%s)', this.isPreview(req) ? 'preview' : 'route excluded');
|
|
46
|
-
return
|
|
136
|
+
return response;
|
|
47
137
|
}
|
|
48
|
-
|
|
138
|
+
// Skip prefetch requests from Next.js, which are not original client requests
|
|
139
|
+
// as they load unnecessary requests that burden the redirects middleware with meaningless traffic
|
|
140
|
+
if (this.isPrefetch(req)) {
|
|
141
|
+
debug.redirects('skipped (prefetch)');
|
|
142
|
+
response.headers.set('x-middleware-cache', 'no-cache');
|
|
143
|
+
response.headers.set('Cache-Control', 'no-store, must-revalidate');
|
|
144
|
+
return response;
|
|
145
|
+
}
|
|
146
|
+
site = this.getSite(req, response);
|
|
49
147
|
// Find the redirect from result of RedirectService
|
|
50
148
|
const existsRedirect = yield this.getExistsRedirect(req, site.name);
|
|
51
149
|
if (!existsRedirect) {
|
|
52
150
|
debug.redirects('skipped (redirect does not exist)');
|
|
53
|
-
return
|
|
151
|
+
return response;
|
|
54
152
|
}
|
|
153
|
+
debug.redirects('Matched redirect rule: %o', { existsRedirect });
|
|
55
154
|
// Find context site language and replace token
|
|
56
155
|
if (REGEXP_CONTEXT_SITE_LANG.test(existsRedirect.target) &&
|
|
57
156
|
!(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
|
|
58
157
|
existsRedirect.target.includes(hostname))) {
|
|
59
158
|
existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
|
|
159
|
+
req.nextUrl.locale = site.language;
|
|
60
160
|
}
|
|
61
|
-
const url = req.nextUrl.clone();
|
|
161
|
+
const url = this.normalizeUrl(req.nextUrl.clone());
|
|
62
162
|
if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
|
|
63
|
-
|
|
163
|
+
return this.dispatchRedirect(existsRedirect.target, existsRedirect.redirectType, req, response, true);
|
|
64
164
|
}
|
|
65
165
|
else {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
const urlFirstPart =
|
|
166
|
+
const isUrl = isRegexOrUrl(existsRedirect.pattern) === 'url';
|
|
167
|
+
const targetParts = existsRedirect.target.split('/');
|
|
168
|
+
const urlFirstPart = targetParts[1];
|
|
69
169
|
if (this.locales.includes(urlFirstPart)) {
|
|
70
|
-
|
|
170
|
+
req.nextUrl.locale = urlFirstPart;
|
|
71
171
|
existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
|
|
72
172
|
}
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
.replace(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
statusText: 'Moved Permanently',
|
|
92
|
-
headers: res === null || res === void 0 ? void 0 : res.headers,
|
|
93
|
-
});
|
|
94
|
-
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);
|
|
102
|
-
default:
|
|
103
|
-
return res || NextResponse.next();
|
|
173
|
+
const targetSegments = isUrl
|
|
174
|
+
? existsRedirect.target.split('?')
|
|
175
|
+
: url.pathname.replace(/\/*$/gi, '') + existsRedirect.matchedQueryString;
|
|
176
|
+
const [targetPath, targetQueryString] = isUrl
|
|
177
|
+
? targetSegments
|
|
178
|
+
: targetSegments
|
|
179
|
+
.replace(regexParser(existsRedirect.pattern), existsRedirect.target)
|
|
180
|
+
.replace(/^\/\//, '/')
|
|
181
|
+
.split('?');
|
|
182
|
+
const mergedQueryString = existsRedirect.isQueryStringPreserved
|
|
183
|
+
? mergeURLSearchParams(new URLSearchParams((_a = url.search) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQueryString || ''))
|
|
184
|
+
: targetQueryString || '';
|
|
185
|
+
const prepareNewURL = new URL(`${targetPath}${mergedQueryString ? `?${mergedQueryString}` : ''}`, url.origin);
|
|
186
|
+
url.href = prepareNewURL.href;
|
|
187
|
+
url.pathname = prepareNewURL.pathname;
|
|
188
|
+
url.search = prepareNewURL.search;
|
|
189
|
+
url.locale = req.nextUrl.locale;
|
|
190
|
+
return this.dispatchRedirect(url, existsRedirect.redirectType, req, response, false);
|
|
104
191
|
}
|
|
105
192
|
});
|
|
106
193
|
const response = yield createResponse();
|
|
@@ -112,59 +199,94 @@ export class RedirectsMiddleware extends MiddlewareBase {
|
|
|
112
199
|
});
|
|
113
200
|
return response;
|
|
114
201
|
});
|
|
115
|
-
// NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
|
|
116
|
-
// (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
|
|
117
|
-
this.redirectsService = new GraphQLRedirectsService(Object.assign(Object.assign({}, config), { fetch: fetch }));
|
|
118
|
-
this.locales = config.locales;
|
|
119
202
|
}
|
|
120
203
|
/**
|
|
121
|
-
*
|
|
122
|
-
* @
|
|
204
|
+
* Fetches all redirects for a given site from the Sitecore instance
|
|
205
|
+
* @param {string} siteName - The name of the site to fetch redirects for
|
|
206
|
+
* @returns {Promise<RedirectInfo[]>} A promise that resolves to an array of redirect information
|
|
207
|
+
* @protected
|
|
123
208
|
*/
|
|
124
|
-
|
|
125
|
-
return
|
|
126
|
-
|
|
127
|
-
return yield this.handler(req, res);
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
console.log('Redirect middleware failed:');
|
|
131
|
-
console.log(error);
|
|
132
|
-
return res || NextResponse.next();
|
|
133
|
-
}
|
|
209
|
+
getRedirects(siteName) {
|
|
210
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
211
|
+
return this.redirectsService.fetchRedirects(siteName);
|
|
134
212
|
});
|
|
135
213
|
}
|
|
136
214
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* @
|
|
141
|
-
* @
|
|
215
|
+
* When a user clicks on a link generated by the Link component from next/link,
|
|
216
|
+
* Next.js adds special parameters in the route called path.
|
|
217
|
+
* This method removes these special parameters.
|
|
218
|
+
* @param {NextURL} url
|
|
219
|
+
* @returns {string} normalize url
|
|
142
220
|
*/
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
221
|
+
normalizeUrl(url) {
|
|
222
|
+
if (!url.search)
|
|
223
|
+
return url;
|
|
224
|
+
/**
|
|
225
|
+
* Prepare special parameters for exclusion.
|
|
226
|
+
*/
|
|
227
|
+
const splittedPathname = url.pathname
|
|
228
|
+
.split('/')
|
|
229
|
+
.filter((route) => route)
|
|
230
|
+
.map((route) => `path=${route}`);
|
|
231
|
+
/**
|
|
232
|
+
* Remove special parameters(Next.JS)
|
|
233
|
+
* Example: /about/contact/us
|
|
234
|
+
* When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
|
|
235
|
+
* http://host/about/contact/us?path=about&path=contact&path=us
|
|
236
|
+
*/
|
|
237
|
+
const newQueryString = url.search
|
|
238
|
+
.replace(/^\?/, '')
|
|
239
|
+
.split('&')
|
|
240
|
+
.filter((param) => !splittedPathname.includes(param))
|
|
241
|
+
.join('&');
|
|
242
|
+
const newUrl = new URL(`${url.pathname.toLowerCase()}?${newQueryString}`, url.origin);
|
|
243
|
+
url.search = newUrl.search;
|
|
244
|
+
url.pathname = newUrl.pathname.toLowerCase();
|
|
245
|
+
url.href = newUrl.href;
|
|
246
|
+
return url;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Dispatch a redirect or rewrite based on type.
|
|
250
|
+
* @param {NextURL | string} target Final target to redirect/rewrite to (NextURL or string for externals).
|
|
251
|
+
* @param {string} type One of `REDIRECT_TYPE_301`, `REDIRECT_TYPE_302`, or `REDIRECT_TYPE_SERVER_TRANSFER`.
|
|
252
|
+
* @param {NextRequest} req Incoming request.
|
|
253
|
+
* @param {NextResponse} res Current response (used for header cleanup/carry-over).
|
|
254
|
+
* @param {boolean} isExternal Set to `true` when target is an external absolute URL.
|
|
255
|
+
* @returns A NextResponse.
|
|
256
|
+
*/
|
|
257
|
+
dispatchRedirect(target, type, req, res, isExternal = false) {
|
|
258
|
+
switch (type) {
|
|
259
|
+
case REDIRECT_TYPE_301:
|
|
260
|
+
return this.createRedirectResponse(target, res, 301, 'Moved Permanently');
|
|
261
|
+
case REDIRECT_TYPE_302:
|
|
262
|
+
return this.createRedirectResponse(target, res, 302, 'Found');
|
|
263
|
+
case REDIRECT_TYPE_SERVER_TRANSFER:
|
|
264
|
+
// rewrite expects a string; unwrap NextURL if needed
|
|
265
|
+
return this.rewrite(typeof target === 'string' ? target : target.href, req, res, isExternal);
|
|
266
|
+
default:
|
|
267
|
+
// Unknown type: return the input response unchanged
|
|
268
|
+
return res;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Helper function to create a redirect response and remove the x-middleware-next header.
|
|
273
|
+
* @param {NextURL} url The URL to redirect to.
|
|
274
|
+
* @param {Response} res The response object.
|
|
275
|
+
* @param {number} status The HTTP status code of the redirect.
|
|
276
|
+
* @param {string} statusText The status text of the redirect.
|
|
277
|
+
* @returns {NextResponse<unknown>} The redirect response.
|
|
278
|
+
*/
|
|
279
|
+
createRedirectResponse(url, res, status, statusText) {
|
|
280
|
+
const redirect = NextResponse.redirect(url, {
|
|
281
|
+
status,
|
|
282
|
+
statusText,
|
|
283
|
+
headers: res === null || res === void 0 ? void 0 : res.headers,
|
|
168
284
|
});
|
|
285
|
+
if (res === null || res === void 0 ? void 0 : res.headers) {
|
|
286
|
+
redirect.headers.delete('x-middleware-next');
|
|
287
|
+
redirect.headers.delete('x-middleware-rewrite');
|
|
288
|
+
redirect.headers.delete(REWRITE_HEADER_NAME);
|
|
289
|
+
}
|
|
290
|
+
return redirect;
|
|
169
291
|
}
|
|
170
292
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sitecore-jss/sitecore-jss-nextjs",
|
|
3
|
-
"version": "21.10.
|
|
3
|
+
"version": "21.10.1-canary.1",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -69,9 +69,9 @@
|
|
|
69
69
|
"react-dom": "^19.1.0"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@sitecore-jss/sitecore-jss": "21.10.
|
|
73
|
-
"@sitecore-jss/sitecore-jss-dev-tools": "21.10.
|
|
74
|
-
"@sitecore-jss/sitecore-jss-react": "21.10.
|
|
72
|
+
"@sitecore-jss/sitecore-jss": "21.10.1-canary.1",
|
|
73
|
+
"@sitecore-jss/sitecore-jss-dev-tools": "21.10.1-canary.1",
|
|
74
|
+
"@sitecore-jss/sitecore-jss-react": "21.10.1-canary.1",
|
|
75
75
|
"@vercel/kv": "^0.2.1",
|
|
76
76
|
"node-html-parser": "^6.1.4",
|
|
77
77
|
"regex-parser": "^2.2.11",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
},
|
|
80
80
|
"description": "",
|
|
81
81
|
"types": "types/index.d.ts",
|
|
82
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "6c273c8e433a802efc8f07323a6c4892585e8acb",
|
|
83
83
|
"files": [
|
|
84
84
|
"dist",
|
|
85
85
|
"types",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SiteInfo, SiteResolver } from '@sitecore-jss/sitecore-jss/site';
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
|
+
export declare const REWRITE_HEADER_NAME = "x-sc-rewrite";
|
|
3
4
|
export type MiddlewareBaseConfig = {
|
|
4
5
|
/**
|
|
5
6
|
* function, determines if middleware should be turned off, based on cookie, header, or other considerations
|
|
@@ -47,6 +48,12 @@ export declare abstract class MiddlewareBase {
|
|
|
47
48
|
protected extractDebugHeaders(incomingHeaders: Headers): {
|
|
48
49
|
[key: string]: string;
|
|
49
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Determines if the request is a Next.js (next/link) prefetch request
|
|
53
|
+
* @param {NextRequest} req request
|
|
54
|
+
* @returns {boolean} is prefetch
|
|
55
|
+
*/
|
|
56
|
+
protected isPrefetch(req: NextRequest): boolean;
|
|
50
57
|
/**
|
|
51
58
|
* Provides used language
|
|
52
59
|
* @param {NextRequest} req request
|
|
@@ -71,6 +78,7 @@ export declare abstract class MiddlewareBase {
|
|
|
71
78
|
* @param {string} rewritePath the destionation path
|
|
72
79
|
* @param {NextRequest} req the current request
|
|
73
80
|
* @param {NextResponse} res the current response
|
|
81
|
+
* @param {boolean} [skipHeader] don't write 'x-sc-rewrite' header
|
|
74
82
|
*/
|
|
75
|
-
protected rewrite(rewritePath: string, req: NextRequest, res: NextResponse): NextResponse;
|
|
83
|
+
protected rewrite(rewritePath: string, req: NextRequest, res: NextResponse, skipHeader?: boolean): NextResponse;
|
|
76
84
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { NextResponse, NextRequest } from 'next/server';
|
|
2
|
-
import { GraphQLRedirectsServiceConfig } from '@sitecore-jss/sitecore-jss/site';
|
|
2
|
+
import { RedirectInfo, GraphQLRedirectsServiceConfig } from '@sitecore-jss/sitecore-jss/site';
|
|
3
3
|
import { MiddlewareBase, MiddlewareBaseConfig } from './middleware';
|
|
4
|
+
type RedirectResult = RedirectInfo & {
|
|
5
|
+
matchedQueryString?: string;
|
|
6
|
+
};
|
|
4
7
|
/**
|
|
5
8
|
* extended RedirectsMiddlewareConfig config type for RedirectsMiddleware
|
|
6
9
|
*/
|
|
@@ -19,22 +22,59 @@ export declare class RedirectsMiddleware extends MiddlewareBase {
|
|
|
19
22
|
protected config: RedirectsMiddlewareConfig;
|
|
20
23
|
private redirectsService;
|
|
21
24
|
private locales;
|
|
22
|
-
/**
|
|
23
|
-
* @param {RedirectsMiddlewareConfig} [config] redirects middleware config
|
|
24
|
-
*/
|
|
25
25
|
constructor(config: RedirectsMiddlewareConfig);
|
|
26
26
|
/**
|
|
27
27
|
* Gets the Next.js middleware handler with error handling
|
|
28
28
|
* @returns route handler
|
|
29
29
|
*/
|
|
30
30
|
getHandler(): (req: NextRequest, res?: NextResponse) => Promise<NextResponse>;
|
|
31
|
-
private handler;
|
|
32
31
|
/**
|
|
33
32
|
* Method returns RedirectInfo when matches
|
|
34
33
|
* @param {NextRequest} req request
|
|
35
34
|
* @param {string} siteName site name
|
|
36
|
-
* @returns Promise<RedirectInfo | undefined>
|
|
37
|
-
* @
|
|
35
|
+
* @returns Promise<RedirectInfo | undefined> The redirect info or undefined if no redirect is found
|
|
36
|
+
* @protected
|
|
37
|
+
*/
|
|
38
|
+
protected getExistsRedirect(req: NextRequest, siteName: string): Promise<RedirectResult | undefined>;
|
|
39
|
+
/**
|
|
40
|
+
* @param {NextRequest} req request
|
|
41
|
+
* @param {Response} res response
|
|
42
|
+
* @returns {Promise<NextResponse>} The redirect response.
|
|
43
|
+
*/
|
|
44
|
+
protected processRedirectRequest(req: NextRequest, res?: NextResponse): Promise<NextResponse>;
|
|
45
|
+
/**
|
|
46
|
+
* Fetches all redirects for a given site from the Sitecore instance
|
|
47
|
+
* @param {string} siteName - The name of the site to fetch redirects for
|
|
48
|
+
* @returns {Promise<RedirectInfo[]>} A promise that resolves to an array of redirect information
|
|
49
|
+
* @protected
|
|
50
|
+
*/
|
|
51
|
+
protected getRedirects(siteName: string): Promise<RedirectInfo[]>;
|
|
52
|
+
/**
|
|
53
|
+
* When a user clicks on a link generated by the Link component from next/link,
|
|
54
|
+
* Next.js adds special parameters in the route called path.
|
|
55
|
+
* This method removes these special parameters.
|
|
56
|
+
* @param {NextURL} url
|
|
57
|
+
* @returns {string} normalize url
|
|
58
|
+
*/
|
|
59
|
+
private normalizeUrl;
|
|
60
|
+
/**
|
|
61
|
+
* Dispatch a redirect or rewrite based on type.
|
|
62
|
+
* @param {NextURL | string} target Final target to redirect/rewrite to (NextURL or string for externals).
|
|
63
|
+
* @param {string} type One of `REDIRECT_TYPE_301`, `REDIRECT_TYPE_302`, or `REDIRECT_TYPE_SERVER_TRANSFER`.
|
|
64
|
+
* @param {NextRequest} req Incoming request.
|
|
65
|
+
* @param {NextResponse} res Current response (used for header cleanup/carry-over).
|
|
66
|
+
* @param {boolean} isExternal Set to `true` when target is an external absolute URL.
|
|
67
|
+
* @returns A NextResponse.
|
|
68
|
+
*/
|
|
69
|
+
private dispatchRedirect;
|
|
70
|
+
/**
|
|
71
|
+
* Helper function to create a redirect response and remove the x-middleware-next header.
|
|
72
|
+
* @param {NextURL} url The URL to redirect to.
|
|
73
|
+
* @param {Response} res The response object.
|
|
74
|
+
* @param {number} status The HTTP status code of the redirect.
|
|
75
|
+
* @param {string} statusText The status text of the redirect.
|
|
76
|
+
* @returns {NextResponse<unknown>} The redirect response.
|
|
38
77
|
*/
|
|
39
|
-
private
|
|
78
|
+
private createRedirectResponse;
|
|
40
79
|
}
|
|
80
|
+
export {};
|