@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.
@@ -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
- const common = commonChars(url, apiUrl);
29
- const remaining = common.length > 0 ? url.substring(common.length) : url;
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
- let page = {
233
+ const page = {
234
234
  type: XP_COMPONENT_TYPE.PAGE,
235
235
  path: '/',
236
236
  };
237
- const tree = {};
238
- if (contentType === FRAGMENT_CONTENTTYPE_NAME) {
239
- page.regions = tree;
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
- let region;
245
- if (cmp.path === '/' && cmp.type === XP_COMPONENT_TYPE.PAGE) {
246
- // add page values to page object
247
- page = Object.assign(page, cmp);
248
- page.page.regions = tree;
249
- return;
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
- else {
252
- region = getParentRegion(tree, contentType, cmpPath, comps, true);
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 page;
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
- return yield propsProcessor(data, context);
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 cmpData = cmp[cmp.type];
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.11.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.4",
17
- "next": "^13.0.4",
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": "^0.1.0",
24
- "@types/node": "^18.11.9",
25
- "@types/react": "^18.0.25",
26
- "@types/react-dom": "^18.0.9",
27
- "@typescript-eslint/eslint-plugin": "^5.43.0",
28
- "@typescript-eslint/parser": "^5.44.0",
29
- "eslint": "^8.28.0",
30
- "eslint-plugin-react": "^7.31.11",
31
- "typescript": "^4.9.3"
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
- if (Object.keys(regionAttrs).length) {
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
+ }