nodoku-core 0.2.3 → 0.2.5

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.
@@ -17,7 +17,7 @@ function calculateTemplateView(dirNodeModules = undefined) {
17
17
  dirNodeModules = `${path.resolve()}/node_modules`;
18
18
  }
19
19
  const manifests = loadManifestsFromFolder(dirNodeModules);
20
- const view = { modules: [], comps: [] };
20
+ const view = { modules: [], comps: [], clSideComps: [] };
21
21
  manifests.forEach((m, k) => {
22
22
  let prefix = "";
23
23
  if (m.namespace) {
@@ -35,9 +35,22 @@ function calculateTemplateView(dirNodeModules = undefined) {
35
35
  else {
36
36
  nb = `${cd.numBlocks}`;
37
37
  }
38
- view.comps.push({ name: cn, impl: `${prefix}${cd.implementation}`, defaultThemeFile: "./" + path.relative(path.resolve("."), path.resolve("./schemas", m.moduleName, cd.defaultThemeFile)).replaceAll("\\", "/"), numBlocks: nb });
38
+ view.comps.push({
39
+ name: cn,
40
+ impl: `${prefix}${cd.implementation}`,
41
+ defaultThemeFile: "./" + path.relative(path.resolve("."), path.resolve("./schemas", m.moduleName, cd.defaultThemeFile)).replaceAll("\\", "/"),
42
+ numBlocks: nb
43
+ });
44
+ if (cd.clientSideComps) {
45
+ cd.clientSideComps.forEach(cl => {
46
+ view.clSideComps.push({ n: `${cn}:${cl}`, isLast: false });
47
+ });
48
+ }
39
49
  });
40
50
  });
51
+ if (view.clSideComps && view.clSideComps.length > 0) {
52
+ view.clSideComps[view.clSideComps.length - 1].isLast = true;
53
+ }
41
54
  return view;
42
55
  }
43
56
  export function generateComponentResolver() {
@@ -48,7 +48,8 @@ function loadComponentsByManifest(dir, moduleName) {
48
48
  themeSchema: v.schemaFile,
49
49
  optionsSchema: v.optionsFile,
50
50
  defaultThemeFile: v.defaultThemeFile,
51
- numBlocks: v.numBlocks
51
+ numBlocks: v.numBlocks,
52
+ clientSideComps: v.clientSideComps
52
53
  });
53
54
  });
54
55
  return manifest;
