nodoku-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/LICENSE +21 -0
  2. package/README.MD +46 -0
  3. package/dist/esm/bin/generate-component-resolver.js +112 -0
  4. package/dist/esm/bin/generate-content-schema.js +30 -0
  5. package/dist/esm/bin/generate-visual-schema.js +200 -0
  6. package/dist/esm/bin/import-load-hooks.js +30 -0
  7. package/dist/esm/bin/mustache/content-schema.json.mtl +92 -0
  8. package/dist/esm/bin/mustache/default-component-resolver.ts.mtl +16 -0
  9. package/dist/esm/bin/mustache/visual-schema.json.mtl +40 -0
  10. package/dist/esm/content/lb-content-block.js +81 -0
  11. package/dist/esm/content/manifest.js +16 -0
  12. package/dist/esm/content/providers.js +1 -0
  13. package/dist/esm/core/dummy-comp.jsx +69 -0
  14. package/dist/esm/core/rendering-page-props.js +16 -0
  15. package/dist/esm/core/rendering-page.jsx +166 -0
  16. package/dist/esm/index.js +6 -0
  17. package/dist/schemas/content-common-schema.json +90 -0
  18. package/dist/schemas/manifest-schema.json +32 -0
  19. package/dist/schemas/visual-common-schema.json +29 -0
  20. package/dist/types/bin/generate-component-resolver.d.ts +2 -0
  21. package/dist/types/bin/generate-content-schema.d.ts +2 -0
  22. package/dist/types/bin/generate-visual-schema.d.ts +2 -0
  23. package/dist/types/bin/import-load-hooks.d.ts +3 -0
  24. package/dist/types/content/lb-content-block.d.ts +60 -0
  25. package/dist/types/content/manifest.d.ts +11 -0
  26. package/dist/types/content/providers.d.ts +9 -0
  27. package/dist/types/core/dummy-comp.d.ts +3 -0
  28. package/dist/types/core/rendering-page-props.d.ts +10 -0
  29. package/dist/types/core/rendering-page.d.ts +4 -0
  30. package/dist/types/index.d.ts +8 -0
  31. package/package.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 nodoku
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.MD ADDED
@@ -0,0 +1,46 @@
1
+ nodoku-core is a foundation library for the nodoku static site generator,
2
+ a Node JS - React based framework for fast page prototyping using yaml files as content and page config
3
+ The site in Nodoku is composed of two yaml files - visual and content.
4
+
5
+ # getting started
6
+
7
+ in order to use nodoku one needs to install the nodoku-core library (this one)
8
+ and at least one component library for nodoku (for example, nodoku-flowbite)
9
+
10
+
11
+ ```shell
12
+ npm install nodoku-core nodoku-flowbite
13
+ ```
14
+
15
+ Then the rendering component would be as follows:
16
+
17
+ ```tsx
18
+ <RenderingPage
19
+ pageName={"main"}
20
+ lng={lng}
21
+ contentYamlProvider={contentYamlProvider}
22
+ visualYamlProvider={visualYamlProvider}
23
+ i18nextProvider={i18nextProvider}
24
+ componentProvider={defaultComponentProvider}
25
+ />
26
+ ```
27
+
28
+ where:
29
+ - **_pageName_**: the name of the page to be rendered. This name is given to the visualYamlProvider funtion to fetch the required page layout yaml file
30
+
31
+ - **_lng_**: the page language for localization. This parameter is given further to the contentYamlProvider to fetch the content on the given language
32
+
33
+ - **_contentYamlProvider_**: function providing the textual content for the page. This function is expected to fetch the content yaml file and return its content as text for the further processing. the function signature is the following: ```(lng: string, ns: string) => Promise<string>```
34
+
35
+ - **_visualYamlProvider_**: function providing the content of a yaml file that is used to render the page - the page structure yaml file. This function is expected to fetch the content yaml file and return its content as text for the further processing. the function signature is the following: ```(pageName: string) => Promise<string>```
36
+
37
+ - **_i18nextProvider_**: the page localization provider. The signature is the following: ```(lng: string) => Promise<{t: (key: string, ns: string) => string}>```. For a given language the localization provider is supposed to return a function, that would provide the value for a given key and namespace
38
+
39
+ - **_componentProvider_**: the function returning an actual implementation of the component, given its name, as specified in the page structure - visual yaml file. The signature is as follows: ```(componentName: string) => Promise<AsyncFunctionComponent>```, where AsyncFunctionComponent is the following function: ```(props: LbComponentProps) => Promise<JSX.Element>```
40
+
41
+ # generation scripts
42
+ to simplify the development, the nodoku-core provides the following scripts, that are used to generate component resolver, content schema and visual schema, by scanning the **node_modules** folder of the project
43
+ - **nodoku-gen-component-resolver**: generates the component resolver by scanning node_modules and searching for nodoku component libraries - the libraries providing the nodoku.manifest.json file
44
+ - **nodoku-gen-content-schema**: generates the json schema file that can be used to validate the **content** yaml file
45
+ - **nodoku-gen-visual-schema**: generates the json schema file thate can be used to validate the **visual** page yaml file
46
+
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ import { register } from 'node:module';
3
+ import { ComponentDef, Manifest } from "../content/manifest.js";
4
+ import fs from "node:fs";
5
+ import path from "path";
6
+ import Mustache from "mustache";
7
+ register('./import-load-hooks.js', import.meta.url);
8
+ /*
9
+ * we need to use a dynamic import here since otherwise we have no guarantee that the template
10
+ * file won't start loading prior to hooks registration
11
+ * we register hooks, and only then use dynamic import to load custom template file
12
+ */
13
+ // @ts-ignore
14
+ const template = (await import("./mustache/default-component-resolver.ts.mtl")).default;
15
+ function loadComponents(dir) {
16
+ const comps = new Map();
17
+ const files = fs.readdirSync(dir);
18
+ console.log("reading ...", dir);
19
+ for (const f of files) {
20
+ const stat = fs.statSync(`${dir}/${f}`);
21
+ if (stat.isDirectory()) {
22
+ const m = loadComponentsByManifest(`${dir}/${f}`, f);
23
+ if (m) {
24
+ comps.set(f, m);
25
+ }
26
+ }
27
+ }
28
+ return comps;
29
+ }
30
+ function loadComponentsByManifest(dir, moduleName) {
31
+ const files = fs.readdirSync(dir);
32
+ for (const f of files) {
33
+ const stat = fs.statSync(`${dir}/${f}`);
34
+ if (stat.isFile()) {
35
+ if (f == "nodoku.manifest.json") {
36
+ const manifest = new Manifest(moduleName);
37
+ console.log("found manifest ", `${dir}/${f}`, "reading...");
38
+ let json;
39
+ if (stat.isSymbolicLink()) {
40
+ json = JSON.parse(fs.readlinkSync(`${dir}/nodoku.manifest.json`));
41
+ }
42
+ else {
43
+ json = JSON.parse(fs.readFileSync(`${dir}/nodoku.manifest.json`).toString());
44
+ }
45
+ console.log("loaded manifest ", path.resolve(dir, f));
46
+ console.log("found manifest json ", json);
47
+ manifest.namespace = json.namespace;
48
+ Object.keys(json.components).forEach((k) => {
49
+ const v = json.components[k];
50
+ console.log("adding ", k, v);
51
+ // comps.set(k, Manifest.from(k, moduleName, v));
52
+ manifest.components.set(k, new ComponentDef(v.implementation, v.schema));
53
+ });
54
+ return manifest;
55
+ }
56
+ }
57
+ }
58
+ return undefined;
59
+ }
60
+ class TemplateView {
61
+ modules = new Map();
62
+ names = new Map();
63
+ }
64
+ function calculateTemplateView(dir = undefined) {
65
+ if (!dir) {
66
+ dir = path.resolve();
67
+ }
68
+ const components = loadComponents(`${dir}/node_modules`);
69
+ const tv = new TemplateView();
70
+ components.forEach((m, k) => {
71
+ if (!tv.modules.get(m.moduleName)) {
72
+ console.log("adding ", m.moduleName);
73
+ tv.modules.set(m.moduleName, []);
74
+ }
75
+ if (m.namespace) {
76
+ tv.modules.get(m.moduleName).push(m.namespace);
77
+ m.components.forEach((cd, cn) => {
78
+ tv.names.set(cn, `${m.namespace}.${cd.implementation}`);
79
+ });
80
+ }
81
+ else {
82
+ m.components.forEach((cd, cn) => {
83
+ tv.modules.get(m.moduleName).push(cd.implementation);
84
+ tv.names.set(cn, cd.implementation);
85
+ });
86
+ }
87
+ });
88
+ return tv;
89
+ }
90
+ export function generateComponentResolver() {
91
+ const args = process.argv.slice(2);
92
+ const dir = path.resolve();
93
+ const src = args[0] ? args[0] : "./src";
94
+ console.log("generating component resolver");
95
+ const tv = calculateTemplateView();
96
+ const view = {
97
+ modules: Array.from(tv.modules.entries()).map((e) => {
98
+ return { "module": e[0], "comps": e[1].join(", ") };
99
+ }),
100
+ names: Array.from(tv.names.entries()).map((n) => { return { n: n[0], c: n[1] }; })
101
+ };
102
+ console.log("view", JSON.stringify(view));
103
+ const output = Mustache.render(template, view);
104
+ const fileName = `${dir}/${src}/nodoku-component-resolver.ts`;
105
+ fs.writeFile(fileName, output, err => {
106
+ if (err) {
107
+ return console.error(err);
108
+ }
109
+ console.log(`${fileName}: File created!`);
110
+ });
111
+ }
112
+ generateComponentResolver();
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { register } from 'node:module';
3
+ import fs from "node:fs";
4
+ import path from "path";
5
+ import Mustache from "mustache";
6
+ register('./import-load-hooks.js', import.meta.url);
7
+ /*
8
+ * we need to use a dynamic import here since otherwise we have no guarantee that the template
9
+ * file won't start loading prior to hooks registration
10
+ * we register hooks, and only then use dynamic import to load custom template file
11
+ */
12
+ // @ts-ignore
13
+ const template = (await import("./mustache/content-schema.json.mtl")).default;
14
+ const linkRegexPattern = "^((http|https)?:\\\\/\\\\/)?\\\\/?([-a-zA-Z0-9._\\\\+~#=]{1,256})([-a-zA-Z0-9@:%._\\\\+~#=]{1,256})([-a-zA-Z0-9()@:%_\\\\+.~#?&\\\\/\\\\/=]*)$";
15
+ export function generateContentSchema() {
16
+ const args = process.argv.slice(2);
17
+ const dir = path.resolve();
18
+ const schemaSrc = args[0] ? args[0] : "./schemas";
19
+ console.log("generating content schema");
20
+ const view = { linkRegexPattern };
21
+ const output = Mustache.render(template, view);
22
+ const fileName = `${dir}/${schemaSrc}/content-schema.json`;
23
+ fs.writeFile(fileName, output, err => {
24
+ if (err) {
25
+ return console.error(err);
26
+ }
27
+ console.log(`${fileName}: File created!`);
28
+ });
29
+ }
30
+ generateContentSchema();
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env node
2
+ import { register } from 'node:module';
3
+ import { ComponentDef, Manifest } from "../content/manifest.js";
4
+ import fs from "node:fs";
5
+ import path from "path";
6
+ import Mustache from "mustache";
7
+ register('./import-load-hooks.js', import.meta.url);
8
+ /*
9
+ * we need to use a dynamic import here since otherwise we have no guarantee that the template
10
+ * file won't start loading prior to hooks registration
11
+ * we register hooks, and only then use dynamic import to load custom template file
12
+ */
13
+ // @ts-ignore
14
+ const template = (await import("./mustache/visual-schema.json.mtl")).default;
15
+ function loadComponents(dir) {
16
+ const comps = new Map();
17
+ const files = fs.readdirSync(dir);
18
+ console.log("reading ...", dir);
19
+ for (const f of files) {
20
+ const stat = fs.statSync(`${dir}/${f}`);
21
+ if (stat.isDirectory()) {
22
+ const m = loadComponentsByManifest(`${dir}/${f}`, f);
23
+ if (m) {
24
+ comps.set(f, m);
25
+ }
26
+ }
27
+ }
28
+ return comps;
29
+ }
30
+ function loadComponentsByManifest(dir, moduleName) {
31
+ const files = fs.readdirSync(dir);
32
+ for (const f of files) {
33
+ const stat = fs.statSync(`${dir}/${f}`);
34
+ if (stat.isFile()) {
35
+ if (f == "nodoku.manifest.json") {
36
+ const manifest = new Manifest(moduleName);
37
+ console.log("found manifest ", `${dir}/${f}`, "reading...");
38
+ let json;
39
+ if (stat.isSymbolicLink()) {
40
+ json = JSON.parse(fs.readlinkSync(`${dir}/nodoku.manifest.json`));
41
+ }
42
+ else {
43
+ json = JSON.parse(fs.readFileSync(`${dir}/nodoku.manifest.json`).toString());
44
+ }
45
+ console.log("loaded manifest ", path.resolve(dir, f));
46
+ console.log("found manifest json ", json);
47
+ manifest.namespace = json.namespace;
48
+ Object.keys(json.components).forEach((k) => {
49
+ const v = json.components[k];
50
+ console.log("adding ", k, v);
51
+ // comps.set(k, Manifest.from(k, moduleName, v));
52
+ manifest.components.set(k, new ComponentDef(v.implementation, v.schemaFile));
53
+ });
54
+ return manifest;
55
+ }
56
+ }
57
+ }
58
+ return undefined;
59
+ }
60
+ class TemplateView {
61
+ components = new Map();
62
+ }
63
+ function calculateTemplateView(schemaDestinationDir, dirNodeModules = undefined) {
64
+ if (!dirNodeModules) {
65
+ dirNodeModules = `${path.resolve()}/node_modules`;
66
+ }
67
+ const components = loadComponents(dirNodeModules);
68
+ const tv = new TemplateView();
69
+ components.forEach((m) => {
70
+ m.components.forEach((cd, cn) => {
71
+ const moduleDir = `${dirNodeModules}/${m.moduleName}`;
72
+ const { content, dest } = readSchema(schemaDestinationDir, m.moduleName, moduleDir, cd.schemaFile);
73
+ const resolvedContent = resolveRefsInSchema(content, m.moduleName, dirNodeModules, schemaDestinationDir, cd.schemaFile);
74
+ const schemaFile = writeSchema(dest, resolvedContent);
75
+ const res = schemaFile.startsWith(schemaDestinationDir) ? schemaFile.replace(stripTrailingSlash(schemaDestinationDir), ".") : schemaFile;
76
+ tv.components.set(cn, res);
77
+ });
78
+ });
79
+ const compSchemas = [];
80
+ let i = 0;
81
+ tv.components.forEach((v, k) => {
82
+ compSchemas.push({ name: k, schema: v, isLast: i == tv.components.size - 1 });
83
+ i++;
84
+ });
85
+ return { components: compSchemas };
86
+ }
87
+ function resolveRefsInSchema(schemaFileContent, moduleName, dirNodeModules, schemaDestinationDir, schemaFileRelativeToModuleDir) {
88
+ const regex = /\s*"\$ref": "(.*)"/;
89
+ const refs = [];
90
+ for (const line of schemaFileContent.split("\n")) {
91
+ const matched = regex.exec(line);
92
+ if (matched) {
93
+ let ref = matched[1];
94
+ if (ref.indexOf("#") >= 0) {
95
+ ref = ref.substring(0, ref.indexOf("#"));
96
+ }
97
+ if (ref.length > 0) {
98
+ refs.push(ref);
99
+ }
100
+ }
101
+ }
102
+ const correctedRefs = new Map();
103
+ refs.forEach(r => {
104
+ correctedRefs.set(r, resolveRef(r, moduleName, dirNodeModules, schemaDestinationDir, schemaFileRelativeToModuleDir));
105
+ });
106
+ let res = schemaFileContent;
107
+ correctedRefs.forEach((v, k) => {
108
+ console.log("replacing ref", k, "by", v);
109
+ res = res.replaceAll(k, v);
110
+ });
111
+ return res;
112
+ }
113
+ function resolveRef(ref, moduleName, dirNodeModules, schemaDestinationDir, schemaFileRelativeToModuleDir) {
114
+ const isAbsolute = path.isAbsolute(ref);
115
+ if (isAbsolute) {
116
+ return ref;
117
+ }
118
+ let relativeSchemaFileLocation = schemaFileRelativeToModuleDir;
119
+ if (schemaFileRelativeToModuleDir.endsWith(".json")) {
120
+ relativeSchemaFileLocation = schemaFileRelativeToModuleDir.substring(0, schemaFileRelativeToModuleDir.lastIndexOf("/"));
121
+ }
122
+ const fileName = `${schemaDestinationDir}/${relativeSchemaFileLocation}/${ref}`;
123
+ let stat = undefined;
124
+ try {
125
+ console.log("searching for ref file: ", path.resolve(fileName));
126
+ stat = fs.statSync(path.resolve(fileName));
127
+ }
128
+ catch (e) {
129
+ // file doesn't exist
130
+ }
131
+ if (stat && stat.isFile()) {
132
+ console.log("file is found");
133
+ return ref;
134
+ }
135
+ const k = ref.indexOf("node_modules");
136
+ if (k >= 0) {
137
+ const schemaFileDir = path.resolve(`${schemaDestinationDir}/${moduleName}/${relativeSchemaFileLocation}`);
138
+ const relativePathToNodeModules = path.relative(schemaFileDir, dirNodeModules);
139
+ console.log("resolving diff: schemaFileDir", schemaFileDir, "dirNodeModules", dirNodeModules, "relative path", relativePathToNodeModules);
140
+ ref = `${relativePathToNodeModules}/${stripLeadingSlash(ref.substring(k + "node_modules".length))}`;
141
+ }
142
+ ref = ref.replaceAll("\\", "/");
143
+ console.log("returning ref", ref);
144
+ return ref;
145
+ }
146
+ function readSchema(schemaDestinationDir, moduleName, moduleDir, compSchema) {
147
+ const moduleSchemaFile = `${stripTrailingSlash(moduleDir)}/${stripLeadingSlash(compSchema)}`;
148
+ console.log("module dir", moduleDir);
149
+ let destSchemaFile = `${moduleName}/${stripLeadingSlash(compSchema)}`;
150
+ const dest = `${schemaDestinationDir}/${destSchemaFile}`;
151
+ const moduleSchemaFilePath = moduleSchemaFile.replaceAll("/", "\\");
152
+ const stat = fs.statSync(moduleSchemaFilePath);
153
+ let content;
154
+ if (stat.isSymbolicLink()) {
155
+ content = fs.readlinkSync(moduleSchemaFile);
156
+ }
157
+ else {
158
+ content = fs.readFileSync(moduleSchemaFile).toString();
159
+ }
160
+ console.log("read content of file:", moduleSchemaFilePath, ", to be copied to", dest);
161
+ return { content, dest };
162
+ }
163
+ function writeSchema(dest, content) {
164
+ console.log("dest", dest);
165
+ const destSchemaDir = dest.substring(0, dest.lastIndexOf("/"));
166
+ fs.mkdirSync(destSchemaDir, { recursive: true });
167
+ fs.writeFileSync(path.resolve(dest), content);
168
+ return dest;
169
+ }
170
+ function stripTrailingSlash(path) {
171
+ if (path.endsWith("/")) {
172
+ return path.substring(0, path.length - 1);
173
+ }
174
+ return path;
175
+ }
176
+ function stripLeadingSlash(path) {
177
+ if (path.startsWith("/")) {
178
+ return path.substring(1);
179
+ }
180
+ else if (path.startsWith("./")) {
181
+ return path.substring(2);
182
+ }
183
+ return path;
184
+ }
185
+ export function generateVisualSchema() {
186
+ const args = process.argv.slice(2);
187
+ const dir = path.resolve();
188
+ const schemaSrc = args[0] ? args[0] : "./schemas";
189
+ console.log("generating visual schema");
190
+ const view = calculateTemplateView(schemaSrc);
191
+ const output = Mustache.render(template, view);
192
+ const fileName = `${dir}/${schemaSrc}/visual-schema.json`;
193
+ fs.writeFile(fileName, output, err => {
194
+ if (err) {
195
+ return console.error(err);
196
+ }
197
+ console.log(`${fileName}: File created!`);
198
+ });
199
+ }
200
+ generateVisualSchema();
@@ -0,0 +1,30 @@
1
+ import path from "path";
2
+ import * as nodeUrl from "node:url";
3
+ import { open } from 'node:fs/promises';
4
+ export async function initialize(props) {
5
+ console.log("Receives data from `register`", props);
6
+ }
7
+ export async function resolve(specifier, context, nextResolve) {
8
+ console.log("Take an `import` or `require` specifier and resolve it to a URL.", specifier, context, nextResolve);
9
+ return nextResolve(specifier, context, nextResolve);
10
+ }
11
+ export async function load(url, context, nextLoad) {
12
+ console.log("Take a resolved URL and return the source code to be evaluated.", url, context, nextLoad);
13
+ if (url.endsWith(".mtl")) {
14
+ const prefix = path.resolve("./");
15
+ const filePath = nodeUrl.fileURLToPath(url);
16
+ const file = await open(filePath);
17
+ var templateContent = [];
18
+ for await (const line of file.readLines()) {
19
+ templateContent.push(`"${line.replaceAll("\"", "\\\"")}"`);
20
+ }
21
+ const src = `const l = ${templateContent.join(" + \"\\n\" + ")}; export default l;`;
22
+ const res = {
23
+ format: "module",
24
+ shortCircuit: true,
25
+ source: src
26
+ };
27
+ return res;
28
+ }
29
+ return nextLoad(url, context, nextLoad);
30
+ }
@@ -0,0 +1,92 @@
1
+ {
2
+ "description": "Landing buddy content page schema",
3
+ "$schema": "http://json-schema.org/draft-07/schema#",
4
+ "definitions": {
5
+ "Link": {
6
+ "type": "string",
7
+ "pattern": "{{{linkRegexPattern}}}"
8
+ },
9
+ "Image": {
10
+ "type": "object",
11
+ "properties": {
12
+ "title": {
13
+ "type": "string"
14
+ },
15
+ "url": {
16
+ "type": "string"
17
+ },
18
+ "alt": {
19
+ "type": "string"
20
+ }
21
+ },
22
+ "required": [
23
+ "url"
24
+ ]
25
+ },
26
+ "ContentBlock": {
27
+ "type": "object",
28
+ "properties": {
29
+ "title": {
30
+ "type": "string"
31
+ },
32
+ "subTitle": {
33
+ "type": "string"
34
+ },
35
+ "footer": {
36
+ "type": "string"
37
+ },
38
+ "images": {
39
+ "type": "array",
40
+ "items": {
41
+ "$ref": "#/definitions/Image"
42
+ }
43
+ },
44
+ "paragraphs": {
45
+ "type": "array",
46
+ "items": {
47
+ "type": "string"
48
+ }
49
+ },
50
+ "bgImage": {
51
+ "$ref": "#/definitions/Image"
52
+ },
53
+ "links": {
54
+ "properties": {
55
+ "titleLink": {
56
+ "$ref": "#/definitions/Link"
57
+ },
58
+ "subTitleLink": {
59
+ "$ref": "#/definitions/Link"
60
+ },
61
+ "footerLink": {
62
+ "$ref": "#/definitions/Link"
63
+ },
64
+ "imageLinks": {
65
+ "type": "array",
66
+ "items": {
67
+ "$ref": "#/definitions/Link"
68
+ }
69
+ },
70
+ "paragraphLinks": {
71
+ "type": "array",
72
+ "items": {
73
+ "$ref": "#/definitions/Link"
74
+ }
75
+ },
76
+ "blockLink": {
77
+ "$ref": "#/definitions/Link"
78
+ }
79
+ },
80
+ "additionalItems": false,
81
+ "additionalProperties": false
82
+ }
83
+ },
84
+ "additionalItems": false,
85
+ "additionalProperties": false
86
+ }
87
+ },
88
+ "type": "object",
89
+ "additionalProperties": {
90
+ "$ref": "#/definitions/ContentBlock"
91
+ }
92
+ }
@@ -0,0 +1,16 @@
1
+ import {AsyncFunctionComponent, DummyComp} from "nodoku-core";
2
+
3
+ {{#modules}}
4
+ import { {{comps}} } from "{{module}}";
5
+ {{/modules}}
6
+
7
+ const components: Map<string, AsyncFunctionComponent> = new Map<string, AsyncFunctionComponent>();
8
+
9
+ {{#names}}
10
+ components.set("{{{n}}}", {{c}})
11
+ {{/names}}
12
+
13
+ export function defaultComponentProvider(componentName: string): Promise<AsyncFunctionComponent> {
14
+ const f: AsyncFunctionComponent | undefined = components.get(componentName);
15
+ return Promise.resolve(f ? f : DummyComp);
16
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "description": "Nodoku visual page schema",
3
+ "$schema": "http://json-schema.org/draft-07/schema#",
4
+ "definitions": {
5
+ "VisualRow": {
6
+ "type": "object",
7
+ "properties": {
8
+ "row": {
9
+ "type": "array",
10
+ "items": {
11
+ "$ref": "#/definitions/VisualRowBlock"
12
+ }
13
+ }
14
+ },
15
+ "additionalProperties": false
16
+ },
17
+ "VisualRowBlock": {
18
+ "type": "object",
19
+ "properties": {
20
+ {{#components}}
21
+ "{{{name}}}": {
22
+ "$ref": "{{{schema}}}"
23
+ }{{^isLast}},{{/isLast}}
24
+ {{/components}}
25
+ },
26
+ "additionalProperties": false
27
+ },
28
+ "additionalProperties": false
29
+ },
30
+ "type": "object",
31
+ "properties": {
32
+ "rows": {
33
+ "type": "array",
34
+ "items": {
35
+ "$ref": "#/definitions/VisualRow"
36
+ }
37
+ },
38
+ "additionalProperties": false
39
+ }
40
+ }
@@ -0,0 +1,81 @@
1
+ export class LbContentImage {
2
+ url;
3
+ title;
4
+ alt;
5
+ }
6
+ export class LbTranslatedText {
7
+ key = "";
8
+ ns = "";
9
+ text = "";
10
+ constructor(ns, key = "", text = "") {
11
+ this.ns = ns;
12
+ this.key = key;
13
+ this.text = text;
14
+ }
15
+ }
16
+ export class LbContentBlock {
17
+ key;
18
+ title;
19
+ subTitle;
20
+ footer;
21
+ paragraphs = [];
22
+ bgImage;
23
+ images = [];
24
+ constructor(key) {
25
+ this.key = key;
26
+ }
27
+ }
28
+ export class LbNsContent {
29
+ blocks = [];
30
+ }
31
+ export class LbContentKey {
32
+ key;
33
+ ns;
34
+ constructor(key, ns) {
35
+ this.key = key;
36
+ this.ns = ns;
37
+ }
38
+ }
39
+ export class LbVisualComponent {
40
+ rowIndex;
41
+ componentIndex;
42
+ visualComponent = "";
43
+ ns = "";
44
+ contentKeys = [];
45
+ theme;
46
+ options;
47
+ implementationModule = "";
48
+ implementationComponent = "";
49
+ constructor(rowIndex, componentIndex) {
50
+ this.rowIndex = rowIndex;
51
+ this.componentIndex = componentIndex;
52
+ }
53
+ }
54
+ export class LbRow {
55
+ rowIndex;
56
+ row = [];
57
+ constructor(rowIndex) {
58
+ this.rowIndex = rowIndex;
59
+ }
60
+ }
61
+ export class LbPageVisual {
62
+ rows = [];
63
+ }
64
+ export class LbComponentProps {
65
+ rowIndex;
66
+ componentIndex;
67
+ content;
68
+ visual;
69
+ options;
70
+ lng;
71
+ i18nextProvider;
72
+ constructor(rowIndex, componentIndex, content, visual, options, lng, i18nextProvider) {
73
+ this.rowIndex = rowIndex;
74
+ this.componentIndex = componentIndex;
75
+ this.content = content;
76
+ this.visual = visual;
77
+ this.options = options;
78
+ this.lng = lng;
79
+ this.i18nextProvider = i18nextProvider;
80
+ }
81
+ }
@@ -0,0 +1,16 @@
1
+ export class ComponentDef {
2
+ implementation;
3
+ schemaFile;
4
+ constructor(componentImplementation, componentSchema) {
5
+ this.implementation = componentImplementation;
6
+ this.schemaFile = componentSchema;
7
+ }
8
+ }
9
+ export class Manifest {
10
+ moduleName;
11
+ namespace = undefined;
12
+ components = new Map();
13
+ constructor(moduleName) {
14
+ this.moduleName = moduleName;
15
+ }
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,69 @@
1
+ export async function DummyComp(props) {
2
+ console.log("content dummy comp", props.content);
3
+ const { content, i18nextProvider, lng } = props;
4
+ const { t } = await i18nextProvider(lng);
5
+ const blocks = content.map((block) => {
6
+ var style = {};
7
+ if (block.bgImage && block.bgImage.url) {
8
+ style = { backgroundImage: `url(${t(block.bgImage.url.key, block.bgImage.url.ns)})` };
9
+ }
10
+ return (<div className={"w-full w-full flex flex-col items-left justify-left border border-4 border-red-200 relative pb-10"}>
11
+ <div className={"top-0 bottom-0 left-0 right-0 absolute bg-cover bg-no-repeat"} style={{ ...style, zIndex: -11 }}>
12
+ </div>
13
+ <div className={"top-0 bottom-0 left-0 right-0 absolute bg-white "} style={{ zIndex: -5, opacity: 0.7 }}>
14
+ </div>
15
+ <div className={"p-5 w-full bg-red-400 text-center"}>dummy component<h3><b>{block.key}</b></h3></div>
16
+
17
+
18
+ <div className="p-5">
19
+ {block.title && <a href="#">
20
+ {block.title && block.title.key}
21
+ <h5 className={"mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"}>
22
+ {block.title && t(block.title.key, block.title.ns)}
23
+ </h5>
24
+ </a>}
25
+ {block.subTitle && block.subTitle.key}
26
+ {block.subTitle && <h6 className={"mb-2 text-xl tracking-tight text-gray-900 dark:text-white"}>
27
+ {block.subTitle && t(block.subTitle.key, block.subTitle.ns)}
28
+ </h6>}
29
+
30
+ paragraphs:
31
+ {block.paragraphs.map((p, ip) => {
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.key, p.ns)}
36
+ </p>
37
+ </div>);
38
+ })}
39
+ images:
40
+ {block.images.map((i, ii) => {
41
+ return (<div>
42
+ <p key={"url" + ii} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
43
+ url: {i && i.url && t(i.url.key, i.url.ns)}
44
+ {i.url && <span className={"bg-cover bg-no-repeat"} style={{
45
+ display: "block",
46
+ width: "200px",
47
+ height: "200px",
48
+ backgroundImage: `url(${t(i.url.key, i.url.ns)})`
49
+ }}></span>}
50
+ </p>
51
+ <p key={"alt" + ii} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
52
+ alt: {i && i.alt && t(i.alt.key, i.alt.ns)}
53
+ </p>
54
+ <p key={"title" + ii} className={"mb-3 font-normal text-gray-700 dark:text-gray-400"}>
55
+ title: {i && i.title && t(i.title.key, i.title.ns)}
56
+ </p>
57
+ </div>);
58
+ })}
59
+ </div>
60
+
61
+ <div className={"absolute bottom-0 p-5"}>
62
+ {block.footer?.key}
63
+ <p>{block.footer && t(block.footer.key, block.footer.ns)}</p>
64
+ </div>
65
+
66
+ </div>);
67
+ });
68
+ return <div>{blocks}</div>;
69
+ }
@@ -0,0 +1,16 @@
1
+ export class RenderingPageProps {
2
+ pageName;
3
+ lng;
4
+ i18nextProvider;
5
+ contentYamlProvider;
6
+ visualYamlProvider;
7
+ componentProvider;
8
+ constructor(pageName, lng, i18nextProvider, contentYamlProvider, visualYamlProvider, componentProvider) {
9
+ this.pageName = pageName;
10
+ this.lng = lng;
11
+ this.i18nextProvider = i18nextProvider;
12
+ this.contentYamlProvider = contentYamlProvider;
13
+ this.visualYamlProvider = visualYamlProvider;
14
+ this.componentProvider = componentProvider;
15
+ }
16
+ }
@@ -0,0 +1,166 @@
1
+ import * as yaml from "js-yaml";
2
+ import React from "react";
3
+ import { LbContentBlock, LbContentImage, LbContentKey, LbNsContent, LbRow, LbTranslatedText, LbVisualComponent } from "../content/lb-content-block";
4
+ import { DummyComp } from "./dummy-comp";
5
+ async function defaultComponentProvider(componentName) {
6
+ return DummyComp;
7
+ }
8
+ async function fetchPageContent(lng, namespaces, provider) {
9
+ const pageContent = new Map();
10
+ await Promise.all(namespaces.map(ns => readFileContent(lng, ns, pageContent, provider)));
11
+ // console.log("content", res)
12
+ return pageContent;
13
+ }
14
+ async function readFileContent(lng, ns, pageContent, provider) {
15
+ console.log("fetching file content", lng, ns);
16
+ await provider(lng, ns)
17
+ .then((fileContent) => pageContent.set(ns, fetchSuccessContent(fileContent, ns)));
18
+ }
19
+ async function fetchPageVisual(pageName, provider) {
20
+ const res = await provider(pageName)
21
+ .then(fetchSuccessVisual);
22
+ console.log("all visual", JSON.stringify(res));
23
+ return res;
24
+ }
25
+ function fetchSuccessContent(fileContents, ns) {
26
+ const data = yaml.load(fileContents);
27
+ console.log("LbPageContent", Object.keys(data));
28
+ const res = new LbNsContent();
29
+ res.blocks = Object.keys(data).map(((key, bi) => {
30
+ const b = data[key];
31
+ const blockPrefix = key;
32
+ const bRes = new LbContentBlock(key);
33
+ bRes.title = new LbTranslatedText(ns);
34
+ bRes.title.key = `${blockPrefix}.title`;
35
+ bRes.title.text = b.header;
36
+ bRes.subTitle = new LbTranslatedText(ns);
37
+ bRes.subTitle.key = `${blockPrefix}.subTitle`;
38
+ bRes.subTitle.text = b.subHeader;
39
+ bRes.footer = new LbTranslatedText(ns);
40
+ bRes.footer.key = `${blockPrefix}.footer`;
41
+ bRes.footer.text = b.footer;
42
+ bRes.paragraphs = b.paragraphs ? b.paragraphs.map(((p, pi) => new LbTranslatedText(ns, `${key}.paragraphs.${pi}`, p))) : [];
43
+ bRes.images = b.images ? b.images.map(((im, imi) => {
44
+ const pRes = new LbContentImage();
45
+ pRes.url = new LbTranslatedText(ns, `${key}.images.${imi}.url`, im.url);
46
+ pRes.alt = new LbTranslatedText(ns, `${key}.images.${imi}.alt`, im.alt);
47
+ pRes.title = new LbTranslatedText(ns, `${key}.images.${imi}.title`, im.title);
48
+ return pRes;
49
+ })) : [];
50
+ if (b.bgImage) {
51
+ bRes.bgImage = new LbContentImage();
52
+ bRes.bgImage.url = new LbTranslatedText(ns, `${key}.bgImage.url`, b.bgImage.url);
53
+ bRes.bgImage.alt = new LbTranslatedText(ns, `${key}.bgImage.alt`, b.bgImage.alt);
54
+ bRes.bgImage.alt = new LbTranslatedText(ns, `${key}.bgImage.title`, b.bgImage.title);
55
+ }
56
+ return bRes;
57
+ }));
58
+ return res;
59
+ }
60
+ function fetchSuccessVisual(fileContents) {
61
+ const data = yaml.load(fileContents);
62
+ return {
63
+ rows: data.rows.map(((r, iRow) => {
64
+ const row = new LbRow(iRow);
65
+ row.row = r.row.map((vc, iVc) => {
66
+ console.log("Object.keys(vc)", Object.keys(vc));
67
+ const vcName = Object.keys(vc)[0];
68
+ const lbVisualComponent = new LbVisualComponent(iRow, iVc);
69
+ lbVisualComponent.visualComponent = vcName;
70
+ const vb = vc[lbVisualComponent.visualComponent];
71
+ lbVisualComponent.ns = vb.ns;
72
+ lbVisualComponent.contentKeys = convertContentKeys(vb, lbVisualComponent.ns);
73
+ lbVisualComponent.theme = vb.theme;
74
+ lbVisualComponent.options = vb.options;
75
+ lbVisualComponent.implementationModule = "nodoku-flowbite";
76
+ lbVisualComponent.implementationComponent = lbVisualComponent.visualComponent;
77
+ return lbVisualComponent;
78
+ });
79
+ return row;
80
+ }))
81
+ };
82
+ }
83
+ function convertContentKeys(vb, defaultNs) {
84
+ var contentKeys = vb.contentKeys;
85
+ if (!contentKeys && vb.contentKey) {
86
+ contentKeys = [vb.contentKey];
87
+ }
88
+ if (!contentKeys || !contentKeys.length) {
89
+ return [];
90
+ }
91
+ return contentKeys.map((ck) => {
92
+ if (typeof ck == "string") {
93
+ return new LbContentKey(ck, defaultNs);
94
+ }
95
+ else {
96
+ return new LbContentKey(ck.key, ck.ns ? ck.ns : defaultNs);
97
+ }
98
+ });
99
+ }
100
+ async function RenderingPage(props) {
101
+ const { pageName, lng, i18nextProvider, contentYamlProvider, visualYamlProvider } = props;
102
+ var { componentProvider } = props;
103
+ if (!componentProvider) {
104
+ componentProvider = defaultComponentProvider;
105
+ }
106
+ const pageVisual = await fetchPageVisual(pageName, visualYamlProvider);
107
+ const namespaces = new Set();
108
+ pageVisual.rows.forEach((r) => {
109
+ r.row.forEach((vc) => {
110
+ vc.contentKeys.forEach((ck) => {
111
+ console.log(ck.ns);
112
+ namespaces.add(ck.ns);
113
+ });
114
+ });
115
+ });
116
+ const pageContent = await fetchPageContent(lng, Array.from(namespaces.keys()), contentYamlProvider);
117
+ const l = await Promise.all(pageVisual.rows.map(async (row, iRow) => {
118
+ return await createSubRows(row, iRow, pageContent, lng, i18nextProvider, componentProvider);
119
+ }));
120
+ const rows = l.flatMap((a) => a);
121
+ return <>{rows}</>;
122
+ }
123
+ async function createSubRows(row, iRow, pageContent, lng, i18nProvider, componentProvider) {
124
+ const rowComponents = await Promise.all(row.row.map(async (visualSection, iComp) => {
125
+ return await createRowBlock(iRow, iComp, visualSection, pageContent, lng, i18nProvider, componentProvider);
126
+ }));
127
+ const numComponents = rowComponents.length;
128
+ if (numComponents == 1) {
129
+ return [rowComponents[0]];
130
+ }
131
+ else {
132
+ const maxCols = 3;
133
+ const numCols = numComponents <= maxCols ? numComponents : maxCols;
134
+ const subRows = [];
135
+ for (var i = 0; i < numComponents / numCols; i++) {
136
+ subRows.push(<div key={`row-${iRow}`} className={`grid grid-cols-${numCols} gap-4 ${iRow == 0 ? "" : "mt-10"}`}>
137
+ {rowComponents.slice(numCols * i, Math.min((i + 1) * numCols, numComponents))}
138
+ </div>);
139
+ }
140
+ return subRows;
141
+ }
142
+ }
143
+ async function createRowBlock(rowIndex, componentIndex, visualSection, pageContent, lng, i18nProvider, componentProvider) {
144
+ const blocks = visualSection.contentKeys
145
+ .map((k) => pageContent.get(k.ns)?.blocks.find((b) => b.key == k.key))
146
+ .filter((b) => b != undefined)
147
+ .map((b) => b);
148
+ console.log("retrieving comp", rowIndex, componentIndex);
149
+ const component = await componentProvider(visualSection.implementationComponent);
150
+ console.log("after component");
151
+ return renderWithVisual(rowIndex, componentIndex, component, blocks, visualSection, lng, i18nProvider);
152
+ }
153
+ async function renderWithVisual(rowIndex, componentIndex, component, section, visualSection, lng, i18nextProvider) {
154
+ console.log("start rendering page");
155
+ const props = {
156
+ rowIndex: rowIndex,
157
+ componentIndex: componentIndex,
158
+ content: section,
159
+ visual: visualSection.theme,
160
+ options: visualSection.options,
161
+ lng: lng,
162
+ i18nextProvider: i18nextProvider
163
+ };
164
+ return await component(props);
165
+ }
166
+ export { RenderingPage };
@@ -0,0 +1,6 @@
1
+ import { LbContentImage, LbTranslatedText, LbContentBlock, LbNsContent, LbContentKey, LbVisualComponent, LbRow, LbPageVisual, LbComponentProps } from "./content/lb-content-block";
2
+ import { RenderingPageProps } from "./core/rendering-page-props";
3
+ import { RenderingPage } from "./core/rendering-page";
4
+ export { LbContentImage, LbTranslatedText, LbContentBlock, LbNsContent, LbContentKey, LbVisualComponent, LbRow, LbPageVisual, LbComponentProps };
5
+ export { RenderingPageProps, RenderingPage };
6
+ export { DummyComp } from "./core/dummy-comp";
@@ -0,0 +1,90 @@
1
+ {
2
+ "description": "Nodoku content page schema",
3
+ "$schema": "http://json-schema.org/draft-07/schema#",
4
+ "definitions": {
5
+ "Link": {
6
+ "type": "string",
7
+ "pattern": "^((http|https)?:\\/\\/)?\\/?([-a-zA-Z0-9._\\+~#=]{1,256})([-a-zA-Z0-9@:%._\\+~#=]{1,256})([-a-zA-Z0-9()@:%_\\+.~#?&\\/\\/=]*)$"
8
+ },
9
+ "Image": {
10
+ "type": "object",
11
+ "properties": {
12
+ "title": {
13
+ "type": "string"
14
+ },
15
+ "url": {
16
+ "type": "string"
17
+ },
18
+ "alt": {
19
+ "type": "string"
20
+ }
21
+ },
22
+ "required": [
23
+ "url"
24
+ ]
25
+ },
26
+ "ContentBlock": {
27
+ "type": "object",
28
+ "properties": {
29
+ "title": {
30
+ "type": "string"
31
+ },
32
+ "subTitle": {
33
+ "type": "string"
34
+ },
35
+ "footer": {
36
+ "type": "string"
37
+ },
38
+ "images": {
39
+ "type": "array",
40
+ "items": {
41
+ "$ref": "#/definitions/Image"
42
+ }
43
+ },
44
+ "paragraphs": {
45
+ "type": "array",
46
+ "items": {
47
+ "type": "string"
48
+ }
49
+ },
50
+ "bgImage": {
51
+ "$ref": "#/definitions/Image"
52
+ },
53
+ "links": {
54
+ "properties": {
55
+ "titleLink": {
56
+ "$ref": "#/definitions/Link"
57
+ },
58
+ "subTitleLink": {
59
+ "$ref": "#/definitions/Link"
60
+ },
61
+ "footerLink": {
62
+ "$ref": "#/definitions/Link"
63
+ },
64
+ "imageLinks": {
65
+ "type": "array",
66
+ "items": {
67
+ "$ref": "#/definitions/Link"
68
+ }
69
+ },
70
+ "paragraphLinks": {
71
+ "type": "array",
72
+ "items": {
73
+ "$ref": "#/definitions/Link"
74
+ }
75
+ },
76
+ "blockLink": {
77
+ "$ref": "#/definitions/Link"
78
+ }
79
+ },
80
+ "additionalProperties": false
81
+ }
82
+ },
83
+ "additionalProperties": false
84
+ }
85
+ },
86
+ "type": "object",
87
+ "additionalProperties": {
88
+ "$ref": "#/definitions/ContentBlock"
89
+ }
90
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "namespace": {
6
+ "type": "string"
7
+ },
8
+ "components": {
9
+ "type": "object",
10
+ "additionalProperties": {
11
+ "type": "object",
12
+ "properties": {
13
+ "schemaFile": {
14
+ "type": "string"
15
+ },
16
+ "implementation": {
17
+ "type": "string"
18
+ }
19
+ },
20
+ "required": [
21
+ "schemaFile",
22
+ "implementation"
23
+ ],
24
+ "additionalProperties": false
25
+ }
26
+ }
27
+ },
28
+ "required": [
29
+ "components"
30
+ ],
31
+ "additionalProperties": false
32
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "definitions": {
4
+ "ContentKey": {
5
+ "oneOf": [
6
+ {
7
+ "type": "object",
8
+ "properties": {
9
+ "ns": {
10
+ "type": "string",
11
+ "pattern": "[\\w-]+"
12
+ },
13
+ "key": {
14
+ "type": "string",
15
+ "pattern": "[\\w-]+"
16
+ }
17
+ },
18
+ "required": [
19
+ "key"
20
+ ],
21
+ "additionalProperties": false
22
+ },
23
+ {
24
+ "type": "string"
25
+ }
26
+ ]
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function generateComponentResolver(): void;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function generateContentSchema(): void;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function generateVisualSchema(): void;
@@ -0,0 +1,3 @@
1
+ export declare function initialize(props: any): Promise<void>;
2
+ export declare function resolve(specifier: any, context: any, nextResolve: any): Promise<any>;
3
+ export declare function load(url: any, context: any, nextLoad: any): Promise<any>;
@@ -0,0 +1,60 @@
1
+ import { i18nextProvider } from "./providers";
2
+ export declare class LbContentImage {
3
+ url?: LbTranslatedText;
4
+ title?: LbTranslatedText;
5
+ alt?: LbTranslatedText;
6
+ }
7
+ export declare class LbTranslatedText {
8
+ key: string;
9
+ ns: string;
10
+ text: string;
11
+ constructor(ns: string, key?: string, text?: string);
12
+ }
13
+ export declare class LbContentBlock {
14
+ key: string;
15
+ title?: LbTranslatedText;
16
+ subTitle?: LbTranslatedText;
17
+ footer?: LbTranslatedText;
18
+ paragraphs: LbTranslatedText[];
19
+ bgImage?: LbContentImage;
20
+ images: LbContentImage[];
21
+ constructor(key: string);
22
+ }
23
+ export declare class LbNsContent {
24
+ blocks: LbContentBlock[];
25
+ }
26
+ export declare class LbContentKey {
27
+ key: string;
28
+ ns: string;
29
+ constructor(key: string, ns: string);
30
+ }
31
+ export declare class LbVisualComponent {
32
+ rowIndex: number;
33
+ componentIndex: number;
34
+ visualComponent: string;
35
+ ns: string;
36
+ contentKeys: LbContentKey[];
37
+ theme: any;
38
+ options: any;
39
+ implementationModule: string;
40
+ implementationComponent: string;
41
+ constructor(rowIndex: number, componentIndex: number);
42
+ }
43
+ export declare class LbRow {
44
+ rowIndex: number;
45
+ row: LbVisualComponent[];
46
+ constructor(rowIndex: number);
47
+ }
48
+ export declare class LbPageVisual {
49
+ rows: LbRow[];
50
+ }
51
+ export declare class LbComponentProps<TComponentTheme = any, TComponentOptions = any> {
52
+ rowIndex: number;
53
+ componentIndex: number;
54
+ content: LbContentBlock[];
55
+ visual: TComponentTheme;
56
+ options: TComponentOptions;
57
+ lng: string;
58
+ i18nextProvider: i18nextProvider;
59
+ constructor(rowIndex: number, componentIndex: number, content: LbContentBlock[], visual: TComponentTheme, options: TComponentOptions, lng: string, i18nextProvider: i18nextProvider);
60
+ }
@@ -0,0 +1,11 @@
1
+ export declare class ComponentDef {
2
+ implementation: string;
3
+ schemaFile: string;
4
+ constructor(componentImplementation: string, componentSchema: string);
5
+ }
6
+ export declare class Manifest {
7
+ moduleName: string;
8
+ namespace: string | undefined;
9
+ components: Map<string, ComponentDef>;
10
+ constructor(moduleName: string);
11
+ }
@@ -0,0 +1,9 @@
1
+ import { LbComponentProps } from "./lb-content-block";
2
+ import { JSX } from "react";
3
+ export type AsyncFunctionComponent = (props: LbComponentProps) => Promise<JSX.Element>;
4
+ export type ContentYamlProvider = (lng: string, ns: string) => Promise<string>;
5
+ export type VisualYamlProvider = (pageName: string) => Promise<string>;
6
+ export type i18nextProvider = (lng: string) => Promise<{
7
+ t: (key: string, ns: string) => string;
8
+ }>;
9
+ export type ComponentProvider = (componentName: string) => Promise<AsyncFunctionComponent>;
@@ -0,0 +1,3 @@
1
+ import { LbComponentProps } from "../content/lb-content-block";
2
+ import { JSX } from "react";
3
+ export declare function DummyComp(props: LbComponentProps): Promise<JSX.Element>;
@@ -0,0 +1,10 @@
1
+ import { ComponentProvider, ContentYamlProvider, i18nextProvider, VisualYamlProvider } from "../content/providers";
2
+ export declare class RenderingPageProps {
3
+ pageName: string;
4
+ lng: string;
5
+ i18nextProvider: i18nextProvider;
6
+ contentYamlProvider: ContentYamlProvider;
7
+ visualYamlProvider: VisualYamlProvider;
8
+ componentProvider: ComponentProvider;
9
+ constructor(pageName: string, lng: string, i18nextProvider: i18nextProvider, contentYamlProvider: ContentYamlProvider, visualYamlProvider: VisualYamlProvider, componentProvider: ComponentProvider);
10
+ }
@@ -0,0 +1,4 @@
1
+ import { JSX } from "react";
2
+ import { RenderingPageProps } from "./rendering-page-props";
3
+ declare function RenderingPage(props: RenderingPageProps): Promise<JSX.Element>;
4
+ export { RenderingPage };
@@ -0,0 +1,8 @@
1
+ import { LbContentImage, LbTranslatedText, LbContentBlock, LbNsContent, LbContentKey, LbVisualComponent, LbRow, LbPageVisual, LbComponentProps } from "./content/lb-content-block";
2
+ import { RenderingPageProps } from "./core/rendering-page-props";
3
+ import { RenderingPage } from "./core/rendering-page";
4
+ import { ContentYamlProvider, VisualYamlProvider, i18nextProvider, AsyncFunctionComponent } from "./content/providers";
5
+ export { LbContentImage, LbTranslatedText, LbContentBlock, LbNsContent, LbContentKey, LbVisualComponent, LbRow, LbPageVisual, LbComponentProps };
6
+ export { RenderingPageProps, RenderingPage };
7
+ export type { ContentYamlProvider, VisualYamlProvider, i18nextProvider, AsyncFunctionComponent };
8
+ export { DummyComp } from "./core/dummy-comp";
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "nodoku-core",
3
+ "version": "0.1.0",
4
+ "description": "basic foundation for nodoku static site generator",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/types/index.d.ts",
8
+ "import": "./dist/esm/index.js"
9
+ },
10
+ "./package.json": "./package.json"
11
+ },
12
+ "module": "dist/esm/index.js",
13
+ "types": "dist/types/index.d.ts",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "type": "module",
18
+ "bin": {
19
+ "nodoku-gen-component-resolver": "./dist/esm/bin/generate-component-resolver.js",
20
+ "nodoku-gen-content-schema": "./dist/esm/bin/generate-content-schema.js",
21
+ "nodoku-gen-visual-schema": "./dist/esm/bin/generate-visual-schema.js"
22
+ },
23
+ "dependencies": {
24
+ "@types/js-yaml": "^4.0.9",
25
+ "@types/mustache": "^4.2.5",
26
+ "js-yaml": "^4.1.0",
27
+ "mustache": "^4.2.0"
28
+ },
29
+ "peerDependencies": {
30
+ "@types/react": "^18.3.2",
31
+ "@types/react-dom": "18.3.0",
32
+ "react": "^18.3.1",
33
+ "react-dom": "^18.3.1"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.12.11",
37
+ "@types/react": "^18.3.3",
38
+ "@types/react-dom": "^18.3.0",
39
+ "eslint": "^8.56.0",
40
+ "eslint-config-next": "14.2.3",
41
+ "react": "^18.3.1",
42
+ "react-dom": "^18.3.1",
43
+ "typescript": "^5.5.3",
44
+ "shx": "^0.3.4"
45
+ },
46
+ "scripts": {
47
+ "dist": "tsc && shx cp -r src/bin/mustache dist/esm/bin/ && shx cp -r schemas dist"
48
+ }
49
+ }