@sitecore-jss/sitecore-jss 0.1.0-beta.2

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 (143) hide show
  1. package/LICENSE.txt +202 -0
  2. package/README.md +7 -0
  3. package/dist/cjs/cache-client.js +54 -0
  4. package/dist/cjs/constants.js +12 -0
  5. package/dist/cjs/debug.js +43 -0
  6. package/dist/cjs/graphql/app-root-query.js +73 -0
  7. package/dist/cjs/graphql/graphql-edge-proxy.js +12 -0
  8. package/dist/cjs/graphql/index.js +11 -0
  9. package/dist/cjs/graphql/search-service.js +60 -0
  10. package/dist/cjs/graphql-request-client.js +106 -0
  11. package/dist/cjs/i18n/dictionary-service.js +45 -0
  12. package/dist/cjs/i18n/graphql-dictionary-service.js +125 -0
  13. package/dist/cjs/i18n/index.js +7 -0
  14. package/dist/cjs/index.js +36 -0
  15. package/dist/cjs/layout/content-styles.js +73 -0
  16. package/dist/cjs/layout/graphql-layout-service.js +84 -0
  17. package/dist/cjs/layout/index.js +18 -0
  18. package/dist/cjs/layout/layout-service.js +9 -0
  19. package/dist/cjs/layout/models.js +27 -0
  20. package/dist/cjs/layout/themes.js +79 -0
  21. package/dist/cjs/layout/utils.js +44 -0
  22. package/dist/cjs/media/index.js +24 -0
  23. package/dist/cjs/media/media-api.js +128 -0
  24. package/dist/cjs/models.js +2 -0
  25. package/dist/cjs/native-fetcher.js +183 -0
  26. package/dist/cjs/personalize/graphql-personalize-service.js +114 -0
  27. package/dist/cjs/personalize/index.js +12 -0
  28. package/dist/cjs/personalize/layout-personalizer.js +75 -0
  29. package/dist/cjs/personalize/utils.js +92 -0
  30. package/dist/cjs/site/graphql-error-pages-service.js +86 -0
  31. package/dist/cjs/site/graphql-redirects-service.js +103 -0
  32. package/dist/cjs/site/graphql-robots-service.js +81 -0
  33. package/dist/cjs/site/graphql-siteinfo-service.js +128 -0
  34. package/dist/cjs/site/graphql-sitemap-service.js +91 -0
  35. package/dist/cjs/site/index.js +22 -0
  36. package/dist/cjs/site/site-resolver.js +79 -0
  37. package/dist/cjs/site/utils.js +43 -0
  38. package/dist/cjs/utils/edit-frame.js +138 -0
  39. package/dist/cjs/utils/editing.js +122 -0
  40. package/dist/cjs/utils/env.js +26 -0
  41. package/dist/cjs/utils/index.js +25 -0
  42. package/dist/cjs/utils/is-server.js +10 -0
  43. package/dist/cjs/utils/timeout-promise.js +31 -0
  44. package/dist/cjs/utils/utils.js +70 -0
  45. package/dist/esm/cache-client.js +50 -0
  46. package/dist/esm/constants.js +9 -0
  47. package/dist/esm/debug.js +36 -0
  48. package/dist/esm/graphql/app-root-query.js +69 -0
  49. package/dist/esm/graphql/graphql-edge-proxy.js +8 -0
  50. package/dist/esm/graphql/index.js +4 -0
  51. package/dist/esm/graphql/search-service.js +56 -0
  52. package/dist/esm/graphql-request-client.js +99 -0
  53. package/dist/esm/i18n/dictionary-service.js +41 -0
  54. package/dist/esm/i18n/graphql-dictionary-service.js +118 -0
  55. package/dist/esm/i18n/index.js +2 -0
  56. package/dist/esm/index.js +7 -0
  57. package/dist/esm/layout/content-styles.js +65 -0
  58. package/dist/esm/layout/graphql-layout-service.js +77 -0
  59. package/dist/esm/layout/index.js +6 -0
  60. package/dist/esm/layout/layout-service.js +5 -0
  61. package/dist/esm/layout/models.js +24 -0
  62. package/dist/esm/layout/themes.js +74 -0
  63. package/dist/esm/layout/utils.js +39 -0
  64. package/dist/esm/media/index.js +2 -0
  65. package/dist/esm/media/media-api.js +117 -0
  66. package/dist/esm/models.js +1 -0
  67. package/dist/esm/native-fetcher.js +176 -0
  68. package/dist/esm/personalize/graphql-personalize-service.js +107 -0
  69. package/dist/esm/personalize/index.js +3 -0
  70. package/dist/esm/personalize/layout-personalizer.js +69 -0
  71. package/dist/esm/personalize/utils.js +85 -0
  72. package/dist/esm/site/graphql-error-pages-service.js +79 -0
  73. package/dist/esm/site/graphql-redirects-service.js +96 -0
  74. package/dist/esm/site/graphql-robots-service.js +74 -0
  75. package/dist/esm/site/graphql-siteinfo-service.js +121 -0
  76. package/dist/esm/site/graphql-sitemap-service.js +84 -0
  77. package/dist/esm/site/index.js +7 -0
  78. package/dist/esm/site/site-resolver.js +75 -0
  79. package/dist/esm/site/utils.js +37 -0
  80. package/dist/esm/utils/edit-frame.js +133 -0
  81. package/dist/esm/utils/editing.js +111 -0
  82. package/dist/esm/utils/env.js +22 -0
  83. package/dist/esm/utils/index.js +5 -0
  84. package/dist/esm/utils/is-server.js +8 -0
  85. package/dist/esm/utils/timeout-promise.js +28 -0
  86. package/dist/esm/utils/utils.js +61 -0
  87. package/graphql.d.ts +1 -0
  88. package/graphql.js +1 -0
  89. package/i18n.d.ts +1 -0
  90. package/i18n.js +1 -0
  91. package/layout.d.ts +1 -0
  92. package/layout.js +1 -0
  93. package/media.d.ts +1 -0
  94. package/media.js +1 -0
  95. package/package.json +71 -0
  96. package/personalize.d.ts +1 -0
  97. package/personalize.js +1 -0
  98. package/site.d.ts +1 -0
  99. package/site.js +1 -0
  100. package/types/cache-client.d.ts +64 -0
  101. package/types/constants.d.ts +6 -0
  102. package/types/debug.d.ts +26 -0
  103. package/types/graphql/app-root-query.d.ts +32 -0
  104. package/types/graphql/graphql-edge-proxy.d.ts +7 -0
  105. package/types/graphql/index.d.ts +4 -0
  106. package/types/graphql/search-service.d.ts +92 -0
  107. package/types/graphql-request-client.d.ts +88 -0
  108. package/types/i18n/dictionary-service.d.ts +56 -0
  109. package/types/i18n/graphql-dictionary-service.d.ts +65 -0
  110. package/types/i18n/index.d.ts +2 -0
  111. package/types/index.d.ts +6 -0
  112. package/types/layout/content-styles.d.ts +18 -0
  113. package/types/layout/graphql-layout-service.d.ts +59 -0
  114. package/types/layout/index.d.ts +6 -0
  115. package/types/layout/layout-service.d.ts +20 -0
  116. package/types/layout/models.d.ts +140 -0
  117. package/types/layout/themes.d.ts +11 -0
  118. package/types/layout/utils.d.ts +17 -0
  119. package/types/media/index.d.ts +2 -0
  120. package/types/media/media-api.d.ts +69 -0
  121. package/types/models.d.ts +6 -0
  122. package/types/native-fetcher.d.ts +92 -0
  123. package/types/personalize/graphql-personalize-service.d.ts +77 -0
  124. package/types/personalize/index.d.ts +3 -0
  125. package/types/personalize/layout-personalizer.d.ts +25 -0
  126. package/types/personalize/utils.d.ts +53 -0
  127. package/types/site/graphql-error-pages-service.d.ts +55 -0
  128. package/types/site/graphql-redirects-service.d.ts +66 -0
  129. package/types/site/graphql-robots-service.d.ts +47 -0
  130. package/types/site/graphql-siteinfo-service.d.ts +69 -0
  131. package/types/site/graphql-sitemap-service.d.ts +53 -0
  132. package/types/site/index.d.ts +7 -0
  133. package/types/site/site-resolver.d.ts +27 -0
  134. package/types/site/utils.d.ts +24 -0
  135. package/types/utils/edit-frame.d.ts +76 -0
  136. package/types/utils/editing.d.ts +58 -0
  137. package/types/utils/env.d.ts +7 -0
  138. package/types/utils/index.d.ts +5 -0
  139. package/types/utils/is-server.d.ts +6 -0
  140. package/types/utils/timeout-promise.d.ts +18 -0
  141. package/types/utils/utils.d.ts +18 -0
  142. package/utils.d.ts +1 -0
  143. package/utils.js +1 -0
