@enonic/nextjs-adapter 0.10.1 → 0.12.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,14 +14,14 @@ export interface ComponentDefinition {
14
14
  processor?: DataProcessor;
15
15
  view?: React.FunctionComponent<any>;
16
16
  }
17
- export declare type DataProcessor = (data: any, context?: Context) => Promise<Record<string, any>>;
18
- export declare type VariablesGetter = (path: string, context?: Context, config?: any) => VariablesGetterResult;
19
- export declare type QueryGetter = (config?: any) => string;
20
- export declare type VariablesGetterResult = {
17
+ export type DataProcessor = (data: any, context?: Context) => Promise<Record<string, any>>;
18
+ export type VariablesGetter = (path: string, context?: Context, config?: any) => VariablesGetterResult;
19
+ export type QueryGetter = (config?: any) => string;
20
+ export type VariablesGetterResult = {
21
21
  [variables: string]: any;
22
22
  path: string;
23
23
  };
24
- export declare type SelectedQueryMaybeVariablesFunc = string | QueryGetter | {
24
+ export type SelectedQueryMaybeVariablesFunc = string | QueryGetter | {
25
25
  query: string | QueryGetter;
26
26
  variables: VariablesGetter;
27
27
  } | [
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);
@@ -7,7 +7,7 @@ import { ParsedUrlQuery } from 'node:querystring';
7
7
  import { type GetServerSidePropsContext } from 'next';
8
8
  import { IncomingMessage } from 'http';
9
9
  import { type NextApiRequestCookies } from 'next/dist/server/api-utils';
10
- export declare type AdapterConstants = {
10
+ export type AdapterConstants = {
11
11
  APP_NAME: string;
12
12
  APP_NAME_DASHED: string;
13
13
  SITE_KEY: string;
@@ -15,22 +15,22 @@ export declare type AdapterConstants = {
15
15
  getRenderMode: (context?: Context) => RENDER_MODE;
16
16
  getSingleComponentPath: (context?: Context) => string | undefined;
17
17
  };
18
- declare type Result = {
18
+ type Result = {
19
19
  error?: {
20
20
  code: string;
21
21
  message: string;
22
22
  } | null;
23
23
  };
24
- declare type GuillotineResult = Result & {
24
+ type GuillotineResult = Result & {
25
25
  [dataKey: string]: any;
26
26
  };
27
- export declare type FetchContentResult = Result & {
27
+ export type FetchContentResult = Result & {
28
28
  data: Record<string, any> | null;
29
29
  common: Record<string, any> | null;
30
30
  meta: MetaData;
31
31
  page: PageComponent | null;
32
32
  };
33
- declare type FetcherConfig<T extends AdapterConstants> = T & {
33
+ type FetcherConfig<T extends AdapterConstants> = T & {
34
34
  componentRegistry: typeof ComponentRegistry;
35
35
  };
36
36
  export interface ServerSideParams extends ParsedUrlQuery {
@@ -41,7 +41,7 @@ export interface PreviewParams {
41
41
  contentPath: string[];
42
42
  headers: Record<string, string>;
43
43
  }
44
- export declare type Context = GetServerSidePropsContext<ServerSideParams, PreviewParams>;
44
+ export type Context = GetServerSidePropsContext<ServerSideParams, PreviewParams>;
45
45
  export interface MinimalContext {
46
46
  req: IncomingMessage & {
47
47
  cookies: NextApiRequestCookies;
@@ -52,17 +52,17 @@ export interface MinimalContext {
52
52
  * @param contentPath string or string array: pre-split or slash-delimited _path to a content available on the API
53
53
  * @returns FetchContentResult object: {data?: T, error?: {code, message}}
54
54
  */
55
- export declare type ContentFetcher = (contentPath: string | string[], context: Context) => Promise<FetchContentResult>;
56
- export declare type ContentApiBaseBody = {
55
+ export type ContentFetcher = (contentPath: string | string[], context: Context) => Promise<FetchContentResult>;
56
+ export type ContentApiBaseBody = {
57
57
  query?: string;
58
58
  variables?: {
59
59
  path?: string;
60
60
  };
61
61
  };
62
62
  /** Generic fetch */
63
- export declare const fetchFromApi: (apiUrl: string, body: {}, method?: string) => Promise<any>;
63
+ export declare const fetchFromApi: (apiUrl: string, body: ContentApiBaseBody, headers?: {}, method?: string) => Promise<any>;
64
64
  /** Guillotine-specialized fetch, using the generic fetch above */
65
- export declare const fetchGuillotine: (contentApiUrl: string, body: ContentApiBaseBody, xpContentPath: string) => Promise<GuillotineResult>;
65
+ export declare const fetchGuillotine: (contentApiUrl: string, body: ContentApiBaseBody, xpContentPath: string, headers?: {}) => Promise<GuillotineResult>;
66
66
  /**
67
67
  * Configures, builds and returns a general fetchContent function.
68
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.
@@ -8,14 +8,14 @@ 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, PAGE_TEMPLATE_CONTENTTYPE_NAME, PAGE_TEMPLATE_FOLDER, RENDER_MODE, sanitizeGraphqlName, SITE_KEY, XP_COMPONENT_TYPE, XP_REQUEST_TYPE, } from '../utils';
11
+ import adapterConstants, { APP_NAME, APP_NAME_DASHED, FRAGMENT_CONTENTTYPE_NAME, FRAGMENT_DEFAULT_REGION_NAME, getContentApiUrl, getXpBaseUrl, IS_DEV_MODE, JSESSIONID_HEADER, 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';
15
15
  const GUILLOTINE_QUERY_REGEXP = /^\s*query\s*(?:\((.*)*\))?\s*{\s*guillotine\s*{((?:.|\s)+)}\s*}\s*$/;
16
16
  const GRAPHQL_FRAGMENTS_REGEXP = /fragment\s+.+\s+on\s+.+\s*{[\s\w{}().,:"'`]+}/;
17
17
  /** Generic fetch */
18
- export const fetchFromApi = (apiUrl, body, method = 'POST') => __awaiter(void 0, void 0, void 0, function* () {
18
+ export const fetchFromApi = (apiUrl, body, headers, method = 'POST') => __awaiter(void 0, void 0, void 0, function* () {
19
19
  const options = {
20
20
  method,
21
21
  headers: {
@@ -25,6 +25,9 @@ export const fetchFromApi = (apiUrl, body, method = 'POST') => __awaiter(void 0,
25
25
  },
26
26
  body: JSON.stringify(body),
27
27
  };
28
+ if (headers) {
29
+ Object.assign(options.headers, headers);
30
+ }
28
31
  let res;
29
32
  try {
30
33
  res = yield fetch(apiUrl, options);
@@ -61,7 +64,7 @@ export const fetchFromApi = (apiUrl, body, method = 'POST') => __awaiter(void 0,
61
64
  return json;
62
65
  });
63
66
  /** Guillotine-specialized fetch, using the generic fetch above */
64
- export const fetchGuillotine = (contentApiUrl, body, xpContentPath) => __awaiter(void 0, void 0, void 0, function* () {
67
+ export const fetchGuillotine = (contentApiUrl, body, xpContentPath, headers) => __awaiter(void 0, void 0, void 0, function* () {
65
68
  if (typeof body.query !== 'string' || !body.query.trim()) {
66
69
  return {
67
70
  error: {
@@ -70,7 +73,7 @@ export const fetchGuillotine = (contentApiUrl, body, xpContentPath) => __awaiter
70
73
  },
71
74
  };
72
75
  }
73
- const result = yield fetchFromApi(contentApiUrl, body)
76
+ const result = yield fetchFromApi(contentApiUrl, body, headers)
74
77
  .then(json => {
75
78
  let errors = (json || {}).errors;
76
79
  if (errors) {
@@ -104,7 +107,7 @@ export const fetchGuillotine = (contentApiUrl, body, xpContentPath) => __awaiter
104
107
  return result;
105
108
  });
106
109
  // /////////////////////////////////////////////////////////////////////////////// Specific fetch wrappers:
107
- const fetchMetaData = (contentApiUrl, xpContentPath) => __awaiter(void 0, void 0, void 0, function* () {
110
+ const fetchMetaData = (contentApiUrl, xpContentPath, headers) => __awaiter(void 0, void 0, void 0, function* () {
108
111
  var _b;
109
112
  const body = {
110
113
  query: getMetaQuery(pageFragmentQuery()),
@@ -112,7 +115,7 @@ const fetchMetaData = (contentApiUrl, xpContentPath) => __awaiter(void 0, void 0
112
115
  path: xpContentPath,
113
116
  },
114
117
  };
115
- const metaResult = yield fetchGuillotine(contentApiUrl, body, xpContentPath);
118
+ const metaResult = yield fetchGuillotine(contentApiUrl, body, xpContentPath, headers);
116
119
  if (metaResult.error) {
117
120
  return metaResult;
118
121
  }
@@ -122,12 +125,12 @@ const fetchMetaData = (contentApiUrl, xpContentPath) => __awaiter(void 0, void 0
122
125
  };
123
126
  }
124
127
  });
125
- const fetchContentData = (contentApiUrl, xpContentPath, query, variables) => __awaiter(void 0, void 0, void 0, function* () {
128
+ const fetchContentData = (contentApiUrl, xpContentPath, query, variables, headers) => __awaiter(void 0, void 0, void 0, function* () {
126
129
  const body = { query };
127
130
  if (variables && Object.keys(variables).length > 0) {
128
131
  body.variables = variables;
129
132
  }
130
- const contentResults = yield fetchGuillotine(contentApiUrl, body, xpContentPath);
133
+ const contentResults = yield fetchGuillotine(contentApiUrl, body, xpContentPath, headers);
131
134
  if (contentResults.error) {
132
135
  return contentResults;
133
136
  }
@@ -505,7 +508,14 @@ function errorResponse(code = '500', message = 'Unknown error', requestType, ren
505
508
  export const buildContentFetcher = (config) => {
506
509
  const { APP_NAME, APP_NAME_DASHED, getXPRequestType, getRenderMode, getSingleComponentPath, componentRegistry, } = config;
507
510
  return (contentPathOrArray, context) => __awaiter(void 0, void 0, void 0, function* () {
508
- var _a, _b;
511
+ var _a, _b, _c;
512
+ let headers;
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
+ }
509
519
  const xpBaseUrl = getXpBaseUrl(context);
510
520
  const contentApiUrl = getContentApiUrl(context);
511
521
  const requestType = getXPRequestType(context);
@@ -518,11 +528,12 @@ export const buildContentFetcher = (config) => {
518
528
  requestedComponentPath = getSingleComponentPath(context);
519
529
  }
520
530
  // ///////////// FIRST GUILLOTINE CALL FOR METADATA /////////////////
521
- const metaResult = yield fetchMetaData(contentApiUrl, '${site}/' + siteRelativeContentPath);
531
+ const metaResult = yield fetchMetaData(contentApiUrl, '${site}/' + siteRelativeContentPath, headers);
522
532
  // ///////////////////////////////////////////////////////////////////////
523
533
  const { type, components, _path } = metaResult.meta || {};
524
534
  contentPath = _path || '';
525
535
  if (metaResult.error) {
536
+ console.error(metaResult.error);
526
537
  return errorResponse(metaResult.error.code, metaResult.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
527
538
  }
528
539
  if (!metaResult.meta) {
@@ -545,14 +556,14 @@ export const buildContentFetcher = (config) => {
545
556
  if (pageCmp) {
546
557
  processComponentConfig(APP_NAME, APP_NAME_DASHED, pageCmp);
547
558
  }
548
- 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);
559
+ const contentQueryAndVars = getQueryAndVariables(type, contentPath, contentTypeDef === null || contentTypeDef === void 0 ? void 0 : contentTypeDef.query, context, (_b = pageCmp === null || pageCmp === void 0 ? void 0 : pageCmp.page) === null || _b === void 0 ? void 0 : _b.config);
549
560
  if (contentQueryAndVars) {
550
561
  allDescriptors.push({
551
562
  type: contentTypeDef,
552
563
  queryAndVariables: contentQueryAndVars,
553
564
  });
554
565
  }
555
- 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);
566
+ const commonQueryAndVars = getQueryAndVariables(type, contentPath, componentRegistry.getCommonQuery(), context, (_c = pageCmp === null || pageCmp === void 0 ? void 0 : pageCmp.page) === null || _c === void 0 ? void 0 : _c.config);
556
567
  if (commonQueryAndVars) {
557
568
  allDescriptors.push({
558
569
  type: contentTypeDef,
@@ -574,8 +585,12 @@ export const buildContentFetcher = (config) => {
574
585
  return errorResponse('400', `Missing or empty query override for content type ${type}`, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
575
586
  }
576
587
  // /////////////// SECOND GUILLOTINE CALL FOR DATA //////////////////////
577
- const contentResults = yield fetchContentData(contentApiUrl, contentPath, query, variables);
588
+ const contentResults = yield fetchContentData(contentApiUrl, contentPath, query, variables, headers);
578
589
  // ///////////////////////////////////////////////////////////////////////////
590
+ if (contentResults.error) {
591
+ console.error(contentResults.error);
592
+ return errorResponse(contentResults.error.code, contentResults.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
593
+ }
579
594
  // Apply processors to every component
580
595
  const datas = yield applyProcessors(allDescriptors, contentResults, context);
581
596
  // 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.10.1",
3
+ "version": "0.12.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
@@ -14,20 +14,20 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "html-react-parser": "^3.0.4",
17
- "next": "^13.0.3",
17
+ "next": "^13.0.7",
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
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.8",
27
- "@typescript-eslint/eslint-plugin": "^5.42.1",
28
- "@typescript-eslint/parser": "^5.42.1",
29
- "eslint": "^8.27.0",
30
- "eslint-plugin-react": "^7.31.10",
31
- "typescript": "^4.8.4"
24
+ "@types/node": "^18.11.17",
25
+ "@types/react": "^18.0.26",
26
+ "@types/react-dom": "^18.0.9",
27
+ "@typescript-eslint/eslint-plugin": "^5.46.1",
28
+ "@typescript-eslint/parser": "^5.46.1",
29
+ "eslint": "^8.30.0",
30
+ "eslint-plugin-react": "^7.31.11",
31
+ "typescript": "^4.9.4"
32
32
  }
33
33
  }
package/utils.d.ts CHANGED
@@ -12,6 +12,7 @@ export declare const FROM_XP_PARAM = "__fromxp__";
12
12
  export declare const XP_BASE_URL_HEADER = "xpbaseurl";
13
13
  export declare const COMPONENT_SUBPATH_HEADER = "xp-component-path";
14
14
  export declare const RENDER_MODE_HEADER = "content-studio-mode";
15
+ export declare const JSESSIONID_HEADER = "jsessionid";
15
16
  export declare const PORTAL_COMPONENT_ATTRIBUTE = "data-portal-component-type";
16
17
  export declare const PORTAL_REGION_ATTRIBUTE = "data-portal-region";
17
18
  export declare const FRAGMENT_CONTENTTYPE_NAME = "portal:fragment";
package/utils.js CHANGED
@@ -16,6 +16,7 @@ export const FROM_XP_PARAM = '__fromxp__';
16
16
  export const XP_BASE_URL_HEADER = 'xpbaseurl';
17
17
  export const COMPONENT_SUBPATH_HEADER = 'xp-component-path';
18
18
  export const RENDER_MODE_HEADER = 'content-studio-mode';
19
+ export const JSESSIONID_HEADER = 'jsessionid';
19
20
  export const PORTAL_COMPONENT_ATTRIBUTE = 'data-portal-component-type';
20
21
  export const PORTAL_REGION_ATTRIBUTE = 'data-portal-region';
21
22
  export const FRAGMENT_CONTENTTYPE_NAME = 'portal:fragment';
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import { MetaData, PageComponent } from '../guillotine/getMetaData';
3
- export declare type BaseComponentProps = {
3
+ export type BaseComponentProps = {
4
4
  component: PageComponent;
5
5
  meta: MetaData;
6
6
  common?: any;
@@ -1,15 +1,16 @@
1
1
  /// <reference types="react" />
2
2
  import { MetaData, RichTextData } from '../guillotine/getMetaData';
3
3
  import { DOMNode } from 'html-react-parser';
4
- export declare type ReplacerResult = JSX.Element | object | void | undefined | null | false;
5
- export declare type Replacer = (domNode: DOMNode, data: RichTextData, meta: MetaData, renderMacroInEditMode: boolean) => ReplacerResult;
6
- declare type Props = {
4
+ export type ReplacerResult = JSX.Element | object | void | undefined | null | false;
5
+ export type Replacer = (domNode: DOMNode, data: RichTextData, meta: MetaData, renderMacroInEditMode: boolean) => ReplacerResult;
6
+ type Props = {
7
7
  data: RichTextData;
8
8
  meta: MetaData;
9
+ className?: string;
9
10
  tag?: string;
10
11
  renderMacroInEditMode?: boolean;
11
12
  customReplacer?: Replacer;
12
13
  };
13
14
  export declare function createReplacer(allData: RichTextData, meta: MetaData, renderMacroInEditMode?: boolean, customReplacer?: Replacer): (domNode: DOMNode) => ReplacerResult;
14
- declare const RichTextView: ({ tag, data, meta, renderMacroInEditMode, customReplacer }: Props) => JSX.Element;
15
+ declare const RichTextView: ({ className, tag, data, meta, renderMacroInEditMode, customReplacer }: Props) => JSX.Element;
15
16
  export default RichTextView;
@@ -46,9 +46,9 @@ export function createReplacer(allData, meta, renderMacroInEditMode = true, cust
46
46
  return el;
47
47
  };
48
48
  }
49
- const RichTextView = ({ tag, data, meta, renderMacroInEditMode, customReplacer }) => {
49
+ const RichTextView = ({ className, tag, data, meta, renderMacroInEditMode, customReplacer }) => {
50
50
  const CustomTag = tag || 'section';
51
- return <CustomTag>
51
+ return <CustomTag className={className}>
52
52
  {data.processedHtml ? HTMLReactParser(data.processedHtml, { replace: createReplacer(data, meta, renderMacroInEditMode, customReplacer) }) : ''}
53
53
  </CustomTag>;
54
54
  };
package/views/Text.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import { MetaData, TextData } from '../guillotine/getMetaData';
3
- declare type Props = {
3
+ type Props = {
4
4
  meta: MetaData;
5
5
  component: TextData;
6
6
  path: string;