@enonic/nextjs-adapter 1.0.2 → 2.0.0-B1
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 +1 -1
- package/ComponentRegistry.js +1 -2
- package/UrlProcessor.d.ts +4 -3
- package/UrlProcessor.js +35 -26
- package/baseMappings.js +4 -0
- package/guillotine/fetchContent.d.ts +10 -25
- package/guillotine/fetchContent.js +122 -32
- package/guillotine/getMetaData.d.ts +3 -0
- package/package.json +14 -14
- package/query/Shortcut.d.ts +1 -0
- package/query/Shortcut.js +14 -0
- package/server/index.js +63 -51
- package/utils.d.ts +61 -4
- package/utils.js +123 -26
- package/views/BaseComponent.d.ts +4 -4
- package/views/BaseContent.d.ts +2 -2
- package/views/BaseLayout.d.ts +2 -2
- package/views/BaseMacro.d.ts +2 -2
- package/views/BasePage.d.ts +2 -2
- package/views/BasePart.d.ts +2 -2
- package/views/Empty.d.ts +2 -2
- package/views/Fragment.d.ts +2 -2
- package/views/Fragment.jsx +12 -7
- package/views/MainView.d.ts +2 -2
- package/views/Region.d.ts +3 -3
- package/views/RichTextView.d.ts +2 -2
- package/views/SingleComponent.d.ts +2 -2
- package/views/StaticContent.d.ts +5 -5
- package/views/StaticContent.jsx +18 -20
- package/views/Text.d.ts +2 -2
package/ComponentRegistry.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { PageComponent } from './guillotine/getMetaData';
|
|
3
|
-
import { Context } from './
|
|
3
|
+
import { Context } from './utils';
|
|
4
4
|
/**
|
|
5
5
|
* Object that configures the handling of a particular content type. All attributes are optional (see examples below), and missing values will fall back to default behavior:
|
|
6
6
|
* - 'query' (used in fetchContent.ts) Guillotine query for fetching content data, may also have a function that supplies guillotine variables. So, 'query' can EITHER be only a query string, OR also add a get-guillotine-variables function. In the latter case, 'query' can be an object with 'query' and 'variables' attributes, or an array where the query string is first and the get-variables function is second. Either way, the get-variables function takes two arguments: path (content path, mandatory) and context (next.js-supplied Context from getServerSideProps etc. Optional, and requires that fetchContent is called with the context, of course).
|
package/ComponentRegistry.js
CHANGED
|
@@ -10,7 +10,7 @@ function toSelectorName(type) {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
export const CATCH_ALL = '*';
|
|
13
|
-
class ComponentRegistry {
|
|
13
|
+
export class ComponentRegistry {
|
|
14
14
|
static getSelector(name) {
|
|
15
15
|
switch (name) {
|
|
16
16
|
case 'contentType':
|
|
@@ -119,4 +119,3 @@ ComponentRegistry.components = {};
|
|
|
119
119
|
ComponentRegistry.parts = {};
|
|
120
120
|
ComponentRegistry.layouts = {};
|
|
121
121
|
ComponentRegistry.macros = {};
|
|
122
|
-
export { ComponentRegistry };
|
package/UrlProcessor.d.ts
CHANGED
|
@@ -11,14 +11,15 @@ export declare class UrlProcessor {
|
|
|
11
11
|
private static startSlashPattern;
|
|
12
12
|
private static localhostPattern;
|
|
13
13
|
private static siteKey;
|
|
14
|
-
static process(url: string, meta: MetaData): string;
|
|
14
|
+
static process(url: string, meta: MetaData, serverSide?: boolean): string;
|
|
15
15
|
static setSiteKey(key: string): void;
|
|
16
16
|
static isMediaLink(ref: string, linkData: LinkData[]): boolean;
|
|
17
17
|
static isContentImage(ref: string, linkData: ImageData[]): boolean;
|
|
18
18
|
static processSrcSet(srcset: string, meta: MetaData): string;
|
|
19
19
|
private static stripApiUrl;
|
|
20
|
-
private static
|
|
20
|
+
private static isAttachmentUrl;
|
|
21
21
|
private static startsWithHash;
|
|
22
|
+
private static isRelative;
|
|
22
23
|
private static getApiUrl;
|
|
23
24
|
}
|
|
24
|
-
export declare const getUrl: (url: string, meta: MetaData) => string;
|
|
25
|
+
export declare const getUrl: (url: string, meta: MetaData, serverSide?: boolean) => string;
|
package/UrlProcessor.js
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
|
-
import { commonChars,
|
|
2
|
-
import {
|
|
3
|
-
class UrlProcessor {
|
|
4
|
-
static process(url, meta) {
|
|
1
|
+
import { commonChars, fixDoubleSlashes, getLocaleProjectConfigs, RENDER_MODE } from './utils';
|
|
2
|
+
import { addBasePath } from 'next/dist/client/add-base-path';
|
|
3
|
+
export class UrlProcessor {
|
|
4
|
+
static process(url, meta, serverSide = false) {
|
|
5
|
+
if (this.startsWithHash(url) || !meta || (this.isAttachmentUrl(url)) && meta.renderMode === RENDER_MODE.NEXT) {
|
|
6
|
+
// do not process if:
|
|
7
|
+
// - url starts with #
|
|
8
|
+
// - meta is absent
|
|
9
|
+
// - attachment urls in NEXT mode
|
|
10
|
+
return url;
|
|
11
|
+
}
|
|
5
12
|
let result;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
const apiUrl = this.getApiUrl(meta);
|
|
14
|
+
const strippedUrl = this.stripApiUrl(url, apiUrl);
|
|
15
|
+
const baseUrl = (meta === null || meta === void 0 ? void 0 : meta.baseUrl) && (meta === null || meta === void 0 ? void 0 : meta.baseUrl) !== '/' ? meta.baseUrl : '';
|
|
16
|
+
if (meta.renderMode === RENDER_MODE.NEXT) {
|
|
17
|
+
// only add basePath and locale in next mode
|
|
18
|
+
result = `/${strippedUrl}`;
|
|
19
|
+
if (!this.isRelative(url) && meta.locale !== meta.defaultLocale) {
|
|
20
|
+
// do not append locale to local assets (having relative urls)
|
|
21
|
+
result = `/${meta.locale}${result}`;
|
|
22
|
+
}
|
|
23
|
+
if (!serverSide) {
|
|
24
|
+
// no need for baseurl and basepath on server
|
|
25
|
+
result = addBasePath(`${baseUrl}${result}`);
|
|
26
|
+
}
|
|
10
27
|
}
|
|
11
28
|
else {
|
|
12
|
-
|
|
13
|
-
const strippedUrl = this.stripApiUrl(url, apiUrl);
|
|
14
|
-
let basePath;
|
|
15
|
-
// only add basePath in next
|
|
16
|
-
if (meta.renderMode === RENDER_MODE.NEXT) {
|
|
17
|
-
try {
|
|
18
|
-
basePath = useRouter().basePath;
|
|
19
|
-
}
|
|
20
|
-
catch (e) {
|
|
21
|
-
console.error('Error getting basePath from nextjs router', e);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
result = (basePath && basePath !== '/' ? basePath : '') + ((meta === null || meta === void 0 ? void 0 : meta.baseUrl) || '') + strippedUrl;
|
|
29
|
+
result = `${baseUrl}/${strippedUrl}`;
|
|
25
30
|
}
|
|
26
|
-
return result;
|
|
31
|
+
return fixDoubleSlashes(result);
|
|
27
32
|
}
|
|
28
33
|
static setSiteKey(key) {
|
|
29
34
|
this.siteKey = key;
|
|
@@ -58,12 +63,15 @@ class UrlProcessor {
|
|
|
58
63
|
const remaining = common.length > 0 ? normalUrl.substring(common.length) : normalUrl;
|
|
59
64
|
return (remaining.length > 0 && remaining.charAt(0) === '/') ? remaining.substring(1) : remaining;
|
|
60
65
|
}
|
|
61
|
-
static
|
|
62
|
-
return
|
|
66
|
+
static isAttachmentUrl(url) {
|
|
67
|
+
return this.IMG_ATMT_REGEXP.test(url);
|
|
63
68
|
}
|
|
64
69
|
static startsWithHash(url) {
|
|
65
70
|
return (url === null || url === void 0 ? void 0 : url.charAt(0)) == '#';
|
|
66
71
|
}
|
|
72
|
+
static isRelative(url) {
|
|
73
|
+
return !/^(?:ht|f)tps?:\/\/[^ :\r\n\t]+/.test(url);
|
|
74
|
+
}
|
|
67
75
|
static getApiUrl(meta) {
|
|
68
76
|
var _a, _b;
|
|
69
77
|
let combinedUrl = ((_a = meta.apiUrl) === null || _a === void 0 ? void 0 : _a.replace(this.endSlashPattern, '')) || '';
|
|
@@ -83,6 +91,7 @@ UrlProcessor.IMG_ATMT_REGEXP = /_\/image\/|_\/attachment\//;
|
|
|
83
91
|
UrlProcessor.endSlashPattern = /\/+$/;
|
|
84
92
|
UrlProcessor.startSlashPattern = /^\/+/;
|
|
85
93
|
UrlProcessor.localhostPattern = /localhost/;
|
|
86
|
-
|
|
87
|
-
|
|
94
|
+
// Actual site is going to be set in fetchContent method, but set a default fallback here too
|
|
95
|
+
const defaultConfig = getLocaleProjectConfigs().default;
|
|
96
|
+
UrlProcessor.setSiteKey(defaultConfig.site);
|
|
88
97
|
export const getUrl = UrlProcessor.process.bind(UrlProcessor);
|
package/baseMappings.js
CHANGED
|
@@ -7,10 +7,14 @@ import BaseLayout from './views/BaseLayout';
|
|
|
7
7
|
import TextView from './views/Text';
|
|
8
8
|
import DefaultMacro from './views/macros/DefaultMacro';
|
|
9
9
|
import DisableMacro from './views/macros/DisableMacro';
|
|
10
|
+
import { getShortcutQuery } from './query/Shortcut';
|
|
10
11
|
// Base Content Types
|
|
11
12
|
ComponentRegistry.addContentType(FRAGMENT_CONTENTTYPE_NAME, {
|
|
12
13
|
view: FragmentView,
|
|
13
14
|
});
|
|
15
|
+
ComponentRegistry.addContentType('base:shortcut', {
|
|
16
|
+
query: getShortcutQuery,
|
|
17
|
+
});
|
|
14
18
|
// Base Components
|
|
15
19
|
ComponentRegistry.addComponent(XP_COMPONENT_TYPE.PAGE, {
|
|
16
20
|
view: BasePage,
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
1
|
import { MetaData, PageComponent } from './getMetaData';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { IncomingMessage } from 'http';
|
|
7
|
-
import { type NextApiRequestCookies } from 'next/dist/server/api-utils';
|
|
2
|
+
import { ContentPathItem, Context, LocaleProjectConfig } from '../utils';
|
|
3
|
+
import { GetStaticPropsResult } from 'next';
|
|
8
4
|
type Result = {
|
|
9
5
|
error?: {
|
|
10
6
|
code: string;
|
|
11
7
|
message: string;
|
|
12
8
|
} | null;
|
|
13
9
|
};
|
|
14
|
-
type GuillotineResult = Result & {
|
|
10
|
+
export type GuillotineResult = Result & {
|
|
15
11
|
[dataKey: string]: any;
|
|
16
12
|
};
|
|
17
13
|
export type FetchContentResult = Result & {
|
|
@@ -20,37 +16,23 @@ export type FetchContentResult = Result & {
|
|
|
20
16
|
meta: MetaData;
|
|
21
17
|
page: PageComponent | null;
|
|
22
18
|
};
|
|
23
|
-
export interface ServerSideParams extends ParsedUrlQuery {
|
|
24
|
-
contentPath?: string[];
|
|
25
|
-
mode?: string;
|
|
26
|
-
}
|
|
27
|
-
export interface PreviewParams {
|
|
28
|
-
contentPath: string[];
|
|
29
|
-
headers: Record<string, string>;
|
|
30
|
-
params: Record<string, string>;
|
|
31
|
-
}
|
|
32
|
-
export type Context = GetServerSidePropsContext<ServerSideParams, PreviewParams>;
|
|
33
|
-
export interface MinimalContext {
|
|
34
|
-
req: IncomingMessage & {
|
|
35
|
-
cookies: NextApiRequestCookies;
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
19
|
/**
|
|
39
20
|
* 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.
|
|
40
21
|
* @param contentPath string or string array: pre-split or slash-delimited _path to a content available on the API
|
|
41
22
|
* @returns FetchContentResult object: {data?: T, error?: {code, message}}
|
|
42
23
|
*/
|
|
43
|
-
export type ContentFetcher = (contentPath: string | string[], context
|
|
24
|
+
export type ContentFetcher = (contentPath: string | string[], context?: Context) => Promise<FetchContentResult>;
|
|
44
25
|
export type ContentApiBaseBody = {
|
|
45
26
|
query?: string;
|
|
46
27
|
variables?: {
|
|
47
28
|
path?: string;
|
|
29
|
+
[key: string]: string | number | undefined;
|
|
48
30
|
};
|
|
49
31
|
};
|
|
50
32
|
/** Generic fetch */
|
|
51
|
-
export declare const fetchFromApi: (apiUrl: string, body: ContentApiBaseBody, headers?: {}, method?: string) => Promise<any>;
|
|
33
|
+
export declare const fetchFromApi: (apiUrl: string, body: ContentApiBaseBody, projectConfig: LocaleProjectConfig, headers?: {}, method?: string) => Promise<any>;
|
|
52
34
|
/** Guillotine-specialized fetch, using the generic fetch above */
|
|
53
|
-
export declare const fetchGuillotine: (contentApiUrl: string, body: ContentApiBaseBody, headers?: {}) => Promise<GuillotineResult>;
|
|
35
|
+
export declare const fetchGuillotine: (contentApiUrl: string, body: ContentApiBaseBody, projectConfig: LocaleProjectConfig, headers?: {}) => Promise<GuillotineResult>;
|
|
54
36
|
/**
|
|
55
37
|
* Default fetchContent function, built with params from imports.
|
|
56
38
|
* It runs custom content-type-specific guillotine calls against an XP guillotine endpoint, returns content data, error and some meta data
|
|
@@ -60,4 +42,7 @@ export declare const fetchGuillotine: (contentApiUrl: string, body: ContentApiBa
|
|
|
60
42
|
* @returns FetchContentResult object: {data?: T, error?: {code, message}}
|
|
61
43
|
*/
|
|
62
44
|
export declare const fetchContent: ContentFetcher;
|
|
45
|
+
export declare function createResponse(props: FetchContentResult, context?: Context): GetStaticPropsResult<FetchContentResult>;
|
|
46
|
+
export declare function fetchContentPathsForAllLocales(path: string, query?: string, countPerLocale?: number): Promise<ContentPathItem[]>;
|
|
47
|
+
export declare function fetchContentPathsForLocale(path: string, config: LocaleProjectConfig, query?: string, count?: number): Promise<ContentPathItem[]>;
|
|
63
48
|
export {};
|
|
@@ -8,20 +8,21 @@ 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,
|
|
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, RENDER_MODE, sanitizeGraphqlName, XP_COMPONENT_TYPE, XP_REQUEST_TYPE, } from '../utils';
|
|
12
12
|
import { ComponentRegistry } from '../ComponentRegistry';
|
|
13
|
+
import { getUrl, UrlProcessor } from '../UrlProcessor';
|
|
13
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 : {}; });
|
|
14
15
|
const ALIAS_PREFIX = 'request';
|
|
15
16
|
const GUILLOTINE_QUERY_REGEXP = /^\s*query\s*(?:\((.*)*\))?\s*{\s*guillotine\s*{((?:.|\s)+)}\s*}\s*$/;
|
|
16
17
|
const GRAPHQL_FRAGMENTS_REGEXP = /fragment\s+.+\s+on\s+.+\s*{[\s\w{}().,:"'`]+}/;
|
|
17
18
|
/** Generic fetch */
|
|
18
|
-
export const fetchFromApi = (apiUrl, body, headers, method = 'POST') => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
+
export const fetchFromApi = (apiUrl, body, projectConfig, headers, method = 'POST') => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
20
|
const options = {
|
|
20
21
|
method,
|
|
21
22
|
headers: {
|
|
22
23
|
'Accept': 'application/json',
|
|
23
24
|
'Content-Type': 'application/json',
|
|
24
|
-
'X-Guillotine-SiteKey':
|
|
25
|
+
'X-Guillotine-SiteKey': projectConfig.site,
|
|
25
26
|
},
|
|
26
27
|
body: JSON.stringify(body),
|
|
27
28
|
};
|
|
@@ -64,7 +65,7 @@ export const fetchFromApi = (apiUrl, body, headers, method = 'POST') => __awaite
|
|
|
64
65
|
return json;
|
|
65
66
|
});
|
|
66
67
|
/** Guillotine-specialized fetch, using the generic fetch above */
|
|
67
|
-
export const fetchGuillotine = (contentApiUrl, body, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
68
|
+
export const fetchGuillotine = (contentApiUrl, body, projectConfig, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
68
69
|
var _b;
|
|
69
70
|
if (typeof body.query !== 'string' || !body.query.trim()) {
|
|
70
71
|
return {
|
|
@@ -75,20 +76,20 @@ export const fetchGuillotine = (contentApiUrl, body, headers) => __awaiter(void
|
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
const path = (_b = body.variables) === null || _b === void 0 ? void 0 : _b.path;
|
|
78
|
-
const pathMessage = path ?
|
|
79
|
-
const result = yield fetchFromApi(contentApiUrl, body, headers)
|
|
79
|
+
const pathMessage = path ? JSON.stringify(path) : '';
|
|
80
|
+
const result = yield fetchFromApi(contentApiUrl, body, projectConfig, headers)
|
|
80
81
|
.then(json => {
|
|
81
82
|
let errors = (json || {}).errors;
|
|
82
83
|
if (errors) {
|
|
83
84
|
if (!Array.isArray(errors)) {
|
|
84
85
|
errors = [errors];
|
|
85
86
|
}
|
|
86
|
-
console.
|
|
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)}`);
|
|
87
90
|
errors.forEach(error => {
|
|
88
91
|
console.error(error);
|
|
89
92
|
});
|
|
90
|
-
console.warn(`Query:\n${body.query}`);
|
|
91
|
-
console.warn(`Variables: ${JSON.stringify(body.variables, null, 2)}`);
|
|
92
93
|
return {
|
|
93
94
|
error: {
|
|
94
95
|
code: '500',
|
|
@@ -110,7 +111,7 @@ export const fetchGuillotine = (contentApiUrl, body, headers) => __awaiter(void
|
|
|
110
111
|
return result;
|
|
111
112
|
});
|
|
112
113
|
// /////////////////////////////////////////////////////////////////////////////// Specific fetch wrappers:
|
|
113
|
-
const fetchMetaData = (contentApiUrl, xpContentPath, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
114
|
+
const fetchMetaData = (contentApiUrl, xpContentPath, projectConfig, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
114
115
|
var _c;
|
|
115
116
|
const body = {
|
|
116
117
|
query: getMetaQuery(pageFragmentQuery()),
|
|
@@ -118,7 +119,7 @@ const fetchMetaData = (contentApiUrl, xpContentPath, headers) => __awaiter(void
|
|
|
118
119
|
path: xpContentPath,
|
|
119
120
|
},
|
|
120
121
|
};
|
|
121
|
-
const metaResult = yield fetchGuillotine(contentApiUrl, body, headers);
|
|
122
|
+
const metaResult = yield fetchGuillotine(contentApiUrl, body, projectConfig, headers);
|
|
122
123
|
if (metaResult.error) {
|
|
123
124
|
return metaResult;
|
|
124
125
|
}
|
|
@@ -128,12 +129,12 @@ const fetchMetaData = (contentApiUrl, xpContentPath, headers) => __awaiter(void
|
|
|
128
129
|
};
|
|
129
130
|
}
|
|
130
131
|
});
|
|
131
|
-
const fetchContentData = (contentApiUrl, xpContentPath, query, variables, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
132
|
+
const fetchContentData = (contentApiUrl, xpContentPath, projectConfig, query, variables, headers) => __awaiter(void 0, void 0, void 0, function* () {
|
|
132
133
|
const body = { query };
|
|
133
134
|
if (variables && Object.keys(variables).length > 0) {
|
|
134
135
|
body.variables = variables;
|
|
135
136
|
}
|
|
136
|
-
const contentResults = yield fetchGuillotine(contentApiUrl, body, headers);
|
|
137
|
+
const contentResults = yield fetchGuillotine(contentApiUrl, body, projectConfig, headers);
|
|
137
138
|
if (contentResults.error) {
|
|
138
139
|
return contentResults;
|
|
139
140
|
}
|
|
@@ -245,8 +246,8 @@ function buildComponentTree(comps, rootComp, contentType) {
|
|
|
245
246
|
if (isFragmentType) {
|
|
246
247
|
rootComp.regions = rootTree;
|
|
247
248
|
}
|
|
248
|
-
comps.forEach(cmp => {
|
|
249
|
-
var _a;
|
|
249
|
+
(comps || []).forEach(cmp => {
|
|
250
|
+
var _a, _b, _c;
|
|
250
251
|
const cmpPath = parseComponentPath(contentType, cmp.path);
|
|
251
252
|
if (cmp.path === '/') {
|
|
252
253
|
if (cmp.type === XP_COMPONENT_TYPE.PAGE && rootComp.type === XP_COMPONENT_TYPE.PAGE) {
|
|
@@ -264,12 +265,12 @@ function buildComponentTree(comps, rootComp, contentType) {
|
|
|
264
265
|
}
|
|
265
266
|
if (cmp.type === XP_COMPONENT_TYPE.FRAGMENT) {
|
|
266
267
|
// build component subtree for fragment
|
|
267
|
-
cmp = buildComponentTree(cmp.fragment.fragment.components, cmp, contentType);
|
|
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);
|
|
268
269
|
}
|
|
269
270
|
const region = getParentRegion(rootTree, contentType, cmpPath, comps, true);
|
|
270
271
|
if (region) {
|
|
271
272
|
// getting the index of component from string like '/main/0/left/11'
|
|
272
|
-
const cmpIndex = (
|
|
273
|
+
const cmpIndex = (_c = cmpPath[cmpPath.length - 1]) === null || _c === void 0 ? void 0 : _c.index;
|
|
273
274
|
if (cmpIndex >= 0) {
|
|
274
275
|
region.components.splice(cmpIndex, 0, cmp);
|
|
275
276
|
}
|
|
@@ -369,6 +370,7 @@ function getComponentConfig(cmp) {
|
|
|
369
370
|
return cmpData && 'config' in cmpData ? cmpData.config : undefined;
|
|
370
371
|
}
|
|
371
372
|
function collectComponentDescriptors(components, componentRegistry, xpContentPath, context) {
|
|
373
|
+
var _a, _b;
|
|
372
374
|
const descriptors = [];
|
|
373
375
|
for (const cmp of (components || [])) {
|
|
374
376
|
processComponentConfig(APP_NAME, APP_NAME_DASHED, cmp);
|
|
@@ -391,7 +393,7 @@ function collectComponentDescriptors(components, componentRegistry, xpContentPat
|
|
|
391
393
|
}
|
|
392
394
|
else {
|
|
393
395
|
// look for parts inside fragments
|
|
394
|
-
const fragPartDescs = collectComponentDescriptors(cmp.fragment.fragment.components, componentRegistry, xpContentPath, context);
|
|
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);
|
|
395
397
|
if (fragPartDescs.length) {
|
|
396
398
|
descriptors.push(...fragPartDescs);
|
|
397
399
|
}
|
|
@@ -467,7 +469,7 @@ function createPageData(contentType, components, componentPath) {
|
|
|
467
469
|
}
|
|
468
470
|
return page;
|
|
469
471
|
}
|
|
470
|
-
function createMetaData(contentType, contentPath, requestType, renderMode, apiUrl, baseUrl, requestedComponentPath, pageCmp, components = []) {
|
|
472
|
+
function createMetaData(contentType, contentPath, requestType, renderMode, apiUrl, baseUrl, locale, defaultLocale, requestedComponentPath, pageCmp, components = []) {
|
|
471
473
|
var _a;
|
|
472
474
|
// .meta will be visible in final rendered inline props.
|
|
473
475
|
// Only adding some .meta attributes here on certain conditions
|
|
@@ -481,6 +483,8 @@ function createMetaData(contentType, contentPath, requestType, renderMode, apiUr
|
|
|
481
483
|
catchAll: false,
|
|
482
484
|
apiUrl,
|
|
483
485
|
baseUrl,
|
|
486
|
+
locale,
|
|
487
|
+
defaultLocale,
|
|
484
488
|
};
|
|
485
489
|
if (requestedComponentPath) {
|
|
486
490
|
meta.requestedComponent = components.find(cmp => cmp.path === requestedComponentPath);
|
|
@@ -504,7 +508,7 @@ function createMetaData(contentType, contentPath, requestType, renderMode, apiUr
|
|
|
504
508
|
}
|
|
505
509
|
return meta;
|
|
506
510
|
}
|
|
507
|
-
function errorResponse(code = '500', message = 'Unknown error', requestType, renderMode, apiUrl, baseUrl, contentPath) {
|
|
511
|
+
function errorResponse(code = '500', message = 'Unknown error', requestType, renderMode, apiUrl, baseUrl, locale, defaultLocale, contentPath) {
|
|
508
512
|
return {
|
|
509
513
|
error: {
|
|
510
514
|
code,
|
|
@@ -522,6 +526,8 @@ function errorResponse(code = '500', message = 'Unknown error', requestType, ren
|
|
|
522
526
|
catchAll: false,
|
|
523
527
|
apiUrl,
|
|
524
528
|
baseUrl,
|
|
529
|
+
locale,
|
|
530
|
+
defaultLocale,
|
|
525
531
|
},
|
|
526
532
|
};
|
|
527
533
|
}
|
|
@@ -578,11 +584,18 @@ const buildContentFetcher = (config) => {
|
|
|
578
584
|
const { APP_NAME, APP_NAME_DASHED, componentRegistry, } = config;
|
|
579
585
|
return (contentPathOrArray, context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
580
586
|
var _a, _b;
|
|
581
|
-
|
|
587
|
+
if (context.preview) {
|
|
588
|
+
populateEnonicHeaders(context);
|
|
589
|
+
}
|
|
590
|
+
const headers = {};
|
|
591
|
+
addJsessionHeaders(headers, context);
|
|
592
|
+
addLocaleHeaders(headers, context);
|
|
582
593
|
const xpBaseUrl = getXpBaseUrl(context);
|
|
583
594
|
const contentApiUrl = getContentApiUrl(context);
|
|
595
|
+
const projectConfig = getLocaleProjectConfig(context);
|
|
584
596
|
const renderMode = getRenderMode(context);
|
|
585
597
|
let requestType = XP_REQUEST_TYPE.TYPE;
|
|
598
|
+
UrlProcessor.setSiteKey(projectConfig.site);
|
|
586
599
|
try {
|
|
587
600
|
const requestContentPath = getCleanContentPathArrayOrThrow400(contentPathOrArray);
|
|
588
601
|
const [siteRelativeContentPath, componentPath] = getContentAndComponentPaths(requestContentPath, context);
|
|
@@ -591,30 +604,30 @@ const buildContentFetcher = (config) => {
|
|
|
591
604
|
requestType = XP_REQUEST_TYPE.COMPONENT;
|
|
592
605
|
}
|
|
593
606
|
// ///////////// FIRST GUILLOTINE CALL FOR METADATA /////////////////
|
|
594
|
-
const metaResult = yield fetchMetaData(contentApiUrl, '${site}/' + siteRelativeContentPath, headers);
|
|
607
|
+
const metaResult = yield fetchMetaData(contentApiUrl, '${site}/' + siteRelativeContentPath, projectConfig, headers);
|
|
595
608
|
// ///////////////////////////////////////////////////////////////////////
|
|
596
609
|
const { _path, type } = metaResult.meta || {};
|
|
597
610
|
const contentPath = _path || siteRelativeContentPath;
|
|
598
611
|
if (metaResult.error) {
|
|
599
612
|
console.error(metaResult.error);
|
|
600
|
-
return errorResponse(metaResult.error.code, metaResult.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
613
|
+
return errorResponse(metaResult.error.code, metaResult.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
601
614
|
}
|
|
602
615
|
if (!metaResult.meta) {
|
|
603
|
-
return errorResponse('404', 'No meta data found for content, most likely content does not exist', requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
616
|
+
return errorResponse('404', 'No meta data found for content, most likely content does not exist', requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
604
617
|
}
|
|
605
618
|
else if (!type) {
|
|
606
|
-
return errorResponse('500', "Server responded with incomplete meta data: missing content 'type' attribute.", requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
619
|
+
return errorResponse('500', "Server responded with incomplete meta data: missing content 'type' attribute.", requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
607
620
|
}
|
|
608
621
|
else if (renderMode === RENDER_MODE.NEXT && !IS_DEV_MODE &&
|
|
609
622
|
(type === FRAGMENT_CONTENTTYPE_NAME ||
|
|
610
623
|
type === PAGE_TEMPLATE_CONTENTTYPE_NAME ||
|
|
611
624
|
type === PAGE_TEMPLATE_FOLDER)) {
|
|
612
|
-
return errorResponse('404', `Content type [${type}] is not accessible in ${renderMode} mode`, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
625
|
+
return errorResponse('404', `Content type [${type}] is not accessible in ${renderMode} mode`, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
613
626
|
}
|
|
614
627
|
const components = restrictComponentsToPath(type, metaResult.meta.components, componentPath);
|
|
615
628
|
if (componentPath && !components.length) {
|
|
616
629
|
// component was not found
|
|
617
|
-
return errorResponse('404', `Component ${componentPath} was not found`, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
630
|
+
return errorResponse('404', `Component ${componentPath} was not found`, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
618
631
|
}
|
|
619
632
|
if (requestType !== XP_REQUEST_TYPE.COMPONENT && components.length > 0) {
|
|
620
633
|
requestType = XP_REQUEST_TYPE.PAGE;
|
|
@@ -653,14 +666,14 @@ const buildContentFetcher = (config) => {
|
|
|
653
666
|
}
|
|
654
667
|
const { query, variables } = combineMultipleQueries(allDescriptors);
|
|
655
668
|
if (!query.trim()) {
|
|
656
|
-
return errorResponse('400', `Missing or empty query override for content type ${type}`, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
669
|
+
return errorResponse('400', `Missing or empty query override for content type ${type}`, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
657
670
|
}
|
|
658
671
|
// /////////////// SECOND GUILLOTINE CALL FOR DATA //////////////////////
|
|
659
|
-
const contentResults = yield fetchContentData(contentApiUrl, contentPath, query, variables, headers);
|
|
672
|
+
const contentResults = yield fetchContentData(contentApiUrl, contentPath, projectConfig, query, variables, headers);
|
|
660
673
|
// ///////////////////////////////////////////////////////////////////////////
|
|
661
674
|
if (contentResults.error) {
|
|
662
675
|
console.error(contentResults.error);
|
|
663
|
-
return errorResponse(contentResults.error.code, contentResults.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPath);
|
|
676
|
+
return errorResponse(contentResults.error.code, contentResults.error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPath);
|
|
664
677
|
}
|
|
665
678
|
// Apply processors to every component
|
|
666
679
|
const datas = yield applyProcessors(allDescriptors, contentResults, context);
|
|
@@ -696,7 +709,7 @@ const buildContentFetcher = (config) => {
|
|
|
696
709
|
}
|
|
697
710
|
}
|
|
698
711
|
const page = createPageData(type, components);
|
|
699
|
-
const meta = createMetaData(type, siteRelativeContentPath, requestType, renderMode, contentApiUrl, xpBaseUrl, componentPath, page, components);
|
|
712
|
+
const meta = createMetaData(type, siteRelativeContentPath, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, componentPath, page, components);
|
|
700
713
|
return {
|
|
701
714
|
data: contentData,
|
|
702
715
|
common,
|
|
@@ -716,7 +729,7 @@ const buildContentFetcher = (config) => {
|
|
|
716
729
|
message: e.message,
|
|
717
730
|
};
|
|
718
731
|
}
|
|
719
|
-
return errorResponse(error.code, error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, contentPathOrArray.toString());
|
|
732
|
+
return errorResponse(error.code, error.message, requestType, renderMode, contentApiUrl, xpBaseUrl, context.locale, context.defaultLocale, contentPathOrArray.toString());
|
|
720
733
|
}
|
|
721
734
|
});
|
|
722
735
|
};
|
|
@@ -730,3 +743,80 @@ const buildContentFetcher = (config) => {
|
|
|
730
743
|
* @returns FetchContentResult object: {data?: T, error?: {code, message}}
|
|
731
744
|
*/
|
|
732
745
|
export const fetchContent = buildContentFetcher(Object.assign(Object.assign({}, adapterConstants), { componentRegistry: ComponentRegistry }));
|
|
746
|
+
export function createResponse(props, context) {
|
|
747
|
+
var _a, _b, _c, _d;
|
|
748
|
+
const { data, meta, error } = props;
|
|
749
|
+
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;
|
|
750
|
+
if (meta.type === 'base:shortcut' && pageUrl) {
|
|
751
|
+
if (meta.renderMode !== RENDER_MODE.NEXT) {
|
|
752
|
+
// console.debug(`Returning 404 for shortcut in ${RENDER_MODE.NEXT} mode`);
|
|
753
|
+
// do not show shortcut targets in preview/edit mode
|
|
754
|
+
return {
|
|
755
|
+
notFound: true,
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
const destination = getUrl(pageUrl, meta, true);
|
|
759
|
+
// console.debug('Redirecting to', destination);
|
|
760
|
+
return {
|
|
761
|
+
redirect: {
|
|
762
|
+
statusCode: 307,
|
|
763
|
+
destination,
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
// we can not set 418 for static paths,
|
|
768
|
+
// but we can show 404 instead to be handled in CS
|
|
769
|
+
const canNotRender = meta && !meta.canRender && meta.renderMode !== RENDER_MODE.EDIT;
|
|
770
|
+
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);
|
|
771
|
+
const notFound = (error && error.code === '404') || ((_d = context.res) === null || _d === void 0 ? void 0 : _d.statusCode) === 404 || canNotRender || catchAllInNextProdMode || undefined;
|
|
772
|
+
// console.debug('Returning view ' + (notFound ? '(not found)' : ''));
|
|
773
|
+
return {
|
|
774
|
+
notFound,
|
|
775
|
+
props,
|
|
776
|
+
revalidate: 3600, // In seconds, meaning every hour
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function populateEnonicHeaders(context) {
|
|
780
|
+
const pd = context.previewData;
|
|
781
|
+
if (!pd) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const req = context.req || {};
|
|
785
|
+
req.headers = Object.assign(req.headers || {}, pd.headers);
|
|
786
|
+
context.params = Object.assign(context.params || {}, pd.params);
|
|
787
|
+
context.req = req;
|
|
788
|
+
}
|
|
789
|
+
export function fetchContentPathsForAllLocales(path, query = GET_STATIC_PATHS_QUERY, countPerLocale) {
|
|
790
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
791
|
+
const promises = Object.values(getLocaleProjectConfigs()).map(config => fetchContentPathsForLocale(path, config, query, countPerLocale));
|
|
792
|
+
return Promise.all(promises).then(results => {
|
|
793
|
+
return results.reduce((all, localePaths) => all.concat(localePaths), []);
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
export function fetchContentPathsForLocale(path, config, query = GET_STATIC_PATHS_QUERY, count) {
|
|
798
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
799
|
+
const contentApiUrl = getContentApiUrl({ locale: config.locale });
|
|
800
|
+
const body = {
|
|
801
|
+
query,
|
|
802
|
+
variables: {
|
|
803
|
+
path,
|
|
804
|
+
count,
|
|
805
|
+
},
|
|
806
|
+
};
|
|
807
|
+
return fetchGuillotine(contentApiUrl, body, config).then((results) => {
|
|
808
|
+
return results.guillotine.query.reduce((prev, child) => {
|
|
809
|
+
var _a;
|
|
810
|
+
const regexp = new RegExp(`/${(_a = child.site) === null || _a === void 0 ? void 0 : _a._name}/?`);
|
|
811
|
+
const contentPath = child._path.replace(regexp, '');
|
|
812
|
+
prev.push({
|
|
813
|
+
params: {
|
|
814
|
+
contentPath: contentPath.split('/'),
|
|
815
|
+
},
|
|
816
|
+
locale: normalizeLocale(config.locale),
|
|
817
|
+
});
|
|
818
|
+
return prev;
|
|
819
|
+
}, []);
|
|
820
|
+
});
|
|
821
|
+
});
|
|
822
|
+
}
|
|
@@ -74,6 +74,7 @@ export interface PageData {
|
|
|
74
74
|
regions?: RegionTree;
|
|
75
75
|
}
|
|
76
76
|
export interface FragmentData {
|
|
77
|
+
id: string;
|
|
77
78
|
fragment: {
|
|
78
79
|
components: PageComponent[];
|
|
79
80
|
};
|
|
@@ -88,4 +89,6 @@ export interface MetaData {
|
|
|
88
89
|
catchAll: boolean;
|
|
89
90
|
apiUrl: string;
|
|
90
91
|
baseUrl: string;
|
|
92
|
+
locale: string;
|
|
93
|
+
defaultLocale: string;
|
|
91
94
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enonic/nextjs-adapter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-B1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -13,22 +13,22 @@
|
|
|
13
13
|
"lint:fix": "eslint --fix server && eslint --fix src"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"html-react-parser": "^
|
|
17
|
-
"lru-cache": "^
|
|
18
|
-
"next": "
|
|
19
|
-
"react": "
|
|
20
|
-
"react-dom": "
|
|
16
|
+
"html-react-parser": "^4.2.0",
|
|
17
|
+
"lru-cache": "^10.0.0",
|
|
18
|
+
"next": "13.4.12",
|
|
19
|
+
"react": "18.2.0",
|
|
20
|
+
"react-dom": "18.2.0",
|
|
21
21
|
"unescape": "^1.0.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@enonic/eslint-config": "^1.1.0",
|
|
25
|
-
"@types/node": "^
|
|
26
|
-
"@types/react": "
|
|
27
|
-
"@types/react-dom": "
|
|
28
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
29
|
-
"@typescript-eslint/parser": "^5.
|
|
30
|
-
"eslint": "^8.
|
|
31
|
-
"eslint-plugin-react": "^7.
|
|
32
|
-
"typescript": "^5.
|
|
25
|
+
"@types/node": "^20.4.4",
|
|
26
|
+
"@types/react": "18.2.15",
|
|
27
|
+
"@types/react-dom": "18.2.7",
|
|
28
|
+
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
|
29
|
+
"@typescript-eslint/parser": "^5.62.0",
|
|
30
|
+
"eslint": "^8.45.0",
|
|
31
|
+
"eslint-plugin-react": "^7.33.0",
|
|
32
|
+
"typescript": "^5.1.6"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getShortcutQuery = "\n query($path:ID!){\n guillotine {\n get(key:$path) {\n ... on base_Shortcut {\n data {\n target {\n pageUrl(type: absolute)\n }\n }\n }\n }\n }\n }";
|