@exdst-sitecore-content-sdk/astro 0.0.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 +3 -0
- package/package.json +101 -0
- package/src/client/index.ts +12 -0
- package/src/client/sitecore-astro-client.test.ts +271 -0
- package/src/client/sitecore-astro-client.ts +137 -0
- package/src/components/AstroImage.astro +114 -0
- package/src/components/Date.astro +76 -0
- package/src/components/DefaultEmptyFieldEditingComponentImage.astro +24 -0
- package/src/components/DefaultEmptyFieldEditingComponentText.astro +12 -0
- package/src/components/EditingScripts.astro +49 -0
- package/src/components/EmptyRendering.astro +3 -0
- package/src/components/ErrorBoundary.astro +77 -0
- package/src/components/FieldMetadata.astro +30 -0
- package/src/components/File.astro +46 -0
- package/src/components/HiddenRendering.astro +22 -0
- package/src/components/Image.astro +155 -0
- package/src/components/Link.astro +105 -0
- package/src/components/MissingComponent.astro +39 -0
- package/src/components/Placeholder/EmptyPlaceholder.astro +9 -0
- package/src/components/Placeholder/Placeholder.astro +100 -0
- package/src/components/Placeholder/PlaceholderMetadata.astro +102 -0
- package/src/components/Placeholder/PlaceholderUtils.astro +153 -0
- package/src/components/Placeholder/index.ts +5 -0
- package/src/components/Placeholder/models.ts +82 -0
- package/src/components/Placeholder/placeholder-utils.test.ts +162 -0
- package/src/components/Placeholder/placeholder-utils.ts +80 -0
- package/src/components/RenderWrapper.astro +31 -0
- package/src/components/RichText.astro +59 -0
- package/src/components/Text.astro +97 -0
- package/src/components/sharedTypes/index.ts +1 -0
- package/src/components/sharedTypes/props.ts +17 -0
- package/src/config/define-config.test.ts +526 -0
- package/src/config/define-config.ts +99 -0
- package/src/config/index.ts +1 -0
- package/src/config-cli/define-cli-config.test.ts +95 -0
- package/src/config-cli/define-cli-config.ts +50 -0
- package/src/config-cli/index.ts +1 -0
- package/src/context.ts +68 -0
- package/src/editing/constants.ts +8 -0
- package/src/editing/editing-config-middleware.test.ts +166 -0
- package/src/editing/editing-config-middleware.ts +111 -0
- package/src/editing/editing-render-middleware.test.ts +801 -0
- package/src/editing/editing-render-middleware.ts +288 -0
- package/src/editing/index.ts +16 -0
- package/src/editing/render-middleware.test.ts +57 -0
- package/src/editing/render-middleware.ts +51 -0
- package/src/editing/utils.test.ts +852 -0
- package/src/editing/utils.ts +308 -0
- package/src/enhancers/WithEmptyFieldEditingComponent.astro +56 -0
- package/src/enhancers/WithFieldMetadata.astro +31 -0
- package/src/env.d.ts +12 -0
- package/src/index.ts +16 -0
- package/src/middleware/index.ts +24 -0
- package/src/middleware/middleware.test.ts +507 -0
- package/src/middleware/middleware.ts +167 -0
- package/src/middleware/multisite-middleware.test.ts +672 -0
- package/src/middleware/multisite-middleware.ts +147 -0
- package/src/middleware/robots-middleware.test.ts +113 -0
- package/src/middleware/robots-middleware.ts +47 -0
- package/src/middleware/sitemap-middleware.test.ts +152 -0
- package/src/middleware/sitemap-middleware.ts +65 -0
- package/src/services/component-props-service.ts +182 -0
- package/src/sharedTypes/component-props.ts +17 -0
- package/src/site/index.ts +1 -0
- package/src/test-data/components/Bar.astro +0 -0
- package/src/test-data/components/Baz.astro +0 -0
- package/src/test-data/components/Foo.astro +0 -0
- package/src/test-data/components/Hero.variant.astro +0 -0
- package/src/test-data/components/NotComponent.bsx +0 -0
- package/src/test-data/components/Qux.astro +0 -0
- package/src/test-data/components/folded/Folded.astro +0 -0
- package/src/test-data/components/folded/random-file-2.docx +0 -0
- package/src/test-data/components/random-file.txt +0 -0
- package/src/test-data/helpers.ts +46 -0
- package/src/test-data/personalizeData.ts +63 -0
- package/src/tools/generate-map.ts +83 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/templating/components.test.ts +305 -0
- package/src/tools/templating/components.ts +49 -0
- package/src/tools/templating/constants.ts +4 -0
- package/src/tools/templating/default-component.test.ts +31 -0
- package/src/tools/templating/default-component.ts +63 -0
- package/src/tools/templating/index.ts +2 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/utils.test.ts +48 -0
- package/src/utils/utils.ts +52 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DesignLibraryRenderPreviewData,
|
|
3
|
+
EDITING_ALLOWED_ORIGINS,
|
|
4
|
+
EditingRenderQueryParams,
|
|
5
|
+
isDesignLibraryMode,
|
|
6
|
+
LayoutKind,
|
|
7
|
+
PREVIEW_KEY,
|
|
8
|
+
QUERY_PARAM_EDITING_SECRET,
|
|
9
|
+
} from '@sitecore-content-sdk/core/editing';
|
|
10
|
+
import { DEFAULT_VARIANT } from '@sitecore-content-sdk/core/personalize';
|
|
11
|
+
import { SITE_KEY } from '@sitecore-content-sdk/core/site';
|
|
12
|
+
import {
|
|
13
|
+
EDITING_PASS_THROUGH_HEADERS,
|
|
14
|
+
QUERY_PARAM_VERCEL_PROTECTION_BYPASS,
|
|
15
|
+
QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE,
|
|
16
|
+
} from './constants';
|
|
17
|
+
import { IncomingHttpHeaders } from 'http';
|
|
18
|
+
import { NativeDataFetcher } from '@sitecore-content-sdk/core';
|
|
19
|
+
import { getAllowedOriginsFromEnv } from '@sitecore-content-sdk/core/utils';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Gets editing secret value from request
|
|
23
|
+
* @param {Request} req incoming request
|
|
24
|
+
* @returns {string | undefined} editing secret value if present
|
|
25
|
+
*/
|
|
26
|
+
export const getEditingSecretFromRequest = (req: Request) => {
|
|
27
|
+
const reqUrl = (req as Request).url;
|
|
28
|
+
|
|
29
|
+
const url = new URL(reqUrl);
|
|
30
|
+
const secret = url.searchParams.get(QUERY_PARAM_EDITING_SECRET);
|
|
31
|
+
|
|
32
|
+
return secret;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parses query string and its parameters to required editing parameters
|
|
37
|
+
* @param {URLSearchParams} query query string values
|
|
38
|
+
* @returns {EditingRenderQueryParams} editing parameters
|
|
39
|
+
*/
|
|
40
|
+
export const mapEditingParams = (query: {
|
|
41
|
+
[key: string]: string | undefined;
|
|
42
|
+
}): { [key: string]: string | undefined } => {
|
|
43
|
+
const params = isDesignLibraryMode(query.mode)
|
|
44
|
+
? {
|
|
45
|
+
itemId: query.sc_itemid,
|
|
46
|
+
componentUid: query.sc_uid,
|
|
47
|
+
renderingId: query.sc_renderingId,
|
|
48
|
+
language: query.sc_lang,
|
|
49
|
+
site: query.sc_site,
|
|
50
|
+
mode: query.mode,
|
|
51
|
+
dataSourceId: query.dataSourceId,
|
|
52
|
+
version: query.sc_version,
|
|
53
|
+
generation: query.generation,
|
|
54
|
+
}
|
|
55
|
+
: {
|
|
56
|
+
site: query.sc_site,
|
|
57
|
+
itemId: query.sc_itemid,
|
|
58
|
+
language: query.sc_lang,
|
|
59
|
+
// for sc_variantId we may employ multiple variants (page-layout + component level)
|
|
60
|
+
// they will be separated by commas (,)
|
|
61
|
+
variantIds: query.sc_variant || DEFAULT_VARIANT,
|
|
62
|
+
version: query.sc_version,
|
|
63
|
+
mode: query.mode,
|
|
64
|
+
layoutKind: query.sc_layoutKind,
|
|
65
|
+
};
|
|
66
|
+
return params;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Preview cookies enum
|
|
71
|
+
*/
|
|
72
|
+
export enum PreviewCookies {
|
|
73
|
+
PREVIEW_DATA = '_preview_data',
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Filters out preview cookies from a cookie string or array
|
|
78
|
+
* @param {string | string[] | null} cookies cookie header value
|
|
79
|
+
* @returns {string[] | null} filtered cookies
|
|
80
|
+
*/
|
|
81
|
+
export const cleanupPreviewCookies = (cookies: string | string[] | null) => {
|
|
82
|
+
if (!cookies) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
if (!Array.isArray(cookies)) {
|
|
86
|
+
cookies = cookies.split(',');
|
|
87
|
+
}
|
|
88
|
+
// Filter out preview cookies
|
|
89
|
+
const filteredCookies = cookies.filter(
|
|
90
|
+
(cookie: string) => !new RegExp(`^${PreviewCookies.PREVIEW_DATA}=`).test(cookie)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
return filteredCookies;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Gets the preview cookies to enable preview mode
|
|
98
|
+
* @param {string} site current site name
|
|
99
|
+
* @returns {string[]} list of cookies to set
|
|
100
|
+
*/
|
|
101
|
+
export const getPreviewCookies = (site: string) => {
|
|
102
|
+
const previewSite = `${SITE_KEY}=${site}; Path=/; HttpOnly; SameSite=None; Secure`;
|
|
103
|
+
const previewCookie = `${PREVIEW_KEY}=true; Path=/; HttpOnly; SameSite=None; Secure`;
|
|
104
|
+
return [previewSite, previewCookie];
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Returns the list of required query parameters based on the page editing mode
|
|
109
|
+
* @param {DesignLibraryMode | LayoutServicePageState.Preview | LayoutServicePageState.Edit} mode current page mode
|
|
110
|
+
* @returns {string[]} list of required parameters for validation
|
|
111
|
+
*/
|
|
112
|
+
export const getRequiredEditingParamsList = (mode: EditingRenderQueryParams['mode']) => {
|
|
113
|
+
const editingRequiredParams = ['sc_site', 'sc_itemid', 'sc_lang', 'route', 'mode'];
|
|
114
|
+
|
|
115
|
+
const componentRequiredParams = [
|
|
116
|
+
'sc_site',
|
|
117
|
+
'sc_itemid',
|
|
118
|
+
'sc_renderingId',
|
|
119
|
+
'sc_uid',
|
|
120
|
+
'sc_lang',
|
|
121
|
+
'mode',
|
|
122
|
+
];
|
|
123
|
+
return isDesignLibraryMode(mode) ? componentRequiredParams : editingRequiredParams;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Gets query parameters that should be passed along to subsequent requests (e.g. for deployment protection bypass)
|
|
128
|
+
* @param {object} query URLSearchParams object from incoming URL
|
|
129
|
+
* @returns object of approved query parameters
|
|
130
|
+
*/
|
|
131
|
+
export const getQueryParamsForPropagation = (
|
|
132
|
+
query: Partial<{ [key: string]: string | string[] }>
|
|
133
|
+
): { [key: string]: string } => {
|
|
134
|
+
const params: { [key: string]: string } = {};
|
|
135
|
+
if (query[QUERY_PARAM_VERCEL_PROTECTION_BYPASS]) {
|
|
136
|
+
params[QUERY_PARAM_VERCEL_PROTECTION_BYPASS] = query[
|
|
137
|
+
QUERY_PARAM_VERCEL_PROTECTION_BYPASS
|
|
138
|
+
] as string;
|
|
139
|
+
}
|
|
140
|
+
if (query[QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE]) {
|
|
141
|
+
params[QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE] = query[
|
|
142
|
+
QUERY_PARAM_VERCEL_SET_BYPASS_COOKIE
|
|
143
|
+
] as string;
|
|
144
|
+
}
|
|
145
|
+
return params;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get headers that should be passed along to subsequent requests
|
|
150
|
+
* @param {IncomingHttpHeaders | Headers} headers Incoming HTTP Headers
|
|
151
|
+
* @returns Object of approved headers
|
|
152
|
+
*/
|
|
153
|
+
export const getHeadersForPropagation = (
|
|
154
|
+
headers: IncomingHttpHeaders | Headers
|
|
155
|
+
): { [key: string]: string } => {
|
|
156
|
+
// Filter and normalize headers
|
|
157
|
+
const filteredHeaders = EDITING_PASS_THROUGH_HEADERS.reduce((acc, header) => {
|
|
158
|
+
const value = (headers as Headers).get
|
|
159
|
+
? (headers as Headers).get(header)
|
|
160
|
+
: (headers as IncomingHttpHeaders)[header];
|
|
161
|
+
if (value) {
|
|
162
|
+
acc[header] = Array.isArray(value) ? value.join(', ') : value;
|
|
163
|
+
}
|
|
164
|
+
return acc;
|
|
165
|
+
}, {} as Record<string, string>);
|
|
166
|
+
|
|
167
|
+
return filteredHeaders;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Performs an internal request to get the HTML for the editing mode
|
|
172
|
+
* @param {string} requestUrl URL to send request to
|
|
173
|
+
* @param {object} propagatedQsParams query string params to use with request
|
|
174
|
+
* @param {object} propagatedHeaders headers to use with request
|
|
175
|
+
* @param {string[]} cookies cookies to use with request
|
|
176
|
+
* @param {NativeDataFetcher} dataFetcher NativeFetcher instance to send request with
|
|
177
|
+
* @returns {string} HTML with editing markup
|
|
178
|
+
*/
|
|
179
|
+
export const getEditingRequestHtml = async (
|
|
180
|
+
requestUrl: URL,
|
|
181
|
+
propagatedQsParams: { [key: string]: string | undefined },
|
|
182
|
+
propagatedHeaders: { [key: string]: string },
|
|
183
|
+
cookies: string[],
|
|
184
|
+
dataFetcher: NativeDataFetcher
|
|
185
|
+
): Promise<string> => {
|
|
186
|
+
// Grab the preview cookies to send on to the render request
|
|
187
|
+
propagatedHeaders.cookie = `${
|
|
188
|
+
propagatedHeaders.cookie ? propagatedHeaders.cookie + ';' : ''
|
|
189
|
+
}${cookies.join(';')}`;
|
|
190
|
+
// enable content sdk preview
|
|
191
|
+
propagatedHeaders.__content_sdk_preview = '1';
|
|
192
|
+
|
|
193
|
+
for (const key in propagatedQsParams) {
|
|
194
|
+
if ({}.hasOwnProperty.call(propagatedQsParams, key)) {
|
|
195
|
+
propagatedQsParams[key] && requestUrl.searchParams.append(key, propagatedQsParams[key]);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
requestUrl.searchParams.append('timestamp', Date.now().toString());
|
|
199
|
+
|
|
200
|
+
const pageRes = await dataFetcher
|
|
201
|
+
.get<string>(requestUrl.toString(), {
|
|
202
|
+
credentials: 'include',
|
|
203
|
+
headers: propagatedHeaders,
|
|
204
|
+
})
|
|
205
|
+
.catch((err) => {
|
|
206
|
+
// We need to handle not found error provided by Vercel
|
|
207
|
+
// for `fallback: false` pages
|
|
208
|
+
if (err.response.status === 404) {
|
|
209
|
+
return err.response;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
throw err;
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
let html = pageRes.data;
|
|
216
|
+
if (!html || html.length === 0) {
|
|
217
|
+
throw new Error(`Failed to render html for ${requestUrl.toString()}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// replace phkey attribute with key attribute so that newly added renderings
|
|
221
|
+
// show correct placeholders, so save and refresh won't be needed after adding each rendering
|
|
222
|
+
html = html.replace(new RegExp('phkey', 'g'), 'key');
|
|
223
|
+
|
|
224
|
+
return html;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Type guard for Design Library mode
|
|
229
|
+
* @param {object} data preview data to check
|
|
230
|
+
* @returns true if the data is EditingPreviewData
|
|
231
|
+
* @see EditingPreviewData
|
|
232
|
+
*/
|
|
233
|
+
export const isDesignLibraryPreviewData = (
|
|
234
|
+
data: unknown
|
|
235
|
+
): data is DesignLibraryRenderPreviewData => {
|
|
236
|
+
return (
|
|
237
|
+
typeof data === 'object' &&
|
|
238
|
+
data !== null &&
|
|
239
|
+
'mode' in data &&
|
|
240
|
+
isDesignLibraryMode((data as DesignLibraryRenderPreviewData).mode)
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Server URL Resolution order (highest to lowest priority):
|
|
246
|
+
* 1. `config.sitecoreInternalEditingHostUrl` (explicitly set in config)
|
|
247
|
+
* 2. Environment variable `SITECORE_INTERNAL_EDITING_HOST_URL`
|
|
248
|
+
* 3. Fallbacks:
|
|
249
|
+
* - For XM Cloud deployments → `'http://localhost:3000'`
|
|
250
|
+
* - For all other cases → use the request `Host` header
|
|
251
|
+
* Note we use https protocol on Vercel due to serverless function architecture.
|
|
252
|
+
* In all other scenarios, including localhost (with or without a proxy e.g. ngrok)
|
|
253
|
+
* and within a nodejs container, http protocol should be used.
|
|
254
|
+
*
|
|
255
|
+
* For information about the VERCEL environment variable, see
|
|
256
|
+
* https://vercel.com/docs/environment-variables#system-environment-variables
|
|
257
|
+
* @param {Request} req
|
|
258
|
+
*/
|
|
259
|
+
export const resolveServerUrl = (req: Request) => {
|
|
260
|
+
const internalHostUrl = process.env.SITECORE_INTERNAL_EDITING_HOST_URL;
|
|
261
|
+
if (internalHostUrl) {
|
|
262
|
+
return internalHostUrl;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// in xmc deployment we always use localhost:3000
|
|
266
|
+
if (process.env.SITECORE) {
|
|
267
|
+
return 'http://localhost:3000';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// to preserve auth headers, use https if we're in our 3 main hosting options
|
|
271
|
+
const useHttps = (process.env.VERCEL || process.env.NETLIFY) !== undefined;
|
|
272
|
+
// use https for requests with auth but also support unsecured http rendering hosts
|
|
273
|
+
return `${useHttps ? 'https' : 'http'}://${req.headers.get('host') ?? undefined}`;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Gets the Content-Security-Policy header value
|
|
278
|
+
* @returns Content-Security-Policy header value
|
|
279
|
+
*/
|
|
280
|
+
export const getCSPHeader = () => {
|
|
281
|
+
return `frame-ancestors 'self' ${[...getAllowedOriginsFromEnv(), ...EDITING_ALLOWED_ORIGINS].join(
|
|
282
|
+
' '
|
|
283
|
+
)}`;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Gets the object with query params from URLSearchParams object
|
|
288
|
+
* @param {URLSearchParams} query - URLSearchParams query object
|
|
289
|
+
* @returns object with query params
|
|
290
|
+
*/
|
|
291
|
+
export const getEditingRenderQueryParams = (query: URLSearchParams): EditingRenderQueryParams => {
|
|
292
|
+
const params = Object.fromEntries(query.entries());
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
...params,
|
|
296
|
+
secret: params.secret ?? '',
|
|
297
|
+
sc_lang: params.sc_lang ?? '',
|
|
298
|
+
sc_itemid: params.sc_itemid ?? '',
|
|
299
|
+
sc_site: params.sc_site ?? '',
|
|
300
|
+
route: params.route ?? '',
|
|
301
|
+
mode: params.mode as EditingRenderQueryParams['mode'],
|
|
302
|
+
sc_layoutKind: params.sc_layoutkind as LayoutKind,
|
|
303
|
+
sc_variant: params.sc_variant ?? undefined,
|
|
304
|
+
sc_version: params.sc_version ?? undefined,
|
|
305
|
+
sc_renderingId: params.sc_renderingid ?? undefined,
|
|
306
|
+
dataSourceId: params.datasourceid ?? undefined,
|
|
307
|
+
};
|
|
308
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Returns the passed field component or default component in case field value is empty and edit mode is 'metadata'
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
GenericFieldValue,
|
|
8
|
+
Field,
|
|
9
|
+
isFieldValueEmpty,
|
|
10
|
+
FieldMetadata,
|
|
11
|
+
} from '@sitecore-content-sdk/core/layout';
|
|
12
|
+
import { AstroContentSdkComponent } from '../sharedTypes/component-props';
|
|
13
|
+
|
|
14
|
+
interface FieldComponentProps {
|
|
15
|
+
// Partial<T> type is used here because _field.value_ could be required or optional for the different field types
|
|
16
|
+
field?: (Partial<Field> | GenericFieldValue) & FieldMetadata;
|
|
17
|
+
editable?: boolean;
|
|
18
|
+
emptyFieldEditingComponent?: AstroContentSdkComponent;
|
|
19
|
+
defaultEmptyFieldEditingComponent: AstroContentSdkComponent;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const getEmptyFieldEditingComponent = (
|
|
23
|
+
props: FieldComponentProps
|
|
24
|
+
): AstroContentSdkComponent | null => {
|
|
25
|
+
const { editable = true } = props;
|
|
26
|
+
if (props.field?.metadata && editable && isFieldValueEmpty(props.field)) {
|
|
27
|
+
return props.emptyFieldEditingComponent || props.defaultEmptyFieldEditingComponent;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const props = Astro.props as FieldComponentProps;
|
|
34
|
+
const { editable = true } = props;
|
|
35
|
+
|
|
36
|
+
const isEmptyEditableField = props.field?.metadata && editable && isFieldValueEmpty(props.field);
|
|
37
|
+
|
|
38
|
+
const EmptyComponent = getEmptyFieldEditingComponent(props);
|
|
39
|
+
|
|
40
|
+
let { defaultEmptyFieldEditingComponent, emptyFieldEditingComponent, ...resolvedProps } = props;
|
|
41
|
+
|
|
42
|
+
// If no custom empty field editing component is provided, we can omit unnecessary props
|
|
43
|
+
// to do not insert them to html
|
|
44
|
+
if (!props.emptyFieldEditingComponent) {
|
|
45
|
+
resolvedProps = {
|
|
46
|
+
...resolvedProps,
|
|
47
|
+
editable: undefined,
|
|
48
|
+
field: undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
<>
|
|
54
|
+
{isEmptyEditableField && EmptyComponent && <EmptyComponent {...resolvedProps} />}
|
|
55
|
+
{!isEmptyEditableField && <slot />}
|
|
56
|
+
</>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Wraps the field component with metadata markup intended to be used for chromes hydration in Pages
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import FieldMetadata from '../components/FieldMetadata.astro';
|
|
7
|
+
|
|
8
|
+
interface WithMetadataProps {
|
|
9
|
+
field?: {
|
|
10
|
+
metadata?: { [key: string]: unknown };
|
|
11
|
+
};
|
|
12
|
+
editable?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const componentProps = Astro.props as WithMetadataProps;
|
|
16
|
+
|
|
17
|
+
const { editable = true } = componentProps;
|
|
18
|
+
const metadata = componentProps.field?.metadata;
|
|
19
|
+
const isEditable = metadata && editable;
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<>
|
|
23
|
+
{!isEditable && <slot />}
|
|
24
|
+
{
|
|
25
|
+
isEditable && (
|
|
26
|
+
<FieldMetadata metadata={metadata}>
|
|
27
|
+
<slot />
|
|
28
|
+
</FieldMetadata>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
</>
|
package/src/env.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* eslint-disable spaced-comment */
|
|
2
|
+
/* eslint-disable @typescript-eslint/triple-slash-reference */
|
|
3
|
+
/// <reference types="astro/client" />
|
|
4
|
+
declare global {
|
|
5
|
+
namespace App {
|
|
6
|
+
interface Locals extends Record<string, any> {
|
|
7
|
+
skipMiddleware?: boolean;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export {};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { HTMLLink } from '@sitecore-content-sdk/core';
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
Field,
|
|
5
|
+
LayoutServiceData,
|
|
6
|
+
ComponentRendering,
|
|
7
|
+
ComponentFields,
|
|
8
|
+
ComponentParams,
|
|
9
|
+
} from '@sitecore-content-sdk/core/layout';
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
AstroContentSdkComponent,
|
|
13
|
+
ComponentMap,
|
|
14
|
+
} from './sharedTypes/component-props';
|
|
15
|
+
|
|
16
|
+
export { PageMode, ErrorPage, Page } from '@sitecore-content-sdk/core/client';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export { debug } from '@sitecore-content-sdk/core';
|
|
2
|
+
export {
|
|
3
|
+
MiddlewareBase,
|
|
4
|
+
MiddlewareBaseConfig,
|
|
5
|
+
Middleware,
|
|
6
|
+
} from './middleware';
|
|
7
|
+
export { RobotsMiddleware } from './robots-middleware';
|
|
8
|
+
export { SitemapMiddleware } from './sitemap-middleware';
|
|
9
|
+
export {
|
|
10
|
+
PersonalizeService,
|
|
11
|
+
PersonalizeServiceConfig,
|
|
12
|
+
} from '@sitecore-content-sdk/core/personalize';
|
|
13
|
+
export {
|
|
14
|
+
MultisiteMiddleware,
|
|
15
|
+
MultisiteMiddlewareConfig,
|
|
16
|
+
} from './multisite-middleware';
|
|
17
|
+
export {
|
|
18
|
+
RedirectsService,
|
|
19
|
+
RedirectsServiceConfig,
|
|
20
|
+
REDIRECT_TYPE_301,
|
|
21
|
+
REDIRECT_TYPE_302,
|
|
22
|
+
REDIRECT_TYPE_SERVER_TRANSFER,
|
|
23
|
+
RedirectInfo,
|
|
24
|
+
} from '@sitecore-content-sdk/core/site';
|