@enonic/nextjs-adapter 1.0.2 → 2.0.0-B2

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/server/index.js CHANGED
@@ -43,7 +43,7 @@ exports.withEnonicCache = exports.PURGE_CACHE_URL = void 0;
43
43
  var http_1 = require("http");
44
44
  var cache_1 = require("./cache");
45
45
  var path_1 = __importDefault(require("path"));
46
- var host = process.env.HOST || '127.0.0.1';
46
+ var hostname = process.env.HOST || '127.0.0.1';
47
47
  var dev = process.env.NODE_ENV !== 'production';
48
48
  var port = process.env.PORT && parseInt(process.env.PORT, 10) || (dev ? 3000 : 4242);
49
49
  exports.PURGE_CACHE_URL = '/_/enonic/cache/purge';
@@ -61,59 +61,71 @@ exports.default = (function (next) { return __awaiter(void 0, void 0, void 0, fu
61
61
  return __generator(this, function (_a) {
62
62
  switch (_a.label) {
63
63
  case 0:
64
- nextServer = next({ dev: dev });
64
+ nextServer = next({ dev: dev, hostname: hostname, port: port });
65
65
  nextHandler = nextServer.getRequestHandler();
66
- return [4 /*yield*/, nextServer.prepare().then(function () {
67
- var nextNodeServer = nextServer['server'];
68
- var opts = nextNodeServer['serverOptions'].conf;
69
- var purgeUrl = ((opts === null || opts === void 0 ? void 0 : opts.basePath) || '') + exports.PURGE_CACHE_URL;
70
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
71
- var server = (0, http_1.createServer)(function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
72
- var parsedUrl, tokenParam, cache, pathParam;
73
- return __generator(this, function (_a) {
74
- switch (_a.label) {
75
- case 0:
76
- parsedUrl = new URL("http://".concat(host, ":").concat(port).concat(req.url || ''));
77
- if (!(parsedUrl.pathname !== purgeUrl)) return [3 /*break*/, 2];
78
- return [4 /*yield*/, nextHandler(req, res)];
79
- case 1:
80
- _a.sent();
81
- return [3 /*break*/, 5];
82
- case 2:
83
- if (!dev) return [3 /*break*/, 3];
84
- console.warn('\nCache clearing is not available in DEV mode.\n');
85
- res.setHeader('Content-Type', 'application/json');
86
- res.end(JSON.stringify({ purged: false }));
87
- return [3 /*break*/, 5];
88
- case 3:
89
- tokenParam = parsedUrl.searchParams.get('token');
90
- if (tokenParam !== process.env.API_TOKEN) {
91
- // XP hijacks 401 to show login page, so send 407 instead
92
- res.statusCode = 407;
93
- res.end(JSON.stringify({ message: 'Invalid token' }));
94
- return [2 /*return*/];
95
- }
96
- cache = nextNodeServer.getIncrementalCache({
97
- requestHeaders: Object.assign({}, req.headers),
66
+ return [4 /*yield*/, nextServer.prepare().then(function () { return __awaiter(void 0, void 0, void 0, function () {
67
+ var nextNodeServer, opts, purgeUrl, server;
68
+ var _a;
69
+ return __generator(this, function (_b) {
70
+ switch (_b.label) {
71
+ case 0: return [4 /*yield*/, ((_a = nextServer['getServer']) === null || _a === void 0 ? void 0 : _a.call(nextServer))];
72
+ case 1:
73
+ nextNodeServer = _b.sent();
74
+ if (!nextNodeServer) {
75
+ console.error('Unable to access incremental cache, cache purge endpoint will be disabled');
76
+ return [2 /*return*/, nextServer];
77
+ }
78
+ opts = nextNodeServer['serverOptions'].conf;
79
+ purgeUrl = ((opts === null || opts === void 0 ? void 0 : opts.basePath) || '') + exports.PURGE_CACHE_URL;
80
+ console.info("Staring cache purge endpoint at ".concat(purgeUrl, "..."));
81
+ server = (0, http_1.createServer)(function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
82
+ var parsedUrl, tokenParam, cache, pathParam;
83
+ return __generator(this, function (_a) {
84
+ switch (_a.label) {
85
+ case 0:
86
+ parsedUrl = new URL("http://".concat(hostname, ":").concat(port).concat(req.url || ''));
87
+ if (!(parsedUrl.pathname !== purgeUrl)) return [3 /*break*/, 2];
88
+ return [4 /*yield*/, nextHandler(req, res)];
89
+ case 1:
90
+ _a.sent();
91
+ return [3 /*break*/, 5];
92
+ case 2:
93
+ if (!dev) return [3 /*break*/, 3];
94
+ console.warn('\nCache clearing is not available in DEV mode.\n');
95
+ res.setHeader('Content-Type', 'application/json');
96
+ res.end(JSON.stringify({ purged: false }));
97
+ return [3 /*break*/, 5];
98
+ case 3:
99
+ tokenParam = parsedUrl.searchParams.get('token');
100
+ if (tokenParam !== process.env.ENONIC_API_TOKEN) {
101
+ // XP hijacks 401 to show login page, so send 407 instead
102
+ res.statusCode = 407;
103
+ res.end(JSON.stringify({ message: 'Invalid token' }));
104
+ return [2 /*return*/];
105
+ }
106
+ cache = nextNodeServer.getIncrementalCache({
107
+ requestHeaders: Object.assign({}, req.headers),
108
+ });
109
+ pathParam = parsedUrl.searchParams.get('path') || undefined;
110
+ return [4 /*yield*/, (0, cache_1.purgeCache)(cache, pathParam)];
111
+ case 4:
112
+ _a.sent();
113
+ res.setHeader('Content-Type', 'application/json');
114
+ res.end(JSON.stringify({ purged: true }));
115
+ _a.label = 5;
116
+ case 5: return [2 /*return*/];
117
+ }
98
118
  });
99
- pathParam = parsedUrl.searchParams.get('path') || undefined;
100
- return [4 /*yield*/, (0, cache_1.purgeCache)(cache, pathParam)];
101
- case 4:
102
- _a.sent();
103
- res.setHeader('Content-Type', 'application/json');
104
- res.end(JSON.stringify({ purged: true }));
105
- _a.label = 5;
106
- case 5: return [2 /*return*/];
107
- }
108
- });
109
- }); }).listen(port, host, function () {
110
- console.info("Server ready on ".concat(host, ":").concat(port));
111
- }).on('error', function (e) {
112
- console.error('Server error:', e);
113
- server.close();
119
+ }); }).listen(port, hostname, function () {
120
+ console.info("Server ready on ".concat(hostname, ":").concat(port));
121
+ }).on('error', function (e) {
122
+ console.error('Server error:', e);
123
+ server.close();
124
+ });
125
+ return [2 /*return*/, nextServer];
126
+ }
114
127
  });
115
- return nextServer;
116
- })];
128
+ }); })];
117
129
  case 1: return [2 /*return*/, _a.sent()];
118
130
  }
119
131
  });
package/utils.d.ts CHANGED
@@ -1,15 +1,24 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
1
3
  /** Import config values from .env, .env.development and .env.production */
2
- import { MinimalContext } from './guillotine/fetchContent';
4
+ import { GetServerSidePropsContext } from 'next';
5
+ import { ParsedUrlQuery } from 'node:querystring';
6
+ import { IncomingHttpHeaders } from 'http';
3
7
  export declare const IS_DEV_MODE: boolean;
8
+ export declare enum ENV_VARS {
9
+ PROJECTS = "ENONIC_PROJECTS",
10
+ API = "ENONIC_API",
11
+ API_TOKEN = "ENONIC_API_TOKEN"
12
+ }
4
13
  /** Optional utility value - defining in one place the name of the target app (the app that defines the content types, the app name is therefore part of the content type strings used both in typeselector and in query introspections) */
5
14
  export declare const APP_NAME: string;
6
15
  /** Optional utility value - derived from APP_NAME, only with underscores instead of dots */
7
16
  export declare const APP_NAME_UNDERSCORED: string;
8
17
  /** Optional utility value - derived from APP_NAME, only with dashes instead of dots */
9
18
  export declare const APP_NAME_DASHED: string;
10
- export declare const SITE_KEY: string;
11
19
  export declare const XP_BASE_URL_HEADER = "xpbaseurl";
12
20
  export declare const RENDER_MODE_HEADER = "content-studio-mode";
21
+ export declare const PROJECT_ID_HEADER = "content-studio-project";
13
22
  export declare const JSESSIONID_HEADER = "jsessionid";
14
23
  export declare const PORTAL_COMPONENT_ATTRIBUTE = "data-portal-component-type";
15
24
  export declare const PORTAL_REGION_ATTRIBUTE = "data-portal-region";
@@ -18,6 +27,16 @@ export declare const FRAGMENT_DEFAULT_REGION_NAME = "fragment";
18
27
  export declare const PAGE_TEMPLATE_CONTENTTYPE_NAME = "portal:page-template";
19
28
  export declare const PAGE_TEMPLATE_FOLDER = "portal:template-folder";
20
29
  export declare const SITE_CONTENTTYPE_NAME = "portal:site";
30
+ export declare const LOCALE_HEADER = "locale";
31
+ export declare const LOCALES_HEADER = "locales";
32
+ export declare const DEFAULT_LOCALE_HEADER = "default-locale";
33
+ /** excluded contents:
34
+ * - folder
35
+ * - shortcuts (redirect can't be used in prerender)
36
+ * - media
37
+ * - paths with '/_' in them
38
+ */
39
+ export declare const GET_STATIC_PATHS_QUERY = "query ($count: Int = 100){\n guillotine {\n query(query: \"type != 'base:folder' AND type != 'base:shortcut' AND type NOT LIKE 'media:*' AND _path NOT LIKE '*/_*'\", first: $count) {\n _name\n _path\n site {\n _name\n }\n }\n }\n}";
21
40
  export declare enum XP_REQUEST_TYPE {
22
41
  COMPONENT = "component",
23
42
  TYPE = "type",
@@ -38,9 +57,48 @@ export declare enum XP_COMPONENT_TYPE {
38
57
  FRAGMENT = "fragment",
39
58
  PAGE = "page"
40
59
  }
60
+ export interface ServerSideParams extends ParsedUrlQuery {
61
+ contentPath?: string[];
62
+ mode?: string;
63
+ }
64
+ export interface PreviewParams {
65
+ contentPath: string[];
66
+ headers: Record<string, string>;
67
+ params: Record<string, string>;
68
+ }
69
+ export type Context = GetServerSidePropsContext<ServerSideParams, PreviewParams>;
70
+ export type LocaleProjectConfig = {
71
+ project: string;
72
+ site: string;
73
+ locale: string;
74
+ };
75
+ export type LocaleProjectConfigs = {
76
+ default: LocaleProjectConfig;
77
+ [locale: string]: LocaleProjectConfig;
78
+ };
79
+ export interface MinimalContext {
80
+ req?: {
81
+ headers: IncomingHttpHeaders;
82
+ };
83
+ locale?: string;
84
+ defaultLocale?: string;
85
+ locales?: string[];
86
+ }
87
+ export interface ContentPathItem {
88
+ params: {
89
+ contentPath: string[];
90
+ };
91
+ locale?: string;
92
+ }
41
93
  export declare const getRenderMode: (context?: MinimalContext) => RENDER_MODE;
94
+ export declare function normalizeLocale(locale: string): string | undefined;
95
+ export declare function getLocaleProjectConfig(context?: MinimalContext): LocaleProjectConfig;
96
+ export declare function getLocaleProjectConfigById(projectId?: string, useDefault?: boolean): LocaleProjectConfig;
97
+ export declare function getLocaleProjectConfigs(): LocaleProjectConfigs;
98
+ export declare function fixDoubleSlashes(str: string): string;
42
99
  export declare function getContentApiUrl(context?: MinimalContext): string;
43
- export declare function getJsessionHeaders(context?: MinimalContext): Object;
100
+ export declare function addJsessionHeaders(headers?: Object, context?: MinimalContext): void;
101
+ export declare function addLocaleHeaders(headers?: Object, context?: Context): void;
44
102
  export declare const getXpBaseUrl: (context?: MinimalContext) => string;
45
103
  export declare const commonChars: (s1?: string, s2?: string) => string;
46
104
  export declare const sanitizeGraphqlName: (text: string) => string;
@@ -49,7 +107,6 @@ declare const adapterConstants: {
49
107
  APP_NAME: string;
50
108
  APP_NAME_UNDERSCORED: string;
51
109
  APP_NAME_DASHED: string;
52
- SITE_KEY: string;
53
110
  PORTAL_COMPONENT_ATTRIBUTE: string;
54
111
  PORTAL_REGION_ATTRIBUTE: string;
55
112
  };
package/utils.js CHANGED
@@ -1,19 +1,27 @@
1
1
  const mode = process.env.MODE || process.env.NEXT_PUBLIC_MODE;
2
2
  export const IS_DEV_MODE = (mode === 'development');
3
+ export var ENV_VARS;
4
+ (function (ENV_VARS) {
5
+ ENV_VARS["PROJECTS"] = "ENONIC_PROJECTS";
6
+ ENV_VARS["API"] = "ENONIC_API";
7
+ ENV_VARS["API_TOKEN"] = "ENONIC_API_TOKEN";
8
+ })(ENV_VARS || (ENV_VARS = {}));
3
9
  /** URL to the guillotine API */
4
- const CONTENT_API_DRAFT = (process.env.CONTENT_API_DRAFT || process.env.NEXT_PUBLIC_CONTENT_API_DRAFT);
5
- const CONTENT_API_MASTER = (process.env.CONTENT_API_MASTER || process.env.NEXT_PUBLIC_CONTENT_API_MASTER);
10
+ const API_URL = (process.env.ENONIC_API || process.env.NEXT_PUBLIC_ENONIC_API);
11
+ /** Locales and Enonic XP projects correspondence list */
12
+ const PROJECTS = (process.env.ENONIC_PROJECTS || process.env.NEXT_PUBLIC_ENONIC_PROJECTS);
6
13
  /** Optional utility value - defining in one place the name of the target app (the app that defines the content types, the app name is therefore part of the content type strings used both in typeselector and in query introspections) */
7
- export const APP_NAME = (process.env.APP_NAME || process.env.NEXT_PUBLIC_APP_NAME);
14
+ export const APP_NAME = (process.env.ENONIC_APP_NAME || process.env.NEXT_PUBLIC_ENONIC_APP_NAME);
8
15
  /** Optional utility value - derived from APP_NAME, only with underscores instead of dots */
9
16
  export const APP_NAME_UNDERSCORED = (APP_NAME || '').replace(/\./g, '_');
10
17
  /** Optional utility value - derived from APP_NAME, only with dashes instead of dots */
11
18
  export const APP_NAME_DASHED = (APP_NAME || '').replace(/\./g, '-');
12
- export const SITE_KEY = (process.env.SITE_KEY || process.env.NEXT_PUBLIC_SITE_KEY);
19
+ const PROJECT_CONFIG_REGEXP = new RegExp('^(?:([\\w-]+):)?([^\\/\\s]+)(\\/[\\w-.]+)?$', 'i');
13
20
  // //////////////////////////////////////////////////////////////////////// Hardcode-able constants
14
21
  // URI parameter marking that a request is for a preview for CS. MUST MATCH THE VALUE OF 'FROM_XP_PARAM' on XP side.
15
22
  export const XP_BASE_URL_HEADER = 'xpbaseurl';
16
23
  export const RENDER_MODE_HEADER = 'content-studio-mode';
24
+ export const PROJECT_ID_HEADER = 'content-studio-project';
17
25
  export const JSESSIONID_HEADER = 'jsessionid';
18
26
  export const PORTAL_COMPONENT_ATTRIBUTE = 'data-portal-component-type';
19
27
  export const PORTAL_REGION_ATTRIBUTE = 'data-portal-region';
@@ -22,6 +30,26 @@ export const FRAGMENT_DEFAULT_REGION_NAME = 'fragment';
22
30
  export const PAGE_TEMPLATE_CONTENTTYPE_NAME = 'portal:page-template';
23
31
  export const PAGE_TEMPLATE_FOLDER = 'portal:template-folder';
24
32
  export const SITE_CONTENTTYPE_NAME = 'portal:site';
33
+ export const LOCALE_HEADER = 'locale';
34
+ export const LOCALES_HEADER = 'locales';
35
+ export const DEFAULT_LOCALE_HEADER = 'default-locale';
36
+ /** excluded contents:
37
+ * - folder
38
+ * - shortcuts (redirect can't be used in prerender)
39
+ * - media
40
+ * - paths with '/_' in them
41
+ */
42
+ export const GET_STATIC_PATHS_QUERY = `query ($count: Int = 100){
43
+ guillotine {
44
+ query(query: "type != 'base:folder' AND type != 'base:shortcut' AND type NOT LIKE 'media:*' AND _path NOT LIKE '*/_*'", first: $count) {
45
+ _name
46
+ _path
47
+ site {
48
+ _name
49
+ }
50
+ }
51
+ }
52
+ }`;
25
53
  // ------------------------------- Exports and auxillary functions derived from values above ------------------------------------
26
54
  export var XP_REQUEST_TYPE;
27
55
  (function (XP_REQUEST_TYPE) {
@@ -53,39 +81,109 @@ export const getRenderMode = (context) => {
53
81
  const enumValue = RENDER_MODE[value === null || value === void 0 ? void 0 : value.toUpperCase()];
54
82
  return enumValue || RENDER_MODE[process.env.RENDER_MODE] || RENDER_MODE.NEXT;
55
83
  };
84
+ export function normalizeLocale(locale) {
85
+ return locale !== 'default' ? locale : undefined;
86
+ }
87
+ export function getLocaleProjectConfig(context) {
88
+ var _a;
89
+ const projectId = (((_a = context === null || context === void 0 ? void 0 : context.req) === null || _a === void 0 ? void 0 : _a.headers) || {})[PROJECT_ID_HEADER];
90
+ if (projectId) {
91
+ return getLocaleProjectConfigById(projectId);
92
+ }
93
+ const projectsConfig = getLocaleProjectConfigs();
94
+ const locale = (context === null || context === void 0 ? void 0 : context.locale) || (context === null || context === void 0 ? void 0 : context.defaultLocale);
95
+ let config = projectsConfig[locale];
96
+ if (!config) {
97
+ config = projectsConfig.default;
98
+ }
99
+ if (!config) {
100
+ throw new Error(`No config for locale "${locale}" found in "${ENV_VARS.PROJECTS}" environmental variable.
101
+ Format: <default-repository-name>/<site-path>,<language>:<repository-name>/<site-path>,...`);
102
+ }
103
+ return config;
104
+ }
105
+ export function getLocaleProjectConfigById(projectId, useDefault = true) {
106
+ const projects = getLocaleProjectConfigs();
107
+ if (!projectId) {
108
+ return useDefault ? projects.default : undefined;
109
+ }
110
+ const match = Object.values(projects).find(p => {
111
+ var _a;
112
+ return ((_a = p === null || p === void 0 ? void 0 : p.project) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === projectId.toLowerCase();
113
+ });
114
+ return match || (useDefault ? projects.default : undefined);
115
+ }
116
+ export function getLocaleProjectConfigs() {
117
+ const str = PROJECTS;
118
+ const envVarName = ENV_VARS.PROJECTS;
119
+ if (!(str === null || str === void 0 ? void 0 : str.length)) {
120
+ throw Error(`"${envVarName}" environmental variable is required.`);
121
+ }
122
+ const result = str.split(',').reduce((config, prjStr) => {
123
+ const matches = PROJECT_CONFIG_REGEXP.exec(prjStr === null || prjStr === void 0 ? void 0 : prjStr.trim());
124
+ if (!(matches === null || matches === void 0 ? void 0 : matches.length)) {
125
+ return config;
126
+ }
127
+ const [full, lang = 'default', project, site] = matches;
128
+ if (project && site) {
129
+ config[lang] = {
130
+ project,
131
+ site,
132
+ locale: lang,
133
+ };
134
+ }
135
+ else {
136
+ throw Error(`Wrong configuration in "${envVarName}": ${prjStr}.
137
+ Format: <default-repository-name>/<site-path>,<language>:<repository-name>/<site-path>,...`);
138
+ }
139
+ return config;
140
+ }, {
141
+ default: {
142
+ project: '',
143
+ site: '',
144
+ locale: '',
145
+ },
146
+ });
147
+ if (!result.default.site || !result.default.project) {
148
+ throw Error(`"${envVarName}" environmental variable should contain default value.
149
+ Format: <default-repository-name>/<site-path>,<language>:<repository-name>/<site-path>,...`);
150
+ }
151
+ return result;
152
+ }
153
+ export function fixDoubleSlashes(str) {
154
+ return str.replace(/(^|[^:/])\/{2,}/g, '$1/');
155
+ }
56
156
  export function getContentApiUrl(context) {
57
- const mode = getRenderMode(context);
58
- return mode === RENDER_MODE.NEXT ? CONTENT_API_MASTER : CONTENT_API_DRAFT;
157
+ const project = getLocaleProjectConfig(context).project;
158
+ const branch = getRenderMode(context) === RENDER_MODE.NEXT ? 'master' : 'draft';
159
+ return fixDoubleSlashes(`${API_URL}/${project}/${branch}`);
59
160
  }
60
- export function getJsessionHeaders(context) {
161
+ export function addJsessionHeaders(headers = {}, context) {
61
162
  var _a;
62
- const headers = {};
63
163
  const jsessionid = (_a = context === null || context === void 0 ? void 0 : context.req) === null || _a === void 0 ? void 0 : _a.headers[JSESSIONID_HEADER];
64
164
  if (jsessionid) {
65
165
  headers['Cookie'] = `${JSESSIONID_HEADER}=${jsessionid}`;
66
166
  }
67
- return headers;
68
167
  }
69
- /** For '<a href="..."' link values in props when clicking the link should navigate to an XP content item page
70
- * and the query returns the XP _path to the target content item:
71
- * When viewed directly, the header will have a `<base href='/' />` (see src/pages/_app.tsx), and when viewed through an
72
- * XP Content Studio preview, lib-nextjs-proxy will add `<base href='xp/relevant/root/site/url/' />`.
73
- * So for content-item links to work in BOTH contexts, the href value should be the path relative to the root site item, not starting with a slash.
74
- * */
75
- // export const getContentLinkUrlFromXpPath = (_path: string): string => _path.replace(siteNamePattern, '')
76
- const xpBaseUrlMap = {};
168
+ export function addLocaleHeaders(headers = {}, context) {
169
+ const locale = context === null || context === void 0 ? void 0 : context.locale;
170
+ if (locale) {
171
+ headers[LOCALE_HEADER] = locale;
172
+ }
173
+ const locales = context === null || context === void 0 ? void 0 : context.locales;
174
+ if (locales) {
175
+ headers[LOCALES_HEADER] = JSON.stringify(locales);
176
+ }
177
+ const defaultLocale = context === null || context === void 0 ? void 0 : context.defaultLocale;
178
+ if (defaultLocale) {
179
+ headers[DEFAULT_LOCALE_HEADER] = defaultLocale;
180
+ }
181
+ }
77
182
  export const getXpBaseUrl = (context) => {
78
183
  var _a;
79
- const mode = getRenderMode(context);
80
- const existingUrl = xpBaseUrlMap[mode];
81
- if (existingUrl) {
82
- return existingUrl;
83
- }
84
184
  const header = ((((_a = context === null || context === void 0 ? void 0 : context.req) === null || _a === void 0 ? void 0 : _a.headers) || {})[XP_BASE_URL_HEADER] || '');
85
185
  // TODO: workaround for XP pattern controller mapping not picked up in edit mode
86
- const resultingUrl = (header || '/').replace(/\/edit\//, '/inline/');
87
- xpBaseUrlMap[mode] = resultingUrl;
88
- return resultingUrl;
186
+ return (header || '/').replace(/\/edit\//, '/inline/');
89
187
  };
90
188
  export const commonChars = (s1, s2) => {
91
189
  let result = '';
@@ -120,7 +218,6 @@ const adapterConstants = {
120
218
  APP_NAME,
121
219
  APP_NAME_UNDERSCORED,
122
220
  APP_NAME_DASHED,
123
- SITE_KEY,
124
221
  PORTAL_COMPONENT_ATTRIBUTE,
125
222
  PORTAL_REGION_ATTRIBUTE,
126
223
  };
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { MetaData, PageComponent } from '../guillotine/getMetaData';
3
3
  export type BaseComponentProps = {
4
4
  component: PageComponent;
@@ -10,16 +10,16 @@ export default BaseComponent;
10
10
  export declare const MissingComponent: ({ descriptor, type }: {
11
11
  descriptor?: string;
12
12
  type: string;
13
- }) => JSX.Element;
13
+ }) => React.JSX.Element;
14
14
  export declare function shouldShowMissingView(meta: MetaData): boolean;
15
15
  export declare const ErrorComponent: ({ type, descriptor, reason }: {
16
16
  type?: string;
17
17
  descriptor?: string;
18
18
  reason: string;
19
- }) => JSX.Element;
19
+ }) => React.JSX.Element;
20
20
  export declare function shouldShowErrorView(meta: MetaData): boolean;
21
21
  export declare const PlaceholderComponent: ({ type, descriptor }: {
22
22
  type?: string;
23
23
  descriptor?: string;
24
- }) => JSX.Element;
24
+ }) => React.JSX.Element;
25
25
  export declare function shouldShowPlaceholderView(meta: MetaData): boolean;
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { FetchContentResult } from '../guillotine/fetchContent';
3
- declare const BaseContent: (props: FetchContentResult) => JSX.Element;
3
+ declare const BaseContent: (props: FetchContentResult) => React.JSX.Element;
4
4
  export default BaseContent;
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { LayoutData, MetaData, RegionTree } from '../guillotine/getMetaData';
3
3
  export interface LayoutProps {
4
4
  layout: LayoutData;
@@ -13,5 +13,5 @@ interface BaseLayoutProps {
13
13
  common?: any;
14
14
  meta: MetaData;
15
15
  }
16
- declare const BaseLayout: (props: BaseLayoutProps) => JSX.Element;
16
+ declare const BaseLayout: (props: BaseLayoutProps) => React.JSX.Element;
17
17
  export default BaseLayout;
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { MacroConfig, MacroData, MetaData } from '../guillotine/getMetaData';
3
3
  export declare const MACRO_DISABLE = "system:disable";
4
4
  export declare const MACRO_EMBED = "system:embed";
@@ -12,5 +12,5 @@ export interface MacroProps {
12
12
  config: MacroConfig;
13
13
  meta: MetaData;
14
14
  }
15
- declare const BaseMacro: (props: BaseMacroProps) => JSX.Element;
15
+ declare const BaseMacro: (props: BaseMacroProps) => React.JSX.Element;
16
16
  export default BaseMacro;
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { MetaData, PageData } from '../guillotine/getMetaData';
3
3
  export interface PageProps {
4
4
  page: PageData;
@@ -15,5 +15,5 @@ export interface BasePageProps {
15
15
  error?: string;
16
16
  meta: MetaData;
17
17
  }
18
- declare const BasePage: (props: BasePageProps) => JSX.Element;
18
+ declare const BasePage: (props: BasePageProps) => React.JSX.Element;
19
19
  export default BasePage;
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { MetaData, PartData } from '../guillotine/getMetaData';
3
3
  export interface PartProps {
4
4
  part: PartData;
@@ -15,5 +15,5 @@ interface BasePartProps {
15
15
  error?: string;
16
16
  meta: MetaData;
17
17
  }
18
- declare const BasePart: (props: BasePartProps) => JSX.Element;
18
+ declare const BasePart: (props: BasePartProps) => React.JSX.Element;
19
19
  export default BasePart;
package/views/Empty.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- /// <reference types="react" />
2
- declare const Empty: () => JSX.Element;
1
+ import React from 'react';
2
+ declare const Empty: () => React.JSX.Element;
3
3
  export default Empty;
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { FragmentData, MetaData, PageData } from '../guillotine/getMetaData';
3
3
  interface FragmentProps {
4
4
  page?: PageData;
@@ -6,5 +6,5 @@ interface FragmentProps {
6
6
  common?: any;
7
7
  meta: MetaData;
8
8
  }
9
- declare const FragmentView: (props: FragmentProps) => JSX.Element;
9
+ declare const FragmentView: (props: FragmentProps) => React.JSX.Element;
10
10
  export default FragmentView;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import BaseComponent from './BaseComponent';
3
- import { FRAGMENT_DEFAULT_REGION_NAME } from '../utils';
2
+ import BaseComponent, { MissingComponent, shouldShowMissingView } from './BaseComponent';
3
+ import { FRAGMENT_DEFAULT_REGION_NAME, XP_COMPONENT_TYPE } from '../utils';
4
4
  const FragmentView = (props) => {
5
5
  var _a;
6
6
  const { component, page } = props;
@@ -13,13 +13,18 @@ const FragmentView = (props) => {
13
13
  comps.push(...regionComps);
14
14
  }
15
15
  }
16
- else if (component) {
16
+ else if (component === null || component === void 0 ? void 0 : component.fragment) {
17
17
  // rendering a part of a page
18
18
  comps.push(...component.fragment.components);
19
19
  }
20
- const { common, meta } = props;
21
- return (<>
22
- {comps.map((comp, i) => (<BaseComponent key={i} component={comp} common={common} meta={meta}/>))}
23
- </>);
20
+ if (!comps.length && shouldShowMissingView(props.meta)) {
21
+ return <MissingComponent type={XP_COMPONENT_TYPE.FRAGMENT} descriptor={(page === null || page === void 0 ? void 0 : page.descriptor) || (component === null || component === void 0 ? void 0 : component.id)}/>;
22
+ }
23
+ else {
24
+ const { common, meta } = props;
25
+ return (<>
26
+ {comps.map((comp, i) => (<BaseComponent key={i} component={comp} common={common} meta={meta}/>))}
27
+ </>);
28
+ }
24
29
  };
25
30
  export default FragmentView;
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { FetchContentResult } from '../guillotine/fetchContent';
3
- declare const _MainView: (props: FetchContentResult) => JSX.Element;
3
+ declare const _MainView: (props: FetchContentResult) => React.JSX.Element;
4
4
  export default _MainView;
package/views/Region.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { MetaData, PageComponent, PageData } from '../guillotine/getMetaData';
3
3
  export interface RegionProps {
4
4
  name: string;
@@ -14,7 +14,7 @@ export interface RegionsProps {
14
14
  meta: MetaData;
15
15
  }
16
16
  /** Single region */
17
- export declare const RegionView: (props: RegionProps) => JSX.Element;
17
+ export declare const RegionView: (props: RegionProps) => React.JSX.Element;
18
18
  /** Multiple regions, or only one if named in props.selected */
19
- declare const RegionsView: (props: RegionsProps) => JSX.Element;
19
+ declare const RegionsView: (props: RegionsProps) => React.JSX.Element;
20
20
  export default RegionsView;
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { MetaData, RichTextData } from '../guillotine/getMetaData';
3
3
  import { DOMNode } from 'html-react-parser';
4
4
  export type ReplacerResult = JSX.Element | object | void | undefined | null | false;
@@ -12,5 +12,5 @@ type Props = {
12
12
  customReplacer?: Replacer;
13
13
  };
14
14
  export declare function createReplacer(allData: RichTextData, meta: MetaData, renderMacroInEditMode?: boolean, customReplacer?: Replacer): (domNode: DOMNode) => ReplacerResult;
15
- declare const RichTextView: ({ className, tag, data, meta, renderMacroInEditMode, customReplacer }: Props) => JSX.Element;
15
+ declare const RichTextView: ({ className, tag, data, meta, renderMacroInEditMode, customReplacer }: Props) => React.JSX.Element;
16
16
  export default RichTextView;
@@ -1,5 +1,5 @@
1
1
  /** Render only one component as a result of a request from XP-side .../_/component/... during edit refresh. */
2
- /// <reference types="react" />
2
+ import React from 'react';
3
3
  import { FetchContentResult } from '../guillotine/fetchContent';
4
- declare const SingleComponent: ({ meta, common }: FetchContentResult) => JSX.Element;
4
+ declare const SingleComponent: ({ meta, common }: FetchContentResult) => React.JSX.Element;
5
5
  export default SingleComponent;