@sitecore-jss/sitecore-jss-nextjs 21.1.0-canary.8 → 21.1.0-canary.81

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 (54) hide show
  1. package/dist/cjs/components/ComponentPropsContext.js +7 -3
  2. package/dist/cjs/components/EditingComponentPlaceholder.js +12 -0
  3. package/dist/cjs/components/Link.js +8 -4
  4. package/dist/cjs/components/NextImage.js +1 -1
  5. package/dist/cjs/components/Placeholder.js +6 -2
  6. package/dist/cjs/components/RichText.js +8 -4
  7. package/dist/cjs/editing/editing-data-cache.js +15 -10
  8. package/dist/cjs/editing/editing-data-middleware.js +4 -4
  9. package/dist/cjs/editing/editing-data-service.js +3 -3
  10. package/dist/cjs/editing/editing-render-middleware.js +13 -4
  11. package/dist/cjs/index.js +16 -6
  12. package/dist/cjs/middleware/index.js +5 -1
  13. package/dist/cjs/middleware/multisite-middleware.js +104 -0
  14. package/dist/cjs/middleware/personalize-middleware.js +19 -4
  15. package/dist/cjs/middleware/redirects-middleware.js +75 -42
  16. package/dist/cjs/monitoring/healthcheck-middleware.js +30 -0
  17. package/dist/cjs/monitoring/index.js +5 -0
  18. package/dist/cjs/services/graphql-sitemap-service.js +48 -25
  19. package/dist/cjs/utils.js +3 -3
  20. package/dist/esm/components/EditingComponentPlaceholder.js +5 -0
  21. package/dist/esm/components/Link.js +2 -2
  22. package/dist/esm/editing/editing-data-cache.js +15 -10
  23. package/dist/esm/editing/editing-data-middleware.js +2 -2
  24. package/dist/esm/editing/editing-data-service.js +2 -2
  25. package/dist/esm/editing/editing-render-middleware.js +11 -2
  26. package/dist/esm/index.js +3 -3
  27. package/dist/esm/middleware/index.js +2 -0
  28. package/dist/esm/middleware/multisite-middleware.js +100 -0
  29. package/dist/esm/middleware/personalize-middleware.js +18 -3
  30. package/dist/esm/middleware/redirects-middleware.js +75 -42
  31. package/dist/esm/monitoring/healthcheck-middleware.js +26 -0
  32. package/dist/esm/monitoring/index.js +1 -0
  33. package/dist/esm/services/graphql-sitemap-service.js +47 -24
  34. package/monitoring.d.ts +1 -0
  35. package/monitoring.js +1 -0
  36. package/package.json +34 -33
  37. package/types/components/ComponentPropsContext.d.ts +1 -1
  38. package/types/components/EditingComponentPlaceholder.d.ts +4 -0
  39. package/types/components/Link.d.ts +1 -1
  40. package/types/components/NextImage.d.ts +1 -1
  41. package/types/components/RichText.d.ts +1 -1
  42. package/types/editing/editing-data-cache.d.ts +4 -4
  43. package/types/editing/editing-data.d.ts +1 -1
  44. package/types/index.d.ts +4 -4
  45. package/types/middleware/index.d.ts +2 -0
  46. package/types/middleware/multisite-middleware.d.ts +46 -0
  47. package/types/middleware/personalize-middleware.d.ts +12 -1
  48. package/types/middleware/redirects-middleware.d.ts +33 -4
  49. package/types/monitoring/healthcheck-middleware.d.ts +12 -0
  50. package/types/monitoring/index.d.ts +1 -0
  51. package/types/services/component-props-service.d.ts +3 -3
  52. package/types/services/graphql-sitemap-service.d.ts +10 -5
  53. package/types/sharedTypes/component-module.d.ts +2 -2
  54. package/types/sharedTypes/component-props.d.ts +4 -4
