@enonic/nextjs-adapter 0.12.0 → 1.0.0-RC1
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 +4 -2
- package/ComponentRegistry.js +6 -0
- package/README.md +623 -8
- package/UrlProcessor.d.ts +2 -1
- package/UrlProcessor.js +26 -1
- package/guillotine/fetchContent.d.ts +2 -21
- package/guillotine/fetchContent.js +128 -58
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/package.json +16 -15
- package/server/ClearableFileCache.js +314 -0
- package/server/cache.js +129 -0
- package/server/index.js +114 -0
- package/utils.d.ts +3 -12
- package/utils.js +10 -23
- package/views/Region.jsx +1 -6
- package/views/RichTextView.jsx +11 -4
- package/views/StaticContent.d.ts +15 -0
- package/views/StaticContent.jsx +38 -0
package/UrlProcessor.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { commonChars, RENDER_MODE, SITE_KEY } from './utils';
|
|
2
|
+
import { useRouter } from 'next/router';
|
|
2
3
|
export class UrlProcessor {
|
|
3
4
|
static process(url, meta) {
|
|
4
5
|
let result;
|
|
@@ -9,7 +10,17 @@ export class UrlProcessor {
|
|
|
9
10
|
else {
|
|
10
11
|
const apiUrl = this.getApiUrl(meta);
|
|
11
12
|
const strippedUrl = this.stripApiUrl(url, apiUrl);
|
|
12
|
-
|
|
13
|
+
let basePath;
|
|
14
|
+
// only add basePath in next
|
|
15
|
+
if (meta.renderMode === RENDER_MODE.NEXT) {
|
|
16
|
+
try {
|
|
17
|
+
basePath = useRouter().basePath;
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.error('Error getting basePath from nextjs router', e);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
result = (basePath && basePath !== '/' ? basePath : '') + ((meta === null || meta === void 0 ? void 0 : meta.baseUrl) || '') + strippedUrl;
|
|
13
24
|
}
|
|
14
25
|
return result;
|
|
15
26
|
}
|
|
@@ -24,6 +35,20 @@ export class UrlProcessor {
|
|
|
24
35
|
var _a;
|
|
25
36
|
return ((_a = linkData.find(data => data.ref === ref)) === null || _a === void 0 ? void 0 : _a.image) !== null;
|
|
26
37
|
}
|
|
38
|
+
static processSrcSet(srcset, meta) {
|
|
39
|
+
return srcset.split(/, */g).map(src => {
|
|
40
|
+
const srcParts = src.trim().split(' ');
|
|
41
|
+
switch (srcParts.length) {
|
|
42
|
+
case 1:
|
|
43
|
+
return this.process(src, meta);
|
|
44
|
+
case 2:
|
|
45
|
+
return this.process(srcParts[0], meta) + ' ' + srcParts[1];
|
|
46
|
+
default:
|
|
47
|
+
console.warn('Can not process image srcset: ' + src);
|
|
48
|
+
return src;
|
|
49
|
+
}
|
|
50
|
+
}).join(', ');
|
|
51
|
+
}
|
|
27
52
|
static stripApiUrl(url, apiUrl) {
|
|
28
53
|
// normalise localhost-127.0.0.1 if present in urls
|
|
29
54
|
const normalUrl = url.replace(this.localhostPattern, '127.0.0.1');
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import { MetaData, PageComponent } from './getMetaData';
|
|
4
|
-
import { RENDER_MODE, XP_REQUEST_TYPE } from '../utils';
|
|
5
|
-
import { ComponentRegistry } from '../ComponentRegistry';
|
|
6
4
|
import { ParsedUrlQuery } from 'node:querystring';
|
|
7
5
|
import { type GetServerSidePropsContext } from 'next';
|
|
8
6
|
import { IncomingMessage } from 'http';
|
|
9
7
|
import { type NextApiRequestCookies } from 'next/dist/server/api-utils';
|
|
10
|
-
export type AdapterConstants = {
|
|
11
|
-
APP_NAME: string;
|
|
12
|
-
APP_NAME_DASHED: string;
|
|
13
|
-
SITE_KEY: string;
|
|
14
|
-
getXPRequestType: (context?: Context) => XP_REQUEST_TYPE;
|
|
15
|
-
getRenderMode: (context?: Context) => RENDER_MODE;
|
|
16
|
-
getSingleComponentPath: (context?: Context) => string | undefined;
|
|
17
|
-
};
|
|
18
8
|
type Result = {
|
|
19
9
|
error?: {
|
|
20
10
|
code: string;
|
|
@@ -30,9 +20,6 @@ export type FetchContentResult = Result & {
|
|
|
30
20
|
meta: MetaData;
|
|
31
21
|
page: PageComponent | null;
|
|
32
22
|
};
|
|
33
|
-
type FetcherConfig<T extends AdapterConstants> = T & {
|
|
34
|
-
componentRegistry: typeof ComponentRegistry;
|
|
35
|
-
};
|
|
36
23
|
export interface ServerSideParams extends ParsedUrlQuery {
|
|
37
24
|
contentPath?: string[];
|
|
38
25
|
mode?: string;
|
|
@@ -40,6 +27,7 @@ export interface ServerSideParams extends ParsedUrlQuery {
|
|
|
40
27
|
export interface PreviewParams {
|
|
41
28
|
contentPath: string[];
|
|
42
29
|
headers: Record<string, string>;
|
|
30
|
+
params: Record<string, string>;
|
|
43
31
|
}
|
|
44
32
|
export type Context = GetServerSidePropsContext<ServerSideParams, PreviewParams>;
|
|
45
33
|
export interface MinimalContext {
|
|
@@ -62,14 +50,7 @@ export type ContentApiBaseBody = {
|
|
|
62
50
|
/** Generic fetch */
|
|
63
51
|
export declare const fetchFromApi: (apiUrl: string, body: ContentApiBaseBody, headers?: {}, method?: string) => Promise<any>;
|
|
64
52
|
/** Guillotine-specialized fetch, using the generic fetch above */
|
|
65
|
-
export declare const fetchGuillotine: (contentApiUrl: string, body: ContentApiBaseBody,
|
|
66
|
-
/**
|
|
67
|
-
* Configures, builds and returns a general fetchContent function.
|
|
68
|
-
* @param adapterConstants Object containing attributes imported from enonic-connecion-config.js: constants and function concerned with connection to the XP backend. Easiest: caller imports enonic-connection-config and just passes that entire object here as adapterConstants.
|
|
69
|
-
* @param componentRegistry ComponentRegistry object from ComponentRegistry.ts, holding user type mappings that are set in typesRegistration.ts file
|
|
70
|
-
* @returns ContentFetcher
|
|
71
|
-
*/
|
|
72
|
-
export declare const buildContentFetcher: <T extends AdapterConstants>(config: FetcherConfig<T>) => ContentFetcher;
|
|
53
|
+
export declare const fetchGuillotine: (contentApiUrl: string, body: ContentApiBaseBody, headers?: {}) => Promise<GuillotineResult>;
|
|
73
54
|
/**
|
|
74
55
|
* Default fetchContent function, built with params from imports.
|
|
75
56
|
* It runs custom content-type-specific guillotine calls against an XP guillotine endpoint, returns content data, error and some meta data
|
|
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { getMetaQuery, pageFragmentQuery } from './getMetaData';
|
|
11
|
-
import adapterConstants, { APP_NAME, APP_NAME_DASHED, FRAGMENT_CONTENTTYPE_NAME, FRAGMENT_DEFAULT_REGION_NAME, getContentApiUrl, getXpBaseUrl, IS_DEV_MODE,
|
|
11
|
+
import adapterConstants, { APP_NAME, APP_NAME_DASHED, FRAGMENT_CONTENTTYPE_NAME, FRAGMENT_DEFAULT_REGION_NAME, getContentApiUrl, getJsessionHeaders, getRenderMode, getXpBaseUrl, IS_DEV_MODE, PAGE_TEMPLATE_CONTENTTYPE_NAME, PAGE_TEMPLATE_FOLDER, RENDER_MODE, sanitizeGraphqlName, SITE_KEY, XP_COMPONENT_TYPE, XP_REQUEST_TYPE, } from '../utils';
|
|
12
12
|
import { ComponentRegistry } from '../ComponentRegistry';
|
|
13
13
|
const NO_PROPS_PROCESSOR = (props) => __awaiter(void 0, void 0, void 0, function* () { var _a; return (_a = yield props) !== null && _a !== void 0 ? _a : {}; });
|
|
14
14
|
const ALIAS_PREFIX = 'request';
|
|
@@ -64,7 +64,8 @@ export const fetchFromApi = (apiUrl, body, headers, method = 'POST') => __awaite
|
|
|
64
64
|
return json;
|
|
65
65
|
});
|
|
66
66
|
/** Guillotine-specialized fetch, using the generic fetch above */
|
|
67
|
-
export const fetchGuillotine = (contentApiUrl, body,
|
|
67
|
+
export const fetchGuillotine = (contentApiUrl, body, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
68
|
+
var _b;
|
|
68
69
|
if (typeof body.query !== 'string' || !body.query.trim()) {
|
|
69
70
|
return {
|
|
70
71
|
error: {
|
|
@@ -73,6 +74,8 @@ export const fetchGuillotine = (contentApiUrl, body, xpContentPath, headers) =>
|
|
|
73
74
|
},
|
|
74
75
|
};
|
|
75
76
|
}
|
|
77
|
+
const path = (_b = body.variables) === null || _b === void 0 ? void 0 : _b.path;
|
|
78
|
+
const pathMessage = path ? `(path = ${JSON.stringify(path)})` : '';
|
|
76
79
|
const result = yield fetchFromApi(contentApiUrl, body, headers)
|
|
77
80
|
.then(json => {
|
|
78
81
|
let errors = (json || {}).errors;
|
|
@@ -80,7 +83,7 @@ export const fetchGuillotine = (contentApiUrl, body, xpContentPath, headers) =>
|
|
|
80
83
|
if (!Array.isArray(errors)) {
|
|
81
84
|
errors = [errors];
|
|
82
85
|
}
|
|
83
|
-
console.warn(`${errors.length} error(s) when trying to fetch data
|
|
86
|
+
console.warn(`${errors.length} error(s) when trying to fetch data ${pathMessage}:`);
|
|
84
87
|
errors.forEach(error => {
|
|
85
88
|
console.error(error);
|
|
86
89
|
});
|
|
@@ -96,7 +99,7 @@ export const fetchGuillotine = (contentApiUrl, body, xpContentPath, headers) =>
|
|
|
96
99
|
return json.data;
|
|
97
100
|
})
|
|
98
101
|
.catch((err) => {
|
|
99
|
-
console.warn(`Client-side error when trying to fetch data
|
|
102
|
+
console.warn(`Client-side error when trying to fetch data ${pathMessage}`, err);
|
|
100
103
|
try {
|
|
101
104
|
return { error: JSON.parse(err.message) };
|
|
102
105
|
}
|
|
@@ -108,20 +111,20 @@ export const fetchGuillotine = (contentApiUrl, body, xpContentPath, headers) =>
|
|
|
108
111
|
});
|
|
109
112
|
// /////////////////////////////////////////////////////////////////////////////// Specific fetch wrappers:
|
|
110
113
|
const fetchMetaData = (contentApiUrl, xpContentPath, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
111
|
-
var
|
|
114
|
+
var _c;
|
|
112
115
|
const body = {
|
|
113
116
|
query: getMetaQuery(pageFragmentQuery()),
|
|
114
117
|
variables: {
|
|
115
118
|
path: xpContentPath,
|
|
116
119
|
},
|
|
117
120
|
};
|
|
118
|
-
const metaResult = yield fetchGuillotine(contentApiUrl, body,
|
|
121
|
+
const metaResult = yield fetchGuillotine(contentApiUrl, body, headers);
|
|
119
122
|
if (metaResult.error) {
|
|
120
123
|
return metaResult;
|
|
121
124
|
}
|
|
122
125
|
else {
|
|
123
126
|
return {
|
|
124
|
-
meta: (
|
|
127
|
+
meta: (_c = metaResult === null || metaResult === void 0 ? void 0 : metaResult.guillotine) === null || _c === void 0 ? void 0 : _c.get,
|
|
125
128
|
};
|
|
126
129
|
}
|
|
127
130
|
});
|
|
@@ -130,7 +133,7 @@ const fetchContentData = (contentApiUrl, xpContentPath, query, variables, header
|
|
|
130
133
|
if (variables && Object.keys(variables).length > 0) {
|
|
131
134
|
body.variables = variables;
|
|
132
135
|
}
|
|
133
|
-
const contentResults = yield fetchGuillotine(contentApiUrl, body,
|
|
136
|
+
const contentResults = yield fetchGuillotine(contentApiUrl, body, headers);
|
|
134
137
|
if (contentResults.error) {
|
|
135
138
|
return contentResults;
|
|
136
139
|
}
|
|
@@ -230,27 +233,40 @@ function prefixLayoutPath(contentType, path) {
|
|
|
230
233
|
}
|
|
231
234
|
}
|
|
232
235
|
function buildPage(contentType, comps = []) {
|
|
233
|
-
|
|
236
|
+
const page = {
|
|
234
237
|
type: XP_COMPONENT_TYPE.PAGE,
|
|
235
238
|
path: '/',
|
|
236
239
|
};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
+
return buildComponentTree(comps, page, contentType);
|
|
241
|
+
}
|
|
242
|
+
function buildComponentTree(comps, rootComp, contentType) {
|
|
243
|
+
const rootTree = {};
|
|
244
|
+
const isFragmentType = contentType === FRAGMENT_CONTENTTYPE_NAME;
|
|
245
|
+
if (isFragmentType) {
|
|
246
|
+
rootComp.regions = rootTree;
|
|
240
247
|
}
|
|
241
248
|
comps.forEach(cmp => {
|
|
242
249
|
var _a;
|
|
243
250
|
const cmpPath = parseComponentPath(contentType, cmp.path);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
251
|
+
if (cmp.path === '/') {
|
|
252
|
+
if (cmp.type === XP_COMPONENT_TYPE.PAGE && rootComp.type === XP_COMPONENT_TYPE.PAGE) {
|
|
253
|
+
// add page values to page object
|
|
254
|
+
rootComp = Object.assign(rootComp, cmp);
|
|
255
|
+
rootComp.page.regions = rootTree;
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
else if (cmp.type === XP_COMPONENT_TYPE.LAYOUT && rootComp.type === XP_COMPONENT_TYPE.FRAGMENT) {
|
|
259
|
+
// when rendering fragment as part of the page with layout at root path
|
|
260
|
+
// remove other children from list as they will be included in layout
|
|
261
|
+
rootComp.fragment.fragment.components = [cmp];
|
|
262
|
+
cmp.regions = rootTree;
|
|
263
|
+
}
|
|
250
264
|
}
|
|
251
|
-
|
|
252
|
-
|
|
265
|
+
if (cmp.type === XP_COMPONENT_TYPE.FRAGMENT) {
|
|
266
|
+
// build component subtree for fragment
|
|
267
|
+
cmp = buildComponentTree(cmp.fragment.fragment.components, cmp, contentType);
|
|
253
268
|
}
|
|
269
|
+
const region = getParentRegion(rootTree, contentType, cmpPath, comps, true);
|
|
254
270
|
if (region) {
|
|
255
271
|
// getting the index of component from string like '/main/0/left/11'
|
|
256
272
|
const cmpIndex = (_a = cmpPath[cmpPath.length - 1]) === null || _a === void 0 ? void 0 : _a.index;
|
|
@@ -262,7 +278,7 @@ function buildPage(contentType, comps = []) {
|
|
|
262
278
|
}
|
|
263
279
|
}
|
|
264
280
|
});
|
|
265
|
-
return
|
|
281
|
+
return rootComp;
|
|
266
282
|
}
|
|
267
283
|
function combineMultipleQueries(queriesWithVars) {
|
|
268
284
|
const queries = [];
|
|
@@ -342,12 +358,17 @@ function applyProcessors(componentDescriptors, contentResults, context) {
|
|
|
342
358
|
// if there is a query then there must be a result for it
|
|
343
359
|
data = contentResults.contents[dataCounter++];
|
|
344
360
|
}
|
|
345
|
-
|
|
361
|
+
const config = getComponentConfig(desc.component);
|
|
362
|
+
return yield propsProcessor(data, context, config);
|
|
346
363
|
}));
|
|
347
364
|
return Promise.allSettled(processorPromises);
|
|
348
365
|
});
|
|
349
366
|
}
|
|
350
|
-
function
|
|
367
|
+
function getComponentConfig(cmp) {
|
|
368
|
+
const cmpData = cmp && cmp[cmp.type];
|
|
369
|
+
return cmpData && 'config' in cmpData ? cmpData.config : undefined;
|
|
370
|
+
}
|
|
371
|
+
function collectComponentDescriptors(components, componentRegistry, xpContentPath, context) {
|
|
351
372
|
const descriptors = [];
|
|
352
373
|
for (const cmp of (components || [])) {
|
|
353
374
|
processComponentConfig(APP_NAME, APP_NAME_DASHED, cmp);
|
|
@@ -357,8 +378,7 @@ function collectComponentDescriptors(components, componentRegistry, requestedCom
|
|
|
357
378
|
const cmpDef = ComponentRegistry.getByComponent(cmp);
|
|
358
379
|
if (cmpDef) {
|
|
359
380
|
// const partPath = `${xpContentPath}/_component${cmp.path}`;
|
|
360
|
-
const
|
|
361
|
-
const config = cmpData && 'config' in cmpData ? cmpData.config : undefined;
|
|
381
|
+
const config = getComponentConfig(cmp);
|
|
362
382
|
const queryAndVariables = getQueryAndVariables(cmp.type, xpContentPath, cmpDef.query, context, config);
|
|
363
383
|
if (queryAndVariables) {
|
|
364
384
|
descriptors.push({
|
|
@@ -371,7 +391,7 @@ function collectComponentDescriptors(components, componentRegistry, requestedCom
|
|
|
371
391
|
}
|
|
372
392
|
else {
|
|
373
393
|
// look for parts inside fragments
|
|
374
|
-
const fragPartDescs = collectComponentDescriptors(cmp.fragment.fragment.components, componentRegistry,
|
|
394
|
+
const fragPartDescs = collectComponentDescriptors(cmp.fragment.fragment.components, componentRegistry, xpContentPath, context);
|
|
375
395
|
if (fragPartDescs.length) {
|
|
376
396
|
descriptors.push(...fragPartDescs);
|
|
377
397
|
}
|
|
@@ -427,7 +447,7 @@ function getQueryAndVariables(type, path, selectedQuery, context, config) {
|
|
|
427
447
|
throw Error(`Query for content type ${type} should be a string or function, not: ${typeof query}`);
|
|
428
448
|
}
|
|
429
449
|
if (typeof query === 'function') {
|
|
430
|
-
query = query(config);
|
|
450
|
+
query = query(path, context, config);
|
|
431
451
|
}
|
|
432
452
|
if (query) {
|
|
433
453
|
return {
|
|
@@ -436,11 +456,15 @@ function getQueryAndVariables(type, path, selectedQuery, context, config) {
|
|
|
436
456
|
};
|
|
437
457
|
}
|
|
438
458
|
}
|
|
439
|
-
function createPageData(contentType, components) {
|
|
459
|
+
function createPageData(contentType, components, componentPath) {
|
|
440
460
|
let page;
|
|
441
|
-
if (components) {
|
|
461
|
+
if (components && !componentPath) {
|
|
442
462
|
page = buildPage(contentType, components);
|
|
443
463
|
}
|
|
464
|
+
else {
|
|
465
|
+
// Don't build page for single component
|
|
466
|
+
page = null;
|
|
467
|
+
}
|
|
444
468
|
return page;
|
|
445
469
|
}
|
|
446
470
|
function createMetaData(contentType, contentPath, requestType, renderMode, apiUrl, baseUrl, requestedComponentPath, pageCmp, components = []) {
|
|
@@ -466,10 +490,13 @@ function createMetaData(contentType, contentPath, requestType, renderMode, apiUr
|
|
|
466
490
|
if ((typeDef === null || typeDef === void 0 ? void 0 : typeDef.view) && !typeDef.catchAll) {
|
|
467
491
|
meta.canRender = true;
|
|
468
492
|
}
|
|
493
|
+
else if (requestType === XP_REQUEST_TYPE.COMPONENT) {
|
|
494
|
+
// always render a single component (show missing if not implemented)
|
|
495
|
+
meta.canRender = true;
|
|
496
|
+
}
|
|
469
497
|
else if (pageDesc) {
|
|
470
|
-
// always render a page if there is a descriptor (show missing
|
|
498
|
+
// always render a page if there is a descriptor (show missing if not implemented)
|
|
471
499
|
meta.canRender = true;
|
|
472
|
-
meta.catchAll = false; // catchAll only refers to content type catch-all
|
|
473
500
|
}
|
|
474
501
|
else if (typeDef === null || typeDef === void 0 ? void 0 : typeDef.view) {
|
|
475
502
|
meta.canRender = true;
|
|
@@ -498,40 +525,76 @@ function errorResponse(code = '500', message = 'Unknown error', requestType, ren
|
|
|
498
525
|
},
|
|
499
526
|
};
|
|
500
527
|
}
|
|
528
|
+
function restrictComponentsToPath(contentType, components, componentPath) {
|
|
529
|
+
if (!componentPath || !(components === null || components === void 0 ? void 0 : components.length)) {
|
|
530
|
+
return components || [];
|
|
531
|
+
}
|
|
532
|
+
// filter components to the requested one only
|
|
533
|
+
const component = components.find(cmp => {
|
|
534
|
+
return cmp.path === componentPath;
|
|
535
|
+
});
|
|
536
|
+
if (!component) {
|
|
537
|
+
return [];
|
|
538
|
+
}
|
|
539
|
+
let result = [component];
|
|
540
|
+
if (component.type !== XP_COMPONENT_TYPE.LAYOUT) {
|
|
541
|
+
// remember to include all parent layouts too !
|
|
542
|
+
const cmpPath = parseComponentPath(contentType, component.path);
|
|
543
|
+
for (let i = cmpPath.length - 2; i >= 0; i--) {
|
|
544
|
+
const parentPath = cmpPath[i];
|
|
545
|
+
const parentCmp = components.find(cmp => cmp.path === `/${parentPath.region}/${parentPath.index}`);
|
|
546
|
+
if (parentCmp) {
|
|
547
|
+
result.unshift(parentCmp);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
// It's a layout, include child components
|
|
553
|
+
const childCmps = components.filter(cmp => cmp.path !== component.path && cmp.path.startsWith(component.path));
|
|
554
|
+
if (childCmps.length) {
|
|
555
|
+
result = result.concat(childCmps);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
const COMPONENT_PATH_KEY = /\/?_\/component/; // first slash is optional for root components
|
|
561
|
+
const getContentAndComponentPaths = (requestPath, context) => {
|
|
562
|
+
let contentPath, componentPath;
|
|
563
|
+
if (COMPONENT_PATH_KEY.test(requestPath)) {
|
|
564
|
+
[contentPath, componentPath] = requestPath.split(COMPONENT_PATH_KEY);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
contentPath = requestPath;
|
|
568
|
+
}
|
|
569
|
+
return [contentPath, componentPath];
|
|
570
|
+
};
|
|
501
571
|
// ///////////////////////////// ENTRY 1 - THE BUILDER:
|
|
502
572
|
/**
|
|
503
573
|
* Configures, builds and returns a general fetchContent function.
|
|
504
|
-
* @param
|
|
505
|
-
* @param componentRegistry ComponentRegistry object from ComponentRegistry.ts, holding user type mappings that are set in typesRegistration.ts file
|
|
574
|
+
* @param config object containing ComponentRegistry as well as constants imported from enonic-connecion-config.js
|
|
506
575
|
* @returns ContentFetcher
|
|
507
576
|
*/
|
|
508
|
-
|
|
509
|
-
const { APP_NAME, APP_NAME_DASHED,
|
|
577
|
+
const buildContentFetcher = (config) => {
|
|
578
|
+
const { APP_NAME, APP_NAME_DASHED, componentRegistry, } = config;
|
|
510
579
|
return (contentPathOrArray, context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
511
|
-
var _a, _b
|
|
512
|
-
|
|
513
|
-
const jsessionid = (_a = context === null || context === void 0 ? void 0 : context.req) === null || _a === void 0 ? void 0 : _a.headers[JSESSIONID_HEADER];
|
|
514
|
-
if (jsessionid) {
|
|
515
|
-
headers = {
|
|
516
|
-
'Cookie': `${JSESSIONID_HEADER}=${jsessionid}`,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
580
|
+
var _a, _b;
|
|
581
|
+
const headers = getJsessionHeaders(context);
|
|
519
582
|
const xpBaseUrl = getXpBaseUrl(context);
|
|
520
583
|
const contentApiUrl = getContentApiUrl(context);
|
|
521
|
-
const requestType = getXPRequestType(context);
|
|
522
584
|
const renderMode = getRenderMode(context);
|
|
523
|
-
let
|
|
585
|
+
let requestType = XP_REQUEST_TYPE.TYPE;
|
|
524
586
|
try {
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
if (
|
|
528
|
-
|
|
587
|
+
const requestContentPath = getCleanContentPathArrayOrThrow400(contentPathOrArray);
|
|
588
|
+
const [siteRelativeContentPath, componentPath] = getContentAndComponentPaths(requestContentPath, context);
|
|
589
|
+
if (componentPath) {
|
|
590
|
+
// set component request type because url contains component path
|
|
591
|
+
requestType = XP_REQUEST_TYPE.COMPONENT;
|
|
529
592
|
}
|
|
530
593
|
// ///////////// FIRST GUILLOTINE CALL FOR METADATA /////////////////
|
|
531
594
|
const metaResult = yield fetchMetaData(contentApiUrl, '${site}/' + siteRelativeContentPath, headers);
|
|
532
595
|
// ///////////////////////////////////////////////////////////////////////
|
|
533
|
-
const {
|
|
534
|
-
contentPath = _path ||
|
|
596
|
+
const { _path, type } = metaResult.meta || {};
|
|
597
|
+
const contentPath = _path || siteRelativeContentPath;
|
|
535
598
|
if (metaResult.error) {
|
|
536
599
|
console.error(metaResult.error);
|
|
537
600
|
return errorResponse(metaResult.error.code, metaResult.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
@@ -548,6 +611,14 @@ export const buildContentFetcher = (config) => {
|
|
|
548
611
|
type === PAGE_TEMPLATE_FOLDER)) {
|
|
549
612
|
return errorResponse('404', `Content type [${type}] is not accessible in ${renderMode} mode`, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
550
613
|
}
|
|
614
|
+
const components = restrictComponentsToPath(type, metaResult.meta.components, componentPath);
|
|
615
|
+
if (componentPath && !components.length) {
|
|
616
|
+
// component was not found
|
|
617
|
+
return errorResponse('404', `Component ${componentPath} was not found`, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
618
|
+
}
|
|
619
|
+
if (requestType !== XP_REQUEST_TYPE.COMPONENT && components.length > 0) {
|
|
620
|
+
requestType = XP_REQUEST_TYPE.PAGE;
|
|
621
|
+
}
|
|
551
622
|
// ////////////////////////////////////////////////// Content type established. Proceed to data call:
|
|
552
623
|
const allDescriptors = [];
|
|
553
624
|
// Add the content type query at all cases
|
|
@@ -556,14 +627,14 @@ export const buildContentFetcher = (config) => {
|
|
|
556
627
|
if (pageCmp) {
|
|
557
628
|
processComponentConfig(APP_NAME, APP_NAME_DASHED, pageCmp);
|
|
558
629
|
}
|
|
559
|
-
const contentQueryAndVars = getQueryAndVariables(type, contentPath, contentTypeDef === null || contentTypeDef === void 0 ? void 0 : contentTypeDef.query, context, (
|
|
630
|
+
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);
|
|
560
631
|
if (contentQueryAndVars) {
|
|
561
632
|
allDescriptors.push({
|
|
562
633
|
type: contentTypeDef,
|
|
563
634
|
queryAndVariables: contentQueryAndVars,
|
|
564
635
|
});
|
|
565
636
|
}
|
|
566
|
-
const commonQueryAndVars = getQueryAndVariables(type, contentPath, componentRegistry.getCommonQuery(), context, (
|
|
637
|
+
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);
|
|
567
638
|
if (commonQueryAndVars) {
|
|
568
639
|
allDescriptors.push({
|
|
569
640
|
type: contentTypeDef,
|
|
@@ -575,7 +646,7 @@ export const buildContentFetcher = (config) => {
|
|
|
575
646
|
processComponentConfig(APP_NAME, APP_NAME_DASHED, cmp);
|
|
576
647
|
}
|
|
577
648
|
// Collect component queries if defined
|
|
578
|
-
const componentDescriptors = collectComponentDescriptors(components, componentRegistry,
|
|
649
|
+
const componentDescriptors = collectComponentDescriptors(components, componentRegistry, contentPath, context);
|
|
579
650
|
if (componentDescriptors.length) {
|
|
580
651
|
allDescriptors.push(...componentDescriptors);
|
|
581
652
|
}
|
|
@@ -625,14 +696,13 @@ export const buildContentFetcher = (config) => {
|
|
|
625
696
|
}
|
|
626
697
|
}
|
|
627
698
|
const page = createPageData(type, components);
|
|
628
|
-
const meta = createMetaData(type, siteRelativeContentPath, requestType, renderMode, contentApiUrl, xpBaseUrl,
|
|
699
|
+
const meta = createMetaData(type, siteRelativeContentPath, requestType, renderMode, contentApiUrl, xpBaseUrl, componentPath, page, components);
|
|
629
700
|
return {
|
|
630
701
|
data: contentData,
|
|
631
702
|
common,
|
|
632
703
|
meta,
|
|
633
|
-
page
|
|
704
|
+
page,
|
|
634
705
|
};
|
|
635
|
-
// /////////////////////////////////////////////////////////// Catch
|
|
636
706
|
}
|
|
637
707
|
catch (e) {
|
|
638
708
|
console.error(e);
|
|
@@ -646,7 +716,7 @@ export const buildContentFetcher = (config) => {
|
|
|
646
716
|
message: e.message,
|
|
647
717
|
};
|
|
648
718
|
}
|
|
649
|
-
return errorResponse(error.code, error.message, requestType, renderMode, contentApiUrl, xpBaseUrl,
|
|
719
|
+
return errorResponse(error.code, error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPathOrArray.toString());
|
|
650
720
|
}
|
|
651
721
|
});
|
|
652
722
|
};
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enonic/nextjs-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-RC1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -8,26 +8,27 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"copy": "npx --yes cpy-cli package.json README.md dist",
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"lint": "eslint --quiet
|
|
13
|
-
"lint:fix": "eslint --fix
|
|
11
|
+
"build": "tsc --build src/tsconfig.json && tsc --build server/tsconfig.json",
|
|
12
|
+
"lint": "eslint --quiet server && eslint --quiet src",
|
|
13
|
+
"lint:fix": "eslint --fix server && eslint --fix src"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"html-react-parser": "^3.0.
|
|
17
|
-
"
|
|
16
|
+
"html-react-parser": "^3.0.11",
|
|
17
|
+
"lru-cache": "^7.17.0",
|
|
18
|
+
"next": "^13.2.3",
|
|
18
19
|
"react": "^18.2.0",
|
|
19
20
|
"react-dom": "^18.2.0",
|
|
20
21
|
"unescape": "^1.0.1"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"@enonic/eslint-config": "^
|
|
24
|
-
"@types/node": "^18.
|
|
25
|
-
"@types/react": "^18.0.
|
|
26
|
-
"@types/react-dom": "^18.0.
|
|
27
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
28
|
-
"@typescript-eslint/parser": "^5.
|
|
29
|
-
"eslint": "^8.
|
|
30
|
-
"eslint-plugin-react": "^7.
|
|
31
|
-
"typescript": "^4.9.
|
|
24
|
+
"@enonic/eslint-config": "^1.1.0",
|
|
25
|
+
"@types/node": "^18.14.6",
|
|
26
|
+
"@types/react": "^18.0.28",
|
|
27
|
+
"@types/react-dom": "^18.0.11",
|
|
28
|
+
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
|
29
|
+
"@typescript-eslint/parser": "^5.54.0",
|
|
30
|
+
"eslint": "^8.35.0",
|
|
31
|
+
"eslint-plugin-react": "^7.32.2",
|
|
32
|
+
"typescript": "^4.9.5"
|
|
32
33
|
}
|
|
33
34
|
}
|