@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.
- package/LICENSE.txt +202 -0
- package/README.md +10 -0
- package/dist/cjs/ComponentBuilder.js +63 -0
- package/dist/cjs/components/BYOCWrapper.js +41 -0
- package/dist/cjs/components/ComponentPropsContext.js +57 -0
- package/dist/cjs/components/FEaaSWrapper.js +43 -0
- package/dist/cjs/components/Link.js +87 -0
- package/dist/cjs/components/NextImage.js +82 -0
- package/dist/cjs/components/Placeholder.js +49 -0
- package/dist/cjs/components/RichText.js +95 -0
- package/dist/cjs/editing/constants.js +10 -0
- package/dist/cjs/editing/editing-config-middleware.js +62 -0
- package/dist/cjs/editing/editing-render-middleware.js +182 -0
- package/dist/cjs/editing/feaas-render-middleware.js +101 -0
- package/dist/cjs/editing/index.js +16 -0
- package/dist/cjs/editing/render-middleware.js +43 -0
- package/dist/cjs/graphql/index.js +7 -0
- package/dist/cjs/index.js +119 -0
- package/dist/cjs/middleware/index.js +13 -0
- package/dist/cjs/middleware/middleware.js +97 -0
- package/dist/cjs/middleware/multisite-middleware.js +93 -0
- package/dist/cjs/middleware/personalize-middleware.js +231 -0
- package/dist/cjs/middleware/redirects-middleware.js +264 -0
- package/dist/cjs/monitoring/healthcheck-middleware.js +30 -0
- package/dist/cjs/monitoring/index.js +5 -0
- package/dist/cjs/services/base-graphql-sitemap-service.js +206 -0
- package/dist/cjs/services/component-props-service.js +167 -0
- package/dist/cjs/services/graphql-sitemap-service.js +64 -0
- package/dist/cjs/services/mutisite-graphql-sitemap-service.js +81 -0
- package/dist/cjs/sharedTypes/component-props.js +2 -0
- package/dist/cjs/sharedTypes/module-factory.js +2 -0
- package/dist/cjs/site/index.js +5 -0
- package/dist/cjs/utils/index.js +11 -0
- package/dist/cjs/utils/utils.js +42 -0
- package/dist/esm/ComponentBuilder.js +59 -0
- package/dist/esm/components/BYOCWrapper.js +36 -0
- package/dist/esm/components/ComponentPropsContext.js +19 -0
- package/dist/esm/components/FEaaSWrapper.js +38 -0
- package/dist/esm/components/Link.js +48 -0
- package/dist/esm/components/NextImage.js +76 -0
- package/dist/esm/components/Placeholder.js +12 -0
- package/dist/esm/components/RichText.js +55 -0
- package/dist/esm/editing/constants.js +7 -0
- package/dist/esm/editing/editing-config-middleware.js +58 -0
- package/dist/esm/editing/editing-render-middleware.js +177 -0
- package/dist/esm/editing/feaas-render-middleware.js +97 -0
- package/dist/esm/editing/index.js +5 -0
- package/dist/esm/editing/render-middleware.js +39 -0
- package/dist/esm/graphql/index.js +1 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/middleware/index.js +5 -0
- package/dist/esm/middleware/middleware.js +93 -0
- package/dist/esm/middleware/multisite-middleware.js +89 -0
- package/dist/esm/middleware/personalize-middleware.js +227 -0
- package/dist/esm/middleware/redirects-middleware.js +257 -0
- package/dist/esm/monitoring/healthcheck-middleware.js +26 -0
- package/dist/esm/monitoring/index.js +1 -0
- package/dist/esm/services/base-graphql-sitemap-service.js +201 -0
- package/dist/esm/services/component-props-service.js +160 -0
- package/dist/esm/services/graphql-sitemap-service.js +59 -0
- package/dist/esm/services/mutisite-graphql-sitemap-service.js +77 -0
- package/dist/esm/sharedTypes/component-props.js +1 -0
- package/dist/esm/sharedTypes/module-factory.js +1 -0
- package/dist/esm/site/index.js +1 -0
- package/dist/esm/utils/index.js +3 -0
- package/dist/esm/utils/utils.js +37 -0
- package/editing.d.ts +1 -0
- package/editing.js +1 -0
- package/global.d.ts +21 -0
- package/graphql.d.ts +1 -0
- package/graphql.js +1 -0
- package/middleware.d.ts +1 -0
- package/middleware.js +1 -0
- package/monitoring.d.ts +1 -0
- package/monitoring.js +1 -0
- package/package.json +92 -0
- package/site.d.ts +1 -0
- package/site.js +1 -0
- package/types/ComponentBuilder.d.ts +59 -0
- package/types/components/BYOCWrapper.d.ts +20 -0
- package/types/components/ComponentPropsContext.d.ts +18 -0
- package/types/components/FEaaSWrapper.d.ts +22 -0
- package/types/components/Link.d.ts +10 -0
- package/types/components/NextImage.d.ts +6 -0
- package/types/components/Placeholder.d.ts +8 -0
- package/types/components/RichText.d.ts +32 -0
- package/types/editing/constants.d.ts +7 -0
- package/types/editing/editing-config-middleware.d.ts +29 -0
- package/types/editing/editing-render-middleware.d.ts +79 -0
- package/types/editing/feaas-render-middleware.d.ts +32 -0
- package/types/editing/index.d.ts +5 -0
- package/types/editing/render-middleware.d.ts +24 -0
- package/types/graphql/index.d.ts +1 -0
- package/types/index.d.ts +24 -0
- package/types/middleware/index.d.ts +5 -0
- package/types/middleware/middleware.d.ts +82 -0
- package/types/middleware/multisite-middleware.d.ts +39 -0
- package/types/middleware/personalize-middleware.d.ts +102 -0
- package/types/middleware/redirects-middleware.d.ts +57 -0
- package/types/monitoring/healthcheck-middleware.d.ts +12 -0
- package/types/monitoring/index.d.ts +1 -0
- package/types/services/base-graphql-sitemap-service.d.ts +148 -0
- package/types/services/component-props-service.d.ts +81 -0
- package/types/services/graphql-sitemap-service.d.ts +51 -0
- package/types/services/mutisite-graphql-sitemap-service.d.ts +42 -0
- package/types/sharedTypes/component-props.d.ts +26 -0
- package/types/sharedTypes/module-factory.d.ts +32 -0
- package/types/site/index.d.ts +1 -0
- package/types/utils/index.d.ts +3 -0
- package/types/utils/utils.d.ts +8 -0
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
|
@@ -0,0 +1,64 @@
|
|
|
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.GraphQLSitemapService = exports.siteError = exports.languageError = void 0;
|
|
13
|
+
exports.getSiteEmptyError = getSiteEmptyError;
|
|
14
|
+
const base_graphql_sitemap_service_1 = require("./base-graphql-sitemap-service");
|
|
15
|
+
/** @private */
|
|
16
|
+
exports.languageError = 'The list of languages cannot be empty';
|
|
17
|
+
exports.siteError = 'The service needs a site name';
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} siteName to inject into error text
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
function getSiteEmptyError(siteName) {
|
|
23
|
+
return `Site "${siteName}" does not exist or site item tree is missing`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Service that fetches the list of site pages using Sitecore's GraphQL API.
|
|
27
|
+
* Used to handle a single site
|
|
28
|
+
* This list is used for SSG and Export functionality.
|
|
29
|
+
* @mixes SearchQueryService<PageListQueryResult>
|
|
30
|
+
*/
|
|
31
|
+
class GraphQLSitemapService extends base_graphql_sitemap_service_1.BaseGraphQLSitemapService {
|
|
32
|
+
/**
|
|
33
|
+
* Creates an instance of graphQL sitemap service with the provided options
|
|
34
|
+
* @param {GraphQLSitemapServiceConfig} options instance
|
|
35
|
+
*/
|
|
36
|
+
constructor(options) {
|
|
37
|
+
super(options);
|
|
38
|
+
this.options = options;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fetch a flat list of all pages that belong to the specificed site and have a
|
|
42
|
+
* version in the specified language(s).
|
|
43
|
+
* @param {string[]} languages Fetch pages that have versions in this language(s).
|
|
44
|
+
* @param {Function} formatStaticPath Function for transforming the raw search results into (@see StaticPath) types.
|
|
45
|
+
* @returns list of pages
|
|
46
|
+
* @throws {RangeError} if the list of languages is empty.
|
|
47
|
+
* @throws {RangeError} if the any of the languages is an empty string.
|
|
48
|
+
*/
|
|
49
|
+
fetchSitemap(languages, formatStaticPath) {
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
const paths = new Array();
|
|
52
|
+
if (!languages.length) {
|
|
53
|
+
throw new RangeError(exports.languageError);
|
|
54
|
+
}
|
|
55
|
+
const siteName = this.options.siteName;
|
|
56
|
+
if (!siteName) {
|
|
57
|
+
throw new RangeError(exports.siteError);
|
|
58
|
+
}
|
|
59
|
+
paths.push(...(yield this.getTranformedPaths(siteName, languages, formatStaticPath)));
|
|
60
|
+
return [].concat(...paths);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.GraphQLSitemapService = GraphQLSitemapService;
|
|
@@ -0,0 +1,81 @@
|
|
|
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.MultisiteGraphQLSitemapService = exports.sitesError = void 0;
|
|
13
|
+
const site_1 = require("@sitecore-content-sdk/core/site");
|
|
14
|
+
const base_graphql_sitemap_service_1 = require("./base-graphql-sitemap-service");
|
|
15
|
+
exports.sitesError = 'The list of sites cannot be empty';
|
|
16
|
+
/**
|
|
17
|
+
* Service that fetches the list of site pages using Sitecore's GraphQL API.
|
|
18
|
+
* Used to handle multiple sites
|
|
19
|
+
* This list is used for SSG and Export functionality.
|
|
20
|
+
* @mixes SearchQueryService<PageListQueryResult>
|
|
21
|
+
*/
|
|
22
|
+
class MultisiteGraphQLSitemapService extends base_graphql_sitemap_service_1.BaseGraphQLSitemapService {
|
|
23
|
+
/**
|
|
24
|
+
* Creates an instance of graphQL sitemap service with the provided options
|
|
25
|
+
* @param {MultisiteGraphQLSitemapServiceConfig} options instance
|
|
26
|
+
*/
|
|
27
|
+
constructor(options) {
|
|
28
|
+
super(options);
|
|
29
|
+
this.options = options;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fetch a flat list of all pages that belong to all the requested sites and have a
|
|
33
|
+
* version in the specified language(s).
|
|
34
|
+
* @param {string[]} languages Fetch pages that have versions in this language(s).
|
|
35
|
+
* @param {Function} formatStaticPath Function for transforming the raw search results into (@see StaticPath) types.
|
|
36
|
+
* @returns list of pages
|
|
37
|
+
* @throws {RangeError} if the list of languages is empty.
|
|
38
|
+
* @throws {RangeError} if the any of the languages is an empty string.
|
|
39
|
+
*/
|
|
40
|
+
fetchSitemap(languages, formatStaticPath) {
|
|
41
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
const paths = new Array();
|
|
43
|
+
if (!languages.length) {
|
|
44
|
+
throw new RangeError(base_graphql_sitemap_service_1.languageError);
|
|
45
|
+
}
|
|
46
|
+
// Get all sites
|
|
47
|
+
const sites = this.options.sites;
|
|
48
|
+
if (!sites || !sites.length) {
|
|
49
|
+
throw new RangeError(exports.sitesError);
|
|
50
|
+
}
|
|
51
|
+
// Fetch paths for each site
|
|
52
|
+
for (let i = 0; i < sites.length; i++) {
|
|
53
|
+
const siteName = sites[i];
|
|
54
|
+
// Fetch paths using all locales
|
|
55
|
+
paths.push(...(yield this.getTranformedPaths(siteName, languages, formatStaticPath)));
|
|
56
|
+
}
|
|
57
|
+
return [].concat(...paths);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Fetch and return site paths for multisite implementation, with prefixes included
|
|
62
|
+
* @param {string} language path language
|
|
63
|
+
* @param {string} siteName site name
|
|
64
|
+
* @returns modified paths
|
|
65
|
+
*/
|
|
66
|
+
fetchLanguageSitePaths(language, siteName) {
|
|
67
|
+
const _super = Object.create(null, {
|
|
68
|
+
fetchLanguageSitePaths: { get: () => super.fetchLanguageSitePaths }
|
|
69
|
+
});
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const results = yield _super.fetchLanguageSitePaths.call(this, language, siteName);
|
|
72
|
+
results.forEach((item) => {
|
|
73
|
+
if (item) {
|
|
74
|
+
item.path = (0, site_1.getSiteRewrite)(item.path, { siteName: siteName });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return results;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.MultisiteGraphQLSitemapService = MultisiteGraphQLSitemapService;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SiteResolver = void 0;
|
|
4
|
+
var site_1 = require("@sitecore-content-sdk/core/site");
|
|
5
|
+
Object.defineProperty(exports, "SiteResolver", { enumerable: true, get: function () { return site_1.SiteResolver; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resetEditorChromes = exports.isEditorActive = exports.resolveUrl = exports.tryParseEnvValue = exports.handleEditorFastRefresh = void 0;
|
|
4
|
+
var utils_1 = require("./utils");
|
|
5
|
+
Object.defineProperty(exports, "handleEditorFastRefresh", { enumerable: true, get: function () { return utils_1.handleEditorFastRefresh; } });
|
|
6
|
+
var utils_2 = require("@sitecore-content-sdk/core/utils");
|
|
7
|
+
Object.defineProperty(exports, "tryParseEnvValue", { enumerable: true, get: function () { return utils_2.tryParseEnvValue; } });
|
|
8
|
+
Object.defineProperty(exports, "resolveUrl", { enumerable: true, get: function () { return utils_2.resolveUrl; } });
|
|
9
|
+
var editing_1 = require("@sitecore-content-sdk/core/editing");
|
|
10
|
+
Object.defineProperty(exports, "isEditorActive", { enumerable: true, get: function () { return editing_1.isEditorActive; } });
|
|
11
|
+
Object.defineProperty(exports, "resetEditorChromes", { enumerable: true, get: function () { return editing_1.resetEditorChromes; } });
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getJssEditingSecret = exports.handleEditorFastRefresh = void 0;
|
|
4
|
+
const editing_1 = require("@sitecore-content-sdk/core/editing");
|
|
5
|
+
/**
|
|
6
|
+
* Since Sitecore editors do not support Fast Refresh:
|
|
7
|
+
* 1. Subscribe on events provided by webpack.
|
|
8
|
+
* 2. Reset editor chromes when build is finished
|
|
9
|
+
* @param {boolean} [forceReload] force page reload instead of reset chromes
|
|
10
|
+
*/
|
|
11
|
+
const handleEditorFastRefresh = (forceReload = false) => {
|
|
12
|
+
if (process.env.NODE_ENV !== 'development' || !(0, editing_1.isEditorActive)()) {
|
|
13
|
+
// Only run if development mode and editor is active
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const eventSource = new window.EventSource('/_next/webpack-hmr');
|
|
17
|
+
window.addEventListener('beforeunload', () => eventSource.close());
|
|
18
|
+
eventSource.onopen = () => console.log('[Sitecore Editor Fast Refresh Listener] Online');
|
|
19
|
+
eventSource.onmessage = (event) => {
|
|
20
|
+
if (event.data.indexOf('{') === -1)
|
|
21
|
+
return; // heartbeat
|
|
22
|
+
const payload = JSON.parse(event.data);
|
|
23
|
+
console.debug(`[Sitecore Editor Fast Refresh Listener] Saw event: ${JSON.stringify(payload)}`);
|
|
24
|
+
if (payload.action !== 'built')
|
|
25
|
+
return;
|
|
26
|
+
if (forceReload)
|
|
27
|
+
return window.location.reload();
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
console.log('[Sitecore Editor HMR Listener] Sitecore editor does not support Fast Refresh, reloading chromes...');
|
|
30
|
+
(0, editing_1.resetEditorChromes)();
|
|
31
|
+
}, 500);
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
exports.handleEditorFastRefresh = handleEditorFastRefresh;
|
|
35
|
+
const getJssEditingSecret = () => {
|
|
36
|
+
const secret = process.env.JSS_EDITING_SECRET;
|
|
37
|
+
if (!secret || secret.length === 0) {
|
|
38
|
+
throw new Error('The JSS_EDITING_SECRET environment variable is missing or invalid.');
|
|
39
|
+
}
|
|
40
|
+
return secret;
|
|
41
|
+
};
|
|
42
|
+
exports.getJssEditingSecret = getJssEditingSecret;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nextjs implementation of component builder class for building components based on the configuration.
|
|
3
|
+
*/
|
|
4
|
+
export class ComponentBuilder {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
/**
|
|
8
|
+
* SXA uses custom default export name
|
|
9
|
+
*/
|
|
10
|
+
this.DEFAULT_EXPORT_NAME = 'Default';
|
|
11
|
+
this.components = new Map([...config.components]);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new instance of module factory
|
|
15
|
+
* Module factory provides a module (file) including all exports.
|
|
16
|
+
* Module can be imported dynamically or statically.
|
|
17
|
+
* @returns {ModuleFactory} Module factory implementation
|
|
18
|
+
*/
|
|
19
|
+
getModuleFactory() {
|
|
20
|
+
return (componentName) => {
|
|
21
|
+
const component = this.components.get(componentName);
|
|
22
|
+
if (!component)
|
|
23
|
+
return null;
|
|
24
|
+
// check if module should be imported dynamically
|
|
25
|
+
if (component.module) {
|
|
26
|
+
return component.module();
|
|
27
|
+
}
|
|
28
|
+
return component;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new instance of component factory
|
|
33
|
+
* Component can be imported dynamically or statically.
|
|
34
|
+
* @param {object} [config] Component factory configuration
|
|
35
|
+
* @param {boolean} [config.isEditing] Indicates if component factory is used in editing mode
|
|
36
|
+
* @returns {ComponentFactory} Component factory implementation
|
|
37
|
+
*/
|
|
38
|
+
getComponentFactory({ isEditing } = {}) {
|
|
39
|
+
return (componentName, exportName) => {
|
|
40
|
+
const component = this.components.get(componentName);
|
|
41
|
+
if (!component)
|
|
42
|
+
return null;
|
|
43
|
+
// check if component should be imported dynamically
|
|
44
|
+
if (component.element) {
|
|
45
|
+
// Editing mode doesn't work well with dynamic components in nextjs: dynamic components are not displayed without refresh after a rendering is added.
|
|
46
|
+
// This happens beacuse Sitecore editors simply insert updated HTML generated on server side. This conflicts with nextjs dynamic logic as no HTML gets rendered for dynamic component
|
|
47
|
+
// So we use require() to obtain dynamic components in editing mode while preserving dynamic logic for non-editing scenarios
|
|
48
|
+
// As we need to be able to seamlessly work with dynamic components in both editing and normal modes, different componentFactory functions will be passed to app
|
|
49
|
+
return component.element(isEditing);
|
|
50
|
+
}
|
|
51
|
+
if (exportName && exportName !== this.DEFAULT_EXPORT_NAME) {
|
|
52
|
+
return component[exportName];
|
|
53
|
+
}
|
|
54
|
+
return (component.Default ||
|
|
55
|
+
component.default ||
|
|
56
|
+
component);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
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 { BYOCWrapper, fetchBYOCComponentServerProps, } from '@sitecore-content-sdk/react';
|
|
11
|
+
/**
|
|
12
|
+
* TODO: remove when framework agnostic forms implemented
|
|
13
|
+
* This is a repackaged version of the React BYOCWrapper component with support for
|
|
14
|
+
* server rendering in Next.js (using component-level data-fetching feature of JSS).
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Will be called during SSG
|
|
18
|
+
* @param {ComponentRendering} rendering
|
|
19
|
+
* @returns {GetStaticPropsContext} context
|
|
20
|
+
*/
|
|
21
|
+
export const getStaticProps = (rendering) => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
const params = rendering.params || {};
|
|
23
|
+
const result = yield fetchBYOCComponentServerProps(params);
|
|
24
|
+
return result;
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Will be called during SSR
|
|
28
|
+
* @param {ComponentRendering} rendering
|
|
29
|
+
* @returns {GetStaticPropsContext} context
|
|
30
|
+
*/
|
|
31
|
+
export const getServerSideProps = (rendering) => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
const params = rendering.params || {};
|
|
33
|
+
const result = yield fetchBYOCComponentServerProps(params);
|
|
34
|
+
return result;
|
|
35
|
+
});
|
|
36
|
+
export default BYOCWrapper;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React, { createContext, useContext } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Component props context which we are using in order to store data fetched on components level (getStaticProps/getServerSideProps)
|
|
4
|
+
*/
|
|
5
|
+
export const ComponentPropsReactContext = createContext({});
|
|
6
|
+
/**
|
|
7
|
+
* Hook in order to get access to props related to specific component. Data comes from ComponentPropsContext.
|
|
8
|
+
* @see ComponentPropsContext
|
|
9
|
+
* @param {string | undefined} componentUid component uId
|
|
10
|
+
* @returns {ComponentData | undefined} component props
|
|
11
|
+
*/
|
|
12
|
+
export function useComponentProps(componentUid) {
|
|
13
|
+
if (!componentUid) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const data = useContext(ComponentPropsReactContext);
|
|
17
|
+
return data[componentUid];
|
|
18
|
+
}
|
|
19
|
+
export const ComponentPropsContext = ({ children, value, }) => (React.createElement(ComponentPropsReactContext.Provider, { value: value }, children));
|
|
@@ -0,0 +1,38 @@
|
|
|
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 { FEaaSWrapper, fetchFEaaSComponentServerProps, } from '@sitecore-content-sdk/react';
|
|
11
|
+
/**
|
|
12
|
+
* TODO: remove when framework agnostic forms implemented
|
|
13
|
+
* This is a repackaged version of the React FEaaSWrapper component with support for
|
|
14
|
+
* server rendering in Next.js (using component-level data-fetching feature of JSS).
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Will be called during SSG
|
|
18
|
+
* @param {ComponentRendering} rendering
|
|
19
|
+
* @param {LayoutServiceData} layoutData
|
|
20
|
+
* @returns {GetStaticPropsContext} context
|
|
21
|
+
*/
|
|
22
|
+
export const getStaticProps = (rendering, layoutData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
+
const params = rendering.params || {};
|
|
24
|
+
const result = yield fetchFEaaSComponentServerProps(params, layoutData.sitecore.context.pageState);
|
|
25
|
+
return result;
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Will be called during SSR
|
|
29
|
+
* @param {ComponentRendering} rendering
|
|
30
|
+
* @param {LayoutServiceData} layoutData
|
|
31
|
+
* @returns {GetStaticPropsContext} context
|
|
32
|
+
*/
|
|
33
|
+
export const getServerSideProps = (rendering, layoutData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
|
+
const params = rendering.params || {};
|
|
35
|
+
const result = yield fetchFEaaSComponentServerProps(params, layoutData.sitecore.context.pageState);
|
|
36
|
+
return result;
|
|
37
|
+
});
|
|
38
|
+
export default FEaaSWrapper;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React, { forwardRef } from 'react';
|
|
13
|
+
import PropTypes from 'prop-types';
|
|
14
|
+
import NextLink from 'next/link';
|
|
15
|
+
import { Link as ReactLink, LinkPropTypes, } from '@sitecore-content-sdk/react';
|
|
16
|
+
/**
|
|
17
|
+
* Matches relative URLs that end with a file extension.
|
|
18
|
+
*/
|
|
19
|
+
const FILE_EXTENSION_MATCHER = /^\/.*\.\w+$/;
|
|
20
|
+
export const Link = forwardRef((props, ref) => {
|
|
21
|
+
const { field, editable = true, children, internalLinkMatcher = /^\//g, showLinkTextWithChildrenPresent } = props, htmlLinkProps = __rest(props, ["field", "editable", "children", "internalLinkMatcher", "showLinkTextWithChildrenPresent"]);
|
|
22
|
+
if (!field || (!field.value && !field.href && !field.metadata)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const value = (field.href
|
|
26
|
+
? field
|
|
27
|
+
: field.value);
|
|
28
|
+
// fallback to {} if value is undefined; could happen if field is LinkFieldValue, href is empty in metadata mode
|
|
29
|
+
const { href, querystring, anchor } = value || {};
|
|
30
|
+
const isEditing = editable && field.metadata;
|
|
31
|
+
if (href && !isEditing) {
|
|
32
|
+
const text = showLinkTextWithChildrenPresent || !children ? value.text || value.href : null;
|
|
33
|
+
const isMatching = internalLinkMatcher.test(href);
|
|
34
|
+
const isFileUrl = FILE_EXTENSION_MATCHER.test(href);
|
|
35
|
+
// determine if a link is a route or not. File extensions are not routes and should not be pre-fetched.
|
|
36
|
+
if (isMatching && !isFileUrl) {
|
|
37
|
+
return (React.createElement(NextLink, Object.assign({ href: { pathname: href, query: querystring, hash: anchor }, key: "link", locale: false, title: value.title, target: value.target, className: value.class }, htmlLinkProps, { ref: ref }, (process.env.TEST ? { 'data-nextjs-link': true } : {})),
|
|
38
|
+
text,
|
|
39
|
+
children));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// prevent passing internalLinkMatcher as it is an invalid DOM element prop
|
|
43
|
+
const reactLinkProps = Object.assign({}, props);
|
|
44
|
+
delete reactLinkProps.internalLinkMatcher;
|
|
45
|
+
return (React.createElement(ReactLink, Object.assign({}, reactLinkProps, { ref: ref }, (process.env.TEST ? { 'data-react-link': true } : {}))));
|
|
46
|
+
});
|
|
47
|
+
Link.displayName = 'NextLink';
|
|
48
|
+
Link.propTypes = Object.assign({ internalLinkMatcher: PropTypes.instanceOf(RegExp) }, LinkPropTypes);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { mediaApi } from '@sitecore-content-sdk/core/media';
|
|
13
|
+
import PropTypes from 'prop-types';
|
|
14
|
+
import React from 'react';
|
|
15
|
+
import { withFieldMetadata, SitecoreContextReactContext, } from '@sitecore-content-sdk/react';
|
|
16
|
+
import Image from 'next/image';
|
|
17
|
+
import { withEmptyFieldEditingComponent } from '@sitecore-content-sdk/react';
|
|
18
|
+
import { DefaultEmptyFieldEditingComponentImage } from '@sitecore-content-sdk/react';
|
|
19
|
+
import { isFieldValueEmpty, LayoutServicePageState } from '@sitecore-content-sdk/core/layout';
|
|
20
|
+
export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
|
|
21
|
+
var _b;
|
|
22
|
+
var { editable = true, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
|
|
23
|
+
const sitecoreContext = React.useContext(SitecoreContextReactContext);
|
|
24
|
+
// next handles src and we use a custom loader,
|
|
25
|
+
// throw error if these are present
|
|
26
|
+
if (otherProps.src) {
|
|
27
|
+
throw new Error('Detected src prop. If you wish to use src, use next/image directly.');
|
|
28
|
+
}
|
|
29
|
+
const dynamicMedia = field;
|
|
30
|
+
if (isFieldValueEmpty(dynamicMedia)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
// some wise-guy/gal is passing in a 'raw' image object value
|
|
34
|
+
const img = dynamicMedia.src
|
|
35
|
+
? field
|
|
36
|
+
: dynamicMedia.value;
|
|
37
|
+
if (!img) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// disable image optimization for Edit and Preview, but preserve original value if true
|
|
41
|
+
const unoptimized = otherProps.unoptimized ||
|
|
42
|
+
((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.pageState) !== LayoutServicePageState.Normal;
|
|
43
|
+
const attrs = Object.assign(Object.assign(Object.assign({}, img), otherProps), { fill,
|
|
44
|
+
priority, src: mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix), unoptimized });
|
|
45
|
+
const imageProps = Object.assign(Object.assign({}, attrs), {
|
|
46
|
+
// force replace /media with /jssmedia in src since we _know_ we will be adding a 'mw' query string parameter
|
|
47
|
+
// this is required for Sitecore media API resizing to work properly
|
|
48
|
+
src: mediaApi.replaceMediaUrlPrefix(attrs.src, mediaUrlPrefix) });
|
|
49
|
+
// Exclude `width`, `height` in case image is responsive, `fill` is used
|
|
50
|
+
if (imageProps.fill) {
|
|
51
|
+
delete imageProps.width;
|
|
52
|
+
delete imageProps.height;
|
|
53
|
+
}
|
|
54
|
+
if (attrs) {
|
|
55
|
+
return (React.createElement(Image, Object.assign({ alt: "" }, imageProps, (process.env.TEST ? { 'data-unoptimized': unoptimized } : {}))));
|
|
56
|
+
}
|
|
57
|
+
return null; // we can't handle the truth
|
|
58
|
+
}, { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentImage }));
|
|
59
|
+
NextImage.propTypes = {
|
|
60
|
+
field: PropTypes.oneOfType([
|
|
61
|
+
PropTypes.shape({
|
|
62
|
+
src: PropTypes.string.isRequired,
|
|
63
|
+
}),
|
|
64
|
+
PropTypes.shape({
|
|
65
|
+
value: PropTypes.object,
|
|
66
|
+
}),
|
|
67
|
+
]),
|
|
68
|
+
editable: PropTypes.bool,
|
|
69
|
+
mediaUrlPrefix: PropTypes.instanceOf(RegExp),
|
|
70
|
+
imageParams: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.number.isRequired, PropTypes.string.isRequired]).isRequired),
|
|
71
|
+
emptyFieldEditingComponent: PropTypes.oneOfType([
|
|
72
|
+
PropTypes.object,
|
|
73
|
+
PropTypes.func,
|
|
74
|
+
]),
|
|
75
|
+
};
|
|
76
|
+
NextImage.displayName = 'NextImage';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { Placeholder as ReactPlaceholder, } from '@sitecore-content-sdk/react';
|
|
3
|
+
import { ComponentPropsReactContext } from './ComponentPropsContext';
|
|
4
|
+
export const Placeholder = (props) => {
|
|
5
|
+
const componentPropsContext = useContext(ComponentPropsReactContext);
|
|
6
|
+
return (React.createElement(ReactPlaceholder, Object.assign({}, props, { modifyComponentProps: (initialProps) => {
|
|
7
|
+
if (!initialProps.rendering.uid)
|
|
8
|
+
return initialProps;
|
|
9
|
+
const data = componentPropsContext[initialProps.rendering.uid];
|
|
10
|
+
return Object.assign(Object.assign({}, initialProps), data);
|
|
11
|
+
} })));
|
|
12
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React, { useEffect, useRef } from 'react';
|
|
13
|
+
import PropTypes from 'prop-types';
|
|
14
|
+
import { useRouter } from 'next/router';
|
|
15
|
+
import { RichText as ReactRichText, RichTextPropTypes, } from '@sitecore-content-sdk/react';
|
|
16
|
+
const prefetched = {};
|
|
17
|
+
export const RichText = (props) => {
|
|
18
|
+
const { internalLinksSelector = 'a[href^="/"]', prefetchLinks = true, editable = true } = props, rest = __rest(props, ["internalLinksSelector", "prefetchLinks", "editable"]);
|
|
19
|
+
const hasText = props.field && props.field.value;
|
|
20
|
+
const isEditing = editable && props.field && props.field.metadata;
|
|
21
|
+
const router = useRouter();
|
|
22
|
+
const richTextRef = useRef(null);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
// NOT IN EDIT MODE
|
|
25
|
+
if (hasText && !isEditing) {
|
|
26
|
+
initializeLinks();
|
|
27
|
+
}
|
|
28
|
+
}, [hasText]);
|
|
29
|
+
const routeHandler = (ev) => {
|
|
30
|
+
if (!ev.currentTarget)
|
|
31
|
+
return;
|
|
32
|
+
ev.preventDefault();
|
|
33
|
+
const pathname = ev.currentTarget.href;
|
|
34
|
+
router.push(pathname, pathname, { locale: false });
|
|
35
|
+
};
|
|
36
|
+
const initializeLinks = () => {
|
|
37
|
+
const node = richTextRef.current;
|
|
38
|
+
// selects all links that start with '/'
|
|
39
|
+
const internalLinks = node && node.querySelectorAll(internalLinksSelector);
|
|
40
|
+
if (!internalLinks || !internalLinks.length)
|
|
41
|
+
return;
|
|
42
|
+
internalLinks.forEach((link) => {
|
|
43
|
+
if (link.target === '_blank')
|
|
44
|
+
return;
|
|
45
|
+
if (prefetchLinks && !prefetched[link.pathname]) {
|
|
46
|
+
router.prefetch(link.pathname, undefined, { locale: false });
|
|
47
|
+
prefetched[link.pathname] = true;
|
|
48
|
+
}
|
|
49
|
+
link.addEventListener('click', routeHandler, false);
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
return React.createElement(ReactRichText, Object.assign({ ref: richTextRef, editable: editable }, rest));
|
|
53
|
+
};
|
|
54
|
+
RichText.propTypes = Object.assign({ internalLinksSelector: PropTypes.string }, RichTextPropTypes);
|
|
55
|
+
RichText.displayName = 'NextRichText';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const QUERY_PARAM_VERCEL_PROTECTION_BYPASS = 'x-vercel-protection-bypass';
|
|
2
|
+
export const QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE = 'x-vercel-set-bypass-cookie';
|
|
3
|
+
/**
|
|
4
|
+
* Headers that should be passed along to (Editing Chromes handler) SSR request.
|
|
5
|
+
* Note these are in lowercase format to match expected `IncomingHttpHeaders`.
|
|
6
|
+
*/
|
|
7
|
+
export const EDITING_PASS_THROUGH_HEADERS = ['authorization', 'cookie'];
|
|
@@ -0,0 +1,58 @@
|
|
|
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 { EDITING_ALLOWED_ORIGINS, QUERY_PARAM_EDITING_SECRET, } from '@sitecore-content-sdk/core/editing';
|
|
11
|
+
import { debug } from '@sitecore-content-sdk/core';
|
|
12
|
+
import { enforceCors } from '@sitecore-content-sdk/core/utils';
|
|
13
|
+
import { EditMode } from '@sitecore-content-sdk/core/layout';
|
|
14
|
+
import { getJssEditingSecret } from '../utils/utils';
|
|
15
|
+
/**
|
|
16
|
+
* Middleware / handler used in the editing config API route in xmcloud add on (e.g. '/api/editing/config')
|
|
17
|
+
* provides configuration information to determine feature compatibility on Pages side.
|
|
18
|
+
*/
|
|
19
|
+
export class EditingConfigMiddleware {
|
|
20
|
+
/**
|
|
21
|
+
* @param {EditingConfigMiddlewareConfig} [config] Editing configuration middleware config
|
|
22
|
+
*/
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.handler = (_req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
const secret = _req.query[QUERY_PARAM_EDITING_SECRET];
|
|
27
|
+
if (!enforceCors(_req, res, EDITING_ALLOWED_ORIGINS)) {
|
|
28
|
+
debug.editing('invalid origin host - set allowed origins in JSS_ALLOWED_ORIGINS environment variable');
|
|
29
|
+
return res.status(401).json({ message: 'Invalid origin' });
|
|
30
|
+
}
|
|
31
|
+
if (secret !== getJssEditingSecret()) {
|
|
32
|
+
debug.editing('invalid editing secret - sent "%s" expected "%s"', secret, getJssEditingSecret());
|
|
33
|
+
return res.status(401).json({ message: 'Missing or invalid editing secret' });
|
|
34
|
+
}
|
|
35
|
+
// Handle preflight request
|
|
36
|
+
if (_req.method === 'OPTIONS') {
|
|
37
|
+
debug.editing('preflight request');
|
|
38
|
+
// CORS headers are set by enforceCors
|
|
39
|
+
return res.status(204).send(null);
|
|
40
|
+
}
|
|
41
|
+
const components = Array.isArray(this.config.components)
|
|
42
|
+
? this.config.components
|
|
43
|
+
: Array.from(this.config.components.keys());
|
|
44
|
+
return res.status(200).json({
|
|
45
|
+
components,
|
|
46
|
+
packages: this.config.metadata.packages,
|
|
47
|
+
editMode: EditMode.Metadata,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Gets the Next.js API route handler
|
|
53
|
+
* @returns middleware handler
|
|
54
|
+
*/
|
|
55
|
+
getHandler() {
|
|
56
|
+
return this.handler;
|
|
57
|
+
}
|
|
58
|
+
}
|