@@ -25,46 +25,61 @@ class RedirectsMiddleware {
25
25
  * @param {RedirectsMiddlewareConfig} [config] redirects middleware config
26
26
  */
27
27
  constructor(config) {
28
- this.handler = (req) => __awaiter(this, void 0, void 0, function* () {
29
- // Find the redirect from result of RedirectService
30
- const existsRedirect = yield this.getExistsRedirect(req);
31
- if (!existsRedirect) {
32
- return server_1.NextResponse.next();
33
- }
34
- const url = req.nextUrl.clone();
35
- const absoluteUrlRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
36
- if (absoluteUrlRegex.test(existsRedirect.target)) {
37
- url.href = existsRedirect.target;
38
- url.locale = req.nextUrl.locale;
39
- }
40
- else {
41
- url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
42
- const urlFirstPart = existsRedirect.target.split('/')[1];
43
- if (this.locales.includes(urlFirstPart)) {
44
- url.locale = urlFirstPart;
45
- url.pathname = existsRedirect.target.replace(`/${urlFirstPart}`, '');
28
+ this.config = config;
29
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
30
+ const hostname = this.getHostname(req);
31
+ const siteName = (res === null || res === void 0 ? void 0 : res.cookies.get('sc_site')) || this.config.getSite(hostname).name;
32
+ const createResponse = () => __awaiter(this, void 0, void 0, function* () {
33
+ if ((this.config.disabled && this.config.disabled(req, server_1.NextResponse.next())) ||
34
+ this.excludeRoute(req.nextUrl.pathname) ||
35
+ (this.config.excludeRoute && this.config.excludeRoute(req.nextUrl.pathname))) {
36
+ return res || server_1.NextResponse.next();
37
+ }
38
+ // Find the redirect from result of RedirectService
39
+ const existsRedirect = yield this.getExistsRedirect(req, siteName);
40
+ if (!existsRedirect) {
41
+ return res || server_1.NextResponse.next();
42
+ }
43
+ const url = req.nextUrl.clone();
44
+ const absoluteUrlRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
45
+ if (absoluteUrlRegex.test(existsRedirect.target)) {
46
+ url.href = existsRedirect.target;
47
+ url.locale = req.nextUrl.locale;
46
48
  }
47
49
  else {
48
- url.pathname = existsRedirect.target;
50
+ url.search = existsRedirect.isQueryStringPreserved ? url.search : '';
51
+ const urlFirstPart = existsRedirect.target.split('/')[1];
52
+ if (this.locales.includes(urlFirstPart)) {
53
+ url.locale = urlFirstPart;
54
+ url.pathname = existsRedirect.target.replace(`/${urlFirstPart}`, '');
55
+ }
56
+ else {
57
+ url.pathname = existsRedirect.target;
58
+ }
49
59
  }
50
- }
51
- const redirectUrl = decodeURIComponent(url.href);
52
- /** return Response redirect with http code of redirect type **/
53
- switch (existsRedirect.redirectType) {
54
- case site_1.REDIRECT_TYPE_301:
55
- return server_1.NextResponse.redirect(redirectUrl, 301);
56
- case site_1.REDIRECT_TYPE_302:
57
- return server_1.NextResponse.redirect(redirectUrl, 302);
58
- case site_1.REDIRECT_TYPE_SERVER_TRANSFER:
59
- return server_1.NextResponse.rewrite(redirectUrl);
60
- default:
61
- return server_1.NextResponse.next();
62
- }
60
+ const redirectUrl = decodeURIComponent(url.href);
61
+ /** return Response redirect with http code of redirect type **/
62
+ switch (existsRedirect.redirectType) {
63
+ case site_1.REDIRECT_TYPE_301:
64
+ return server_1.NextResponse.redirect(redirectUrl, 301);
65
+ case site_1.REDIRECT_TYPE_302:
66
+ return server_1.NextResponse.redirect(redirectUrl, 302);
67
+ case site_1.REDIRECT_TYPE_SERVER_TRANSFER:
68
+ return server_1.NextResponse.rewrite(redirectUrl);
69
+ default:
70
+ return server_1.NextResponse.next();
71
+ }
72
+ });
73
+ const response = yield createResponse();
74
+ // Share site name with the following executed middlewares
75
+ response.cookies.set('sc_site', siteName);
76
+ return response;
63
77
  });
64
78
  // NOTE: we provide native fetch for compatibility on Next.js Edge Runtime
65
79
  // (underlying default 'cross-fetch' is not currently compatible: https://github.com/lquixada/cross-fetch/issues/78)
66
80
  this.redirectsService = new site_1.GraphQLRedirectsService(Object.assign(Object.assign({}, config), { fetch: fetch }));
67
81
  this.locales = config.locales;
82
+ this.defaultHostname = config.defaultHostname || 'localhost';
68
83
  }
69
84
  /**
70
85
  * Gets the Next.js API route handler
@@ -73,22 +88,40 @@ class RedirectsMiddleware {
73
88
  getHandler() {
74
89
  return this.handler;
75
90
  }
91
+ excludeRoute(pathname) {
92
+ if (pathname.includes('.') || // Ignore files
93
+ pathname.startsWith('/api/') || // Ignore Next.js API calls
94
+ pathname.startsWith('/sitecore/') || // Ignore Sitecore API calls
95
+ pathname.startsWith('/_next') // Ignore next service calls
96
+ ) {
97
+ return true;
98
+ }
99
+ return false;
100
+ }
101
+ getHostname(req) {
102
+ var _a;
103
+ const hostHeader = (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
104
+ return hostHeader || this.defaultHostname;
105
+ }
76
106
  /**
77
107
  * Method returns RedirectInfo when matches
78
- * @param {NextRequest} req
108
+ * @param {NextRequest} req request
109
+ * @param {string} siteName site name
79
110
  * @returns Promise<RedirectInfo | undefined>
80
111
  * @private
81
112
  */
82
- getExistsRedirect(req) {
113
+ getExistsRedirect(req, siteName) {
83
114
  return __awaiter(this, void 0, void 0, function* () {
84
- const redirects = yield this.redirectsService.fetchRedirects();
85
- return redirects.find((redirect) => {
86
- return ((regex_parser_1.default(redirect.pattern.toLowerCase()).test(req.nextUrl.pathname.toLowerCase()) ||
87
- regex_parser_1.default(redirect.pattern.toLowerCase()).test(`/${req.nextUrl.locale}${req.nextUrl.pathname}`.toLowerCase())) &&
88
- (redirect.locale
89
- ? redirect.locale.toLowerCase() === req.nextUrl.locale.toLowerCase()
90
- : true));
91
- });
115
+ const redirects = yield this.redirectsService.fetchRedirects(siteName);
116
+ return redirects.length
117
+ ? redirects.find((redirect) => {
118
+ return (((0, regex_parser_1.default)(redirect.pattern.toLowerCase()).test(req.nextUrl.pathname.toLowerCase()) ||
119
+ (0, regex_parser_1.default)(redirect.pattern.toLowerCase()).test(`/${req.nextUrl.locale}${req.nextUrl.pathname}`.toLowerCase())) &&
120
+ (redirect.locale
121
+ ? redirect.locale.toLowerCase() === req.nextUrl.locale.toLowerCase()
122
+ : true));
123
+ })
124
+ : undefined;
92
125
  });
93
126
  }
94
127
  }
@@ -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; } });
@@ -9,12 +9,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.GraphQLSitemapService = exports.getSiteEmptyError = exports.languageError = void 0;
12
+ exports.GraphQLSitemapService = exports.getSiteEmptyError = exports.sitesError = exports.languageError = void 0;
13
13
  const graphql_1 = require("@sitecore-jss/sitecore-jss/graphql");
