@enonic/nextjs-adapter 2.0.1 → 2.0.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.
- package/ComponentRegistry.d.ts +64 -0
- package/ComponentRegistry.js +121 -0
- package/UrlProcessor.d.ts +25 -0
- package/{src/UrlProcessor.ts → UrlProcessor.js} +35 -53
- package/baseMappings.d.ts +1 -0
- package/{src/baseMappings.ts → baseMappings.js} +3 -13
- package/guillotine/fetchContent.d.ts +49 -0
- package/{src/guillotine/fetchContent.ts → guillotine/fetchContent.js} +243 -493
- package/guillotine/getMetaData.d.ts +94 -0
- package/guillotine/getMetaData.js +128 -0
- package/{src/index.ts → index.d.ts} +1 -1
- package/index.js +5 -0
- package/package.json +2 -2
- package/query/Shortcut.d.ts +1 -0
- package/server/ClearableFileCache.d.ts +31 -0
- package/server/ClearableFileCache.js +472 -0
- package/server/cache.d.ts +2 -0
- package/server/cache.js +129 -0
- package/server/index.d.ts +5 -0
- package/server/index.js +132 -0
- package/utils.d.ts +114 -0
- package/{src/utils.ts → utils.js} +69 -148
- package/views/BaseComponent.d.ts +25 -0
- package/{src/views/BaseComponent.tsx → views/BaseComponent.jsx} +39 -73
- package/views/BaseContent.d.ts +4 -0
- package/{src/views/BaseContent.tsx → views/BaseContent.jsx} +17 -24
- package/views/BaseLayout.d.ts +17 -0
- package/views/BaseLayout.jsx +24 -0
- package/views/BaseMacro.d.ts +16 -0
- package/{src/views/BaseMacro.tsx → views/BaseMacro.jsx} +29 -45
- package/views/BasePage.d.ts +19 -0
- package/views/BasePage.jsx +34 -0
- package/views/BasePart.d.ts +19 -0
- package/views/BasePart.jsx +24 -0
- package/views/Empty.d.ts +3 -0
- package/{src/views/Empty.tsx → views/Empty.jsx} +0 -2
- package/views/Fragment.d.ts +10 -0
- package/views/Fragment.jsx +30 -0
- package/views/MainView.d.ts +4 -0
- package/views/MainView.jsx +14 -0
- package/views/Region.d.ts +20 -0
- package/views/Region.jsx +52 -0
- package/views/RichTextView.d.ts +16 -0
- package/{src/views/RichTextView.tsx → views/RichTextView.jsx} +10 -31
- package/views/SingleComponent.d.ts +5 -0
- package/{src/views/SingleComponent.tsx → views/SingleComponent.jsx} +2 -8
- package/views/StaticContent.d.ts +15 -0
- package/views/StaticContent.jsx +36 -0
- package/views/Text.d.ts +9 -0
- package/views/Text.jsx +4 -0
- package/views/macros/DefaultMacro.d.ts +4 -0
- package/views/macros/DefaultMacro.jsx +3 -0
- package/views/macros/DisableMacro.d.ts +3 -0
- package/views/macros/DisableMacro.jsx +2 -0
- package/.eslint-childrc.js +0 -17
- package/.eslintrc.js +0 -15
- package/.github/dependabot.yml +0 -20
- package/.github/pull-merger.yml +0 -1
- package/.github/workflows/release.yml +0 -39
- package/LICENSE +0 -201
- package/RELEASE.md +0 -75
- package/server/.eslintrc.js +0 -11
- package/server/ClearableFileCache.ts +0 -410
- package/server/cache.ts +0 -50
- package/server/index.ts +0 -74
- package/server/tsconfig.json +0 -23
- package/src/.eslintrc.js +0 -11
- package/src/ComponentRegistry.ts +0 -189
- package/src/guillotine/getMetaData.ts +0 -246
- package/src/tsconfig.json +0 -20
- package/src/views/BaseLayout.tsx +0 -45
- package/src/views/BasePage.tsx +0 -56
- package/src/views/BasePart.tsx +0 -50
- package/src/views/Fragment.tsx +0 -45
- package/src/views/MainView.tsx +0 -21
- package/src/views/Region.tsx +0 -89
- package/src/views/StaticContent.tsx +0 -40
- package/src/views/Text.tsx +0 -15
- package/src/views/macros/DefaultMacro.tsx +0 -9
- package/src/views/macros/DisableMacro.tsx +0 -8
- /package/{src/query/Shortcut.ts → query/Shortcut.js} +0 -0
|
@@ -1,121 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Context,
|
|
10
|
-
FRAGMENT_CONTENTTYPE_NAME,
|
|
11
|
-
FRAGMENT_DEFAULT_REGION_NAME,
|
|
12
|
-
GET_STATIC_PATHS_QUERY,
|
|
13
|
-
getContentApiUrl,
|
|
14
|
-
getLocaleProjectConfig,
|
|
15
|
-
getLocaleProjectConfigs,
|
|
16
|
-
getRenderMode,
|
|
17
|
-
getXpBaseUrl,
|
|
18
|
-
IS_DEV_MODE,
|
|
19
|
-
LocaleProjectConfig,
|
|
20
|
-
normalizeLocale,
|
|
21
|
-
PAGE_TEMPLATE_CONTENTTYPE_NAME,
|
|
22
|
-
PAGE_TEMPLATE_FOLDER,
|
|
23
|
-
PURGE_CACHE_URL,
|
|
24
|
-
RENDER_MODE,
|
|
25
|
-
sanitizeGraphqlName,
|
|
26
|
-
XP_COMPONENT_TYPE,
|
|
27
|
-
XP_REQUEST_TYPE,
|
|
28
|
-
} from '../utils';
|
|
29
|
-
import {ComponentDefinition, ComponentRegistry, SelectedQueryMaybeVariablesFunc} from '../ComponentRegistry';
|
|
30
|
-
import {getUrl, UrlProcessor} from '../UrlProcessor';
|
|
31
|
-
import {GetServerSidePropsResult, GetStaticPropsResult} from 'next';
|
|
32
|
-
|
|
33
|
-
type AdapterConstants = {
|
|
34
|
-
APP_NAME: string,
|
|
35
|
-
APP_NAME_DASHED: string,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
type Result = {
|
|
39
|
-
error?: {
|
|
40
|
-
code: string,
|
|
41
|
-
message: string
|
|
42
|
-
} | null;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export type GuillotineResult = Result & {
|
|
46
|
-
[dataKey: string]: any;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
type MetaResult = Result & {
|
|
50
|
-
meta?: {
|
|
51
|
-
_path: string;
|
|
52
|
-
type: string,
|
|
53
|
-
pageAsJson?: PageData,
|
|
54
|
-
components?: PageComponent[],
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
type ContentResult = Result & {
|
|
59
|
-
contents?: Record<string, any>[];
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
interface ComponentDescriptor {
|
|
63
|
-
type?: ComponentDefinition;
|
|
64
|
-
component?: PageComponent;
|
|
65
|
-
queryAndVariables?: QueryAndVariables;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export type FetchContentResult = Result & {
|
|
69
|
-
data: Record<string, any> | null,
|
|
70
|
-
common: Record<string, any> | null,
|
|
71
|
-
meta: MetaData,
|
|
72
|
-
page: PageComponent | null,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
type FetcherConfig<T extends AdapterConstants> = T & {
|
|
77
|
-
componentRegistry: typeof ComponentRegistry
|
|
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
|
+
});
|
|
78
9
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Sends one query to the guillotine API and asks for content type, then uses the type to select a second query and variables, which is sent to the API and fetches content data.
|
|
87
|
-
* @param contentPath string or string array: pre-split or slash-delimited _path to a content available on the API
|
|
88
|
-
* @returns FetchContentResult object: {data?: T, error?: {code, message}}
|
|
89
|
-
*/
|
|
90
|
-
export type ContentFetcher = (contentPath: string | string[], context?: Context) => Promise<FetchContentResult>;
|
|
91
|
-
|
|
92
|
-
const NO_PROPS_PROCESSOR = async (props: any) => await props ?? {};
|
|
93
|
-
|
|
10
|
+
import { getMetaQuery, pageFragmentQuery } from './getMetaData';
|
|
11
|
+
import adapterConstants, { addJsessionHeaders, addLocaleHeaders, APP_NAME, APP_NAME_DASHED, FRAGMENT_CONTENTTYPE_NAME, FRAGMENT_DEFAULT_REGION_NAME, GET_STATIC_PATHS_QUERY, getContentApiUrl, getLocaleProjectConfig, getLocaleProjectConfigs, getRenderMode, getXpBaseUrl, IS_DEV_MODE, normalizeLocale, PAGE_TEMPLATE_CONTENTTYPE_NAME, PAGE_TEMPLATE_FOLDER, PURGE_CACHE_URL, RENDER_MODE, sanitizeGraphqlName, XP_COMPONENT_TYPE, XP_REQUEST_TYPE, } from '../utils';
|
|
12
|
+
import { ComponentRegistry } from '../ComponentRegistry';
|
|
13
|
+
import { getUrl, UrlProcessor } from '../UrlProcessor';
|
|
14
|
+
const NO_PROPS_PROCESSOR = (props) => __awaiter(void 0, void 0, void 0, function* () { var _a; return (_a = yield props) !== null && _a !== void 0 ? _a : {}; });
|
|
94
15
|
const ALIAS_PREFIX = 'request';
|
|
95
|
-
|
|
96
16
|
const GUILLOTINE_QUERY_REGEXP = /^\s*query\s*(?:\((.*)*\))?\s*{\s*guillotine\s*{((?:.|\s)+)}\s*}\s*$/;
|
|
97
|
-
|
|
98
17
|
const GRAPHQL_FRAGMENTS_REGEXP = /fragment\s+.+\s+on\s+.+\s*{[\s\w{}().,:"'`]+}/;
|
|
99
|
-
|
|
100
|
-
// /////////////////////////////////////////////////////////////////////////////// Data
|
|
101
|
-
|
|
102
|
-
// Shape of content base-data API body
|
|
103
|
-
export type ContentApiBaseBody = {
|
|
104
|
-
query?: string, // Override the default base-data query
|
|
105
|
-
variables?: { // GraphQL variables inserted into the query
|
|
106
|
-
path?: string, // Full content item _path
|
|
107
|
-
[key: string]: string | number | undefined,
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
|
|
111
18
|
/** Generic fetch */
|
|
112
|
-
export const fetchFromApi =
|
|
113
|
-
apiUrl: string,
|
|
114
|
-
body: ContentApiBaseBody,
|
|
115
|
-
projectConfig: LocaleProjectConfig,
|
|
116
|
-
headers?: {},
|
|
117
|
-
method = 'POST',
|
|
118
|
-
) => {
|
|
19
|
+
export const fetchFromApi = (apiUrl, body, projectConfig, headers, method = 'POST') => __awaiter(void 0, void 0, void 0, function* () {
|
|
119
20
|
const options = {
|
|
120
21
|
method,
|
|
121
22
|
headers: {
|
|
@@ -125,55 +26,47 @@ export const fetchFromApi = async (
|
|
|
125
26
|
},
|
|
126
27
|
body: JSON.stringify(body),
|
|
127
28
|
};
|
|
128
|
-
|
|
129
29
|
if (headers) {
|
|
130
30
|
Object.assign(options.headers, headers);
|
|
131
31
|
}
|
|
132
|
-
|
|
133
32
|
let res;
|
|
134
33
|
try {
|
|
135
|
-
res =
|
|
136
|
-
}
|
|
34
|
+
res = yield fetch(apiUrl, options);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
137
37
|
console.warn(apiUrl, e);
|
|
138
38
|
throw new Error(JSON.stringify({
|
|
139
39
|
code: 'API',
|
|
140
40
|
message: e.message,
|
|
141
41
|
}));
|
|
142
42
|
}
|
|
143
|
-
|
|
144
43
|
if (!res.ok) {
|
|
145
44
|
throw new Error(JSON.stringify({
|
|
146
45
|
code: res.status,
|
|
147
|
-
message: `Data fetching failed (message: '${
|
|
46
|
+
message: `Data fetching failed (message: '${yield res.text()}')`,
|
|
148
47
|
}));
|
|
149
48
|
}
|
|
150
|
-
|
|
151
49
|
let json;
|
|
152
50
|
try {
|
|
153
|
-
json =
|
|
154
|
-
}
|
|
51
|
+
json = yield res.json();
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
155
54
|
throw new Error(JSON.stringify({
|
|
156
55
|
code: 500,
|
|
157
|
-
message: `API call completed but with non-JSON data: ${JSON.stringify(
|
|
56
|
+
message: `API call completed but with non-JSON data: ${JSON.stringify(yield res.text())}`,
|
|
158
57
|
}));
|
|
159
58
|
}
|
|
160
|
-
|
|
161
59
|
if (!json) {
|
|
162
60
|
throw new Error(JSON.stringify({
|
|
163
61
|
code: 500,
|
|
164
|
-
message: `API call completed but with unexpectedly empty data: ${JSON.stringify(
|
|
62
|
+
message: `API call completed but with unexpectedly empty data: ${JSON.stringify(yield res.text())}`,
|
|
165
63
|
}));
|
|
166
64
|
}
|
|
167
|
-
|
|
168
65
|
return json;
|
|
169
|
-
};
|
|
170
|
-
|
|
66
|
+
});
|
|
171
67
|
/** Guillotine-specialized fetch, using the generic fetch above */
|
|
172
|
-
export const fetchGuillotine =
|
|
173
|
-
|
|
174
|
-
body: ContentApiBaseBody,
|
|
175
|
-
projectConfig: LocaleProjectConfig,
|
|
176
|
-
headers?: {}): Promise<GuillotineResult> => {
|
|
68
|
+
export const fetchGuillotine = (contentApiUrl, body, projectConfig, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
+
var _b;
|
|
177
70
|
if (typeof body.query !== 'string' || !body.query.trim()) {
|
|
178
71
|
return {
|
|
179
72
|
error: {
|
|
@@ -182,129 +75,98 @@ export const fetchGuillotine = async (
|
|
|
182
75
|
},
|
|
183
76
|
};
|
|
184
77
|
}
|
|
185
|
-
const path = body.variables
|
|
78
|
+
const path = (_b = body.variables) === null || _b === void 0 ? void 0 : _b.path;
|
|
186
79
|
const pathMessage = path ? JSON.stringify(path) : '';
|
|
187
|
-
const result =
|
|
188
|
-
contentApiUrl,
|
|
189
|
-
body,
|
|
190
|
-
projectConfig,
|
|
191
|
-
headers,
|
|
192
|
-
)
|
|
80
|
+
const result = yield fetchFromApi(contentApiUrl, body, projectConfig, headers)
|
|
193
81
|
.then(json => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (errors) {
|
|
197
|
-
|
|
198
|
-
errors = [errors];
|
|
199
|
-
}
|
|
200
|
-
console.error(`${errors.length} error(s) when fetching data from: ${contentApiUrl}`);
|
|
201
|
-
console.error(`headers: ${JSON.stringify(headers, null, 2)}
|
|
202
|
-
variables: ${JSON.stringify(body.variables, null, 2)}`);
|
|
203
|
-
errors.forEach(error => {
|
|
204
|
-
console.error(error);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
error: {
|
|
209
|
-
code: '500',
|
|
210
|
-
message: `Server responded with ${errors.length} error(s), probably from guillotine - see log.`,
|
|
211
|
-
},
|
|
212
|
-
};
|
|
82
|
+
let errors = (json || {}).errors;
|
|
83
|
+
if (errors) {
|
|
84
|
+
if (!Array.isArray(errors)) {
|
|
85
|
+
errors = [errors];
|
|
213
86
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
87
|
+
console.error(`${errors.length} error(s) when fetching data from: ${contentApiUrl}`);
|
|
88
|
+
console.error(`headers: ${JSON.stringify(headers, null, 2)}
|
|
89
|
+
variables: ${JSON.stringify(body.variables, null, 2)}`);
|
|
90
|
+
errors.forEach(error => {
|
|
91
|
+
console.error(error);
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
error: {
|
|
95
|
+
code: '500',
|
|
96
|
+
message: `Server responded with ${errors.length} error(s), probably from guillotine - see log.`,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return json.data;
|
|
101
|
+
})
|
|
217
102
|
.catch((err) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return result
|
|
227
|
-
};
|
|
228
|
-
|
|
103
|
+
console.warn(`Client-side error when trying to fetch data ${pathMessage}`, err);
|
|
104
|
+
try {
|
|
105
|
+
return { error: JSON.parse(err.message) };
|
|
106
|
+
}
|
|
107
|
+
catch (e2) {
|
|
108
|
+
return { error: { code: 'Client-side error', message: err.message } };
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return result;
|
|
112
|
+
});
|
|
229
113
|
// /////////////////////////////////////////////////////////////////////////////// Specific fetch wrappers:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const body
|
|
114
|
+
const fetchMetaData = (contentApiUrl, xpContentPath, projectConfig, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
115
|
+
var _c;
|
|
116
|
+
const body = {
|
|
233
117
|
query: getMetaQuery(pageFragmentQuery()),
|
|
234
118
|
variables: {
|
|
235
119
|
path: xpContentPath,
|
|
236
120
|
},
|
|
237
121
|
};
|
|
238
|
-
const metaResult =
|
|
122
|
+
const metaResult = yield fetchGuillotine(contentApiUrl, body, projectConfig, headers);
|
|
239
123
|
if (metaResult.error) {
|
|
240
124
|
return metaResult;
|
|
241
|
-
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
242
127
|
return {
|
|
243
|
-
meta: metaResult
|
|
128
|
+
meta: (_c = metaResult === null || metaResult === void 0 ? void 0 : metaResult.guillotine) === null || _c === void 0 ? void 0 : _c.get,
|
|
244
129
|
};
|
|
245
130
|
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const fetchContentData = async <T>(
|
|
250
|
-
contentApiUrl: string,
|
|
251
|
-
xpContentPath: string,
|
|
252
|
-
projectConfig: LocaleProjectConfig,
|
|
253
|
-
query: string,
|
|
254
|
-
variables?: {},
|
|
255
|
-
headers?: {},
|
|
256
|
-
): Promise<ContentResult> => {
|
|
257
|
-
|
|
258
|
-
const body: ContentApiBaseBody = {query};
|
|
131
|
+
});
|
|
132
|
+
const fetchContentData = (contentApiUrl, xpContentPath, projectConfig, query, variables, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
133
|
+
const body = { query };
|
|
259
134
|
if (variables && Object.keys(variables).length > 0) {
|
|
260
135
|
body.variables = variables;
|
|
261
136
|
}
|
|
262
|
-
const contentResults =
|
|
263
|
-
|
|
137
|
+
const contentResults = yield fetchGuillotine(contentApiUrl, body, projectConfig, headers);
|
|
264
138
|
if (contentResults.error) {
|
|
265
139
|
return contentResults;
|
|
266
|
-
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
267
142
|
return {
|
|
268
143
|
// omit the aliases and return values
|
|
269
144
|
contents: Object.values(contentResults),
|
|
270
145
|
};
|
|
271
146
|
}
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
|
|
147
|
+
});
|
|
275
148
|
// /////////////////////////////////////////////////////////////////////////////// Error checking:
|
|
276
|
-
|
|
277
149
|
/** Checks a site-relative contentPath as a slash-delimited string or a string array, and returns a pure site-relative path string (no double slashes, starts with a slash, does not end with one). */
|
|
278
|
-
const getCleanContentPathArrayOrThrow400 = (contentPath
|
|
150
|
+
const getCleanContentPathArrayOrThrow400 = (contentPath) => {
|
|
279
151
|
if (contentPath === undefined) {
|
|
280
152
|
return '';
|
|
281
153
|
}
|
|
282
154
|
const isArray = Array.isArray(contentPath);
|
|
283
|
-
|
|
284
155
|
if (!isArray) {
|
|
285
156
|
if (typeof contentPath !== 'string') {
|
|
286
157
|
throw Error(JSON.stringify({
|
|
287
158
|
code: 400,
|
|
288
|
-
message: `Unexpected target content _path: contentPath must be a string or pure string array (contentPath=${JSON.stringify(
|
|
289
|
-
contentPath)})`,
|
|
159
|
+
message: `Unexpected target content _path: contentPath must be a string or pure string array (contentPath=${JSON.stringify(contentPath)})`,
|
|
290
160
|
}));
|
|
291
161
|
}
|
|
292
|
-
|
|
293
162
|
return contentPath;
|
|
294
|
-
|
|
295
|
-
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
296
165
|
return (contentPath).join('/');
|
|
297
166
|
}
|
|
298
167
|
};
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
// ------------------------------------------------------------- XP view component data handling
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
type PathFragment = { region: string, index: number };
|
|
305
|
-
|
|
306
|
-
function parseComponentPath(contentType: string, path: string): PathFragment[] {
|
|
307
|
-
const matches: PathFragment[] = [];
|
|
168
|
+
function parseComponentPath(contentType, path) {
|
|
169
|
+
const matches = [];
|
|
308
170
|
let match;
|
|
309
171
|
const myRegexp = /(?:(\w+)\/(\d+))+/g;
|
|
310
172
|
while ((match = myRegexp.exec(path)) !== null) {
|
|
@@ -323,20 +185,15 @@ function parseComponentPath(contentType: string, path: string): PathFragment[] {
|
|
|
323
185
|
}
|
|
324
186
|
return matches;
|
|
325
187
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
let currentTree: RegionTree = source;
|
|
331
|
-
let currentRegion: PageRegion | undefined;
|
|
188
|
+
function getParentRegion(source, contentType, cmpPath, components = [], createMissing) {
|
|
189
|
+
let currentTree = source;
|
|
190
|
+
let currentRegion;
|
|
332
191
|
let parentPath = '';
|
|
333
|
-
|
|
334
192
|
for (let i = 0; i < cmpPath.length; i++) {
|
|
335
193
|
const pathFragment = cmpPath[i];
|
|
336
194
|
const regionName = pathFragment.region;
|
|
337
195
|
parentPath += `/${pathFragment.region}/${pathFragment.index}`;
|
|
338
196
|
currentRegion = currentTree[regionName];
|
|
339
|
-
|
|
340
197
|
if (!currentRegion) {
|
|
341
198
|
if (createMissing) {
|
|
342
199
|
currentRegion = {
|
|
@@ -344,15 +201,14 @@ function getParentRegion(source: RegionTree, contentType: string, cmpPath: PathF
|
|
|
344
201
|
components: [],
|
|
345
202
|
};
|
|
346
203
|
currentTree[regionName] = currentRegion;
|
|
347
|
-
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
348
206
|
throw `Region [${regionName}] was not found`;
|
|
349
207
|
}
|
|
350
208
|
}
|
|
351
|
-
|
|
352
209
|
if (i < cmpPath.length - 1) {
|
|
353
210
|
// look for layouts inside if this is not the last path fragment
|
|
354
|
-
|
|
355
|
-
const layout = components.find((cmp: PageComponent) => {
|
|
211
|
+
const layout = components.find((cmp) => {
|
|
356
212
|
return cmp.type === XP_COMPONENT_TYPE.LAYOUT && prefixLayoutPath(contentType, cmp.path) === parentPath;
|
|
357
213
|
});
|
|
358
214
|
if (!layout) {
|
|
@@ -364,107 +220,94 @@ function getParentRegion(source: RegionTree, contentType: string, cmpPath: PathF
|
|
|
364
220
|
currentTree = layout.regions;
|
|
365
221
|
}
|
|
366
222
|
}
|
|
367
|
-
|
|
368
223
|
return currentRegion;
|
|
369
224
|
}
|
|
370
|
-
|
|
371
|
-
function prefixLayoutPath(contentType: string, path: string): string {
|
|
225
|
+
function prefixLayoutPath(contentType, path) {
|
|
372
226
|
if (contentType !== FRAGMENT_CONTENTTYPE_NAME) {
|
|
373
227
|
return path;
|
|
374
|
-
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
375
230
|
// prepend FRAGMENT_DEFAULT_REGION_NAME to path to conform to page structure
|
|
376
231
|
// so that component with path '/' becomes /FRAGMENT_DEFAULT_REGION_NAME/0
|
|
377
232
|
// path /left/1 becomes /FRAGMENT_DEFAULT_REGION_NAME/0/left/1
|
|
378
233
|
return `/${FRAGMENT_DEFAULT_REGION_NAME}/0${path === '/' ? '' : path}`;
|
|
379
234
|
}
|
|
380
235
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const page: PageComponent = {
|
|
236
|
+
function buildPage(contentType, comps = []) {
|
|
237
|
+
const page = {
|
|
385
238
|
type: XP_COMPONENT_TYPE.PAGE,
|
|
386
239
|
path: '/',
|
|
387
240
|
};
|
|
388
|
-
|
|
389
241
|
return buildComponentTree(comps, page, contentType);
|
|
390
242
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const rootTree: RegionTree = {};
|
|
243
|
+
function buildComponentTree(comps, rootComp, contentType) {
|
|
244
|
+
const rootTree = {};
|
|
394
245
|
const isFragmentType = contentType === FRAGMENT_CONTENTTYPE_NAME;
|
|
395
|
-
|
|
396
246
|
if (isFragmentType) {
|
|
397
247
|
rootComp.regions = rootTree;
|
|
398
248
|
}
|
|
399
|
-
|
|
400
249
|
(comps || []).forEach(cmp => {
|
|
250
|
+
var _a, _b, _c;
|
|
401
251
|
const cmpPath = parseComponentPath(contentType, cmp.path);
|
|
402
|
-
|
|
403
252
|
if (cmp.path === '/') {
|
|
404
253
|
if (cmp.type === XP_COMPONENT_TYPE.PAGE && rootComp.type === XP_COMPONENT_TYPE.PAGE) {
|
|
405
254
|
// add page values to page object
|
|
406
255
|
rootComp = Object.assign(rootComp, cmp);
|
|
407
256
|
rootComp.page.regions = rootTree;
|
|
408
257
|
return;
|
|
409
|
-
}
|
|
258
|
+
}
|
|
259
|
+
else if (cmp.type === XP_COMPONENT_TYPE.LAYOUT && rootComp.type === XP_COMPONENT_TYPE.FRAGMENT) {
|
|
410
260
|
// when rendering fragment as part of the page with layout at root path
|
|
411
261
|
// remove other children from list as they will be included in layout
|
|
412
262
|
rootComp.fragment.fragment.components = [cmp];
|
|
413
263
|
cmp.regions = rootTree;
|
|
414
264
|
}
|
|
415
265
|
}
|
|
416
|
-
|
|
417
266
|
if (cmp.type === XP_COMPONENT_TYPE.FRAGMENT) {
|
|
418
267
|
// build component subtree for fragment
|
|
419
|
-
cmp = buildComponentTree(cmp.fragment
|
|
268
|
+
cmp = buildComponentTree((_b = (_a = cmp.fragment) === null || _a === void 0 ? void 0 : _a.fragment) === null || _b === void 0 ? void 0 : _b.components, cmp, contentType);
|
|
420
269
|
}
|
|
421
|
-
|
|
422
270
|
const region = getParentRegion(rootTree, contentType, cmpPath, comps, true);
|
|
423
|
-
|
|
424
271
|
if (region) {
|
|
425
272
|
// getting the index of component from string like '/main/0/left/11'
|
|
426
|
-
const cmpIndex = cmpPath[cmpPath.length - 1]
|
|
273
|
+
const cmpIndex = (_c = cmpPath[cmpPath.length - 1]) === null || _c === void 0 ? void 0 : _c.index;
|
|
427
274
|
if (cmpIndex >= 0) {
|
|
428
275
|
region.components.splice(cmpIndex, 0, cmp);
|
|
429
|
-
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
430
278
|
throw Error(`Could not find [${cmp.type}] component index at ${JSON.stringify(cmpPath, null, 2)}, rendering not possible.`);
|
|
431
279
|
}
|
|
432
280
|
}
|
|
433
281
|
});
|
|
434
|
-
|
|
435
282
|
return rootComp;
|
|
436
283
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
const superParams: string[] = [];
|
|
444
|
-
|
|
445
|
-
queriesWithVars.forEach((componentDescriptor: ComponentDescriptor, index: number) => {
|
|
284
|
+
function combineMultipleQueries(queriesWithVars) {
|
|
285
|
+
const queries = [];
|
|
286
|
+
const fragments = [];
|
|
287
|
+
const superVars = {};
|
|
288
|
+
const superParams = [];
|
|
289
|
+
queriesWithVars.forEach((componentDescriptor, index) => {
|
|
446
290
|
const queryAndVars = componentDescriptor.queryAndVariables;
|
|
447
291
|
if (!queryAndVars) {
|
|
448
292
|
return;
|
|
449
293
|
}
|
|
450
|
-
|
|
451
294
|
// Extract fragments first if exist
|
|
452
295
|
let q = queryAndVars.query;
|
|
453
296
|
let match = q.match(GRAPHQL_FRAGMENTS_REGEXP);
|
|
454
|
-
if (match
|
|
297
|
+
if ((match === null || match === void 0 ? void 0 : match.length) === 1) {
|
|
455
298
|
// extract a fragment to put it at root level
|
|
456
299
|
fragments.push(match[0]);
|
|
457
300
|
// remove it from query because queries are going to get wrapped
|
|
458
301
|
q = q.replace(match[0], '');
|
|
459
302
|
}
|
|
460
|
-
|
|
461
303
|
// Extract graphql query and its params and add prefixes to exclude collisions with other queries
|
|
462
304
|
match = q.match(GUILLOTINE_QUERY_REGEXP);
|
|
463
305
|
let query = '';
|
|
464
306
|
if (match && match.length === 2) {
|
|
465
307
|
// no params, just query
|
|
466
308
|
query = match[1];
|
|
467
|
-
}
|
|
309
|
+
}
|
|
310
|
+
else if (match && match.length === 3) {
|
|
468
311
|
// both query and params are present
|
|
469
312
|
query = match[2];
|
|
470
313
|
// process args
|
|
@@ -485,62 +328,50 @@ function combineMultipleQueries(queriesWithVars: ComponentDescriptor[]): QueryAn
|
|
|
485
328
|
if (query.length) {
|
|
486
329
|
queries.push(`${ALIAS_PREFIX}${index}:guillotine {${query}}`);
|
|
487
330
|
}
|
|
488
|
-
|
|
489
331
|
// Update variables with the same prefixes
|
|
490
332
|
Object.entries(queryAndVars.variables || {}).forEach(entry => {
|
|
491
333
|
superVars[`${ALIAS_PREFIX}${index}_${entry[0]}`] = entry[1];
|
|
492
334
|
});
|
|
493
335
|
});
|
|
494
|
-
|
|
495
336
|
// Compose the super query
|
|
496
337
|
const superQuery = `query ${superParams.length ? `(${superParams.join(', ')})` : ''} {
|
|
497
338
|
${queries.join('\n')}
|
|
498
339
|
}
|
|
499
340
|
${fragments.join('\n')}
|
|
500
341
|
`;
|
|
501
|
-
|
|
502
342
|
return {
|
|
503
343
|
query: superQuery,
|
|
504
344
|
variables: superVars,
|
|
505
345
|
};
|
|
506
346
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
return
|
|
347
|
+
function applyProcessors(componentDescriptors, contentResults, context) {
|
|
348
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
349
|
+
let dataCounter = 0;
|
|
350
|
+
const processorPromises = componentDescriptors.map((desc) => __awaiter(this, void 0, void 0, function* () {
|
|
351
|
+
var _a;
|
|
352
|
+
// we're iterating component descriptors here
|
|
353
|
+
// some of them might not have provided graphql requests
|
|
354
|
+
// but we still need to run props processor for them
|
|
355
|
+
// in case they want to fetch their data from elsewhere
|
|
356
|
+
const propsProcessor = ((_a = desc.type) === null || _a === void 0 ? void 0 : _a.processor) || NO_PROPS_PROCESSOR;
|
|
357
|
+
let data;
|
|
358
|
+
if (desc.queryAndVariables) {
|
|
359
|
+
// if there is a query then there must be a result for it
|
|
360
|
+
data = contentResults.contents[dataCounter++];
|
|
361
|
+
}
|
|
362
|
+
const config = getComponentConfig(desc.component);
|
|
363
|
+
return yield propsProcessor(data, context, config);
|
|
364
|
+
}));
|
|
365
|
+
return Promise.allSettled(processorPromises);
|
|
526
366
|
});
|
|
527
|
-
|
|
528
|
-
return Promise.allSettled(processorPromises);
|
|
529
367
|
}
|
|
530
|
-
|
|
531
|
-
function getComponentConfig(cmp?: PageComponent) {
|
|
368
|
+
function getComponentConfig(cmp) {
|
|
532
369
|
const cmpData = cmp && cmp[cmp.type];
|
|
533
370
|
return cmpData && 'config' in cmpData ? cmpData.config : undefined;
|
|
534
371
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
xpContentPath: string,
|
|
539
|
-
context: Context | undefined,
|
|
540
|
-
): ComponentDescriptor[] {
|
|
541
|
-
|
|
542
|
-
const descriptors: ComponentDescriptor[] = [];
|
|
543
|
-
|
|
372
|
+
function collectComponentDescriptors(components, componentRegistry, xpContentPath, context) {
|
|
373
|
+
var _a, _b;
|
|
374
|
+
const descriptors = [];
|
|
544
375
|
for (const cmp of (components || [])) {
|
|
545
376
|
processComponentConfig(APP_NAME, APP_NAME_DASHED, cmp);
|
|
546
377
|
// only look for parts
|
|
@@ -559,19 +390,18 @@ function collectComponentDescriptors(components: PageComponent[],
|
|
|
559
390
|
});
|
|
560
391
|
}
|
|
561
392
|
}
|
|
562
|
-
}
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
563
395
|
// look for parts inside fragments
|
|
564
|
-
const fragPartDescs = collectComponentDescriptors(cmp.fragment
|
|
396
|
+
const fragPartDescs = collectComponentDescriptors((_b = (_a = cmp.fragment) === null || _a === void 0 ? void 0 : _a.fragment) === null || _b === void 0 ? void 0 : _b.components, componentRegistry, xpContentPath, context);
|
|
565
397
|
if (fragPartDescs.length) {
|
|
566
398
|
descriptors.push(...fragPartDescs);
|
|
567
399
|
}
|
|
568
400
|
}
|
|
569
401
|
}
|
|
570
|
-
|
|
571
402
|
return descriptors;
|
|
572
403
|
}
|
|
573
|
-
|
|
574
|
-
function processComponentConfig(myAppName: string, myAppNameDashed: string, cmp: PageComponent) {
|
|
404
|
+
function processComponentConfig(myAppName, myAppNameDashed, cmp) {
|
|
575
405
|
const cmpData = cmp[cmp.type];
|
|
576
406
|
if (cmpData && 'descriptor' in cmpData && cmpData.descriptor) {
|
|
577
407
|
const [appName, cmpName] = cmpData.descriptor.split(':');
|
|
@@ -579,7 +409,7 @@ function processComponentConfig(myAppName: string, myAppNameDashed: string, cmp:
|
|
|
579
409
|
return;
|
|
580
410
|
}
|
|
581
411
|
let config;
|
|
582
|
-
const configArray
|
|
412
|
+
const configArray = [];
|
|
583
413
|
if ('configAsJson' in cmpData && cmpData.configAsJson && cmpData.configAsJson[myAppNameDashed]) {
|
|
584
414
|
config = cmpData.configAsJson[myAppNameDashed][cmpName];
|
|
585
415
|
if (config) {
|
|
@@ -599,108 +429,86 @@ function processComponentConfig(myAppName: string, myAppNameDashed: string, cmp:
|
|
|
599
429
|
}
|
|
600
430
|
}
|
|
601
431
|
}
|
|
602
|
-
|
|
603
|
-
function getQueryAndVariables(type: string,
|
|
604
|
-
path: string,
|
|
605
|
-
selectedQuery?: SelectedQueryMaybeVariablesFunc,
|
|
606
|
-
context?: Context,
|
|
607
|
-
config?: any): QueryAndVariables | undefined {
|
|
608
|
-
|
|
432
|
+
function getQueryAndVariables(type, path, selectedQuery, context, config) {
|
|
609
433
|
let query, getVariables;
|
|
610
|
-
|
|
611
434
|
if (typeof selectedQuery === 'string' || typeof selectedQuery === 'function') {
|
|
612
435
|
query = selectedQuery;
|
|
613
|
-
|
|
614
|
-
|
|
436
|
+
}
|
|
437
|
+
else if (Array.isArray(selectedQuery)) {
|
|
615
438
|
query = selectedQuery[0];
|
|
616
439
|
getVariables = selectedQuery[1];
|
|
617
|
-
|
|
618
|
-
|
|
440
|
+
}
|
|
441
|
+
else if (typeof selectedQuery === 'object') {
|
|
619
442
|
query = selectedQuery.query;
|
|
620
443
|
getVariables = selectedQuery.variables;
|
|
621
444
|
}
|
|
622
|
-
|
|
623
445
|
if (getVariables && typeof getVariables !== 'function') {
|
|
624
446
|
throw Error(`getVariables for content type ${type} should be a function, not: ${typeof getVariables}`);
|
|
625
447
|
}
|
|
626
|
-
|
|
627
448
|
if (query && typeof query !== 'string' && typeof query !== 'function') {
|
|
628
449
|
throw Error(`Query for content type ${type} should be a string or function, not: ${typeof query}`);
|
|
629
450
|
}
|
|
630
|
-
|
|
631
451
|
if (typeof query === 'function') {
|
|
632
452
|
query = query(path, context, config);
|
|
633
453
|
}
|
|
634
|
-
|
|
635
454
|
if (query) {
|
|
636
455
|
return {
|
|
637
456
|
query,
|
|
638
|
-
variables: getVariables ? getVariables(path, context, config) : {path},
|
|
457
|
+
variables: getVariables ? getVariables(path, context, config) : { path },
|
|
639
458
|
};
|
|
640
459
|
}
|
|
641
460
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
function createPageData(contentType: string, components: PageComponent[], componentPath?: string): PageComponent | undefined {
|
|
461
|
+
function createPageData(contentType, components, componentPath) {
|
|
645
462
|
let page;
|
|
646
463
|
if (components && !componentPath) {
|
|
647
464
|
page = buildPage(contentType, components);
|
|
648
|
-
}
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
649
467
|
// Don't build page for single component
|
|
650
468
|
page = null;
|
|
651
469
|
}
|
|
652
|
-
return page
|
|
470
|
+
return page;
|
|
653
471
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
function createMetaData(contentType: string, contentPath: string,
|
|
657
|
-
requestType: XP_REQUEST_TYPE, renderMode: RENDER_MODE,
|
|
658
|
-
apiUrl: string, baseUrl: string,
|
|
659
|
-
locale: string, defaultLocale: string,
|
|
660
|
-
requestedComponentPath: string | undefined,
|
|
661
|
-
pageCmp?: PageComponent, components: PageComponent[] = []): MetaData {
|
|
472
|
+
function createMetaData(contentType, contentPath, requestType, renderMode, apiUrl, baseUrl, locale, defaultLocale, requestedComponentPath, pageCmp, components = []) {
|
|
473
|
+
var _a;
|
|
662
474
|
// .meta will be visible in final rendered inline props.
|
|
663
475
|
// Only adding some .meta attributes here on certain conditions
|
|
664
476
|
// (instead of always adding them and letting them be visible as false/undefined etc)
|
|
665
|
-
const meta
|
|
477
|
+
const meta = {
|
|
666
478
|
type: contentType,
|
|
667
479
|
path: contentPath,
|
|
668
480
|
requestType: requestType,
|
|
669
481
|
renderMode: renderMode,
|
|
670
482
|
canRender: false,
|
|
671
|
-
catchAll: false,
|
|
483
|
+
catchAll: false,
|
|
672
484
|
apiUrl,
|
|
673
485
|
baseUrl,
|
|
674
486
|
locale,
|
|
675
487
|
defaultLocale,
|
|
676
488
|
};
|
|
677
|
-
|
|
678
489
|
if (requestedComponentPath) {
|
|
679
490
|
meta.requestedComponent = components.find(cmp => cmp.path === requestedComponentPath);
|
|
680
491
|
}
|
|
681
|
-
|
|
682
|
-
const pageDesc = pageCmp?.page?.descriptor;
|
|
492
|
+
const pageDesc = (_a = pageCmp === null || pageCmp === void 0 ? void 0 : pageCmp.page) === null || _a === void 0 ? void 0 : _a.descriptor;
|
|
683
493
|
const typeDef = ComponentRegistry.getContentType(contentType);
|
|
684
|
-
if (typeDef
|
|
494
|
+
if ((typeDef === null || typeDef === void 0 ? void 0 : typeDef.view) && !typeDef.catchAll) {
|
|
685
495
|
meta.canRender = true;
|
|
686
|
-
}
|
|
496
|
+
}
|
|
497
|
+
else if (requestType === XP_REQUEST_TYPE.COMPONENT) {
|
|
687
498
|
// always render a single component (show missing if not implemented)
|
|
688
499
|
meta.canRender = true;
|
|
689
|
-
}
|
|
500
|
+
}
|
|
501
|
+
else if (pageDesc) {
|
|
690
502
|
// always render a page if there is a descriptor (show missing if not implemented)
|
|
691
503
|
meta.canRender = true;
|
|
692
|
-
}
|
|
504
|
+
}
|
|
505
|
+
else if (typeDef === null || typeDef === void 0 ? void 0 : typeDef.view) {
|
|
693
506
|
meta.canRender = true;
|
|
694
507
|
meta.catchAll = true;
|
|
695
508
|
}
|
|
696
|
-
|
|
697
509
|
return meta;
|
|
698
510
|
}
|
|
699
|
-
|
|
700
|
-
function errorResponse(code = '500', message = 'Unknown error',
|
|
701
|
-
requestType: XP_REQUEST_TYPE, renderMode: RENDER_MODE,
|
|
702
|
-
apiUrl: string, baseUrl: string, locale: string, defaultLocale: string,
|
|
703
|
-
contentPath?: string): FetchContentResult {
|
|
511
|
+
function errorResponse(code = '500', message = 'Unknown error', requestType, renderMode, apiUrl, baseUrl, locale, defaultLocale, contentPath) {
|
|
704
512
|
return {
|
|
705
513
|
error: {
|
|
706
514
|
code,
|
|
@@ -723,22 +531,18 @@ function errorResponse(code = '500', message = 'Unknown error',
|
|
|
723
531
|
},
|
|
724
532
|
};
|
|
725
533
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (!componentPath || !components?.length) {
|
|
534
|
+
function restrictComponentsToPath(contentType, components, componentPath) {
|
|
535
|
+
if (!componentPath || !(components === null || components === void 0 ? void 0 : components.length)) {
|
|
729
536
|
return components || [];
|
|
730
537
|
}
|
|
731
|
-
|
|
732
538
|
// filter components to the requested one only
|
|
733
539
|
const component = components.find(cmp => {
|
|
734
540
|
return cmp.path === componentPath;
|
|
735
541
|
});
|
|
736
|
-
|
|
737
542
|
if (!component) {
|
|
738
543
|
return [];
|
|
739
544
|
}
|
|
740
|
-
|
|
741
|
-
let result: PageComponent[] = [component];
|
|
545
|
+
let result = [component];
|
|
742
546
|
if (component.type !== XP_COMPONENT_TYPE.LAYOUT) {
|
|
743
547
|
// remember to include all parent layouts too !
|
|
744
548
|
const cmpPath = parseComponentPath(contentType, component.path);
|
|
@@ -749,7 +553,8 @@ function restrictComponentsToPath(contentType: string, components?: PageComponen
|
|
|
749
553
|
result.unshift(parentCmp);
|
|
750
554
|
}
|
|
751
555
|
}
|
|
752
|
-
}
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
753
558
|
// It's a layout, include child components
|
|
754
559
|
const childCmps = components.filter(cmp => cmp.path !== component.path && cmp.path.startsWith(component.path));
|
|
755
560
|
if (childCmps.length) {
|
|
@@ -758,40 +563,30 @@ function restrictComponentsToPath(contentType: string, components?: PageComponen
|
|
|
758
563
|
}
|
|
759
564
|
return result;
|
|
760
565
|
}
|
|
761
|
-
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
const getContentAndComponentPaths = (requestPath: string, context: Context): string[] => {
|
|
566
|
+
const COMPONENT_PATH_KEY = /\/?_\/component/; // first slash is optional for root components
|
|
567
|
+
const getContentAndComponentPaths = (requestPath, context) => {
|
|
765
568
|
let contentPath, componentPath;
|
|
766
569
|
if (COMPONENT_PATH_KEY.test(requestPath)) {
|
|
767
570
|
[contentPath, componentPath] = requestPath.split(COMPONENT_PATH_KEY);
|
|
768
|
-
}
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
769
573
|
contentPath = requestPath;
|
|
770
574
|
}
|
|
771
575
|
return [contentPath, componentPath];
|
|
772
576
|
};
|
|
773
|
-
|
|
774
577
|
// ///////////////////////////// ENTRY 1 - THE BUILDER:
|
|
775
|
-
|
|
776
578
|
/**
|
|
777
579
|
* Configures, builds and returns a general fetchContent function.
|
|
778
580
|
* @param config object containing ComponentRegistry as well as constants imported from enonic-connecion-config.js
|
|
779
581
|
* @returns ContentFetcher
|
|
780
582
|
*/
|
|
781
|
-
const buildContentFetcher =
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
APP_NAME_DASHED,
|
|
786
|
-
componentRegistry,
|
|
787
|
-
} = config;
|
|
788
|
-
|
|
789
|
-
return async (contentPathOrArray: string | string[], context: Context): Promise<FetchContentResult> => {
|
|
790
|
-
|
|
583
|
+
const buildContentFetcher = (config) => {
|
|
584
|
+
const { APP_NAME, APP_NAME_DASHED, componentRegistry, } = config;
|
|
585
|
+
return (contentPathOrArray, context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
586
|
+
var _a, _b;
|
|
791
587
|
if (context.preview) {
|
|
792
588
|
populateEnonicHeaders(context);
|
|
793
589
|
}
|
|
794
|
-
|
|
795
590
|
const headers = {};
|
|
796
591
|
addJsessionHeaders(headers, context);
|
|
797
592
|
addLocaleHeaders(headers, context);
|
|
@@ -800,91 +595,70 @@ const buildContentFetcher = <T extends AdapterConstants>(config: FetcherConfig<T
|
|
|
800
595
|
const projectConfig = getLocaleProjectConfig(context);
|
|
801
596
|
const renderMode = getRenderMode(context);
|
|
802
597
|
let requestType = XP_REQUEST_TYPE.TYPE;
|
|
803
|
-
|
|
804
598
|
UrlProcessor.setSiteKey(projectConfig.site);
|
|
805
|
-
|
|
806
599
|
try {
|
|
807
600
|
const requestContentPath = getCleanContentPathArrayOrThrow400(contentPathOrArray);
|
|
808
|
-
|
|
809
601
|
if ('/' + requestContentPath === PURGE_CACHE_URL) {
|
|
810
602
|
// return 404 for /_/cache/enonic/purge because it should've been handled by standalone endpoint
|
|
811
603
|
return errorResponse('404', 'Not found', requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, requestContentPath);
|
|
812
604
|
}
|
|
813
|
-
|
|
814
605
|
const [siteRelativeContentPath, componentPath] = getContentAndComponentPaths(requestContentPath, context);
|
|
815
606
|
if (componentPath) {
|
|
816
607
|
// set component request type because url contains component path
|
|
817
608
|
requestType = XP_REQUEST_TYPE.COMPONENT;
|
|
818
609
|
}
|
|
819
|
-
|
|
820
610
|
// ///////////// FIRST GUILLOTINE CALL FOR METADATA /////////////////
|
|
821
|
-
const metaResult =
|
|
611
|
+
const metaResult = yield fetchMetaData(contentApiUrl, '${site}/' + siteRelativeContentPath, projectConfig, headers);
|
|
822
612
|
// ///////////////////////////////////////////////////////////////////////
|
|
823
|
-
|
|
824
|
-
const {_path, type} = metaResult.meta || {};
|
|
613
|
+
const { _path, type } = metaResult.meta || {};
|
|
825
614
|
const contentPath = _path || siteRelativeContentPath;
|
|
826
|
-
|
|
827
615
|
if (metaResult.error) {
|
|
828
616
|
console.error(metaResult.error);
|
|
829
|
-
return errorResponse(metaResult.error.code, metaResult.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl,
|
|
830
|
-
context.locale, context.defaultLocale, contentPath);
|
|
617
|
+
return errorResponse(metaResult.error.code, metaResult.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
831
618
|
}
|
|
832
|
-
|
|
833
619
|
if (!metaResult.meta) {
|
|
834
|
-
return errorResponse('404', 'No meta data found for content, most likely content does not exist', requestType, renderMode,
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
return errorResponse('500', "Server responded with incomplete meta data: missing content 'type' attribute.", requestType,
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
} else if (renderMode === RENDER_MODE.NEXT && !IS_DEV_MODE &&
|
|
620
|
+
return errorResponse('404', 'No meta data found for content, most likely content does not exist', requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
621
|
+
}
|
|
622
|
+
else if (!type) {
|
|
623
|
+
return errorResponse('500', "Server responded with incomplete meta data: missing content 'type' attribute.", requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
624
|
+
}
|
|
625
|
+
else if (renderMode === RENDER_MODE.NEXT && !IS_DEV_MODE &&
|
|
841
626
|
(type === FRAGMENT_CONTENTTYPE_NAME ||
|
|
842
627
|
type === PAGE_TEMPLATE_CONTENTTYPE_NAME ||
|
|
843
628
|
type === PAGE_TEMPLATE_FOLDER)) {
|
|
844
|
-
return errorResponse('404', `Content type [${type}] is not accessible in ${renderMode} mode`, requestType, renderMode,
|
|
845
|
-
contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
629
|
+
return errorResponse('404', `Content type [${type}] is not accessible in ${renderMode} mode`, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
846
630
|
}
|
|
847
|
-
|
|
848
631
|
const components = restrictComponentsToPath(type, metaResult.meta.components, componentPath);
|
|
849
632
|
if (componentPath && !components.length) {
|
|
850
633
|
// component was not found
|
|
851
|
-
return errorResponse('404', `Component ${componentPath} was not found`, requestType, renderMode,
|
|
852
|
-
contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
634
|
+
return errorResponse('404', `Component ${componentPath} was not found`, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
853
635
|
}
|
|
854
|
-
|
|
855
636
|
if (requestType !== XP_REQUEST_TYPE.COMPONENT && components.length > 0) {
|
|
856
637
|
requestType = XP_REQUEST_TYPE.PAGE;
|
|
857
638
|
}
|
|
858
|
-
|
|
859
639
|
// ////////////////////////////////////////////////// Content type established. Proceed to data call:
|
|
860
|
-
|
|
861
|
-
const allDescriptors: ComponentDescriptor[] = [];
|
|
862
|
-
|
|
640
|
+
const allDescriptors = [];
|
|
863
641
|
// Add the content type query at all cases
|
|
864
|
-
const contentTypeDef = componentRegistry
|
|
642
|
+
const contentTypeDef = componentRegistry === null || componentRegistry === void 0 ? void 0 : componentRegistry.getContentType(type);
|
|
865
643
|
const pageCmp = (components || []).find(cmp => cmp.type === XP_COMPONENT_TYPE.PAGE);
|
|
866
644
|
if (pageCmp) {
|
|
867
645
|
processComponentConfig(APP_NAME, APP_NAME_DASHED, pageCmp);
|
|
868
646
|
}
|
|
869
|
-
|
|
870
|
-
const contentQueryAndVars = getQueryAndVariables(type, contentPath, contentTypeDef?.query, context, pageCmp?.page?.config);
|
|
647
|
+
const contentQueryAndVars = getQueryAndVariables(type, contentPath, contentTypeDef === null || contentTypeDef === void 0 ? void 0 : contentTypeDef.query, context, (_a = pageCmp === null || pageCmp === void 0 ? void 0 : pageCmp.page) === null || _a === void 0 ? void 0 : _a.config);
|
|
871
648
|
if (contentQueryAndVars) {
|
|
872
649
|
allDescriptors.push({
|
|
873
650
|
type: contentTypeDef,
|
|
874
651
|
queryAndVariables: contentQueryAndVars,
|
|
875
652
|
});
|
|
876
653
|
}
|
|
877
|
-
|
|
878
|
-
const commonQueryAndVars = getQueryAndVariables(type, contentPath, componentRegistry.getCommonQuery(), context,
|
|
879
|
-
pageCmp?.page?.config);
|
|
654
|
+
const commonQueryAndVars = getQueryAndVariables(type, contentPath, componentRegistry.getCommonQuery(), context, (_b = pageCmp === null || pageCmp === void 0 ? void 0 : pageCmp.page) === null || _b === void 0 ? void 0 : _b.config);
|
|
880
655
|
if (commonQueryAndVars) {
|
|
881
656
|
allDescriptors.push({
|
|
882
657
|
type: contentTypeDef,
|
|
883
658
|
queryAndVariables: commonQueryAndVars,
|
|
884
659
|
});
|
|
885
660
|
}
|
|
886
|
-
|
|
887
|
-
if (components?.length && componentRegistry) {
|
|
661
|
+
if ((components === null || components === void 0 ? void 0 : components.length) && componentRegistry) {
|
|
888
662
|
for (const cmp of (components || [])) {
|
|
889
663
|
processComponentConfig(APP_NAME, APP_NAME_DASHED, cmp);
|
|
890
664
|
}
|
|
@@ -894,29 +668,20 @@ const buildContentFetcher = <T extends AdapterConstants>(config: FetcherConfig<T
|
|
|
894
668
|
allDescriptors.push(...componentDescriptors);
|
|
895
669
|
}
|
|
896
670
|
}
|
|
897
|
-
|
|
898
|
-
const {query, variables} = combineMultipleQueries(allDescriptors);
|
|
899
|
-
|
|
671
|
+
const { query, variables } = combineMultipleQueries(allDescriptors);
|
|
900
672
|
if (!query.trim()) {
|
|
901
|
-
return errorResponse('400', `Missing or empty query override for content type ${type}`, requestType, renderMode,
|
|
902
|
-
contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
673
|
+
return errorResponse('400', `Missing or empty query override for content type ${type}`, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
903
674
|
}
|
|
904
|
-
|
|
905
675
|
// /////////////// SECOND GUILLOTINE CALL FOR DATA //////////////////////
|
|
906
|
-
const contentResults =
|
|
676
|
+
const contentResults = yield fetchContentData(contentApiUrl, contentPath, projectConfig, query, variables, headers);
|
|
907
677
|
// ///////////////////////////////////////////////////////////////////////////
|
|
908
|
-
|
|
909
678
|
if (contentResults.error) {
|
|
910
679
|
console.error(contentResults.error);
|
|
911
|
-
return errorResponse(contentResults.error.code, contentResults.error.message, requestType, renderMode, contentApiUrl,
|
|
912
|
-
xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
680
|
+
return errorResponse(contentResults.error.code, contentResults.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
913
681
|
}
|
|
914
|
-
|
|
915
682
|
// Apply processors to every component
|
|
916
|
-
const datas =
|
|
917
|
-
|
|
683
|
+
const datas = yield applyProcessors(allDescriptors, contentResults, context);
|
|
918
684
|
// Unwind the data back to components
|
|
919
|
-
|
|
920
685
|
let contentData = null, common = null;
|
|
921
686
|
let startFrom = 0;
|
|
922
687
|
if (contentQueryAndVars) {
|
|
@@ -929,7 +694,6 @@ const buildContentFetcher = <T extends AdapterConstants>(config: FetcherConfig<T
|
|
|
929
694
|
common = item.status === 'fulfilled' ? item.value : item.reason;
|
|
930
695
|
startFrom++;
|
|
931
696
|
}
|
|
932
|
-
|
|
933
697
|
for (let i = startFrom; i < datas.length; i++) {
|
|
934
698
|
// component descriptors hold references to components
|
|
935
699
|
// that will later be used for creating page regions
|
|
@@ -938,46 +702,42 @@ const buildContentFetcher = <T extends AdapterConstants>(config: FetcherConfig<T
|
|
|
938
702
|
let reason = datum.reason;
|
|
939
703
|
if (reason instanceof Error) {
|
|
940
704
|
reason = reason.message;
|
|
941
|
-
}
|
|
705
|
+
}
|
|
706
|
+
else if (typeof reason !== 'string') {
|
|
942
707
|
reason = String(reason);
|
|
943
708
|
}
|
|
944
709
|
allDescriptors[i].component.error = reason;
|
|
945
|
-
}
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
946
712
|
allDescriptors[i].component.data = datum.value;
|
|
947
713
|
}
|
|
948
714
|
}
|
|
949
|
-
|
|
950
715
|
const page = createPageData(type, components);
|
|
951
716
|
const meta = createMetaData(type, siteRelativeContentPath, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, componentPath, page, components);
|
|
952
|
-
|
|
953
717
|
return {
|
|
954
718
|
data: contentData,
|
|
955
719
|
common,
|
|
956
720
|
meta,
|
|
957
721
|
page,
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
catch (e) {
|
|
961
725
|
console.error(e);
|
|
962
|
-
|
|
963
726
|
let error;
|
|
964
727
|
try {
|
|
965
728
|
error = JSON.parse(e.message);
|
|
966
|
-
}
|
|
729
|
+
}
|
|
730
|
+
catch (e2) {
|
|
967
731
|
error = {
|
|
968
732
|
code: 'Local',
|
|
969
733
|
message: e.message,
|
|
970
734
|
};
|
|
971
735
|
}
|
|
972
|
-
return errorResponse(error.code, error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale,
|
|
973
|
-
context.defaultLocale, contentPathOrArray.toString());
|
|
736
|
+
return errorResponse(error.code, error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPathOrArray.toString());
|
|
974
737
|
}
|
|
975
|
-
};
|
|
738
|
+
});
|
|
976
739
|
};
|
|
977
|
-
|
|
978
|
-
|
|
979
740
|
// //////////////////////////// ENTRY 2: ready-to-use fetchContent function
|
|
980
|
-
|
|
981
741
|
/**
|
|
982
742
|
* Default fetchContent function, built with params from imports.
|
|
983
743
|
* It runs custom content-type-specific guillotine calls against an XP guillotine endpoint, returns content data, error and some meta data
|
|
@@ -986,16 +746,11 @@ const buildContentFetcher = <T extends AdapterConstants>(config: FetcherConfig<T
|
|
|
986
746
|
* @param context object from Next, contains .query info
|
|
987
747
|
* @returns FetchContentResult object: {data?: T, error?: {code, message}}
|
|
988
748
|
*/
|
|
989
|
-
export const fetchContent
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
export function createResponse(props: FetchContentResult, context?: Context, isStatic?: true): GetStaticPropsResult<FetchContentResult>;
|
|
995
|
-
export function createResponse(props: FetchContentResult, context?: Context, isStatic?: false): GetServerSidePropsResult<FetchContentResult>;
|
|
996
|
-
export function createResponse(props: FetchContentResult, context?: Context, isStatic = true): GetStaticPropsResult<FetchContentResult> | GetServerSidePropsResult<FetchContentResult> {
|
|
997
|
-
const {data, meta, error} = props;
|
|
998
|
-
const pageUrl = data?.get?.data?.target?.pageUrl;
|
|
749
|
+
export const fetchContent = buildContentFetcher(Object.assign(Object.assign({}, adapterConstants), { componentRegistry: ComponentRegistry }));
|
|
750
|
+
export function createResponse(props, context, isStatic = true) {
|
|
751
|
+
var _a, _b, _c, _d;
|
|
752
|
+
const { data, meta, error } = props;
|
|
753
|
+
const pageUrl = (_c = (_b = (_a = data === null || data === void 0 ? void 0 : data.get) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.target) === null || _c === void 0 ? void 0 : _c.pageUrl;
|
|
999
754
|
if (meta.type === 'base:shortcut' && pageUrl) {
|
|
1000
755
|
if (meta.renderMode !== RENDER_MODE.NEXT) {
|
|
1001
756
|
// console.debug(`Returning 404 for shortcut in ${RENDER_MODE.NEXT} mode`);
|
|
@@ -1004,7 +759,6 @@ export function createResponse(props: FetchContentResult, context?: Context, isS
|
|
|
1004
759
|
notFound: true,
|
|
1005
760
|
};
|
|
1006
761
|
}
|
|
1007
|
-
|
|
1008
762
|
const destination = getUrl(pageUrl, meta, true);
|
|
1009
763
|
// console.debug('Redirecting to', destination);
|
|
1010
764
|
return {
|
|
@@ -1014,66 +768,62 @@ export function createResponse(props: FetchContentResult, context?: Context, isS
|
|
|
1014
768
|
},
|
|
1015
769
|
};
|
|
1016
770
|
}
|
|
1017
|
-
|
|
1018
771
|
// we can not set 418 for static paths,
|
|
1019
772
|
// but we can show 404 instead to be handled in CS
|
|
1020
773
|
const canNotRender = meta && !meta.canRender && meta.renderMode !== RENDER_MODE.EDIT;
|
|
1021
|
-
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
const notFound = (error && error.code === '404') || context.res?.statusCode === 404 || canNotRender || catchAllInNextProdMode || undefined;
|
|
1025
|
-
|
|
774
|
+
const catchAllInNextProdMode = (meta === null || meta === void 0 ? void 0 : meta.renderMode) === RENDER_MODE.NEXT && !IS_DEV_MODE && (meta === null || meta === void 0 ? void 0 : meta.catchAll);
|
|
775
|
+
const notFound = (error && error.code === '404') || ((_d = context.res) === null || _d === void 0 ? void 0 : _d.statusCode) === 404 || canNotRender || catchAllInNextProdMode || undefined;
|
|
1026
776
|
// console.debug('Returning view ' + (notFound ? '(not found)' : ''));
|
|
1027
|
-
const result
|
|
777
|
+
const result = {
|
|
1028
778
|
notFound,
|
|
1029
779
|
props,
|
|
1030
780
|
};
|
|
1031
|
-
|
|
1032
781
|
if (isStatic) {
|
|
1033
|
-
|
|
782
|
+
result.revalidate = 3600; // In seconds, meaning every hour
|
|
1034
783
|
}
|
|
1035
|
-
|
|
1036
784
|
return result;
|
|
1037
785
|
}
|
|
1038
|
-
|
|
1039
|
-
function populateEnonicHeaders(context: Context) {
|
|
786
|
+
function populateEnonicHeaders(context) {
|
|
1040
787
|
const pd = context.previewData;
|
|
1041
788
|
if (!pd) {
|
|
1042
789
|
return;
|
|
1043
790
|
}
|
|
1044
|
-
const req = context.req || {}
|
|
791
|
+
const req = context.req || {};
|
|
1045
792
|
req.headers = Object.assign(req.headers || {}, pd.headers);
|
|
1046
793
|
context.params = Object.assign(context.params || {}, pd.params);
|
|
1047
794
|
context.req = req;
|
|
1048
795
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
796
|
+
export function fetchContentPathsForAllLocales(path, query = GET_STATIC_PATHS_QUERY, countPerLocale = 999) {
|
|
797
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
798
|
+
const promises = Object.values(getLocaleProjectConfigs()).map(config => fetchContentPathsForLocale(path, config, query, countPerLocale));
|
|
799
|
+
return Promise.all(promises).then(results => {
|
|
800
|
+
return results.reduce((all, localePaths) => all.concat(localePaths), []);
|
|
801
|
+
});
|
|
1054
802
|
});
|
|
1055
803
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
804
|
+
export function fetchContentPathsForLocale(path, config, query = GET_STATIC_PATHS_QUERY, count = 999) {
|
|
805
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
806
|
+
const contentApiUrl = getContentApiUrl({ locale: config.locale });
|
|
807
|
+
const body = {
|
|
808
|
+
query,
|
|
809
|
+
variables: {
|
|
810
|
+
path,
|
|
811
|
+
count,
|
|
812
|
+
},
|
|
813
|
+
};
|
|
814
|
+
return fetchGuillotine(contentApiUrl, body, config).then((results) => {
|
|
815
|
+
return results.guillotine.queryDsl.reduce((prev, child) => {
|
|
816
|
+
var _a;
|
|
817
|
+
const regexp = new RegExp(`/${(_a = child.site) === null || _a === void 0 ? void 0 : _a._name}/?`);
|
|
818
|
+
const contentPath = child._path.replace(regexp, '');
|
|
819
|
+
prev.push({
|
|
820
|
+
params: {
|
|
821
|
+
contentPath: contentPath.split('/'),
|
|
822
|
+
},
|
|
823
|
+
locale: normalizeLocale(config.locale),
|
|
824
|
+
});
|
|
825
|
+
return prev;
|
|
826
|
+
}, []);
|
|
827
|
+
});
|
|
1078
828
|
});
|
|
1079
829
|
}
|