@sitecore-content-sdk/nextjs 0.1.0-beta.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.
Files changed (112) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +10 -0
  3. package/dist/cjs/ComponentBuilder.js +63 -0
  4. package/dist/cjs/components/BYOCWrapper.js +41 -0
  5. package/dist/cjs/components/ComponentPropsContext.js +57 -0
  6. package/dist/cjs/components/FEaaSWrapper.js +43 -0
  7. package/dist/cjs/components/Link.js +87 -0
  8. package/dist/cjs/components/NextImage.js +82 -0
  9. package/dist/cjs/components/Placeholder.js +49 -0
  10. package/dist/cjs/components/RichText.js +95 -0
  11. package/dist/cjs/editing/constants.js +10 -0
  12. package/dist/cjs/editing/editing-config-middleware.js +62 -0
  13. package/dist/cjs/editing/editing-render-middleware.js +182 -0
  14. package/dist/cjs/editing/feaas-render-middleware.js +101 -0
  15. package/dist/cjs/editing/index.js +16 -0
  16. package/dist/cjs/editing/render-middleware.js +43 -0
  17. package/dist/cjs/graphql/index.js +7 -0
  18. package/dist/cjs/index.js +119 -0
  19. package/dist/cjs/middleware/index.js +13 -0
  20. package/dist/cjs/middleware/middleware.js +97 -0
  21. package/dist/cjs/middleware/multisite-middleware.js +93 -0
  22. package/dist/cjs/middleware/personalize-middleware.js +231 -0
  23. package/dist/cjs/middleware/redirects-middleware.js +264 -0
  24. package/dist/cjs/monitoring/healthcheck-middleware.js +30 -0
  25. package/dist/cjs/monitoring/index.js +5 -0
  26. package/dist/cjs/services/base-graphql-sitemap-service.js +206 -0
  27. package/dist/cjs/services/component-props-service.js +167 -0
  28. package/dist/cjs/services/graphql-sitemap-service.js +64 -0
  29. package/dist/cjs/services/mutisite-graphql-sitemap-service.js +81 -0
  30. package/dist/cjs/sharedTypes/component-props.js +2 -0
  31. package/dist/cjs/sharedTypes/module-factory.js +2 -0
  32. package/dist/cjs/site/index.js +5 -0
  33. package/dist/cjs/utils/index.js +11 -0
  34. package/dist/cjs/utils/utils.js +42 -0
  35. package/dist/esm/ComponentBuilder.js +59 -0
  36. package/dist/esm/components/BYOCWrapper.js +36 -0
  37. package/dist/esm/components/ComponentPropsContext.js +19 -0
  38. package/dist/esm/components/FEaaSWrapper.js +38 -0
  39. package/dist/esm/components/Link.js +48 -0
  40. package/dist/esm/components/NextImage.js +76 -0
  41. package/dist/esm/components/Placeholder.js +12 -0
  42. package/dist/esm/components/RichText.js +55 -0
  43. package/dist/esm/editing/constants.js +7 -0
  44. package/dist/esm/editing/editing-config-middleware.js +58 -0
  45. package/dist/esm/editing/editing-render-middleware.js +177 -0
  46. package/dist/esm/editing/feaas-render-middleware.js +97 -0
  47. package/dist/esm/editing/index.js +5 -0
  48. package/dist/esm/editing/render-middleware.js +39 -0
  49. package/dist/esm/graphql/index.js +1 -0
  50. package/dist/esm/index.js +23 -0
  51. package/dist/esm/middleware/index.js +5 -0
  52. package/dist/esm/middleware/middleware.js +93 -0
  53. package/dist/esm/middleware/multisite-middleware.js +89 -0
  54. package/dist/esm/middleware/personalize-middleware.js +227 -0
  55. package/dist/esm/middleware/redirects-middleware.js +257 -0
  56. package/dist/esm/monitoring/healthcheck-middleware.js +26 -0
  57. package/dist/esm/monitoring/index.js +1 -0
  58. package/dist/esm/services/base-graphql-sitemap-service.js +201 -0
  59. package/dist/esm/services/component-props-service.js +160 -0
  60. package/dist/esm/services/graphql-sitemap-service.js +59 -0
  61. package/dist/esm/services/mutisite-graphql-sitemap-service.js +77 -0
  62. package/dist/esm/sharedTypes/component-props.js +1 -0
  63. package/dist/esm/sharedTypes/module-factory.js +1 -0
  64. package/dist/esm/site/index.js +1 -0
  65. package/dist/esm/utils/index.js +3 -0
  66. package/dist/esm/utils/utils.js +37 -0
  67. package/editing.d.ts +1 -0
  68. package/editing.js +1 -0
  69. package/global.d.ts +21 -0
  70. package/graphql.d.ts +1 -0
  71. package/graphql.js +1 -0
  72. package/middleware.d.ts +1 -0
  73. package/middleware.js +1 -0
  74. package/monitoring.d.ts +1 -0
  75. package/monitoring.js +1 -0
  76. package/package.json +92 -0
  77. package/site.d.ts +1 -0
  78. package/site.js +1 -0
  79. package/types/ComponentBuilder.d.ts +59 -0
  80. package/types/components/BYOCWrapper.d.ts +20 -0
  81. package/types/components/ComponentPropsContext.d.ts +18 -0
  82. package/types/components/FEaaSWrapper.d.ts +22 -0
  83. package/types/components/Link.d.ts +10 -0
  84. package/types/components/NextImage.d.ts +6 -0
  85. package/types/components/Placeholder.d.ts +8 -0
  86. package/types/components/RichText.d.ts +32 -0
  87. package/types/editing/constants.d.ts +7 -0
  88. package/types/editing/editing-config-middleware.d.ts +29 -0
  89. package/types/editing/editing-render-middleware.d.ts +79 -0
  90. package/types/editing/feaas-render-middleware.d.ts +32 -0
  91. package/types/editing/index.d.ts +5 -0
  92. package/types/editing/render-middleware.d.ts +24 -0
  93. package/types/graphql/index.d.ts +1 -0
  94. package/types/index.d.ts +24 -0
  95. package/types/middleware/index.d.ts +5 -0
  96. package/types/middleware/middleware.d.ts +82 -0
  97. package/types/middleware/multisite-middleware.d.ts +39 -0
  98. package/types/middleware/personalize-middleware.d.ts +102 -0
  99. package/types/middleware/redirects-middleware.d.ts +57 -0
  100. package/types/monitoring/healthcheck-middleware.d.ts +12 -0
  101. package/types/monitoring/index.d.ts +1 -0
  102. package/types/services/base-graphql-sitemap-service.d.ts +148 -0
  103. package/types/services/component-props-service.d.ts +81 -0
  104. package/types/services/graphql-sitemap-service.d.ts +51 -0
  105. package/types/services/mutisite-graphql-sitemap-service.d.ts +42 -0
  106. package/types/sharedTypes/component-props.d.ts +26 -0
  107. package/types/sharedTypes/module-factory.d.ts +32 -0
  108. package/types/site/index.d.ts +1 -0
  109. package/types/utils/index.d.ts +3 -0
  110. package/types/utils/utils.d.ts +8 -0
  111. package/utils.d.ts +1 -0
  112. package/utils.js +1 -0
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RedirectsMiddleware = void 0;
16
+ const core_1 = require("@sitecore-content-sdk/core");
17
+ const site_1 = require("@sitecore-content-sdk/core/site");
18
+ const utils_1 = require("@sitecore-content-sdk/core/utils");
19
+ const server_1 = require("next/server");
20
+ const regex_parser_1 = __importDefault(require("regex-parser"));
21
+ const middleware_1 = require("./middleware");
22
+ const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
23
+ const REGEXP_ABSOLUTE_URL = new RegExp('^(?:[a-z]+:)?//', 'i');
24
+ /**
25
+ * Middleware / handler fetches all redirects from Sitecore instance by grapqhl service
26
+ * compares with current url and redirects to target url
27
+ */
28
+ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
29
+ /**
30
+ * @param {RedirectsMiddlewareConfig} [config] redirects middleware config
31
+ */
32
+ constructor(config) {
33
+ super(config);
34
+ this.config = config;
35
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
36
+ const pathname = req.nextUrl.pathname;
37
+ const language = this.getLanguage(req);
38
+ const hostname = this.getHostHeader(req) || this.defaultHostname;
39
+ let site;
40
+ const startTimestamp = Date.now();
41
+ core_1.debug.redirects('redirects middleware start: %o', {
42
+ pathname,
43
+ language,
44
+ hostname,
45
+ });
46
+ const createResponse = () => __awaiter(this, void 0, void 0, function* () {
47
+ var _a;
48
+ const response = res || server_1.NextResponse.next();
49
+ if (this.config.disabled && this.config.disabled(req, res || server_1.NextResponse.next())) {
50
+ core_1.debug.redirects('skipped (redirects middleware is disabled)');
51
+ return response;
52
+ }
53
+ if (this.isPreview(req) || this.excludeRoute(pathname)) {
54
+ core_1.debug.redirects('skipped (%s)', this.isPreview(req) ? 'preview' : 'route excluded');
55
+ return response;
56
+ }
57
+ // Skip prefetch requests from Next.js, which are not original client requests
58
+ // as they load unnecessary requests that burden the redirects middleware with meaningless traffic
59
+ if (this.isPrefetch(req)) {
60
+ core_1.debug.redirects('skipped (prefetch)');
61
+ response.headers.set('x-middleware-cache', 'no-cache');
62
+ return response;
63
+ }
64
+ site = this.getSite(req, res);
65
+ // Find the redirect from result of RedirectService
66
+ const existsRedirect = yield this.getExistsRedirect(req, site.name);
67
+ if (!existsRedirect) {
68
+ core_1.debug.redirects('skipped (redirect does not exist)');
69
+ return response;
70
+ }
71
+ // Find context site language and replace token
72
+ if (REGEXP_CONTEXT_SITE_LANG.test(existsRedirect.target) &&
73
+ !(REGEXP_ABSOLUTE_URL.test(existsRedirect.target) &&
74
+ existsRedirect.target.includes(hostname))) {
75
+ existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
76
+ req.nextUrl.locale = site.language;
77
+ }
78
+ const url = this.normalizeUrl(req.nextUrl.clone());
79
+ if (REGEXP_ABSOLUTE_URL.test(existsRedirect.target)) {
80
+ url.href = existsRedirect.target;
81
+ }
82
+ else {
83
+ const isUrl = (0, utils_1.isRegexOrUrl)(existsRedirect.pattern) === 'url';
84
+ const targetParts = existsRedirect.target.split('/');
85
+ const urlFirstPart = targetParts[1];
86
+ if (this.locales.includes(urlFirstPart)) {
87
+ req.nextUrl.locale = urlFirstPart;
88
+ existsRedirect.target = existsRedirect.target.replace(`/${urlFirstPart}`, '');
89
+ }
90
+ const targetSegments = isUrl
91
+ ? existsRedirect.target.split('?')
92
+ : url.pathname.replace(/\/*$/gi, '') + existsRedirect.matchedQueryString;
93
+ const [targetPath, targetQueryString] = isUrl
94
+ ? targetSegments
95
+ : targetSegments
96
+ .replace((0, regex_parser_1.default)(existsRedirect.pattern), existsRedirect.target)
97
+ .replace(/^\/\//, '/')
98
+ .split('?');
99
+ const mergedQueryString = existsRedirect.isQueryStringPreserved
100
+ ? (0, utils_1.mergeURLSearchParams)(new URLSearchParams((_a = url.search) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQueryString || ''))
101
+ : targetQueryString || '';
102
+ const prepareNewURL = new URL(`${targetPath}${mergedQueryString ? '?' + mergedQueryString : ''}`, url.origin);
103
+ url.href = prepareNewURL.href;
104
+ url.pathname = prepareNewURL.pathname;
105
+ url.search = prepareNewURL.search;
106
+ url.locale = req.nextUrl.locale;
107
+ }
108
+ /** return Response redirect with http code of redirect type */
109
+ switch (existsRedirect.redirectType) {
110
+ case site_1.REDIRECT_TYPE_301: {
111
+ return this.createRedirectResponse(url, response, 301, 'Moved Permanently');
112
+ }
113
+ case site_1.REDIRECT_TYPE_302: {
114
+ return this.createRedirectResponse(url, response, 302, 'Found');
115
+ }
116
+ case site_1.REDIRECT_TYPE_SERVER_TRANSFER: {
117
+ return this.rewrite(url.href, req, response);
118
+ }
119
+ default:
120
+ return response;
121
+ }
122
+ });
123
+ const response = yield createResponse();
124
+ core_1.debug.redirects('redirects middleware end in %dms: %o', Date.now() - startTimestamp, {
125
+ redirected: response.redirected,
126
+ status: response.status,
127
+ url: response.url,
128
+ headers: this.extractDebugHeaders(response.headers),
129
+ });
130
+ return response;
131
+ });
132
+ // NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
133
+ // (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
134
+ this.redirectsService = new site_1.GraphQLRedirectsService(Object.assign(Object.assign({}, config), { fetch: fetch }));
135
+ this.locales = config.locales;
136
+ }
137
+ /**
138
+ * Gets the Next.js middleware handler with error handling
139
+ * @returns route handler
140
+ */
141
+ getHandler() {
142
+ return (req, res) => __awaiter(this, void 0, void 0, function* () {
143
+ try {
144
+ return yield this.handler(req, res);
145
+ }
146
+ catch (error) {
147
+ console.log('Redirect middleware failed:');
148
+ console.log(error);
149
+ return res || server_1.NextResponse.next();
150
+ }
151
+ });
152
+ }
153
+ /**
154
+ * Method returns RedirectInfo when matches
155
+ * @param {NextRequest} req request
156
+ * @param {string} siteName site name
157
+ * @returns Promise<RedirectInfo | undefined>
158
+ * @private
159
+ */
160
+ getExistsRedirect(req, siteName) {
161
+ return __awaiter(this, void 0, void 0, function* () {
162
+ const { pathname: targetURL, search: targetQS = '', locale } = this.normalizeUrl(req.nextUrl.clone());
163
+ const normalizedPath = targetURL.replace(/\/*$/gi, '');
164
+ const redirects = yield this.redirectsService.fetchRedirects(siteName);
165
+ const language = this.getLanguage(req);
166
+ const modifyRedirects = structuredClone(redirects);
167
+ let matchedQueryString;
168
+ return modifyRedirects.length
169
+ ? modifyRedirects.find((redirect) => {
170
+ var _a;
171
+ if ((0, utils_1.isRegexOrUrl)(redirect.pattern) === 'url') {
172
+ const parseUrlPattern = redirect.pattern.endsWith('/')
173
+ ? redirect.pattern.slice(0, -1).split('?')
174
+ : redirect.pattern.split('?');
175
+ return ((parseUrlPattern[0] === normalizedPath ||
176
+ parseUrlPattern[0] === `/${locale}${normalizedPath}`) &&
177
+ (0, utils_1.areURLSearchParamsEqual)(new URLSearchParams((_a = parseUrlPattern[1]) !== null && _a !== void 0 ? _a : ''), new URLSearchParams(targetQS)));
178
+ }
179
+ // Modify the redirect pattern to ignore the language prefix in the path
180
+ // And escapes non-special "?" characters in a string or regex.
181
+ redirect.pattern = (0, utils_1.escapeNonSpecialQuestionMarks)(redirect.pattern.replace(new RegExp(`^[^]?/${language}/`, 'gi'), ''));
182
+ // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
183
+ redirect.pattern = `/^\/${redirect.pattern
184
+ .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
185
+ .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
186
+ .replace(/^\^|\$$/g, '') // Further cleans up anchors
187
+ .replace(/\$\/gi$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
188
+ matchedQueryString = [
189
+ (0, regex_parser_1.default)(redirect.pattern).test(`${normalizedPath}${targetQS}`),
190
+ (0, regex_parser_1.default)(redirect.pattern).test(`/${locale}${normalizedPath}${targetQS}`),
191
+ ].some(Boolean)
192
+ ? targetQS
193
+ : undefined;
194
+ // Save the matched query string (if found) into the redirect object
195
+ redirect.matchedQueryString = matchedQueryString || '';
196
+ return (!!((0, regex_parser_1.default)(redirect.pattern).test(targetURL) ||
197
+ (0, regex_parser_1.default)(redirect.pattern).test(`/${req.nextUrl.locale}${targetURL}`) ||
198
+ matchedQueryString) && (redirect.locale ? redirect.locale.toLowerCase() === locale.toLowerCase() : true));
199
+ })
200
+ : undefined;
201
+ });
202
+ }
203
+ /**
204
+ * When a user clicks on a link generated by the Link component from next/link,
205
+ * Next.js adds special parameters in the route called path.
206
+ * This method removes these special parameters.
207
+ * @param {NextURL} url
208
+ * @returns {string} normalize url
209
+ */
210
+ normalizeUrl(url) {
211
+ if (!url.search) {
212
+ return url;
213
+ }
214
+ /**
215
+ * Prepare special parameters for exclusion.
216
+ */
217
+ const splittedPathname = url.pathname
218
+ .split('/')
219
+ .filter((route) => route)
220
+ .map((route) => `path=${route}`);
221
+ /**
222
+ * Remove special parameters(Next.JS)
223
+ * Example: /about/contact/us
224
+ * When a user clicks on this link, Next.js should generate a link for the middleware, formatted like this:
225
+ * http://host/about/contact/us?path=about&path=contact&path=us
226
+ */
227
+ const newQueryString = url.search
228
+ .replace(/^\?/, '')
229
+ .split('&')
230
+ .filter((param) => {
231
+ if (!splittedPathname.includes(param)) {
232
+ return param;
233
+ }
234
+ return false;
235
+ })
236
+ .join('&');
237
+ const newUrl = new URL(`${url.pathname}?${newQueryString}`, url.origin);
238
+ url.search = newUrl.search;
239
+ url.pathname = newUrl.pathname;
240
+ url.href = newUrl.href;
241
+ return url;
242
+ }
243
+ /**
244
+ * Helper function to create a redirect response and remove the x-middleware-next header.
245
+ * @param {NextURL} url The URL to redirect to.
246
+ * @param {Response} res The response object.
247
+ * @param {number} status The HTTP status code of the redirect.
248
+ * @param {string} statusText The status text of the redirect.
249
+ * @returns {NextResponse<unknown>} The redirect response.
250
+ */
251
+ createRedirectResponse(url, res, status, statusText) {
252
+ const redirect = server_1.NextResponse.redirect(url, {
253
+ status,
254
+ statusText,
255
+ headers: res === null || res === void 0 ? void 0 : res.headers,
256
+ });
257
+ if (res === null || res === void 0 ? void 0 : res.headers) {
258
+ redirect.headers.delete('x-middleware-next');
259
+ redirect.headers.delete('x-middleware-rewrite');
260
+ }
261
+ return redirect;
262
+ }
263
+ }
264
+ exports.RedirectsMiddleware = RedirectsMiddleware;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.HealthcheckMiddleware = void 0;
13
+ /**
14
+ * Middleware / handler for use in healthcheck Next.js API route (e.g. '/api/healthz').
15
+ */
16
+ class HealthcheckMiddleware {
17
+ constructor() {
18
+ this.handler = (_req, res) => __awaiter(this, void 0, void 0, function* () {
19
+ res.status(200).send('Healthy');
20
+ });
21
+ }
22
+ /**
23
+ * Gets the Next.js API route handler
24
+ * @returns route handler
25
+ */
26
+ getHandler() {
27
+ return this.handler;
28
+ }
29
+ }
30
+ exports.HealthcheckMiddleware = HealthcheckMiddleware;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HealthcheckMiddleware = void 0;
4
+ var healthcheck_middleware_1 = require("./healthcheck-middleware");
5
+ Object.defineProperty(exports, "HealthcheckMiddleware", { enumerable: true, get: function () { return healthcheck_middleware_1.HealthcheckMiddleware; } });
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.BaseGraphQLSitemapService = exports.siteError = exports.languageError = void 0;
13
+ exports.getSiteEmptyError = getSiteEmptyError;
14
+ const core_1 = require("@sitecore-content-sdk/core");
15
+ const personalize_1 = require("@sitecore-content-sdk/core/personalize");
16
+ /** @private */
17
+ exports.languageError = 'The list of languages cannot be empty';
18
+ exports.siteError = 'The service needs a site name';
19
+ /**
20
+ * @param {string} siteName to inject into error text
21
+ * @private
22
+ */
23
+ function getSiteEmptyError(siteName) {
24
+ return `Site "${siteName}" does not exist or site item tree is missing`;
25
+ }
26
+ const languageEmptyError = 'The language must be a non-empty string';
27
+ /**
28
+ * GQL query made dynamic based whether personalization is enabled or not
29
+ * @param {boolean} usesPersonalize flag to detrmine which variation of a query to run
30
+ * @returns GraphQL query to fetch site paths with
31
+ */
32
+ const defaultQuery = (usesPersonalize) => /* GraphQL */ `
33
+ query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
34
+ $siteName: String!
35
+ $language: String!
36
+ $includedPaths: [String]
37
+ $excludedPaths: [String]
38
+ $pageSize: Int = 100
39
+ $after: String
40
+ ) {
41
+ site {
42
+ siteInfo(site: $siteName) {
43
+ routes(
44
+ language: $language
45
+ includedPaths: $includedPaths
46
+ excludedPaths: $excludedPaths
47
+ first: $pageSize
48
+ after: $after
49
+ ){
50
+ total
51
+ pageInfo {
52
+ endCursor
53
+ hasNext
54
+ }
55
+ results {
56
+ path: routePath
57
+ ${usesPersonalize
58
+ ? `
59
+ route {
60
+ personalization {
61
+ variantIds
62
+ }
63
+ }`
64
+ : ''}
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
70
+ `;
71
+ /**
72
+ * Service that fetches the list of site pages using Sitecore's GraphQL API.
73
+ * Used to handle a single site
74
+ * This list is used for SSG and Export functionality.
75
+ * @mixes SearchQueryService<PageListQueryResult>
76
+ */
77
+ class BaseGraphQLSitemapService {
78
+ /**
79
+ * Creates an instance of graphQL sitemap service with the provided options
80
+ * @param {GraphQLSitemapServiceConfig} options instance
81
+ */
82
+ constructor(options) {
83
+ this.options = options;
84
+ this._graphQLClient = this.getGraphQLClient();
85
+ }
86
+ /**
87
+ * GraphQL client accessible by descendant classes when needed
88
+ */
89
+ get graphQLClient() {
90
+ return this._graphQLClient;
91
+ }
92
+ /**
93
+ * Gets the default query used for fetching the list of site pages
94
+ */
95
+ get query() {
96
+ return defaultQuery(this.options.includePersonalizedRoutes);
97
+ }
98
+ /**
99
+ * Fetch sitemap which could be used for generation of static pages during `next export`.
100
+ * The `locale` parameter will be used in the item query, but since i18n is not supported,
101
+ * the output paths will not include a `language` property.
102
+ * @param {string} locale which application supports
103
+ * @returns an array of @see StaticPath objects
104
+ */
105
+ fetchExportSitemap(locale) {
106
+ return __awaiter(this, void 0, void 0, function* () {
107
+ const formatPath = (path) => ({
108
+ params: {
109
+ path,
110
+ },
111
+ });
112
+ return this.fetchSitemap([locale], formatPath);
113
+ });
114
+ }
115
+ /**
116
+ * Fetch sitemap which could be used for generation of static pages using SSG mode
117
+ * @param {string[]} locales locales which application supports
118
+ * @returns an array of @see StaticPath objects
119
+ */
120
+ fetchSSGSitemap(locales) {
121
+ return __awaiter(this, void 0, void 0, function* () {
122
+ const formatPath = (path, locale) => ({
123
+ params: {
124
+ path,
125
+ },
126
+ locale,
127
+ });
128
+ return this.fetchSitemap(locales, formatPath);
129
+ });
130
+ }
131
+ getTranformedPaths(siteName, languages, formatStaticPath) {
132
+ return __awaiter(this, void 0, void 0, function* () {
133
+ const paths = new Array();
134
+ for (const language of languages) {
135
+ if (language === '') {
136
+ throw new RangeError(languageEmptyError);
137
+ }
138
+ core_1.debug.sitemap('fetching sitemap data for %s %s', language, siteName);
139
+ const results = yield this.fetchLanguageSitePaths(language, siteName);
140
+ const transformedPaths = yield this.transformLanguageSitePaths(results, formatStaticPath, language);
141
+ paths.push(...transformedPaths);
142
+ }
143
+ return paths;
144
+ });
145
+ }
146
+ transformLanguageSitePaths(sitePaths, formatStaticPath, language) {
147
+ return __awaiter(this, void 0, void 0, function* () {
148
+ const formatPath = (path) => formatStaticPath(path.replace(/^\/|\/$/g, '').split('/'), language);
149
+ const aggregatedPaths = [];
150
+ sitePaths.forEach((item) => {
151
+ var _a, _b, _c;
152
+ if (!item)
153
+ return;
154
+ aggregatedPaths.push(formatPath(item.path));
155
+ const variantIds = (_c = (_b = (_a = item.route) === null || _a === void 0 ? void 0 : _a.personalization) === null || _b === void 0 ? void 0 : _b.variantIds) === null || _c === void 0 ? void 0 : _c.filter((variantId) => !variantId.includes('_') // exclude component A/B test variants
156
+ );
157
+ if (variantIds === null || variantIds === void 0 ? void 0 : variantIds.length) {
158
+ aggregatedPaths.push(...variantIds.map((varId) => formatPath((0, personalize_1.getPersonalizedRewrite)(item.path, [varId]))));
159
+ }
160
+ });
161
+ return aggregatedPaths;
162
+ });
163
+ }
164
+ fetchLanguageSitePaths(language, siteName) {
165
+ return __awaiter(this, void 0, void 0, function* () {
166
+ var _a, _b, _c, _d;
167
+ const args = {
168
+ siteName: siteName,
169
+ language: language,
170
+ pageSize: this.options.pageSize,
171
+ includedPaths: this.options.includedPaths,
172
+ excludedPaths: this.options.excludedPaths,
173
+ };
174
+ let results = [];
175
+ let hasNext = true;
176
+ let after = '';
177
+ while (hasNext) {
178
+ const fetchResponse = yield this.graphQLClient.request(this.query, Object.assign(Object.assign({}, args), { after }));
179
+ if (!((_a = fetchResponse === null || fetchResponse === void 0 ? void 0 : fetchResponse.site) === null || _a === void 0 ? void 0 : _a.siteInfo)) {
180
+ throw new RangeError(getSiteEmptyError(siteName));
181
+ }
182
+ else {
183
+ results = results.concat((_b = fetchResponse.site.siteInfo.routes) === null || _b === void 0 ? void 0 : _b.results);
184
+ hasNext = (_c = fetchResponse.site.siteInfo.routes) === null || _c === void 0 ? void 0 : _c.pageInfo.hasNext;
185
+ after = (_d = fetchResponse.site.siteInfo.routes) === null || _d === void 0 ? void 0 : _d.pageInfo.endCursor;
186
+ }
187
+ }
188
+ return results;
189
+ });
190
+ }
191
+ /**
192
+ * Gets a GraphQL client that can make requests to the API. Uses graphql-request as the default
193
+ * library for fetching graphql data (@see GraphQLRequestClient). Override this method if you
194
+ * want to use something else.
195
+ * @returns {GraphQLClient} implementation
196
+ */
197
+ getGraphQLClient() {
198
+ if (!this.options.clientFactory) {
199
+ throw new Error('clientFactory needs to be provided when initializing GraphQL client.');
200
+ }
201
+ return this.options.clientFactory({
202
+ debugger: core_1.debug.sitemap,
203
+ });
204
+ }
205
+ }
206
+ exports.BaseGraphQLSitemapService = BaseGraphQLSitemapService;
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ComponentPropsService = void 0;
16
+ const chalk_1 = __importDefault(require("chalk"));
17
+ class ComponentPropsService {
18
+ /**
19
+ * SSR mode
20
+ * Fetch component props using getServerSideProps function
21
+ * @param {FetchComponentPropsArguments<GetServerSidePropsContext>} params fetch params
22
+ * @returns {Promise<ComponentPropsCollection>} props
23
+ */
24
+ fetchServerSideComponentProps(params) {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ const { moduleFactory, layoutData, context } = params;
27
+ const fetchFunctionFactory = (componentName) => __awaiter(this, void 0, void 0, function* () {
28
+ const module = yield moduleFactory(componentName);
29
+ return module === null || module === void 0 ? void 0 : module.getServerSideProps;
30
+ });
31
+ return this.fetchComponentProps(fetchFunctionFactory, layoutData, context);
32
+ });
33
+ }
34
+ /**
35
+ * SSG mode
36
+ * Fetch component props using getStaticProps function
37
+ * @param {FetchComponentPropsArguments<GetStaticPropsContext>} params fetch arguments
38
+ * @returns {Promise<ComponentPropsCollection>} props
39
+ */
40
+ fetchStaticComponentProps(params) {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ const { moduleFactory, layoutData, context } = params;
43
+ const fetchFunctionFactory = (componentName) => __awaiter(this, void 0, void 0, function* () {
44
+ const module = yield moduleFactory(componentName);
45
+ return module === null || module === void 0 ? void 0 : module.getStaticProps;
46
+ });
47
+ return this.fetchComponentProps(fetchFunctionFactory, layoutData, context);
48
+ });
49
+ }
50
+ /**
51
+ * Traverse Layout Service data tree and call side effects on component level.
52
+ * Side effect function can be: getStaticProps (SSG) or getServerSideProps (SSR)
53
+ * @param {FetchFunctionFactory<NextContext>} fetchFunctionFactory fetch function factory
54
+ * @param {LayoutServiceData} layoutData layout data
55
+ * @param {NextContext} context next context
56
+ * @returns {Promise<ComponentPropsCollection>} component props
57
+ */
58
+ fetchComponentProps(fetchFunctionFactory, layoutData, context) {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ var _a;
61
+ // Array of side effect functions
62
+ const requests = yield this.collectRequests({
63
+ placeholders: (_a = layoutData.sitecore.route) === null || _a === void 0 ? void 0 : _a.placeholders,
64
+ fetchFunctionFactory,
65
+ layoutData,
66
+ context,
67
+ });
68
+ return yield this.execRequests(requests);
69
+ });
70
+ }
71
+ /**
72
+ * Go through layout service data, check all renderings using displayName, which should make some side effects.
73
+ * Write result in requests variable
74
+ * @param {object} params params
75
+ * @param {PlaceholdersData} [params.placeholders]
76
+ * @param {FetchFunctionFactory<NextContext>} params.fetchFunctionFactory
77
+ * @param {LayoutServiceData} params.layoutData
78
+ * @param {NextContext} params.context
79
+ * @param {ComponentPropsRequest<NextContext>[]} params.requests
80
+ * @returns {ComponentPropsRequest<NextContext>[]} array of requests
81
+ */
82
+ collectRequests(params) {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ const { placeholders = {}, fetchFunctionFactory, layoutData, context } = params;
85
+ // Will be called on first round
86
+ if (!params.requests) {
87
+ params.requests = [];
88
+ }
89
+ const renderings = this.flatRenderings(placeholders);
90
+ const actions = renderings.map((r) => __awaiter(this, void 0, void 0, function* () {
91
+ const fetchFunc = yield fetchFunctionFactory(r.componentName);
92
+ if (fetchFunc) {
93
+ params.requests &&
94
+ params.requests.push({
95
+ fetch: fetchFunc,
96
+ rendering: r,
97
+ layoutData: layoutData,
98
+ context,
99
+ });
100
+ }
101
+ // If placeholders exist in current rendering
102
+ if (r.placeholders) {
103
+ yield this.collectRequests(Object.assign(Object.assign({}, params), { placeholders: r.placeholders }));
104
+ }
105
+ }));
106
+ yield Promise.all(actions);
107
+ return params.requests;
108
+ });
109
+ }
110
+ /**
111
+ * Execute request for component props
112
+ * @param {ComponentPropsRequest<NextContext>[]} requests requests
113
+ * @returns {Promise<ComponentPropsCollection>} requests result
114
+ */
115
+ execRequests(requests) {
116
+ return __awaiter(this, void 0, void 0, function* () {
117
+ const componentProps = {};
118
+ const promises = requests.map((req) => {
119
+ const { uid } = req.rendering;
120
+ if (!uid) {
121
+ console.log(`Component ${req.rendering.componentName} doesn't have uid, can't store data for this component`);
122
+ return;
123
+ }
124
+ return req
125
+ .fetch(req.rendering, req.layoutData, req.context)
126
+ .then((result) => {
127
+ // Set component specific data in componentProps store
128
+ componentProps[uid] = result;
129
+ })
130
+ .catch((error) => {
131
+ const errLog = `Error during preload data for component ${req.rendering.componentName} (${uid}): ${error.message || error}`;
132
+ console.error(chalk_1.default.red(errLog));
133
+ componentProps[uid] = {
134
+ error: error.message || errLog,
135
+ componentName: req.rendering.componentName,
136
+ };
137
+ });
138
+ });
139
+ yield Promise.all(promises);
140
+ return componentProps;
141
+ });
142
+ }
143
+ /**
144
+ * Take renderings from all placeholders and returns a flat array of renderings.
145
+ * @example
146
+ * const placeholders = {
147
+ * x1: [{ uid: 1 }, { uid: 2 }],
148
+ * x2: [{ uid: 11 }, { uid: 22 }]
149
+ * }
150
+ *
151
+ * flatRenderings(placeholders);
152
+ *
153
+ * RESULT: [{ uid: 1 }, { uid: 2 }, { uid: 11 }, { uid: 22 }]
154
+ * @param {PlaceholdersData} placeholders placeholders
155
+ * @returns {ComponentRendering[]} renderings
156
+ */
157
+ flatRenderings(placeholders) {
158
+ const allComponentRenderings = [];
159
+ const placeholdersArr = Object.values(placeholders);
160
+ placeholdersArr.forEach((pl) => {
161
+ const renderings = pl;
162
+ allComponentRenderings.push(...renderings);
163
+ });
164
+ return allComponentRenderings;
165
+ }
166
+ }
167
+ exports.ComponentPropsService = ComponentPropsService;