14
14
  const sitecore_jss_1 = require("@sitecore-jss/sitecore-jss");
15
15
  const personalize_1 = require("@sitecore-jss/sitecore-jss/personalize");
16
+ const site_1 = require("@sitecore-jss/sitecore-jss/site");
16
17
  /** @private */
17
18
  exports.languageError = 'The list of languages cannot be empty';
19
+ exports.sitesError = 'The list of sites cannot be empty';
18
20
  /**
19
21
  * @param {string} siteName to inject into error text
20
22
  * @private
@@ -53,7 +55,7 @@ query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
53
55
  hasNext
54
56
  }
55
57
  results {
56
- path: routePath
58
+ path: routePath
57
59
  ${usesPersonalize
58
60
  ? `
59
61
  route {
@@ -66,7 +68,7 @@ query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
66
68
  }
67
69
  }
68
70
  }
69
- }
71
+ }
70
72
  `;
71
73
  /**
72
74
  * Service that fetches the list of site pages using Sitecore's GraphQL API.
@@ -74,6 +76,12 @@ query ${usesPersonalize ? 'PersonalizeSitemapQuery' : 'DefaultSitemapQuery'}(
74
76
  * @mixes SearchQueryService<PageListQueryResult>
75
77
  */
76
78
  class GraphQLSitemapService {
79
+ /**
80
+ * Gets the default query used for fetching the list of site pages
81
+ */
82
+ get query() {
83
+ return defaultQuery(this.options.includePersonalizedRoutes);
84
+ }
77
85
  /**
78
86
  * Creates an instance of graphQL sitemap service with the provided options
79
87
  * @param {GraphQLSitemapServiceConfig} options instance
@@ -82,12 +90,6 @@ class GraphQLSitemapService {
82
90
  this.options = options;
83
91
  this.graphQLClient = this.getGraphQLClient();
84
92
  }
85
- /**
86
- * Gets the default query used for fetching the list of site pages
87
- */
88
- get query() {
89
- return defaultQuery(this.options.includePersonalizedRoutes);
90
- }
91
93
  /**
92
94
  * Fetch sitemap which could be used for generation of static pages during `next export`.
93
95
  * The `locale` parameter will be used in the item query, but since i18n is not supported,
@@ -132,43 +134,64 @@ class GraphQLSitemapService {
132
134
  */
133
135
  fetchSitemap(languages, formatStaticPath) {
134
136
  return __awaiter(this, void 0, void 0, function* () {
137
+ const paths = new Array();
135
138
  if (!languages.length) {
136
139
  throw new RangeError(exports.languageError);
137
140
  }
138
- // Fetch paths using all locales
139
- const paths = yield Promise.all(languages.map((language) => {
140
- if (language === '') {
141
- throw new RangeError(languageEmptyError);
142
- }
143
- sitecore_jss_1.debug.sitemap('fetching sitemap data for %s', language);
144
- return this.fetchLanguageSitePaths(language).then((results) => this.transformLanguageSitePaths(results, formatStaticPath, language));
145
- }));
146
- // merge promises results into single result
141
+ // Get all sites
142
+ const sites = this.options.sites;
143
+ if (!sites || !sites.length) {
144
+ throw new RangeError(exports.sitesError);
145
+ }
146
+ // Fetch paths for each site
147
+ for (let i = 0; i < sites.length; i++) {
148
+ const siteName = sites[i];
149
+ const multiSiteName = sites.length > 1 ? siteName : undefined;
150
+ // Fetch paths using all locales
151
+ yield Promise.all(languages.map((language) => __awaiter(this, void 0, void 0, function* () {
152
+ if (language === '') {
153
+ throw new RangeError(languageEmptyError);
154
+ }
155
+ sitecore_jss_1.debug.sitemap('fetching sitemap data for %s %s', language, siteName);
156
+ const results = yield this.fetchLanguageSitePaths(language, siteName);
157
+ const transformedPaths = yield this.transformLanguageSitePaths(results, formatStaticPath, language, multiSiteName);
158
+ paths.push(...transformedPaths);
159
+ })));
160
+ }
147
161
  return [].concat(...paths);
148
162
  });
149
163
  }
150
- transformLanguageSitePaths(sitePaths, formatStaticPath, language) {
164
+ transformLanguageSitePaths(sitePaths, formatStaticPath, language, multiSiteName) {
151
165
  return __awaiter(this, void 0, void 0, function* () {
152
166
  const formatPath = (path) => formatStaticPath(path.replace(/^\/|\/$/g, '').split('/'), language);
153
167
  const aggregatedPaths = [];
154
168
  sitePaths.forEach((item) => {
155
- var _a, _b, _c, _d;
169
+ var _a, _b, _c, _d, _e, _f;
156
170
  if (!item)
157
171
  return;
158
- aggregatedPaths.push(formatPath(item.path));
172
+ if (!multiSiteName) {
173
+ aggregatedPaths.push(formatPath(item.path));
174
+ }
175
+ else {
176
+ aggregatedPaths.push(formatPath((0, site_1.getSiteRewrite)(item.path, { siteName: multiSiteName })));
177
+ }
159
178
  // check for type safety's sake - personalize may be empty depending on query type
160
179
  if ((_b = (_a = item.route) === null || _a === void 0 ? void 0 : _a.personalization) === null || _b === void 0 ? void 0 : _b.variantIds.length) {
161
- aggregatedPaths.push(...(_d = (_c = item.route) === null || _c === void 0 ? void 0 : _c.personalization) === null || _d === void 0 ? void 0 : _d.variantIds.map((varId) => formatPath(personalize_1.getPersonalizedRewrite(item.path, { variantId: varId }))));
180
+ multiSiteName
181
+ ? aggregatedPaths.push(...(((_d = (_c = item.route) === null || _c === void 0 ? void 0 : _c.personalization) === null || _d === void 0 ? void 0 : _d.variantIds.map((varId) => formatPath((0, personalize_1.getPersonalizedRewrite)((0, site_1.getSiteRewrite)(item.path, { siteName: multiSiteName }), {
182
+ variantId: varId,
183
+ })))) || {}))
184
+ : aggregatedPaths.push(...(((_f = (_e = item.route) === null || _e === void 0 ? void 0 : _e.personalization) === null || _f === void 0 ? void 0 : _f.variantIds.map((varId) => formatPath((0, personalize_1.getPersonalizedRewrite)(item.path, { variantId: varId })))) || {}));
162
185
  }
163
186
  });
164
187
  return aggregatedPaths;
165
188
  });
166
189
  }
167
- fetchLanguageSitePaths(language) {
190
+ fetchLanguageSitePaths(language, siteName) {
168
191
  var _a, _b, _c, _d;
169
192
  return __awaiter(this, void 0, void 0, function* () {
170
193
  const args = {
171
- siteName: this.options.siteName,
194
+ siteName: siteName,
172
195
  language: language,
173
196
  pageSize: this.options.pageSize,
174
197
  includedPaths: this.options.includedPaths,
@@ -180,7 +203,7 @@ class GraphQLSitemapService {
180
203
  while (hasNext) {
181
204
  const fetchResponse = yield this.graphQLClient.request(this.query, Object.assign(Object.assign({}, args), { after }));
182
205
  if (!((_a = fetchResponse === null || fetchResponse === void 0 ? void 0 : fetchResponse.site) === null || _a === void 0 ? void 0 : _a.siteInfo)) {
183
- throw new RangeError(getSiteEmptyError(this.options.siteName));
206
+ throw new RangeError(getSiteEmptyError(siteName));
184
207
  }
185
208
  else {
186
209
  results = results.concat((_b = fetchResponse.site.siteInfo.routes) === null || _b === void 0 ? void 0 : _b.results);
package/dist/cjs/utils.js CHANGED
@@ -41,11 +41,11 @@ exports.getPublicUrl = getPublicUrl;
41
41
  * @default forceReload false
42
42
  */
43
43
  const handleEditorFastRefresh = (forceReload = false) => {
44
- if (process.env.NODE_ENV !== 'development' || !utils_1.isEditorActive()) {
44
+ if (process.env.NODE_ENV !== 'development' || !(0, utils_1.isEditorActive)()) {
45
45
  // Only run if development mode and editor is active
46
46
  return;
47
47
  }
48
- const eventSource = new window.EventSource(`${exports.getPublicUrl()}/_next/webpack-hmr`);
48
+ const eventSource = new window.EventSource(`${(0, exports.getPublicUrl)()}/_next/webpack-hmr`);
49
49
  window.addEventListener('beforeunload', () => eventSource.close());
50
50
  eventSource.onopen = () => console.log('[Sitecore Editor Fast Refresh Listener] Online');
51
51
  eventSource.onmessage = (event) => {
@@ -59,7 +59,7 @@ const handleEditorFastRefresh = (forceReload = false) => {
59
59
  return window.location.reload();
60
60
  setTimeout(() => {
61
61
  console.log('[Sitecore Editor HMR Listener] Sitecore editor does not support Fast Refresh, reloading chromes...');
62
- utils_1.resetEditorChromes();
62
+ (0, utils_1.resetEditorChromes)();
63
63
  }, 500);
64
64
  };
65
65
  };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import { EDITING_COMPONENT_ID, EDITING_COMPONENT_PLACEHOLDER, } from '@sitecore-jss/sitecore-jss/layout';
3
+ import { Placeholder } from './Placeholder';
4
+ export const EditingComponentPlaceholder = ({ rendering, }) => (React.createElement("div", { id: EDITING_COMPONENT_ID },
5
+ React.createElement(Placeholder, { name: EDITING_COMPONENT_PLACEHOLDER, rendering: rendering })));
@@ -18,13 +18,13 @@ export const Link = forwardRef((props, ref) => {
18
18
  const value = (field.href
19
19
  ? field
20
20
  : field.value);
21
- const { href, querystring } = value;
21
+ const { href, querystring, anchor } = value;
22
22
  const isEditing = editable && field.editable;
23
23
  if (href && !isEditing) {
24
24
  const text = showLinkTextWithChildrenPresent || !children ? value.text || value.href : null;
25
25
  // determine if a link is a route or not.
26
26
  if (internalLinkMatcher.test(href)) {
27
- return (React.createElement(NextLink, { href: { pathname: href, query: querystring }, key: "link", locale: false },
27
+ return (React.createElement(NextLink, { href: { pathname: href, query: querystring, hash: anchor }, key: "link", locale: false },
28
28
  React.createElement("a", Object.assign({ title: value.title, target: value.target, className: value.class }, htmlLinkProps, { ref: ref }),
29
29
  text,
30
30
  children)));
@@ -16,19 +16,24 @@ export class EditingDataDiskCache {
16
16
  }
17
17
  set(key, editingData) {
18
18
  const filePath = this.cache.set(key, JSON.stringify(editingData));
19
- if (!filePath || filePath.length === 0) {
20
- throw new Error(`Editing data cache not set for key ${key} at ${this.cache.root}`);
21
- }
19
+ return new Promise((resolve, reject) => {
20
+ if (!filePath || filePath.length === 0) {
21
+ reject(new Error(`Editing data cache not set for key ${key} at ${this.cache.root}`));
22
+ }
23
+ resolve();
24
+ });
22
25
  }
23
26
  get(key) {
24
27
  const entry = this.cache.get(key);
25
- if (!entry.isCached) {
26
- console.warn(`Editing data cache miss for key ${key} at ${this.cache.root}`);
27
- return undefined;
28
- }
29
- // Remove to preserve disk-space (as a macrotask so as not to block current execution)
30
- setTimeout(() => this.cache.remove(key));
31
- return JSON.parse(entry.value);
28
+ return new Promise((resolve) => {
29
+ if (!entry.isCached) {
30
+ console.warn(`Editing data cache miss for key ${key} at ${this.cache.root}`);
31
+ resolve(undefined);
32
+ }
33
+ // Remove to preserve disk-space (as a macrotask so as not to block current execution)
34
+ setTimeout(() => this.cache.remove(key));
35
+ resolve(JSON.parse(entry.value));
36
+ });
32
37
  }
33
38
  }
34
39
  /** EditingDataDiskCache singleton */
@@ -33,7 +33,7 @@ export class EditingDataMiddleware {
33
33
  switch (method) {
34
34
  case 'GET': {
35
35
  // Get cache value
36
- const data = this.editingDataCache.get(key);
36
+ const data = yield this.editingDataCache.get(key);
37
37
  res.status(200).json(data);
38
38
  break;
39
39
  }
@@ -43,7 +43,7 @@ export class EditingDataMiddleware {
43
43
  }
44
44
  else {
45
45
  // Set cache value
46
- this.editingDataCache.set(key, body);
46
+ yield this.editingDataCache.set(key, body);
47
47
  res.status(200).end();
48
48
  }
49
49
  break;
@@ -52,7 +52,7 @@ export class BasicEditingDataService {
52
52
  key,
53
53
  };
54
54
  debug.editing('storing editing data for %o: %o', previewData, data);
55
- this.editingDataCache.set(key, data);
55
+ yield this.editingDataCache.set(key, data);
56
56
  return { key };
57
57
  });
58
58
  }
@@ -65,7 +65,7 @@ export class BasicEditingDataService {
65
65
  return __awaiter(this, void 0, void 0, function* () {
66
66
  const editingPreviewData = previewData;
67
67
  debug.editing('retrieving editing data for %o', previewData);
68
- return this.editingDataCache.get(editingPreviewData.key);
68
+ return yield this.editingDataCache.get(editingPreviewData.key);
69
69
  });
70
70
  }
71
71
  }
@@ -9,6 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { STATIC_PROPS_ID, SERVER_PROPS_ID } from 'next/constants';
11
11
  import { AxiosDataFetcher, debug } from '@sitecore-jss/sitecore-jss';
12
+ import { EDITING_COMPONENT_ID, RenderingType } from '@sitecore-jss/sitecore-jss/layout';
13
+ import { parse } from 'node-html-parser';
12
14
  import { editingDataService, QUERY_PARAM_EDITING_SECRET, } from './editing-data-service';
13
15
  import { getJssEditingSecret } from '../utils';
14
16
  /**
@@ -22,7 +24,7 @@ export class EditingRenderMiddleware {
22
24
  constructor(config) {
23
25
  var _a, _b, _c, _d;
24
26
  this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
25
- var _e;
27
+ var _e, _f;
26
28
  const { method, query, body, headers } = req;
27
29
  debug.editing('editing render middleware start: %o', {
28
30
  method,
@@ -91,12 +93,19 @@ export class EditingRenderMiddleware {
91
93
  // certain route configurations (e.g. multiple catch-all routes).
92
94
  // The following line will trick it into thinking we're SSR, thus avoiding any router.replace.
93
95
  html = html.replace(STATIC_PROPS_ID, SERVER_PROPS_ID);
96
+ if (editingData.layoutData.sitecore.context.renderingType === RenderingType.Component) {
97
+ // Handle component rendering. Extract component markup only
98
+ html = (_f = parse(html).getElementById(EDITING_COMPONENT_ID)) === null || _f === void 0 ? void 0 : _f.innerHTML;
99
+ if (!html)
100
+ throw new Error(`Failed to render component for ${requestUrl}`);
101
+ }
94
102
  const body = { html };
95
103
  // Return expected JSON result
96
104
  debug.editing('editing render middleware end: %o', { status: 200, body });
97
105
  res.status(200).json(body);
98
106
  }
99
- catch (error) {
107
+ catch (err) {
108
+ const error = err;
100
109
  console.error(error);
101
110
  if (error.response || error.request) {
102
111
  // Axios error, which could mean the server or page URL isn't quite right, so provide a more helpful hint
package/dist/esm/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  export { constants, AxiosDataFetcher, NativeDataFetcher, enableDebug, } from '@sitecore-jss/sitecore-jss';
2
2
  export { isEditorActive, resetEditorChromes, resolveUrl } from '@sitecore-jss/sitecore-jss/utils';
3
- export { LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, } from '@sitecore-jss/sitecore-jss/layout';
3
+ export { LayoutServicePageState, GraphQLLayoutService, RestLayoutService, getChildPlaceholder, getFieldValue, RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, } from '@sitecore-jss/sitecore-jss/layout';
4
4
  export { mediaApi } from '@sitecore-jss/sitecore-jss/media';
5
5
  export { trackingApi, } from '@sitecore-jss/sitecore-jss/tracking';
6
6
  export { GraphQLDictionaryService, RestDictionaryService, } from '@sitecore-jss/sitecore-jss/i18n';
7
7
  export { personalizeLayout, getPersonalizedRewrite, getPersonalizedRewriteData, normalizePersonalizedRewrite, CdpHelper, } from '@sitecore-jss/sitecore-jss/personalize';
8
- export { GraphQLRobotsService, } from '@sitecore-jss/sitecore-jss/site';
9
8
  export { GraphQLRequestClient } from '@sitecore-jss/sitecore-jss';
10
9
  export { ComponentPropsService } from './services/component-props-service';
11
10
  export { DisconnectedSitemapService } from './services/disconnected-sitemap-service';
12
11
  export { GraphQLSitemapService, } from './services/graphql-sitemap-service';
13
- export { GraphQLSitemapXmlService, GraphQLErrorPagesService, } from '@sitecore-jss/sitecore-jss/site';
12
+ export { GraphQLSitemapXmlService, GraphQLErrorPagesService, GraphQLRobotsService, SiteResolver, GraphQLSiteInfoService, getSiteRewrite, getSiteRewriteData, normalizeSiteRewrite, } from '@sitecore-jss/sitecore-jss/site';
14
13
  export { ComponentPropsReactContext, ComponentPropsContext, useComponentProps, } from './components/ComponentPropsContext';
15
14
  export { handleEditorFastRefresh, getPublicUrl } from './utils';
16
15
  export { Link } from './components/Link';
17
16
  export { RichText } from './components/RichText';
18
17
  export { Placeholder } from './components/Placeholder';
18
+ export { EditingComponentPlaceholder } from './components/EditingComponentPlaceholder';
19
19
  export { NextImage } from './components/NextImage';
20
20
  export { Image, Text, DateField, File, VisitorIdentification, SitecoreContext, SitecoreContextReactContext, withSitecoreContext, useSitecoreContext, withEditorChromes, withPlaceholder, withDatasourceCheck, } from '@sitecore-jss/sitecore-jss-react';
@@ -1,2 +1,4 @@
1
1
  export { RedirectsMiddleware } from './redirects-middleware';
2
2
  export { PersonalizeMiddleware } from './personalize-middleware';
3
+ export { MultisiteMiddleware } from './multisite-middleware';
4
+ export { SiteResolver } from '@sitecore-jss/sitecore-jss/site';
@@ -0,0 +1,100 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { NextResponse } from 'next/server';
11
+ import { getSiteRewrite } from '@sitecore-jss/sitecore-jss/site';
12
+ import { debug } from '@sitecore-jss/sitecore-jss';
13
+ /**
14
+ * Middleware / handler for multisite support
15
+ */
16
+ export class MultisiteMiddleware {
17
+ /**
18
+ * @param {MultisiteMiddlewareConfig} [config] Multisite middleware config
19
+ */
20
+ constructor(config) {
21
+ this.config = config;
22
+ this.handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
23
+ var _a;
24
+ const pathname = req.nextUrl.pathname;
25
+ const hostHeader = (_a = req.headers.get('host')) === null || _a === void 0 ? void 0 : _a.split(':')[0];
26
+ const hostname = hostHeader || this.defaultHostname;
27
+ debug.multisite('multisite middleware start: %o', {
28
+ pathname,
29
+ hostname,
30
+ });
31
+ if (!hostHeader) {
32
+ debug.multisite(`host header is missing, default ${hostname} is used`);
33
+ }
34
+ // Response will be provided if other middleware is run before us
35
+ let response = res || NextResponse.next();
36
+ if (this.excludeRoute(pathname) ||
37
+ (this.config.excludeRoute && this.config.excludeRoute(pathname))) {
38
+ debug.multisite('skipped (route excluded)');
39
+ return response;
40
+ }
41
+ // Site name can be forced by query string parameter or cookie
42
+ const siteName = req.nextUrl.searchParams.get('sc_site') ||
43
+ (this.config.useCookieResolution &&
44
+ this.config.useCookieResolution(req) &&
45
+ req.cookies.get('sc_site')) ||
46
+ this.config.getSite(hostname).name;
47
+ // Rewrite to site specific path
48
+ const rewritePath = getSiteRewrite(pathname, {
49
+ siteName,
50
+ });
51
+ // Note an absolute URL is required: https://nextjs.org/docs/messages/middleware-relative-urls
52
+ const rewriteUrl = req.nextUrl.clone();
53
+ rewriteUrl.pathname = rewritePath;
54
+ response = NextResponse.rewrite(rewriteUrl);
55
+ // Share site name with the following executed middlewares
56
+ response.cookies.set('sc_site', siteName);
57
+ // Share rewrite path with following executed middlewares
58
+ response.headers.set('x-sc-rewrite', rewritePath);
59
+ debug.multisite('multisite middleware end: %o', {
60
+ rewritePath,
61
+ siteName,
62
+ headers: this.extractDebugHeaders(response.headers),
63
+ cookies: response.cookies,
64
+ });
65
+ return response;
66
+ });
67
+ this.defaultHostname = config.defaultHostname || 'localhost';
68
+ }
69
+ /**
70
+ * Gets the Next.js middleware handler with error handling
71
+ * @returns middleware handler
72
+ */
73
+ getHandler() {
74
+ return (req, res) => __awaiter(this, void 0, void 0, function* () {
75
+ try {
76
+ return yield this.handler(req, res);
77
+ }
78
+ catch (error) {
79
+ console.log('Multisite middleware failed:');
80
+ console.log(error);
81
+ return res || NextResponse.next();
82
+ }
83
+ });
84
+ }
85
+ excludeRoute(pathname) {
86
+ if (pathname.includes('.') || // Ignore files
87
+ pathname.startsWith('/api/') || // Ignore Next.js API calls
88
+ pathname.startsWith('/sitecore/') || // Ignore Sitecore API calls
89
+ pathname.startsWith('/_next') // Ignore next service calls
90
+ ) {
91
+ return true;
92
+ }
93
+ return false;
94
+ }
95
+ extractDebugHeaders(incomingHeaders) {
96
+ const headers = {};
97
+ incomingHeaders.forEach((value, key) => (headers[key] = value));
98
+ return headers;
99
+ }
100
+ }