@@ -0,0 +1,41 @@
1
+ import { MemoryCacheClient } from '../cache-client';
2
+ /**
3
+ * Base implementation of @see DictionaryService that handles caching dictionary values
4
+ */
5
+ export class DictionaryServiceBase {
6
+ /**
7
+ * Initializes a new instance of @see DictionaryService using the provided @see CacheOptions
8
+ * @param {CacheOptions} options Configuration options
9
+ */
10
+ constructor(options) {
11
+ this.options = options;
12
+ this.cache = this.getCacheClient();
13
+ }
14
+ /**
15
+ * Caches a @see DictionaryPhrases value for the specified cache key.
16
+ * @param {string} key The cache key.
17
+ * @param {DictionaryPhrases} value The value to cache.
18
+ * @returns The value added to the cache.
19
+ * @mixes CacheClient<DictionaryPhrases>
20
+ */
21
+ setCacheValue(key, value) {
22
+ return this.cache.setCacheValue(key, value);
23
+ }
24
+ /**
25
+ * Retrieves a @see DictionaryPhrases value from the cache.
26
+ * @param {string} key The cache key.
27
+ * @returns The @see DictionaryPhrases value, or null if the specified key is not found in the cache.
28
+ */
29
+ getCacheValue(key) {
30
+ return this.cache.getCacheValue(key);
31
+ }
32
+ /**
33
+ * Gets a cache client that can cache data. Uses memory-cache as the default
34
+ * library for caching (@see MemoryCacheClient). Override this method if you
35
+ * want to use something else.
36
+ * @returns {CacheClient} implementation
37
+ */
38
+ getCacheClient() {
39
+ return new MemoryCacheClient(this.options);
40
+ }
41
+ }
@@ -0,0 +1,118 @@
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 { SitecoreTemplateId } from '../constants';
11
+ import { DictionaryServiceBase } from './dictionary-service';
12
+ import { getAppRootId, SearchQueryService } from '../graphql';
13
+ import debug from '../debug';
14
+ /** @private */
15
+ export const queryError = 'Valid value for rootItemId not provided and failed to auto-resolve app root item.';
16
+ const query = /* GraphQL */ `
17
+ query DictionarySearch(
18
+ $rootItemId: String!
19
+ $language: String!
20
+ $templates: String!
21
+ $pageSize: Int = 10
22
+ $after: String
23
+ ) {
24
+ search(
25
+ where: {
26
+ AND: [
27
+ { name: "_path", value: $rootItemId, operator: CONTAINS }
28
+ { name: "_language", value: $language }
29
+ { name: "_templates", value: $templates, operator: CONTAINS }
30
+ ]
31
+ }
32
+ first: $pageSize
33
+ after: $after
34
+ ) {
35
+ total
36
+ pageInfo {
37
+ endCursor
38
+ hasNext
39
+ }
40
+ results {
41
+ key: field(name: "Key") {
42
+ value
43
+ }
44
+ phrase: field(name: "Phrase") {
45
+ value
46
+ }
47
+ }
48
+ }
49
+ }
50
+ `;
51
+ /**
52
+ * Service that fetch dictionary data using Sitecore's GraphQL API.
53
+ * @augments DictionaryServiceBase
54
+ * @mixes SearchQueryService<DictionaryQueryResult>
55
+ */
56
+ export class GraphQLDictionaryService extends DictionaryServiceBase {
57
+ /**
58
+ * Creates an instance of graphQL dictionary service with the provided options
59
+ * @param {GraphQLDictionaryService} options instance
60
+ */
61
+ constructor(options) {
62
+ super(options);
63
+ this.options = options;
64
+ this.graphQLClient = this.getGraphQLClient();
65
+ this.searchService = new SearchQueryService(this.graphQLClient);
66
+ }
67
+ /**
68
+ * Fetches dictionary data for internalization.
69
+ * @param {string} language the language to fetch
70
+ * @default query (@see query)
71
+ * @returns {Promise<DictionaryPhrases>} dictionary phrases
72
+ * @throws {Error} if the app root was not found for the specified site and language.
73
+ */
74
+ fetchDictionaryData(language) {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ const cacheKey = this.options.siteName + language;
77
+ const cachedValue = this.getCacheValue(cacheKey);
78
+ if (cachedValue) {
79
+ debug.dictionary('using cached dictionary data for %s %s', language, this.options.siteName);
80
+ return cachedValue;
81
+ }
82
+ debug.dictionary('fetching site root for %s %s', language, this.options.siteName);
83
+ // If the caller does not specify a root item ID, then we try to figure it out
84
+ const rootItemId = this.options.rootItemId ||
85
+ (yield getAppRootId(this.graphQLClient, this.options.siteName, language, this.options.jssAppTemplateId));
86
+ if (!rootItemId) {
87
+ throw new Error(queryError);
88
+ }
89
+ debug.dictionary('fetching dictionary data for %s %s', language, this.options.siteName);
90
+ const phrases = {};
91
+ yield this.searchService
92
+ .fetch(query, {
93
+ rootItemId,
94
+ language,
95
+ templates: this.options.dictionaryEntryTemplateId || SitecoreTemplateId.DictionaryEntry,
96
+ pageSize: this.options.pageSize,
97
+ })
98
+ .then((results) => {
99
+ results.forEach((item) => (phrases[item.key.value] = item.phrase.value));
100
+ });
101
+ this.setCacheValue(cacheKey, phrases);
102
+ return phrases;
103
+ });
104
+ }
105
+ /**
106
+ * Gets a GraphQL client that can make requests to the API.
107
+ * @returns {GraphQLClient} implementation
108
+ */
109
+ getGraphQLClient() {
110
+ if (!this.options.clientFactory) {
111
+ throw new Error('You should provide a clientFactory.');
112
+ }
113
+ return this.options.clientFactory({
114
+ debugger: debug.dictionary,
115
+ retries: this.options.retries,
116
+ });
117
+ }
118
+ }
@@ -0,0 +1,2 @@
1
+ export { DictionaryServiceBase } from './dictionary-service';
2
+ export { GraphQLDictionaryService, } from './graphql-dictionary-service';
@@ -0,0 +1,7 @@
1
+ // NOTE: all imports are now named as to not make breaking changes
2
+ // and to keep react-native working with cjs modules.
3
+ import * as constants from './constants';
4
+ export { default as debug, enableDebug } from './debug';
5
+ export { GraphQLRequestClient, } from './graphql-request-client';
6
+ export { NativeDataFetcher, } from './native-fetcher';
7
+ export { constants };
@@ -0,0 +1,65 @@
1
+ import { SITECORE_EDGE_URL_DEFAULT } from '../constants';
2
+ /**
3
+ * Regular expression to check if the content styles are used in the field value
4
+ */
5
+ const CLASS_REGEXP = /class=".*(\bck-content\b).*"/g;
6
+ /**
7
+ * Get the content styles link to be loaded from the Sitecore Edge Platform
8
+ * @param {LayoutServiceData} layoutData Layout service data
9
+ * @param {string} sitecoreEdgeContextId Sitecore Edge Context ID
10
+ * @param {string} [sitecoreEdgeUrl] Sitecore Edge Platform URL. Default is https://edge-platform.sitecorecloud.io
11
+ * @returns {HTMLLink | null} content styles link, null if no styles are used in layout
12
+ */
13
+ export const getContentStylesheetLink = (layoutData, sitecoreEdgeContextId, sitecoreEdgeUrl = SITECORE_EDGE_URL_DEFAULT) => {
14
+ if (!layoutData.sitecore.route)
15
+ return null;
16
+ const config = { loadStyles: false };
17
+ traverseComponent(layoutData.sitecore.route, config);
18
+ if (!config.loadStyles)
19
+ return null;
20
+ return {
21
+ href: getContentStylesheetUrl(sitecoreEdgeContextId, sitecoreEdgeUrl),
22
+ rel: 'stylesheet',
23
+ };
24
+ };
25
+ export const getContentStylesheetUrl = (sitecoreEdgeContextId, sitecoreEdgeUrl = SITECORE_EDGE_URL_DEFAULT) => `${sitecoreEdgeUrl}/v1/files/pages/styles/content-styles.css?sitecoreContextId=${sitecoreEdgeContextId}`;
26
+ export const traversePlaceholder = (components, config) => {
27
+ if (config.loadStyles)
28
+ return;
29
+ components.forEach((component) => {
30
+ traverseComponent(component, config);
31
+ });
32
+ };
33
+ export const traverseField = (field, config) => {
34
+ if (!field || config.loadStyles)
35
+ return;
36
+ if ('editable' in field && field.editable) {
37
+ config.loadStyles = CLASS_REGEXP.test(field.editable);
38
+ }
39
+ else if ('value' in field && typeof field.value === 'string') {
40
+ config.loadStyles = CLASS_REGEXP.test(field.value);
41
+ }
42
+ else if ('fields' in field) {
43
+ Object.values(field.fields).forEach((field) => {
44
+ traverseField(field, config);
45
+ });
46
+ }
47
+ else if (Array.isArray(field)) {
48
+ field.forEach((field) => {
49
+ traverseField(field, config);
50
+ });
51
+ }
52
+ };
53
+ export const traverseComponent = (component, config) => {
54
+ if (config.loadStyles)
55
+ return;
56
+ if ('fields' in component && component.fields) {
57
+ Object.values(component.fields).forEach((field) => {
58
+ traverseField(field, config);
59
+ });
60
+ }
61
+ const placeholders = component.placeholders || {};
62
+ Object.keys(placeholders).forEach((placeholder) => {
63
+ traversePlaceholder(placeholders[placeholder], config);
64
+ });
65
+ };
@@ -0,0 +1,77 @@
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 { LayoutServiceBase } from './layout-service';
11
+ import debug from '../debug';
12
+ /**
13
+ * Service that fetch layout data using Sitecore's GraphQL API.
14
+ * @augments LayoutServiceBase
15
+ * @mixes GraphQLRequestClient
16
+ */
17
+ export class GraphQLLayoutService extends LayoutServiceBase {
18
+ /**
19
+ * Fetch layout data using the Sitecore GraphQL endpoint.
20
+ * @param {GraphQLLayoutServiceConfig} serviceConfig configuration
21
+ */
22
+ constructor(serviceConfig) {
23
+ super();
24
+ this.serviceConfig = serviceConfig;
25
+ this.graphQLClient = this.getGraphQLClient();
26
+ }
27
+ /**
28
+ * Fetch layout data for an item.
29
+ * @param {string} itemPath item path to fetch layout data for.
30
+ * @param {string} [language] the language to fetch layout data for.
31
+ * @returns {Promise<LayoutServiceData>} layout service data
32
+ */
33
+ fetchLayoutData(itemPath, language) {
34
+ var _a, _b;
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ const query = this.getLayoutQuery(itemPath, language);
37
+ debug.layout('fetching layout data for %s %s %s', itemPath, language, this.serviceConfig.siteName);
38
+ const data = yield this.graphQLClient.request(query);
39
+ // If `rendered` is empty -> not found
40
+ return (((_b = (_a = data === null || data === void 0 ? void 0 : data.layout) === null || _a === void 0 ? void 0 : _a.item) === null || _b === void 0 ? void 0 : _b.rendered) || {
41
+ sitecore: { context: { pageEditing: false, language }, route: null },
42
+ });
43
+ });
44
+ }
45
+ /**
46
+ * Gets a GraphQL client that can make requests to the API.
47
+ * @returns {GraphQLClient} implementation
48
+ */
49
+ getGraphQLClient() {
50
+ if (!this.serviceConfig.clientFactory) {
51
+ throw new Error('You should provide a clientFactory.');
52
+ }
53
+ return this.serviceConfig.clientFactory({
54
+ debugger: debug.layout,
55
+ retries: this.serviceConfig.retries,
56
+ });
57
+ }
58
+ /**
59
+ * Returns GraphQL Layout query
60
+ * @param {string} itemPath page route
61
+ * @param {string} [language] language
62
+ * @returns {string} GraphQL query
63
+ */
64
+ getLayoutQuery(itemPath, language) {
65
+ const languageVariable = language ? `, language:"${language}"` : '';
66
+ const layoutQuery = this.serviceConfig.formatLayoutQuery
67
+ ? this.serviceConfig.formatLayoutQuery(this.serviceConfig.siteName, itemPath, language)
68
+ : `layout(site:"${this.serviceConfig.siteName}", routePath:"${itemPath}"${languageVariable})`;
69
+ return `query {
70
+ ${layoutQuery}{
71
+ item {
72
+ rendered
73
+ }
74
+ }
75
+ }`;
76
+ }
77
+ }
@@ -0,0 +1,6 @@
1
+ // layout
2
+ export { LayoutServicePageState, RenderingType, EDITING_COMPONENT_PLACEHOLDER, EDITING_COMPONENT_ID, } from './models';
3
+ export { getFieldValue, getChildPlaceholder } from './utils';
4
+ export { getContentStylesheetLink } from './content-styles';
5
+ export { GraphQLLayoutService } from './graphql-layout-service';
6
+ export { getComponentLibraryStylesheetLinks } from './themes';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Base abstraction to implement custom layout service
3
+ */
4
+ export class LayoutServiceBase {
5
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Static placeholder name used for component rendering
3
+ */
4
+ export const EDITING_COMPONENT_PLACEHOLDER = 'editing-componentmode-placeholder';
5
+ /**
6
+ * Id of wrapper for component rendering
7
+ */
8
+ export const EDITING_COMPONENT_ID = 'editing-component';
9
+ /**
10
+ * Layout Service page state enum
11
+ */
12
+ export var LayoutServicePageState;
13
+ (function (LayoutServicePageState) {
14
+ LayoutServicePageState["Preview"] = "preview";
15
+ LayoutServicePageState["Edit"] = "edit";
16
+ LayoutServicePageState["Normal"] = "normal";
17
+ })(LayoutServicePageState || (LayoutServicePageState = {}));
18
+ /**
19
+ * Editing rendering type
20
+ */
21
+ export var RenderingType;
22
+ (function (RenderingType) {
23
+ RenderingType["Component"] = "component";
24
+ })(RenderingType || (RenderingType = {}));
@@ -0,0 +1,74 @@
1
+ import { getFieldValue, } from '.';
2
+ import { SITECORE_EDGE_URL_DEFAULT } from '../constants';
3
+ /**
4
+ * Pattern for library ids
5
+ * @example -library--foo
6
+ */
7
+ const STYLES_LIBRARY_ID_REGEX = /-library--([^\s]+)/;
8
+ /**
9
+ * Walks through rendering tree and returns list of links of all FEAAS, BYOC or SXA Component Library Stylesheets that are used
10
+ * @param {LayoutServiceData} layoutData Layout service data
11
+ * @param {string} sitecoreEdgeContextId Sitecore Edge Context ID
12
+ * @param {string} [sitecoreEdgeUrl] Sitecore Edge Platform URL. Default is https://edge-platform.sitecorecloud.io
13
+ * @returns {HTMLLink[]} library stylesheet links
14
+ */
15
+ export function getComponentLibraryStylesheetLinks(layoutData, sitecoreEdgeContextId, sitecoreEdgeUrl = SITECORE_EDGE_URL_DEFAULT) {
16
+ const ids = new Set();
17
+ if (!layoutData.sitecore.route)
18
+ return [];
19
+ traverseComponent(layoutData.sitecore.route, ids);
20
+ return [...ids].map((id) => ({
21
+ href: getStylesheetUrl(id, sitecoreEdgeContextId, sitecoreEdgeUrl),
22
+ rel: 'stylesheet',
23
+ }));
24
+ }
25
+ export const getStylesheetUrl = (id, sitecoreEdgeContextId, sitecoreEdgeUrl = SITECORE_EDGE_URL_DEFAULT) => {
26
+ return `${sitecoreEdgeUrl}/v1/files/components/styles/${id}.css?sitecoreContextId=${sitecoreEdgeContextId}`;
27
+ };
28
+ /**
29
+ * Traverse placeholder and components to add library ids
30
+ * @param {Array<ComponentRendering | HtmlElementRendering>} components
31
+ * @param {Set<string>} ids library ids
32
+ */
33
+ const traversePlaceholder = (components, ids) => {
34
+ components.map((component) => {
35
+ const rendering = component;
36
+ return traverseComponent(rendering, ids);
37
+ });
38
+ };
39
+ /**
40
+ * Traverse component and children to add library ids
41
+ * @param {RouteData | ComponentRendering | HtmlElementRendering} component component data
42
+ * @param {Set<string>} ids library ids
43
+ */
44
+ const traverseComponent = (component, ids) => {
45
+ var _a, _b, _c, _d, _e, _f, _g;
46
+ let libraryId = undefined;
47
+ if ('params' in component && component.params) {
48
+ // LibraryID in css class name takes precedence over LibraryId attribute
49
+ libraryId =
50
+ ((_b = (_a = component.params.CSSStyles) === null || _a === void 0 ? void 0 : _a.match(STYLES_LIBRARY_ID_REGEX)) === null || _b === void 0 ? void 0 : _b[1]) ||
51
+ ((_d = (_c = component.params.Styles) === null || _c === void 0 ? void 0 : _c.match(STYLES_LIBRARY_ID_REGEX)) === null || _d === void 0 ? void 0 : _d[1]) ||
52
+ component.params.LibraryId ||
53
+ undefined;
54
+ }
55
+ // if params are empty we try to fall back to data source or attributes
56
+ if (!libraryId && 'fields' in component && component.fields) {
57
+ libraryId =
58
+ ((_e = getFieldValue(component.fields, 'CSSStyles', '').match(STYLES_LIBRARY_ID_REGEX)) === null || _e === void 0 ? void 0 : _e[1]) ||
59
+ ((_f = getFieldValue(component.fields, 'Styles', '').match(STYLES_LIBRARY_ID_REGEX)) === null || _f === void 0 ? void 0 : _f[1]) ||
60
+ getFieldValue(component.fields, 'LibraryId', '') ||
61
+ undefined;
62
+ }
63
+ // HTMLRendering its class attribute
64
+ if (!libraryId && 'attributes' in component && typeof component.attributes.class === 'string') {
65
+ libraryId = (_g = component.attributes.class.match(STYLES_LIBRARY_ID_REGEX)) === null || _g === void 0 ? void 0 : _g[1];
66
+ }
67
+ if (libraryId) {
68
+ ids.add(libraryId);
69
+ }
70
+ const placeholders = component.placeholders || {};
71
+ Object.keys(placeholders).forEach((placeholder) => {
72
+ traversePlaceholder(placeholders[placeholder], ids);
73
+ });
74
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @param {ComponentRendering | Fields} renderingOrFields the rendering or fields object to extract the field from
3
+ * @param {string} fieldName the name of the field to extract
4
+ * @param {T} [defaultValue] the default value to return if the field is not defined
5
+ * @returns {Field | T} the field value or the default value if the field is not defined
6
+ */
7
+ // eslint-disable-next-line no-redeclare
8
+ export function getFieldValue(renderingOrFields, fieldName, defaultValue) {
9
+ if (!renderingOrFields || !fieldName) {
10
+ return defaultValue;
11
+ }
12
+ const fields = renderingOrFields;
13
+ const field = fields[fieldName];
14
+ if (field && typeof field.value !== 'undefined') {
15
+ return field.value;
16
+ }
17
+ const rendering = renderingOrFields;
18
+ if (!rendering.fields ||
19
+ !rendering.fields[fieldName] ||
20
+ typeof rendering.fields[fieldName].value === 'undefined') {
21
+ return defaultValue;
22
+ }
23
+ return rendering.fields[fieldName].value;
24
+ }
25
+ /**
26
+ * Gets rendering definitions in a given child placeholder under a current rendering.
27
+ * @param {ComponentRendering} rendering
28
+ * @param {string} placeholderName
29
+ * @returns {Array<ComponentRendering | HtmlElementRendering>} child placeholder
30
+ */
31
+ export function getChildPlaceholder(rendering, placeholderName) {
32
+ if (!rendering ||
33
+ !placeholderName ||
34
+ !rendering.placeholders ||
35
+ !rendering.placeholders[placeholderName]) {
36
+ return [];
37
+ }
38
+ return rendering.placeholders[placeholderName];
39
+ }
@@ -0,0 +1,2 @@
1
+ import * as mediaApi from './media-api';
2
+ export { mediaApi };
@@ -0,0 +1,117 @@
1
+ import unescape from 'lodash.unescape';
2
+ import URL from 'url-parse';
3
+ // finds an img tag with HTML attributes
4
+ const imgTagRegex = /<img([^>]+)\/>/i;
5
+ // finds all the HTML attributes in a string
6
+ const htmlAttrsRegex = /([^=\s]+)(="([^"]*)")?/gi;
7
+ // finds the Sitecore media URL prefix
8
+ const mediaUrlPrefixRegex = /\/([-~]{1})\/media\//i;
9
+ /**
10
+ * Makes a request to Sitecore Content Service for the specified item path.
11
+ * @param {string} editorMarkup the markup to parse
12
+ * @returns {Object | null} found image tag; null in case if not found
13
+ */
14
+ export const findEditorImageTag = (editorMarkup) => {
15
+ // match the tag
16
+ const tagMatch = editorMarkup.match(imgTagRegex);
17
+ if (!tagMatch || tagMatch.length < 2) {
18
+ return null;
19
+ }
20
+ // find the attrs and turn them into a Map
21
+ const attrs = {};
22
+ let match = htmlAttrsRegex.exec(tagMatch[1]);
23
+ while (match !== null) {
24
+ attrs[match[1]] = unescape(match[3]);
25
+ match = htmlAttrsRegex.exec(tagMatch[1]);
26
+ }
27
+ return {
28
+ imgTag: tagMatch[0],
29
+ attrs,
30
+ };
31
+ };
32
+ /**
33
+ * Get required query string params which should be merged with user params
34
+ * @param {object} qs layout service parsed query string
35
+ * @returns {object} requiredParams
36
+ */
37
+ export const getRequiredParams = (qs) => {
38
+ const { rev, db, la, vs, ts } = qs;
39
+ return { rev, db, la, vs, ts };
40
+ };
41
+ /**
42
+ * Replace `/~/media` or `/-/media` with `/~/jssmedia` or `/-/jssmedia`, respectively.
43
+ * Can use `mediaUrlPrefix` in order to use a custom prefix.
44
+ * @param {string} url The URL to replace the media URL prefix in
45
+ * @param {RegExp} [mediaUrlPrefix=mediaUrlPrefixRegex] The regex to match the media URL prefix
46
+ * @returns {string} The URL with the media URL prefix replaced
47
+ */
48
+ export const replaceMediaUrlPrefix = (url, mediaUrlPrefix = mediaUrlPrefixRegex) => {
49
+ const parsed = URL(url, {}, true);
50
+ const match = mediaUrlPrefix.exec(parsed.pathname);
51
+ if (match && match.length > 1) {
52
+ // regex will provide us with /-/ or /~/ type
53
+ parsed.set('pathname', parsed.pathname.replace(mediaUrlPrefix, `/${match[1]}/jssmedia/`));
54
+ }
55
+ return parsed.toString();
56
+ };
57
+ /**
58
+ * Prepares a Sitecore media URL with `params` for use by the JSS media handler.
59
+ * This is done by replacing `/~/media` or `/-/media` with `/~/jssmedia` or `/-/jssmedia`, respectively.
60
+ * Provided `params` are used as the querystring parameters for the media URL.
61
+ * Can use `mediaUrlPrefix` in order to use a custom prefix.
62
+ * If no `params` are sent, the original media URL is returned.
63
+ * @param {string} url The URL to prepare
64
+ * @param {Object} [params] The querystring parameters to use
65
+ * @param {RegExp} [mediaUrlPrefix=mediaUrlPrefixRegex] The regex to match the media URL prefix
66
+ * @returns {string} The prepared URL
67
+ */
68
+ export const updateImageUrl = (url, params, mediaUrlPrefix = mediaUrlPrefixRegex) => {
69
+ if (!params || Object.keys(params).length === 0) {
70
+ // if params aren't supplied, no need to run it through JSS media handler
71
+ return url;
72
+ }
73
+ // polyfill node `global` in browser to workaround https://github.com/unshiftio/url-parse/issues/150
74
+ if (typeof window !== 'undefined' && !window.global) {
75
+ window.global = {};
76
+ }
77
+ const parsed = URL(replaceMediaUrlPrefix(url, mediaUrlPrefix), {}, true);
78
+ const requiredParams = getRequiredParams(parsed.query);
79
+ const query = Object.assign({}, params);
80
+ Object.entries(requiredParams).forEach(([key, param]) => {
81
+ if (param) {
82
+ query[key] = param;
83
+ }
84
+ });
85
+ parsed.set('query', query);
86
+ return parsed.toString();
87
+ };
88
+ /**
89
+ * Receives an array of `srcSet` parameters that are iterated and used as parameters to generate
90
+ * a corresponding set of updated Sitecore media URLs via @see updateImageUrl. The result is a comma-delimited
91
+ * list of media URLs with respective dimension parameters.
92
+ *
93
+ * @example
94
+ * // returns '/ipsum.jpg?h=1000&w=1000 1000w, /ipsum.jpg?mh=250&mw=250 250w'
95
+ * getSrcSet('/ipsum.jpg', [{ h: 1000, w: 1000 }, { mh: 250, mw: 250 } ])
96
+ *
97
+ * More information about `srcSet`: {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img}
98
+ *
99
+ * @param {string} url The URL to prepare
100
+ * @param {Array} srcSet The array of parameters to use
101
+ * @param {Object} [imageParams] The querystring parameters to use
102
+ * @param {RegExp} [mediaUrlPrefix] The regex to match the media URL prefix
103
+ * @returns {string} The prepared URL
104
+ */
105
+ export const getSrcSet = (url, srcSet, imageParams, mediaUrlPrefix) => {
106
+ return srcSet
107
+ .map((params) => {
108
+ const newParams = Object.assign(Object.assign({}, imageParams), params);
109
+ const imageWidth = newParams.w || newParams.mw;
110
+ if (!imageWidth) {
111
+ return null;
112
+ }
113
+ return `${updateImageUrl(url, newParams, mediaUrlPrefix)} ${imageWidth}w`;
114
+ })
115
+ .filter((value) => value)
116
+ .join(', ');
117
+ };
@@ -0,0 +1 @@
1
+ export {};