@enonic/nextjs-adapter 0.11.0 → 0.13.0
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 +2 -2
- package/UrlProcessor.d.ts +1 -0
- package/UrlProcessor.js +6 -2
- package/guillotine/fetchContent.d.ts +1 -0
- package/guillotine/fetchContent.js +39 -17
- package/package.json +12 -12
- package/views/Region.jsx +1 -6
- package/views/StaticContent.d.ts +15 -0
- package/views/StaticContent.jsx +38 -0
package/ComponentRegistry.d.ts
CHANGED
|
@@ -14,9 +14,9 @@ export interface ComponentDefinition {
|
|
|
14
14
|
processor?: DataProcessor;
|
|
15
15
|
view?: React.FunctionComponent<any>;
|
|
16
16
|
}
|
|
17
|
-
export type DataProcessor = (data: any, context?: Context) => Promise<Record<string, any>>;
|
|
17
|
+
export type DataProcessor = (data: any, context?: Context, config?: any) => Promise<Record<string, any>>;
|
|
18
18
|
export type VariablesGetter = (path: string, context?: Context, config?: any) => VariablesGetterResult;
|
|
19
|
-
export type QueryGetter = (config?: any) => string;
|
|
19
|
+
export type QueryGetter = (path: string, context?: Context, config?: any) => string;
|
|
20
20
|
export type VariablesGetterResult = {
|
|
21
21
|
[variables: string]: any;
|
|
22
22
|
path: string;
|
package/UrlProcessor.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare class UrlProcessor {
|
|
|
9
9
|
private static IMG_ATMT_REGEXP;
|
|
10
10
|
private static endSlashPattern;
|
|
11
11
|
private static startSlashPattern;
|
|
12
|
+
private static localhostPattern;
|
|
12
13
|
private static siteKey;
|
|
13
14
|
static process(url: string, meta: MetaData): string;
|
|
14
15
|
static setSiteKey(key: string): void;
|
package/UrlProcessor.js
CHANGED
|
@@ -25,8 +25,11 @@ export class UrlProcessor {
|
|
|
25
25
|
return ((_a = linkData.find(data => data.ref === ref)) === null || _a === void 0 ? void 0 : _a.image) !== null;
|
|
26
26
|
}
|
|
27
27
|
static stripApiUrl(url, apiUrl) {
|
|
28
|
-
|
|
29
|
-
const
|
|
28
|
+
// normalise localhost-127.0.0.1 if present in urls
|
|
29
|
+
const normalUrl = url.replace(this.localhostPattern, '127.0.0.1');
|
|
30
|
+
const normalApiUrl = apiUrl.replace(this.localhostPattern, '127.0.0.1');
|
|
31
|
+
const common = commonChars(normalUrl, normalApiUrl);
|
|
32
|
+
const remaining = common.length > 0 ? normalUrl.substring(common.length) : normalUrl;
|
|
30
33
|
return (remaining.length > 0 && remaining.charAt(0) === '/') ? remaining.substring(1) : remaining;
|
|
31
34
|
}
|
|
32
35
|
static isPageUrl(url) {
|
|
@@ -50,5 +53,6 @@ UrlProcessor.MACRO_ATTR = 'data-macro-ref';
|
|
|
50
53
|
UrlProcessor.IMG_ATMT_REGEXP = /_\/image\/|_\/attachment\//;
|
|
51
54
|
UrlProcessor.endSlashPattern = /\/+$/;
|
|
52
55
|
UrlProcessor.startSlashPattern = /^\/+/;
|
|
56
|
+
UrlProcessor.localhostPattern = /localhost/;
|
|
53
57
|
UrlProcessor.setSiteKey(SITE_KEY);
|
|
54
58
|
export const getUrl = UrlProcessor.process.bind(UrlProcessor);
|
|
@@ -40,6 +40,7 @@ export interface ServerSideParams extends ParsedUrlQuery {
|
|
|
40
40
|
export interface PreviewParams {
|
|
41
41
|
contentPath: string[];
|
|
42
42
|
headers: Record<string, string>;
|
|
43
|
+
params: Record<string, string>;
|
|
43
44
|
}
|
|
44
45
|
export type Context = GetServerSidePropsContext<ServerSideParams, PreviewParams>;
|
|
45
46
|
export interface MinimalContext {
|
|
@@ -230,27 +230,40 @@ function prefixLayoutPath(contentType, path) {
|
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
function buildPage(contentType, comps = []) {
|
|
233
|
-
|
|
233
|
+
const page = {
|
|
234
234
|
type: XP_COMPONENT_TYPE.PAGE,
|
|
235
235
|
path: '/',
|
|
236
236
|
};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
237
|
+
return buildComponentTree(comps, page, contentType);
|
|
238
|
+
}
|
|
239
|
+
function buildComponentTree(comps, rootComp, contentType) {
|
|
240
|
+
const rootTree = {};
|
|
241
|
+
const isFragmentType = contentType === FRAGMENT_CONTENTTYPE_NAME;
|
|
242
|
+
if (isFragmentType) {
|
|
243
|
+
rootComp.regions = rootTree;
|
|
240
244
|
}
|
|
241
245
|
comps.forEach(cmp => {
|
|
242
246
|
var _a;
|
|
243
247
|
const cmpPath = parseComponentPath(contentType, cmp.path);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
if (cmp.path === '/') {
|
|
249
|
+
if (cmp.type === XP_COMPONENT_TYPE.PAGE && rootComp.type === XP_COMPONENT_TYPE.PAGE) {
|
|
250
|
+
// add page values to page object
|
|
251
|
+
rootComp = Object.assign(rootComp, cmp);
|
|
252
|
+
rootComp.page.regions = rootTree;
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
else if (cmp.type === XP_COMPONENT_TYPE.LAYOUT && rootComp.type === XP_COMPONENT_TYPE.FRAGMENT) {
|
|
256
|
+
// when rendering fragment as part of the page with layout at root path
|
|
257
|
+
// remove other children from list as they will be included in layout
|
|
258
|
+
rootComp.fragment.fragment.components = [cmp];
|
|
259
|
+
cmp.regions = rootTree;
|
|
260
|
+
}
|
|
250
261
|
}
|
|
251
|
-
|
|
252
|
-
|
|
262
|
+
if (cmp.type === XP_COMPONENT_TYPE.FRAGMENT) {
|
|
263
|
+
// build component subtree for fragment
|
|
264
|
+
cmp = buildComponentTree(cmp.fragment.fragment.components, cmp, contentType);
|
|
253
265
|
}
|
|
266
|
+
const region = getParentRegion(rootTree, contentType, cmpPath, comps, true);
|
|
254
267
|
if (region) {
|
|
255
268
|
// getting the index of component from string like '/main/0/left/11'
|
|
256
269
|
const cmpIndex = (_a = cmpPath[cmpPath.length - 1]) === null || _a === void 0 ? void 0 : _a.index;
|
|
@@ -262,7 +275,7 @@ function buildPage(contentType, comps = []) {
|
|
|
262
275
|
}
|
|
263
276
|
}
|
|
264
277
|
});
|
|
265
|
-
return
|
|
278
|
+
return rootComp;
|
|
266
279
|
}
|
|
267
280
|
function combineMultipleQueries(queriesWithVars) {
|
|
268
281
|
const queries = [];
|
|
@@ -342,11 +355,16 @@ function applyProcessors(componentDescriptors, contentResults, context) {
|
|
|
342
355
|
// if there is a query then there must be a result for it
|
|
343
356
|
data = contentResults.contents[dataCounter++];
|
|
344
357
|
}
|
|
345
|
-
|
|
358
|
+
const config = getComponentConfig(desc.component);
|
|
359
|
+
return yield propsProcessor(data, context, config);
|
|
346
360
|
}));
|
|
347
361
|
return Promise.allSettled(processorPromises);
|
|
348
362
|
});
|
|
349
363
|
}
|
|
364
|
+
function getComponentConfig(cmp) {
|
|
365
|
+
const cmpData = cmp && cmp[cmp.type];
|
|
366
|
+
return cmpData && 'config' in cmpData ? cmpData.config : undefined;
|
|
367
|
+
}
|
|
350
368
|
function collectComponentDescriptors(components, componentRegistry, requestedComponentPath, xpContentPath, context) {
|
|
351
369
|
const descriptors = [];
|
|
352
370
|
for (const cmp of (components || [])) {
|
|
@@ -357,8 +375,7 @@ function collectComponentDescriptors(components, componentRegistry, requestedCom
|
|
|
357
375
|
const cmpDef = ComponentRegistry.getByComponent(cmp);
|
|
358
376
|
if (cmpDef) {
|
|
359
377
|
// const partPath = `${xpContentPath}/_component${cmp.path}`;
|
|
360
|
-
const
|
|
361
|
-
const config = cmpData && 'config' in cmpData ? cmpData.config : undefined;
|
|
378
|
+
const config = getComponentConfig(cmp);
|
|
362
379
|
const queryAndVariables = getQueryAndVariables(cmp.type, xpContentPath, cmpDef.query, context, config);
|
|
363
380
|
if (queryAndVariables) {
|
|
364
381
|
descriptors.push({
|
|
@@ -427,7 +444,7 @@ function getQueryAndVariables(type, path, selectedQuery, context, config) {
|
|
|
427
444
|
throw Error(`Query for content type ${type} should be a string or function, not: ${typeof query}`);
|
|
428
445
|
}
|
|
429
446
|
if (typeof query === 'function') {
|
|
430
|
-
query = query(config);
|
|
447
|
+
query = query(path, context, config);
|
|
431
448
|
}
|
|
432
449
|
if (query) {
|
|
433
450
|
return {
|
|
@@ -533,6 +550,7 @@ export const buildContentFetcher = (config) => {
|
|
|
533
550
|
const { type, components, _path } = metaResult.meta || {};
|
|
534
551
|
contentPath = _path || '';
|
|
535
552
|
if (metaResult.error) {
|
|
553
|
+
console.error(metaResult.error);
|
|
536
554
|
return errorResponse(metaResult.error.code, metaResult.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
537
555
|
}
|
|
538
556
|
if (!metaResult.meta) {
|
|
@@ -586,6 +604,10 @@ export const buildContentFetcher = (config) => {
|
|
|
586
604
|
// /////////////// SECOND GUILLOTINE CALL FOR DATA //////////////////////
|
|
587
605
|
const contentResults = yield fetchContentData(contentApiUrl, contentPath, query, variables, headers);
|
|
588
606
|
// ///////////////////////////////////////////////////////////////////////////
|
|
607
|
+
if (contentResults.error) {
|
|
608
|
+
console.error(contentResults.error);
|
|
609
|
+
return errorResponse(contentResults.error.code, contentResults.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
610
|
+
}
|
|
589
611
|
// Apply processors to every component
|
|
590
612
|
const datas = yield applyProcessors(allDescriptors, contentResults, context);
|
|
591
613
|
// Unwind the data back to components
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enonic/nextjs-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -13,21 +13,21 @@
|
|
|
13
13
|
"lint:fix": "eslint --fix ."
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"html-react-parser": "^3.0.
|
|
17
|
-
"next": "^13.
|
|
16
|
+
"html-react-parser": "^3.0.7",
|
|
17
|
+
"next": "^13.1.2",
|
|
18
18
|
"react": "^18.2.0",
|
|
19
19
|
"react-dom": "^18.2.0",
|
|
20
20
|
"unescape": "^1.0.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@enonic/eslint-config": "^
|
|
24
|
-
"@types/node": "^18.11.
|
|
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.
|
|
23
|
+
"@enonic/eslint-config": "^1.1.0",
|
|
24
|
+
"@types/node": "^18.11.18",
|
|
25
|
+
"@types/react": "^18.0.26",
|
|
26
|
+
"@types/react-dom": "^18.0.10",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
|
28
|
+
"@typescript-eslint/parser": "^5.48.1",
|
|
29
|
+
"eslint": "^8.32.0",
|
|
30
|
+
"eslint-plugin-react": "^7.32.0",
|
|
31
|
+
"typescript": "^4.9.4"
|
|
32
32
|
}
|
|
33
33
|
}
|
package/views/Region.jsx
CHANGED
|
@@ -15,12 +15,7 @@ export const RegionView = (props) => {
|
|
|
15
15
|
regionAttrs.id = name + 'Region';
|
|
16
16
|
regionAttrs[PORTAL_REGION_ATTRIBUTE] = name;
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
return <div {...regionAttrs}>{children}</div>;
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
return <>{children}</>;
|
|
23
|
-
}
|
|
18
|
+
return <div {...regionAttrs}>{children}</div>;
|
|
24
19
|
};
|
|
25
20
|
/** Multiple regions, or only one if named in props.selected */
|
|
26
21
|
const RegionsView = (props) => {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export interface StaticContentProps extends Record<string, any> {
|
|
3
|
+
children?: ReactNode | ReactNode[];
|
|
4
|
+
element?: string;
|
|
5
|
+
condition: boolean;
|
|
6
|
+
}
|
|
7
|
+
export default function StaticContent({ children, element, condition, ...props }: StaticContentProps): import("react").DOMElement<{
|
|
8
|
+
[x: string]: any;
|
|
9
|
+
}, Element> | import("react").ReactElement<{
|
|
10
|
+
ref: boolean | import("react").MutableRefObject<any>;
|
|
11
|
+
suppressHydrationWarning: boolean;
|
|
12
|
+
dangerouslySetInnerHTML: {
|
|
13
|
+
__html: string;
|
|
14
|
+
};
|
|
15
|
+
}, string | import("react").JSXElementConstructor<any>>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { createElement, useEffect, useRef, useState } from 'react';
|
|
13
|
+
function useStaticContent() {
|
|
14
|
+
const ref = useRef(null);
|
|
15
|
+
const [render, setRender] = useState(typeof window === 'undefined');
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
var _a;
|
|
18
|
+
// check if the innerHTML is empty as client side navigation
|
|
19
|
+
// need to render the component without server-side backup
|
|
20
|
+
const isEmpty = ((_a = ref === null || ref === void 0 ? void 0 : ref.current) === null || _a === void 0 ? void 0 : _a.innerHTML) === '';
|
|
21
|
+
if (isEmpty) {
|
|
22
|
+
setRender(true);
|
|
23
|
+
}
|
|
24
|
+
}, []);
|
|
25
|
+
return [render, ref];
|
|
26
|
+
}
|
|
27
|
+
export default function StaticContent(_a) {
|
|
28
|
+
var { children, element = 'div', condition = true } = _a, props = __rest(_a, ["children", "element", "condition"]);
|
|
29
|
+
const [render, ref] = useStaticContent();
|
|
30
|
+
// just render if:
|
|
31
|
+
// - condition is false
|
|
32
|
+
// - we're in the server or a spa navigation
|
|
33
|
+
if (render || !condition) {
|
|
34
|
+
return createElement(element, props, ...[].concat(children));
|
|
35
|
+
}
|
|
36
|
+
// avoid re-render on the client
|
|
37
|
+
return createElement(element, Object.assign(Object.assign({}, props), { ref, suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: '' } }));
|
|
38
|
+
}
|