@@ -10,6 +10,29 @@ const components: Map<string, {compo: AsyncFunctionComponent, compoDef: NdCompon
10
10
  components.set("{{{name}}}", {compo: {{impl}}, compoDef: new NdComponentDefinition({{{numBlocks}}}, "{{{defaultThemeFile}}}")});
11
11
  {{/comps}}
12
12
 
13
+ {{^clSideComps.length}}
14
+ export type ClientSideComponentNameEnum = void;
15
+ {{/clSideComps.length}}
16
+
17
+ {{#clSideComps.length}}
18
+ export type ClientSideComponentNameEnum =
19
+ {{#clSideComps.length}}
20
+ {{#clSideComps}}
21
+ "{{{n}}}"{{^isLast}} | {{/isLast}}{{#isLast}};{{/isLast}}
22
+ {{/clSideComps}}
23
+ {{/clSideComps.length}}
24
+
25
+ export const fromStringToClientSideComponentNameEnum = (c: string): ClientSideComponentNameEnum | undefined => {
26
+ {{#clSideComps}}
27
+ if (c === "{{{n}}}") {
28
+ return "{{{n}}}";
29
+ }
30
+ {{/clSideComps}}
31
+ return undefined;
32
+ }
33
+
34
+ {{/clSideComps.length}}
35
+
13
36
  export async function nodokuComponentResolver(componentName: string): Promise<{compo: AsyncFunctionComponent, compoDef: NdComponentDefinition}> {
14
37
  const f: {compo: AsyncFunctionComponent, compoDef: NdComponentDefinition} | undefined = components.get(componentName);
15
38
  return f ? f : {compo: DummyComp, compoDef: new NdComponentDefinition(1)};
@@ -41,6 +41,14 @@ export class NdCode {
41
41
  this.code = code;
42
42
  }
43
43
  }
44
+ export class NdLink {
45
+ urlText;
46
+ url;
47
+ constructor(text, url) {
48
+ this.urlText = text;
49
+ this.url = url;
50
+ }
51
+ }
44
52
  export class NdContentBlock {
45
53
  id;
46
54
  lng;
@@ -1,8 +1,9 @@
1
1
  import { NdCode, NdTranslatableText } from "../content/nd-content";
2
+ import { NdLink } from "../content/nd-content";
2
3
  export async function DummyComp(props) {
3
4
  // console.log("content dummy comp", props.theme)
4
- const { content, i18nextProvider, lng, rowIndex, componentIndex } = props;
5
- const { t } = await i18nextProvider(lng);
5
+ const { content, i18nextTrustedHtmlProvider, lng, rowIndex, componentIndex } = props;
6
+ const { t } = await i18nextTrustedHtmlProvider(lng);
6
7
  return <div>{await render(rowIndex, componentIndex, content[0], t)}</div>;
7
8
  }
8
9
  async function render(rowIndex, componentIndex, block, t) {
@@ -18,51 +19,21 @@ async function render(rowIndex, componentIndex, block, t) {
18
19
  {block.title && <a href="#">
19
20
  {block.title && block.title.key}
20
21
  <h5 className={"mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"}>
21
- {block.title && t(block.title)}
22
+ {block.title && t(block.title).__html}
22
23
  </h5>
23
24
  </a>}
24
25
  {block.subTitle && block.subTitle.key}
25
26
  {block.subTitle && <h6 className={"mb-2 text-xl tracking-tight text-gray-900 dark:text-white"}>
26
- {block.subTitle && t(block.subTitle)}
27
+ {block.subTitle && t(block.subTitle).__html}
27
28
  </h6>}
28
29
 
29
30
  paragraphs:
30
- {await Promise.all(block.paragraphs.map(async (p, ip) => {
31
- if (p instanceof NdTranslatableText) {
32
- return (<div>
33
- {p && p.key}
34
- <p key={ip} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
35
- {p && t(p)}
36
- </p>
37
- </div>);
38
- }
39
- else if (p instanceof NdCode) {
40
- const code = p;
41
- return (<div className={"border border-gray-200 p-4"}>
42
- <pre className={"text-pretty"}>
43
- <code lang={code.lang} className={"hljs"} dangerouslySetInnerHTML={{ __html: code.code }}/>
44
- </pre>
45
- </div>);
46
- }
47
- else {
48
- const list = p;
49
- if (list.ordered) {
50
- return (<ol className={"list-disc list-outside"}>
51
- {list.items.map(i => <li className={"ml-4"}>{t(i)} <small>(<i>{i.key}</i>)</small></li>)}
52
- </ol>);
53
- }
54
- else {
55
- return (<ul className={"list-disc list-outside"}>
56
- {list.items.map(i => <li className={"ml-4"}>{t(i)} <small>(<i>{i.key}</i>)</small></li>)}
57
- </ul>);
58
- }
59
- }
60
- }))}
31
+ {await Promise.all(block.paragraphs.map(async (p, ip) => renderParagraph(p, ip)))}
61
32
  images:
62
33
  {block.images.map((img, ii) => {
63
34
  return (<div>
64
35
  <p key={"url" + ii} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
65
- url: {img && img.url && t(img.url)}
36
+ url: {img && img.url && t(img.url).__html}
66
37
  {img.url && <span className={"bg-cover bg-no-repeat"} style={{
67
38
  display: "block",
68
39
  width: "200px",
@@ -71,10 +42,10 @@ async function render(rowIndex, componentIndex, block, t) {
71
42
  }}></span>}
72
43
  </p>
73
44
  <p key={"alt" + ii} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
74
- alt: {img && img.alt && t(img.alt)}
45
+ alt: {img && img.alt && t(img.alt).__html}
75
46
  </p>
76
47
  <p key={"title" + ii} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
77
- title: {img && img.title && t(img.title)}
48
+ title: {img && img.title && t(img.title).__html}
78
49
  </p>
79
50
  </div>);
80
51
  })}
@@ -88,4 +59,60 @@ async function render(rowIndex, componentIndex, block, t) {
88
59
  </div>
89
60
 
90
61
  </div>);
62
+ function renderParagraph(p, ip) {
63
+ if (p instanceof NdTranslatableText) {
64
+ return (<div>
65
+ {p && p.key}
66
+ <p key={ip} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
67
+ {p && t(p).__html}
68
+ </p>
69
+ </div>);
70
+ }
71
+ else if (p instanceof NdCode) {
72
+ const code = p;
73
+ return (<div className={"border border-gray-200 p-4"}>
74
+ <pre className={"text-pretty"}>
75
+ <code lang={code.lang} className={"hljs"} dangerouslySetInnerHTML={{ __html: code.code }}/>
76
+ </pre>
77
+ </div>);
78
+ }
79
+ else if (p instanceof NdLink) {
80
+ const link = p;
81
+ return (<div>
82
+ link
83
+ <div key={ip} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
84
+ urlText: {link.urlText && showTranslatableText(link.urlText)}
85
+ url: {showTranslatableText(link.url)}
86
+ </div>
87
+
88
+ </div>);
89
+ }
90
+ else {
91
+ const list = p;
92
+ if (list.ordered) {
93
+ return (<ol className={"list-disc list-outside"}>
94
+ {list.items.map(i => <li className={"ml-4"}>
95
+ {showListItem(i)}
96
+ {i.subList && renderParagraph(i.subList, ip)}
97
+ </li>)}
98
+ </ol>);
99
+ }
100
+ else {
101
+ return (<ul className={"list-disc list-outside"}>
102
+ {list.items.map(i => <li className={"ml-4"}>
103
+ {showListItem(i)}
104
+ {i.subList && renderParagraph(i.subList, ip)}
105
+ </li>)}
106
+ </ul>);
107
+ }
108
+ }
109
+ }
110
+ }
111
+ function showTranslatableText(text) {
112
+ return <span>{text.text} <small>(<i>{text.key}</i>)</small></span>;
113
+ }
114
+ function showListItem(i) {
115
+ return i.text instanceof NdTranslatableText ?
116
+ showTranslatableText(i.text) :
117
+ <span>link item {(i.text.urlText ? showTranslatableText(i.text.urlText) : "link text n/a")} : {showTranslatableText(i.text.url)}</span>;
91
118
  }
@@ -23,7 +23,10 @@ export class RenderingPageProps {
23
23
  componentResolver = undefined;
24
24
  imageProvider = undefined;
25
25
  i18nextProvider = undefined;
26
- constructor(lng, content, componentResolver, skin = undefined, renderingPriority = RenderingPriority.content_first, imageProvider = undefined, i18nextProvider = undefined) {
26
+ i18nextPostProcessor;
27
+ htmlSanitizer;
28
+ clientSideComponentProvider;
29
+ constructor(lng, content, componentResolver, skin = undefined, renderingPriority = RenderingPriority.content_first, imageProvider = undefined, i18nextProvider = undefined, htmlSanitizer = undefined, clientSideComponentProvider = undefined) {
27
30
  this.lng = lng;
28
31
  this.renderingPriority = renderingPriority;
29
32
  this.content = content;
@@ -31,5 +34,7 @@ export class RenderingPageProps {
31
34
  this.skin = skin;
32
35
  this.imageProvider = imageProvider;
33
36
  this.i18nextProvider = i18nextProvider;
37
+ this.htmlSanitizer = htmlSanitizer;
38
+ this.clientSideComponentProvider = clientSideComponentProvider;
34
39
  }
35
40
  }
@@ -16,7 +16,7 @@ async function defaultImageProvider(imageProps) {
16
16
  return <img className={`${imageStyle?.base} ${imageStyle?.decoration}`} src={url} alt={alt}/>;
17
17
  }
18
18
  async function RenderingPage(props) {
19
- const { lng, renderingPriority, content, componentResolver, skin, imageProvider, i18nextProvider } = props;
19
+ const { lng, renderingPriority, content, componentResolver, skin, imageProvider, i18nextProvider, i18nextPostProcessor, htmlSanitizer, clientSideComponentProvider } = props;
20
20
  const actualComponentResolver = componentResolver ? componentResolver : defaultComponentResolver;
21
21
  let l;
22
22
  if (skin) {
@@ -26,10 +26,10 @@ async function RenderingPage(props) {
26
26
  }
27
27
  // console.log(" >>> this is my content <<< ", content)
28
28
  // console.log(" >>> this is my skin <<< ", JSON.stringify(blockSkin))
29
- l = await Promise.all(blockSkin.rows.map(async (row, iRow) => await createRow(row, iRow, content, lng, imageProvider, i18nextProvider, actualComponentResolver)));
29
+ l = await Promise.all(blockSkin.rows.map(async (row, iRow) => await createRow(row, iRow, content, lng, imageProvider, i18nextProvider, i18nextPostProcessor, actualComponentResolver, htmlSanitizer, clientSideComponentProvider)));
30
30
  }
31
31
  else {
32
- l = [await createRow(undefined, 0, content, lng, imageProvider, i18nextProvider, actualComponentResolver)];
32
+ l = [await createRow(undefined, 0, content, lng, imageProvider, i18nextProvider, i18nextPostProcessor, actualComponentResolver, htmlSanitizer, clientSideComponentProvider)];
33
33
  }
34
34
  const actualSkin = mergeTheme(skin, { renderingPage: { base: "", decoration: "" }, rows: [] });
35
35
  return <div className={`rows-container ${ts(actualSkin, "renderingPage")}`}>{l}</div>;
@@ -66,13 +66,13 @@ function generateSkinByContentBlocks(blocks, skin) {
66
66
  // console.log("generated skin", JSON.stringify(res))
67
67
  return res;
68
68
  }
69
- async function createRow(row, iRow, blocks, lng, imageProvider, i18nProvider, componentResolver) {
69
+ async function createRow(row, iRow, blocks, lng, imageProvider, i18nProvider, i18nextPostProcessor, componentResolver, htmlSanitizer, clientSideComponentProvider) {
70
70
  let l;
71
71
  if (row) {
72
- l = await Promise.all(row.components.map(async (visualSection, iComp) => await createRowComponents(iRow, iComp, visualSection, blocks, lng, imageProvider, i18nProvider, componentResolver)));
72
+ l = await Promise.all(row.components.map(async (visualSection, iComp) => await createRowComponents(iRow, iComp, visualSection, blocks, lng, imageProvider, i18nProvider, i18nextPostProcessor, componentResolver, htmlSanitizer, clientSideComponentProvider)));
73
73
  }
74
74
  else {
75
- l = await Promise.all(blocks.map(async (block, iComp) => await createRowComponents(iRow, iComp, undefined, [block], lng, imageProvider, i18nProvider, componentResolver)));
75
+ l = await Promise.all(blocks.map(async (block, iComp) => await createRowComponents(iRow, iComp, undefined, [block], lng, imageProvider, i18nProvider, i18nextPostProcessor, componentResolver, htmlSanitizer, clientSideComponentProvider)));
76
76
  }
77
77
  const rowComponents = l.flatMap((p) => p);
78
78
  if (rowComponents.length == 0) {
@@ -144,7 +144,7 @@ async function createRow(row, iRow, blocks, lng, imageProvider, i18nProvider, co
144
144
  </div>)}
145
145
  </div>);
146
146
  }
147
- async function createRowComponents(rowIndex, blockIndex, skinComponent, pageContent, lng, imageProvider, i18nProvider, componentResolver) {
147
+ async function createRowComponents(rowIndex, blockIndex, skinComponent, pageContent, lng, imageProvider, i18nProvider, i18nextPostProcessor, componentResolver, htmlSanitizer, clientSideComponentProvider) {
148
148
  // console.log("before component", skinComponent)
149
149
  const filteredBlocks = skinComponent ? skinComponent.selector.filterBlocks(pageContent) : pageContent;
150
150
  if (filteredBlocks.length == 0) {
@@ -170,17 +170,67 @@ async function createRowComponents(rowIndex, blockIndex, skinComponent, pageCont
170
170
  const blocks = filteredBlocks.slice(start, end);
171
171
  const compIndex = blockIndex * filteredBlocks.length + i;
172
172
  // console.log("calculated compo index ", compIndex, "filteredBlocks.length", filteredBlocks.length, "blockIndex", blockIndex)
173
- res.push(await renderSingleComponent(rowIndex, compIndex, compo, blocks, skinComponent?.defaultThemeName || "light", compoDef.defaultTheme, skinComponent?.themeHierarchy, lng, imageProvider, i18nProvider));
173
+ res.push(await renderSingleComponent(rowIndex, compIndex, compo, blocks, skinComponent?.defaultThemeName || "light", compoDef.defaultTheme, skinComponent?.themeHierarchy, lng, imageProvider, i18nProvider, i18nextPostProcessor, htmlSanitizer, clientSideComponentProvider));
174
174
  }
175
175
  start = end;
176
176
  ++i;
177
177
  } while (end < filteredBlocks.length);
178
178
  return res;
179
179
  }
180
- async function renderSingleComponent(rowIndex, componentIndex, component, blocks, defaultThemeName, defaultTheme, themeHierarchy, lng, imageProvider, i18nextProvider) {
181
- let actualI18nextProvider;
180
+ function wrapInPostProcessor(provider, postProcessor) {
181
+ // if (postProcessor) {
182
+ //
183
+ // return async (lng: string): Promise<{t: (text: NdTranslatableText) => NdTrustedHtml}> => {
184
+ //
185
+ // const {t} = await provider(lng)
186
+ // const tt = (txt: NdTranslatableText): NdTrustedHtml => {
187
+ // // const translated: NdTrustedHtml = t(txt);
188
+ // // let res = translated;
189
+ // // if (typeof translated === "string") {
190
+ // // res = postProcessor(translated)
191
+ // // }
192
+ // // return res;
193
+ // return postProcessor(t(txt))
194
+ // }
195
+ //
196
+ // return {t: tt}
197
+ // }
198
+ // } else {
199
+ // return provider;
200
+ // }
201
+ return async (lng) => {
202
+ const { t } = await provider(lng);
203
+ const tt = (txt) => {
204
+ // const translated: NdTrustedHtml = t(txt);
205
+ // let res = translated;
206
+ // if (typeof translated === "string") {
207
+ // res = postProcessor(translated)
208
+ // }
209
+ // return res;
210
+ return postProcessor ? postProcessor(t(txt)) : t(txt);
211
+ };
212
+ return { t: tt };
213
+ };
214
+ }
215
+ function makeTrusted(provider, sanitizer) {
216
+ return async (lng) => {
217
+ const { t } = await provider(lng);
218
+ const tt = (txt) => {
219
+ // const translated: NdTrustedHtml = t(txt);
220
+ // let res = translated;
221
+ // if (typeof translated === "string") {
222
+ // res = postProcessor(translated)
223
+ // }
224
+ // return res;
225
+ return sanitizer(t(txt));
226
+ };
227
+ return { t: tt };
228
+ };
229
+ }
230
+ async function renderSingleComponent(rowIndex, componentIndex, component, blocks, defaultThemeName, defaultTheme, themeHierarchy, lng, imageProvider, i18nextProvider, i18nextPostProcessor, htmlSanitizer, clientSideComponentProvider) {
231
+ let actualI18nextProvider_;
182
232
  if (lng == blocks[0].lng || !i18nextProvider) {
183
- actualI18nextProvider = async (lng) => {
233
+ actualI18nextProvider_ = async (lng) => {
184
234
  return { t: (text) => {
185
235
  const b = blocks.map((b) => b.getByKey(text.key, text.ns)).find((s) => s);
186
236
  return b ? b : `key not found: ${text.ns}:${text.key}`;
@@ -188,9 +238,11 @@ async function renderSingleComponent(rowIndex, componentIndex, component, blocks
188
238
  };
189
239
  }
190
240
  else {
191
- actualI18nextProvider = i18nextProvider;
241
+ actualI18nextProvider_ = i18nextProvider;
192
242
  }
243
+ const actualI18nextProvider = makeTrusted(wrapInPostProcessor(actualI18nextProvider_, i18nextPostProcessor), htmlSanitizer || ((text) => ({ __html: text })));
193
244
  const actualImageProvider = imageProvider ? imageProvider : defaultImageProvider;
245
+ const actualClientSideComponentProvider = clientSideComponentProvider ? clientSideComponentProvider : (c) => <span>[placeholder for: {c}]</span>;
194
246
  // if (themeHierarchy) {
195
247
  // console.log("themeHierarchy", themeHierarchy)
196
248
  // }
@@ -206,7 +258,8 @@ async function renderSingleComponent(rowIndex, componentIndex, component, blocks
206
258
  options: effectiveOptions,
207
259
  lng: lng,
208
260
  imageProvider: actualImageProvider,
209
- i18nextProvider: actualI18nextProvider
261
+ i18nextTrustedHtmlProvider: actualI18nextProvider,
262
+ clientSideComponentProvider: actualClientSideComponentProvider
210
263
  };
211
264
  // console.log("start rendering page with props", props);
212
265
  const res = await component(props);
package/esm/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { NdContentImage, NdTranslatableText, NdList, NdCode, NdCallToAction, NdContentBlock } from "./content/nd-content";
1
+ import { NdContentImage, NdTranslatableText, NdList, NdLink, NdCode, NdCallToAction, NdContentBlock } from "./content/nd-content";
2
2
  import { NdSkinComponent, NdRow, NdPageSkin, NdSkinComponentProps, NdContentSelector, NdComponentDefinition, NdThemeHierarchy } from "./skin/nd-skin";
3
3
  import { RenderingPageProps, RenderingPriority } from "./core/rendering-page-props";
4
4
  import { RenderingPage } from "./core/rendering-page";
5
5
  import { mergeTheme } from "./theme-utils/theme-merger";
6
6
  import { defaultRowThemeImpl } from "./theme-utils/row-style";
7
- export { NdContentImage, NdTranslatableText, NdList, NdCode, NdCallToAction, NdContentBlock, NdSkinComponent, NdRow, NdPageSkin, NdSkinComponentProps, NdContentSelector, NdComponentDefinition, NdThemeHierarchy };
7
+ export { NdContentImage, NdTranslatableText, NdList, NdLink, NdCode, NdCallToAction, NdContentBlock, NdSkinComponent, NdRow, NdPageSkin, NdSkinComponentProps, NdContentSelector, NdComponentDefinition, NdThemeHierarchy };
8
8
  export { RenderingPageProps, RenderingPriority, RenderingPage };
9
9
  export { mergeTheme };
10
10
  export { DummyComp } from "./core/dummy-comp";
@@ -15,4 +15,31 @@ export const ts = function (t, key) {
15
15
  const k = key;
16
16
  return k + " " + themeStyle?.base + " " + themeStyle?.decoration;
17
17
  };
18
+ export const tsi = function (t, key, i) {
19
+ // console.log("in tsi ", key, t[key], Array.isArray(t[key]), [1, 2, 3], Array.isArray([1, 2, 3]))
20
+ if (typeof t[key] === 'object') {
21
+ // console.log("this is tsi object", key)
22
+ const len = Object.keys(t[key]).length;
23
+ const obj = t[key];
24
+ const themeStyle = obj[i % len];
25
+ const k = key;
26
+ return k + `[${i}]` + themeStyle?.base + " " + themeStyle?.decoration;
27
+ }
28
+ else if (Array.isArray(t[key])) {
29
+ // console.log("this is tsi array", key)
30
+ const len = t[key].length;
31
+ const themeStyle = t[key][i % len];
32
+ const k = key;
33
+ return k + `[${i}]` + themeStyle?.base + " " + themeStyle?.decoration;
34
+ }
35
+ else {
36
+ return ts(t, key);
37
+ }
38
+ };
39
+ export const extractValueFromText = (text, attrName) => {
40
+ if (text && text.__html.startsWith(`{${attrName}}`)) {
41
+ return { __html: text.__html.substring(`{${attrName}}`.length) };
42
+ }
43
+ return text;
44
+ };
18
45
  export const defaultRowTheme = defaultRowThemeImpl;
@@ -1,8 +1,9 @@
1
1
  import { NdTranslatableText, NdList, NdContentBlock, NdContentImage, NdCode } from "nodoku-core";
2
2
  import { Marked } from '@ts-stack/markdown';
3
- import { parse } from 'node-html-parser';
3
+ import { parse, TextNode } from 'node-html-parser';
4
4
  import yaml from "js-yaml";
5
5
  import { NdCallToAction } from "../../content/nd-content";
6
+ import { NdLink } from "../../content/nd-content";
6
7
  const nsRegex = /.*\/(.*)\.md/;
7
8
  export async function contentMarkdownProvider(mdFileUrl, contentLng, ns = undefined) {
8
9
  return await fetch(mdFileUrl)
@@ -203,7 +204,7 @@ class BlockHolder {
203
204
  text = newBlock.h6;
204
205
  }
205
206
  else if (this.blockContent.paragraphsIndex.indexOf(i) >= 0) {
206
- const para = this.parseParagraph(blockId, htmlElem, pi);
207
+ const para = this.parseParagraph(`${blockId}.paragraphs.${pi}`, htmlElem);
207
208
  if (para) {
208
209
  newBlock.paragraphs.push(para);
209
210
  text = para;
@@ -285,9 +286,23 @@ class BlockHolder {
285
286
  // console.log("returnin created cta: ", cta)
286
287
  return cta;
287
288
  }
288
- parseParagraph(blockId, p, pi) {
289
+ parseParagraph(idPrefix, cn) {
290
+ if (!cn) {
291
+ return undefined;
292
+ }
293
+ if (cn instanceof TextNode) {
294
+ return new NdTranslatableText(this.ns, `${idPrefix}`, cn.text);
295
+ }
296
+ const p = cn;
297
+ if (!p.rawTagName) {
298
+ return undefined;
299
+ }
300
+ console.log("parsing paragraph", p.innerHTML);
289
301
  if (p.rawTagName === "p" || p.rawTagName === "blockquote") {
290
- return new NdTranslatableText(this.ns, `${blockId}.paragraphs.${pi}`, p.innerHTML);
302
+ return new NdTranslatableText(this.ns, `${idPrefix}`, p.innerHTML);
303
+ }
304
+ else if (p.rawTagName === "a") {
305
+ return new NdLink(new NdTranslatableText(this.ns, `${idPrefix}.urlText`, p.innerHTML), new NdTranslatableText(this.ns, `${idPrefix}.url`, p.attributes["href"], true));
291
306
  }
292
307
  else if (p.rawTagName === "pre") {
293
308
  const codeHtml = p.childNodes[0];
@@ -301,24 +316,49 @@ class BlockHolder {
301
316
  }
302
317
  }
303
318
  else if (p.rawTagName === "ol") {
304
- return NdList.createOrdered(p.childNodes
305
- .filter(lin => lin.innerText && lin.innerText.trim().length > 0)
306
- .map((lin, k) => {
307
- const li = lin;
308
- return new NdTranslatableText(this.ns, `${blockId}.paragraphs.${pi}.items.${k}`, li.innerHTML);
309
- }));
319
+ return NdList.createOrdered(this.parseListItems(idPrefix, p));
310
320
  }
311
321
  else if (p.rawTagName === "ul") {
312
- return NdList.createUnOrdered(p.childNodes
313
- .filter(lin => lin.innerText && lin.innerText.trim().length > 0)
314
- .map((lin, k) => {
315
- const li = lin;
316
- return new NdTranslatableText(this.ns, `${blockId}.paragraphs.${pi}.items.${k}`, li.innerHTML);
317
- }));
318
- }
319
- console.log("couldn't parse paragraph: ", p);
322
+ return NdList.createUnOrdered(this.parseListItems(idPrefix, p));
323
+ } /*else {
324
+ return new NdTranslatableText(this.ns, `${idPrefix}`, p.innerHTML);
325
+ }*/
326
+ console.log("couldn't parse paragraph: ", p.innerHTML);
320
327
  return undefined;
321
328
  }
329
+ parseListItems(idPrefix, p) {
330
+ return p.childNodes
331
+ .filter(lin => lin.innerText && lin.innerText.trim().length > 0)
332
+ .map((lin, k) => {
333
+ const li = lin;
334
+ let innerList = undefined;
335
+ let liText; //TextNode | undefined;
336
+ if (li.childNodes.length > 0) {
337
+ console.log("found inner item", li.childNodes.length, (li.childNodes.map(i => i)
338
+ .map((i1) => i1.rawTagName ? i1.rawTagName : "N.A")), "<<<");
339
+ console.log("found inner list", li.childNodes[0].innerText);
340
+ innerList = li.childNodes
341
+ .map(cn => this.parseParagraph(`${idPrefix}.items.${k}.subList`, cn))
342
+ .find((p) => p != undefined && p instanceof NdList);
343
+ liText = li.childNodes
344
+ .map(cn => this.parseParagraph(`${idPrefix}.items.${k}.text`, cn))
345
+ .find((p) => p != undefined && (p instanceof NdLink || p instanceof NdTranslatableText));
346
+ // innerList = innerNodes.find((p: NdParagraph | undefined) => p != undefined && p instanceof NdList)
347
+ // if (li.childNodes[0].rawTagName in ["ol", "ul"]) {
348
+ // }
349
+ // liText = li.childNodes.filter(cn => cn instanceof TextNode).find(t => t && t.text.length > 0);
350
+ // liText = innerNodes.find((p: NdParagraph | undefined) => p != undefined && (p instanceof NdLink || p instanceof NdTranslatableText))
351
+ }
352
+ // type l = keyof HTMLElement
353
+ // console.log("found list", li.childNodes.filter(cn => Object.keys(cn).map(k => k == "parentNode" ? "parent" : (cn as HTMLElement)[k as l])), "<<", lin.innerText, "<<")
354
+ // const foundList = {text: new NdTranslatableText(this.ns, `${idPrefix}.items.${k}`, liText)/*liText*/, subList: innerList};
355
+ const foundList = { text: liText ? liText : new NdTranslatableText(this.ns, `${idPrefix}.items.${k}`, "n/a"), subList: innerList };
356
+ console.log("found list", ">>", foundList, "<<");
357
+ return foundList;
358
+ // const listItem = this.parseParagraph(`${idPrefix}.items.${k}`, li)
359
+ // return listItem ? listItem : new NdTranslatableText(this.ns, `${idPrefix}.items.${k}`, "undefined list item" + li.innerHTML);
360
+ }).filter(lin => lin != undefined);
361
+ }
322
362
  }
323
363
  export function parseMarkdownAsContent(fileContents, contentLng, ns) {
324
364
  Marked.setOptions({ isNoP: true });
@@ -118,8 +118,9 @@ export class NdSkinComponentProps {
118
118
  options;
119
119
  lng;
120
120
  imageProvider;
121
- i18nextProvider;
122
- constructor(rowIndex, componentIndex, content, defaultThemeName, theme, themes, options, lng, imageProvider, i18nextProvider) {
121
+ i18nextTrustedHtmlProvider;
122
+ clientSideComponentProvider;
123
+ constructor(rowIndex, componentIndex, content, defaultThemeName, theme, themes, options, lng, imageProvider, i18nextTrustedHtmlProvider, clientSideComponentProvider) {
123
124
  this.rowIndex = rowIndex;
124
125
  this.componentIndex = componentIndex;
125
126
  this.content = content;
@@ -129,6 +130,7 @@ export class NdSkinComponentProps {
129
130
  this.options = options;
130
131
  this.lng = lng;
131
132
  this.imageProvider = imageProvider;
132
- this.i18nextProvider = i18nextProvider;
133
+ this.i18nextTrustedHtmlProvider = i18nextTrustedHtmlProvider;
134
+ this.clientSideComponentProvider = clientSideComponentProvider;
133
135
  }
134
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodoku-core",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "basic foundation for nodoku static site generator",
5
5
  "exports": {
6
6
  ".": {
@@ -34,6 +34,12 @@
34
34
  "type": "number"
35
35
  }
36
36
  ]
37
+ },
38
+ "clientSideComps": {
39
+ "type": "array",
40
+ "items": {
41
+ "type": "string"
42
+ }
37
43
  }
38
44
  },
39
45
  "required": [
@@ -4,6 +4,7 @@ export type ComponentDef = {
4
4
  optionsSchema: string;
5
5
  defaultThemeFile: string;
6
6
  numBlocks: string | number;
7
+ clientSideComps: string[];
7
8
  };
8
9
  export type Manifest = {
9
10
  moduleName: string;
@@ -15,18 +15,28 @@ export declare class NdTranslatableText {
15
15
  excludeFromTranslation: boolean;
16
16
  constructor(ns: string, key?: string, text?: string, excludeFromTranslation?: boolean);
17
17
  }
18
+ export type NdListItem = {
19
+ text: NdTranslatableText | NdLink;
20
+ subList: NdParagraph | undefined;
21
+ };
18
22
  export declare class NdList {
19
- items: NdTranslatableText[];
23
+ items: NdListItem[];
20
24
  ordered: boolean;
21
25
  private constructor();
22
- static createOrdered(items: NdTranslatableText[]): NdList;
23
- static createUnOrdered(items: NdTranslatableText[]): NdList;
26
+ static createOrdered(items: NdListItem[]): NdList;
27
+ static createUnOrdered(items: NdListItem[]): NdList;
24
28
  }
25
29
  export declare class NdCode {
26
30
  lang: string;
27
31
  code: string;
28
32
  constructor(lang: string, code: string);
29
33
  }
34
+ export declare class NdLink {
35
+ urlText: NdTranslatableText | undefined;
36
+ url: NdTranslatableText;
37
+ constructor(text: NdTranslatableText, url: NdTranslatableText);
38
+ }
39
+ export type NdParagraph = NdTranslatableText | NdList | NdCode | NdLink;
30
40
  export declare class NdContentBlock {
31
41
  id: string;
32
42
  lng: string;
@@ -43,7 +53,7 @@ export declare class NdContentBlock {
43
53
  h5?: NdTranslatableText;
44
54
  h6?: NdTranslatableText;
45
55
  callToActions: NdCallToAction[];
46
- paragraphs: (NdTranslatableText | NdList | NdCode)[];
56
+ paragraphs: NdParagraph[];
47
57
  images: NdContentImage[];
48
58
  htmlElements: {
49
59
  htmlElem: HTMLElement;
@@ -8,12 +8,21 @@ export type NdImageProps = {
8
8
  title?: string;
9
9
  imageStyle?: ImageStyle;
10
10
  };
11
+ export type NdTrustedHtml = {
12
+ __html: TrustedHTML;
13
+ };
11
14
  export type AsyncFunctionComponent = (props: NdSkinComponentProps) => Promise<JSX.Element>;
12
15
  export type ComponentResolver = (componentName: string) => Promise<{
13
16
  compo: AsyncFunctionComponent;
14
17
  compoDef: NdComponentDefinition;
15
18
  }>;
16
- export type I18nextProvider = (lng: string) => Promise<{
19
+ export type NdI18nextProvider = (lng: string) => Promise<{
17
20
  t: (text: NdTranslatableText) => string;
18
21
  }>;
19
- export type ImageProvider = (imageProps: NdImageProps) => Promise<JSX.Element>;
22
+ export type NdI18nextTrustedHtmlProvider = (lng: string) => Promise<{
23
+ t: (text: NdTranslatableText) => NdTrustedHtml;
24
+ }>;
25
+ export type NdImageProvider = (imageProps: NdImageProps) => Promise<JSX.Element>;
26
+ export type NdI18NextPostProcessor = (text: string) => string;
27
+ export type NdHtmlSanitizer = (text: string) => NdTrustedHtml;
28
+ export type NdClientSideComponentProvider = (componentName: string) => JSX.Element;
@@ -1,6 +1,9 @@
1
- import { ComponentResolver, I18nextProvider, ImageProvider } from "./providers";
1
+ import { ComponentResolver, NdI18nextProvider, NdImageProvider } from "./providers";
2
2
  import { NdContentBlock } from "../content/nd-content";
3
3
  import { NdPageSkin } from "../skin/nd-skin";
4
+ import { NdI18NextPostProcessor } from "./providers";
5
+ import { NdHtmlSanitizer } from "./providers";
6
+ import { NdClientSideComponentProvider } from "./providers";
4
7
  export declare enum RenderingPriority {
5
8
  content_first = 0,
6
9
  skin_first = 1
@@ -11,7 +14,10 @@ export declare class RenderingPageProps {
11
14
  skin: NdPageSkin | undefined;
12
15
  renderingPriority: RenderingPriority;
13
16
  componentResolver: ComponentResolver | undefined;
14
- imageProvider: ImageProvider | undefined;
15
- i18nextProvider: I18nextProvider | undefined;
16
- constructor(lng: string, content: NdContentBlock[], componentResolver: ComponentResolver | undefined, skin?: NdPageSkin | undefined, renderingPriority?: RenderingPriority, imageProvider?: ImageProvider | undefined, i18nextProvider?: I18nextProvider | undefined);
17
+ imageProvider: NdImageProvider | undefined;
18
+ i18nextProvider: NdI18nextProvider | undefined;
19
+ i18nextPostProcessor: NdI18NextPostProcessor | undefined;
20
+ htmlSanitizer: NdHtmlSanitizer | undefined;
21
+ clientSideComponentProvider: NdClientSideComponentProvider | undefined;
22
+ constructor(lng: string, content: NdContentBlock[], componentResolver: ComponentResolver | undefined, skin?: NdPageSkin | undefined, renderingPriority?: RenderingPriority, imageProvider?: NdImageProvider | undefined, i18nextProvider?: NdI18nextProvider | undefined, htmlSanitizer?: NdHtmlSanitizer | undefined, clientSideComponentProvider?: NdClientSideComponentProvider | undefined);
17
23
  }
package/types/index.d.ts CHANGED
@@ -1,19 +1,21 @@
1
- import { NdContentImage, NdTranslatableText, NdList, NdCode, NdCallToAction, NdContentBlock } from "./content/nd-content";
1
+ import { NdContentImage, NdTranslatableText, NdList, NdLink, NdListItem, NdCode, NdCallToAction, NdParagraph, NdContentBlock } from "./content/nd-content";
2
2
  import { NdSkinComponent, NdRow, NdPageSkin, NdSkinComponentProps, NdContentSelector, NdComponentDefinition, NdThemeHierarchy, NdDefaultThemeName } from "./skin/nd-skin";
3
3
  import { RenderingPageProps, RenderingPriority } from "./core/rendering-page-props";
4
4
  import { RenderingPage } from "./core/rendering-page";
5
- import { I18nextProvider, AsyncFunctionComponent, ImageProvider, NdImageProps } from "./core/providers";
5
+ import { NdI18nextProvider, AsyncFunctionComponent, NdImageProvider, NdImageProps, NdI18nextTrustedHtmlProvider, NdHtmlSanitizer, NdTrustedHtml, NdI18NextPostProcessor } from "./core/providers";
6
6
  import { mergeTheme } from "./theme-utils/theme-merger";
7
7
  import { ThemeStyle } from "./theme-utils/theme-style";
8
8
  import { ExtendedThemeStyle } from "./theme-utils/extended-theme-style";
9
9
  import { ImageStyle } from "./theme-utils/image-style";
10
10
  import { RowStyle } from "./theme-utils/row-style";
11
- export { NdContentImage, NdTranslatableText, NdList, NdCode, NdCallToAction, NdContentBlock, NdSkinComponent, NdRow, NdPageSkin, NdSkinComponentProps, NdContentSelector, NdComponentDefinition, NdThemeHierarchy, type NdDefaultThemeName, type NdImageProps };
11
+ export { NdContentImage, NdTranslatableText, NdList, type NdListItem, NdLink, NdCode, NdCallToAction, NdContentBlock, NdSkinComponent, NdRow, NdPageSkin, NdSkinComponentProps, NdContentSelector, NdComponentDefinition, NdThemeHierarchy, type NdParagraph, type NdDefaultThemeName, type NdImageProps, type NdI18nextTrustedHtmlProvider, type NdHtmlSanitizer, type NdTrustedHtml, type NdI18NextPostProcessor };
12
12
  export { RenderingPageProps, RenderingPriority, RenderingPage };
13
13
  export { mergeTheme, type ThemeStyle, type ExtendedThemeStyle, type ImageStyle };
14
- export type { I18nextProvider, AsyncFunctionComponent, ImageProvider };
14
+ export type { NdI18nextProvider, AsyncFunctionComponent, NdImageProvider };
15
15
  export { DummyComp } from "./core/dummy-comp";
16
16
  export { contentMarkdownProvider, parseMarkdownAsContent } from "./providers/content/content-markdown-provider";
17
17
  export { skinYamlProvider, parseYamlContentAsSkin } from "./providers/skin/skin-yaml-provider";
18
18
  export declare const ts: <T>(t: T, key: keyof T) => string;
19
+ export declare const tsi: <T>(t: T, key: keyof T, i: number) => string;
20
+ export declare const extractValueFromText: (text: NdTrustedHtml | undefined, attrName: string) => NdTrustedHtml | undefined;
19
21
  export declare const defaultRowTheme: RowStyle;
@@ -1,7 +1,9 @@
1
- import { I18nextProvider, ImageProvider } from "../core/providers";
1
+ import { NdImageProvider } from "../core/providers";
2
2
  import { NdContentBlock } from "../content/nd-content";
3
3
  import { ThemeStyle } from "../theme-utils/theme-style";
4
4
  import { RowStyle } from "../theme-utils/row-style";
5
+ import { NdI18nextTrustedHtmlProvider } from "../core/providers";
6
+ import { NdClientSideComponentProvider } from "../core/providers";
5
7
  export type NdDefaultThemeName = "light" | "dark";
6
8
  export declare class NdThemeHierarchy {
7
9
  defaultThemeName: NdDefaultThemeName;
@@ -69,7 +71,8 @@ export declare class NdSkinComponentProps<TComponentTheme = any, TComponentOptio
69
71
  themes: TComponentTheme[];
70
72
  options: TComponentOptions | undefined;
71
73
  lng: string;
72
- imageProvider: ImageProvider;
73
- i18nextProvider: I18nextProvider;
74
- constructor(rowIndex: number, componentIndex: number, content: NdContentBlock[], defaultThemeName: NdDefaultThemeName, theme: TComponentTheme, themes: TComponentTheme[], options: TComponentOptions, lng: string, imageProvider: ImageProvider, i18nextProvider: I18nextProvider);
74
+ imageProvider: NdImageProvider;
75
+ i18nextTrustedHtmlProvider: NdI18nextTrustedHtmlProvider;
76
+ clientSideComponentProvider: NdClientSideComponentProvider;
77
+ constructor(rowIndex: number, componentIndex: number, content: NdContentBlock[], defaultThemeName: NdDefaultThemeName, theme: TComponentTheme, themes: TComponentTheme[], options: TComponentOptions, lng: string, imageProvider: NdImageProvider, i18nextTrustedHtmlProvider: NdI18nextTrustedHtmlProvider, clientSideComponentProvider: NdClientSideComponentProvider);
75
78
  }