nodoku-core 0.3.6 → 0.3.13

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/README.md CHANGED
@@ -1,960 +1,960 @@
1
- <!-- TOC -->
2
- * [Nodoku foundation](#nodoku-foundation)
3
- * [Nodoku content flow](#nodoku-content-flow)
4
- * [Nodoku skin](#nodoku-skin)
5
- * [Getting started](#getting-started)
6
- * [Prerequisites](#prerequisites)
7
- * [Installation](#installation)
8
- * [Integrating Nodoku into a project](#integrating-nodoku-into-a-project)
9
- * [Loding and parsing of the content MD file](#loding-and-parsing-of-the-content-md-file)
10
- * [Explicitly providing content blockId](#explicitly-providing-content-blockid)
11
- * [Automatic generation of content blockId](#automatic-generation-of-content-blockid)
12
- * [Loading of the skin Yaml file](#loading-of-the-skin-yaml-file)
13
- * [Rendering Nodoku](#rendering-nodoku)
14
- * [Nodoku component resolver](#nodoku-component-resolver)
15
- * [Configuring Nodoku project](#configuring-nodoku-project)
16
- * [Tailwind configuration](#tailwind-configuration)
17
- * [Webpack configuration](#webpack-configuration)
18
- * [Nodoku component bundle](#nodoku-component-bundle)
19
- * [Nodoku manifest](#nodoku-manifest-)
20
- * [Nodoku skin](#nodoku-skin-1)
21
- * [Nodoku visual component theme](#nodoku-visual-component-theme)
22
- * [The Nodoku skin Yaml file](#the-nodoku-skin-yaml-file)
23
- * [Schema for Nodoku skin Yaml file](#schema-for-nodoku-skin-yaml-file)
24
- * [Customizing Nodoku page appearance](#customizing-nodoku-page-appearance)
25
- * [Nodoku generation scripts](#nodoku-generation-scripts)
26
- * [Nodoku license](#nodoku-license)
27
- * [Use for private, educational or commercial needs, excluding applications for webpage or website building tools](#use-for-private-educational-or-commercial-needs-excluding-applications-for-webpage-or-website-building-tools)
28
- * [Use as part of website or webpage builders](#use-as-part-of-website-or-webpage-builders)
29
- * [Examples](#examples)
30
- * [Proper use, free of charge, under the MIT License](#proper-use-free-of-charge-under-the-mit-license)
31
- * [Prohibited use](#prohibited-use)
32
- <!-- TOC -->
33
-
34
- GitHub link: https://github.com/nodoku/nodoku-core
35
-
36
- Nodoku is a static site generator, where the content is provided via MD (markdown) files, and the visual representation is controlled using Yaml files - called _skin_.
37
-
38
- Nodoku promotes the content-first approach, when the content creation process is not being distracted by the considerations related to the visual representation and design.
39
-
40
- Instead, the content is created first as an MD file, demarcated by the special markers into content blocks, and then each block is rendered using the configuration provided in a Yaml file called _skin_.
41
-
42
- Figure 1 shows a screenshot of a part of a landing page created with Nodoku.
43
-
44
- <figure>
45
- <img
46
- src="./docs/nodoku-way-card-screenshot.png"
47
- alt="Nodoku landing page part with 3 cards"
48
- title="Nodoku landing page part with 3 cards"
49
- />
50
- <figcaption>
51
- <b>Figure 1</b>: A part of a landing page, created using Nodoku.
52
- It has 3 cards, organized in a row, where the content of each card is supplied by an MD file. The mapping between content and visual representation is configured in a Yaml file.
53
- </figcaption>
54
- </figure>
55
-
56
-
57
-
58
- Nodoku is a set of libraries, the most important of which is nodoku-core, intended to be used with the **_NextJS_** framework for generation of static sites.
59
-
60
- Nodoku is intended to be used in server side rendering, it is not suitable for client side.
61
-
62
- nodoku-core doesn't contain the visual representation for content blocks. Instead, the visual representation is supplied via separated dependencies, such as **_nodoku-flowbite_** (components based on [Flowbite](https://flowbite.com/docs/getting-started/introduction/)) and **_nodoku-mambaui_** (components based on [MambaUI](https://mambaui.com/components/hero)).
63
-
64
- More set of components can be added, and included in the project as required.
65
-
66
- The structure of the skin files is organized by rows, each row having one or more components.
67
-
68
- The mapping between visual representation and the content block is performed using the concept of _selector_. Selector is a set of matching conditions, attached to a visual component in the skin Yaml file.
69
-
70
- The actual rendering is performed in two steps:
71
- - first, for a given visual component a set of matching content blocks is determined, using the selector and the meta-data of the content block
72
-
73
-
74
- - second, this flow of content blocks is provided to the rendering mechanism of the visual component for actual rendering.
75
-
76
- This decoupling allows for great level of flexibility and reuse between the content and the visual representation.
77
-
78
- If the visual components support [Tailwind](https://tailwindcss.com/docs/installation), the Tailwind customization can be provided in the skin Yaml file to fine tune the visual representation.
79
-
80
- You can learn more about the rationale behind Nodoku and the main principles in the blog article: [Nodoku: a lo-code static site generator, promoting a content-first approach based on Markdown files](https://epanikas.hashnode.dev/nodoku-a-lo-code-static-site-generator-promoting-a-content-first-approach-based-on-markdown-files)
81
-
82
- # Nodoku foundation
83
-
84
- Nodoku is organized around two flows of data:
85
- - the content flow (supplied via a Markdown file)
86
- - the visual representation flow (supplied via Yaml file called _skin_)
87
-
88
- The Nodoku engine will take care of parsing these files and supply them to the designated visual components for rendering.
89
-
90
- ## Nodoku content flow
91
-
92
- As has been mentioned above, the Nodoku content flow is supplied via an MD file.
93
-
94
- The content flow in Nodoku is organized around an entity called _content block_, which is a single piece of content suitable for rendering by a Nodoku visual component. The set of such components constitute the Nodoku content flow.
95
-
96
- The content block has a predefined structure. It is designed to be more or less generic, and _visual representation agnostic_. In particular this is important to implement the _content-first_ principle, where the content can (and should!) be created first, without any visual design considerations.
97
-
98
- Nodoku engine parses the MD file to extract the set of content blocks, that are contained in such file. The content blocks are delimited by a special markers - the content block delimiters.
99
-
100
- The content block delimiter is a small piece of Yaml code snippet, embedded directly into the MD file.
101
-
102
- For example, the content, that has been used to generate the screeshnot on Figure 1 is the following:
103
-
104
- ```markdown
105
-
106
- ```yaml
107
- nd-block:
108
- attributes:
109
- sectionName: nodoku-way
110
- ``
111
-
112
- # Step 1: _Think_
113
- ## Create content promoting your product or service as an **MD file**
114
- ...
115
- Concentrate on the subject of your product / service to highlight its advantages.
116
- ...
117
- |Get started|
118
-
119
- # Step 2: _Skin_
120
- ## Skin the MD file using simple **Yaml config** and available components
121
- ...
122
- Once you are happy with the message your landing page conveys,
123
- start by skinning it up.
124
- ...
125
- |Get started|
126
-
127
- # Step 3: _Fine tune_
128
- ## Use configuration options to fine tune your landing page presentation
129
- ...
130
- If the default presentation doesn't suit your needs, you can tweak it up
131
- using the config options of each component to fine tune it for your needs.
132
- ...
133
- |Get started|
134
-
135
- ```
136
-
137
- The first thing one would notice in this MD excerpt is the small Yaml code snippet.
138
-
139
- ```yaml
140
- nd-block:
141
- attributes:
142
- sectionName: nodoku-way
143
- ```
144
-
145
- This Yaml code snippet is a content block delimiter, and it contains the content block meta-data, such as _id_, _attributes_ and _tags_.
146
-
147
- The schema for this Yaml code snippet can be found in the Json schema file : **_nodoku-core/schemas/md-content-block-delimiter.json_**
148
-
149
- The content block has the following predefined, fixed structure:
150
- ```typescript
151
- class NdContentBlock {
152
- // content block meta-data
153
- id: string;
154
- lng: string;
155
- attributes: {key: string, value: string}[] = [];
156
- tags: string[] = []
157
- namespace: string;
158
-
159
- // the actual textual content
160
- title?: NdTranslatedText; // a title
161
- subTitle?: NdTranslatedText; // a subtitle
162
- h3?: NdTranslatedText; // h3 header
163
- h4?: NdTranslatedText; // h4 header
164
- h5?: NdTranslatedText; // h5 header
165
- h6?: NdTranslatedText; // h6 header
166
- footer?: NdTranslatedText; // footer
167
- paragraphs: NdTranslatedText[]; // set of paragraphs
168
- bgImageUrl?: NdTranslatedText; // background image
169
- images: NdContentImage[] = []; // set of images
170
- }
171
- ```
172
- where _NdTranslatedText_ represents a piece of text, that can be used for i18next translation (see below)
173
- ```typescript
174
- class NdTranslatedText {
175
- key: string; // the translation key
176
- ns: string; // the translation namespace
177
- text: string; // the fallback text, extracted from content
178
- }
179
- ```
180
-
181
- As one can see, the content block _cannot_ contain more than instance of each type of headers: title (h1), subtitle (h2), h3, etc.
182
-
183
- But it _can_ contain several headers of different types, for example, a title (h1) and subtitle (h2)
184
-
185
- > Hence, the parsing of the MD file goes as follows:
186
- > - if the Yaml nd-block is encountered, a new content block is started, which would absord all the content elements that are discovered below, until a new content block is started
187
- > - if a header (of any kind) is encountered
188
- > - if a header of the same kind or below exists in the current block
189
- > - finalize the current block
190
- > - start a new block
191
- > - and copy the metadata from the current block to the new one
192
- > - otherwise
193
- > - add the corresponding header to the current block
194
-
195
- This approach allows for smooth content definition, where a common content block metadata is defined only once, and all the subsequent pieces of content are treated as new blocks with the same metadata.
196
-
197
- Thanks to this parsing approach, the excerpt of the MD file, shown above, defines 3 content blocks - one per card shown on the screenshot of Figure 1 - instead of only one.
198
-
199
-
200
- ## Nodoku skin
201
-
202
- Nodoku skin is a Yaml file which configures the visual representation of the content defined in a markdown file.
203
-
204
- The Nodoku page layout is organized as a set of rows, each row having its configured set of components.
205
-
206
- Each layout row can have one or more visual components.
207
-
208
- Here is an example of a typical Nodoku skin file:
209
-
210
- ```yaml
211
- rows:
212
- - row:
213
- components:
214
- - mambaui/card:
215
- selector:
216
- attributes:
217
- sectionName: nodoku-way
218
- ```
219
-
220
- In this example, we define a row containing components of type _mambaui/card_
221
-
222
- The selector defines the content blocks that should be rendered using this visual component. In our case, these are all the content blocks having an attribute `sectionName` equal to `nodoku-way`.
223
-
224
- Recall, that according to our MD file we are actually having 3 content blocks matching this criteria:
225
- - Step 1: _Think_
226
- - Step 2: _Skin_
227
- - Step 3: _Fine tune_
228
-
229
- Naturally, a single card component cannot display more than one content block.
230
-
231
- Consequently, according to the skin Yaml file, the Nodoku engine will apply the same visual component definition to all the three matching content blocks.
232
-
233
- And this process will end up rendering the screenshot presented on Figure 1.
234
-
235
- It's worth noting that a visual component can support arbitrary number of content blocks. For example, the _flowbite/carousel_ component can display any number of content blocks.
236
-
237
- Consequently, if the skin Yaml file had the following configuration:
238
- ```yaml
239
- rows:
240
- - row:
241
- components:
242
- - flowbite/carousel:
243
- selector:
244
- attributes:
245
- sectionName: nodoku-way
246
- ```
247
-
248
- it would have had the following visual rendering:
249
-
250
- <figure>
251
- <img
252
- src="./docs/nodoku-way-carousel-screenshot.png"
253
- alt="Nodoku landing page part with carousel"
254
- title="Nodoku landing page part with carousel"
255
- />
256
- <figcaption>
257
- <b>Figure 2</b>: Three content blocks, rendered as the carousel component.
258
- </figcaption>
259
- </figure>
260
-
261
-
262
-
263
- # Getting started
264
-
265
- ## Prerequisites
266
-
267
- As has been mentioned above, Nodoku is a library intended to be used within the NextJS framework. The creation of a NextJS project is out of scope for the current documentation.
268
-
269
- We assume that the user is already familiar with the following concepts:
270
- - **_NextJS_**:
271
- - Nodoku is written to be used in server side rendering in NextJS
272
- - **_Typescript_**:
273
- - Nodoku is written using Typescript (5.x), it uses the ECMAScript Modules (ESM) as module standard. It doesn't ship as CommonJS or UMD modules.
274
- - **_Tailwind CSS_**:
275
- - Nodoku heavily relies on Tailwind for visual appearance. All the Nodoku components are using Tailwind for their default visual configuration. And Tailwind can be used for skin customization.
276
- - **_React and JSX_**:
277
- - you need a basic knowledge of React and JSX as all the Nodoku visual compnents are async functions returning JSX (see the declaration of _AsyncFunctionComponent_).
278
- - **_Webpack config_**:
279
- - when using Nodoku with flowbite (see [nodoku-flowbite](https://github.com/nodoku/nodoku-flowbite)) it might be required to modify the default Webpack configuration to make sure only one instance of flowbite is used
280
-
281
- ## Installation
282
- In order to use Nodoku one needs to install the **_nodoku-core_** library (this one) and at least one component library for Nodoku (for example, nodoku-flowbite)
283
-
284
-
285
- ```shell
286
- npm install nodoku-core nodoku-flowbite
287
- ```
288
-
289
- ## Integrating Nodoku into a project
290
-
291
- The entry point of a Nodoku library is the **_RenderingPage_** component.
292
-
293
- This component receives as properties the flow of content blocks and the skin, and renders accordingly.
294
-
295
- Here is a typical example of the usage of the RenderingPage TSX component:
296
-
297
- ```tsx
298
-
299
- // load and parse the content MD file
300
- const content: NdContentBlock[] = await contentMarkdownProvider("<url location of the content file>.md", "en", "nodoku-landing")
301
-
302
- // load the Yaml skin file
303
- const skin: NdPageSkin = await skinYamlProvider("<url location of the skin file>.yaml")
304
-
305
- ...
306
-
307
- <RenderingPage
308
- lng={lng}
309
- renderingPriority={RenderingPriority.skin_first}
310
- skin={skin}
311
- content={content}
312
- componentResolver={defaultComponentResolver}
313
- />
314
- ```
315
-
316
- ### Loding and parsing of the content MD file
317
-
318
- First, we parse the content MD file, using the predefined function _contentMarkdownProvider_.
319
-
320
- The implementation of this function is intentionally kept simple, so that it can easily be replaced by a custom provider, if for some reason the standard one cannot be used.
321
-
322
- ```ts
323
- async function contentMarkdownProvider(
324
- mdFileUrl: string,
325
- contentLng: string,
326
- ns: string): Promise<NdContentBlock[]> {
327
-
328
- return await fetch(mdFileUrl)
329
- .then(response => response.text())
330
- .then(fileContent => {
331
- return parseMarkdownAsContent(fileContent, contentLng, ns)
332
- })
333
- }
334
- ```
335
-
336
- The real parsing happens in the function _parseMarkdownAsContent_, which can also be used directly, if for some reason the URL of the MD file is not available.
337
-
338
- The result of this phase is the set of content blocks, represented as an array of _NdContentBlock_ items.
339
-
340
- Each content block that is extracted from the content MD file has an id - called **_blcokId_**.
341
-
342
- The content block id is an important concept as it serves as a prefix for the translation keys, generated for that content block.
343
-
344
- The Nodoku content blocks are made available for translation and localization out of the box.
345
-
346
- Hence, the unique translation keys given for each piece of textual information is important.
347
-
348
- The contenb blockId can
349
- - either be provided directly as metadata in the delimiting Yaml code snippet
350
- - or, if not explicitly provided, it is generated automatically, using the metadata available
351
-
352
- #### Explicitly providing content blockId
353
-
354
- The content blockId can be provided directly in the Yaml code snippet, delimiting the content blocks:
355
-
356
- ```yaml
357
- nd-block:
358
- id: nodoku-way-think
359
- ```
360
-
361
- As this:
362
-
363
- ```markdown
364
- ```yaml
365
- nd-block:
366
- id: nodoku-way-think
367
- attributes:
368
- sectionName: nodoku-way
369
- ``
370
-
371
- # Step 1: _Think_
372
- ## Create content promoting your product or service as an **MD file**
373
-
374
- ```
375
-
376
- This would generate the content block with blockId **_nodoku-way-think_**
377
-
378
-
379
- and consequently the following translation keys will be generated for this block:
380
- > - nodoku-way-think.title
381
- > - nodoku-way-think.subTitle
382
-
383
- #### Automatic generation of content blockId
384
-
385
- It might be tedious to require the end user to explicitly provide a metadata, including blockId, for each and every content.
386
-
387
- After all, Nodoku's philosophy is a _content first_ approach, where the textual content creation should be as seamless and straightforward, as possible.
388
-
389
- Hence, Nodoku parser will generate the blockId automatically, if not provided, using the sequential index of the content block.
390
-
391
-
392
- Consider this example:
393
-
394
- ```markdown
395
- ```yaml
396
- nd-block:
397
- attributes:
398
- sectionName: nodoku-way
399
- ``
400
-
401
- # Step 1: _Think_
402
- ## Create content promoting your product or service as an **MD file**
403
- ```
404
-
405
-
406
-
407
-
408
-
409
- ### Loading of the skin Yaml file
410
-
411
- Likewise, there is a readily available parser for Yaml files, used as Nodoku skin. Recall, that _skin_ is a configuration for visual page rendering.
412
-
413
- ```ts
414
- async function skinYamlProvider(yamlFileUrl: string): Promise<NdPageSkin> {
415
- return await fetch(yamlFileUrl)
416
- .then(response => response.text())
417
- .then(parseYamlContentAsSkin);
418
- }
419
- ```
420
-
421
- If, for some reason, the standard implementation is not suitable for particular needs, one can easily replace this function by another one, using _parseYamlContentAsSkin_ as the actual parser.
422
-
423
- The result of this parsing is an instance of _NdPageSkin_, which conveys the necessary information regarding the Nodoku skin.
424
-
425
- ### Rendering Nodoku
426
-
427
- Finally, when the skin and content are loaded, we can invoke the actual rendering using the provided component _RenderingPage_.
428
-
429
- RenderingPage is an async JSX function, which is suitable for usage in the NextJS environment.
430
-
431
- Let's have a closer look at its properties:
432
-
433
- ```ts
434
- class RenderingPageProps {
435
- lng: string;
436
- content: NdContentBlock[];
437
- skin: NdPageSkin | undefined = undefined;
438
- renderingPriority: RenderingPriority = RenderingPriority.content_first;
439
- componentResolver: ComponentResolver | undefined = undefined;
440
- imageUrlProvider: ImageUrlProvider | undefined = undefined;
441
- i18nextProvider: I18nextProvider | undefined = undefined;
442
- }
443
- ```
444
-
445
- - **_lng_**: the language in which the page should be rendered. Nodoku supports localization out of the box (see more on Nodoku localization in [nodoku-i18n](https://github.com/nodoku/nodoku-i18n))
446
-
447
-
448
- - **_content_**: the content flow, represented as an array of NdContentBlock instances. The content blocks are usually parsed from a Markdown file using the provided parser _contentMarkdownProvider_.
449
-
450
-
451
- - **_skin_**: an instance of NdPageSkin class representing the Nodoku skin - configuration of the visual representation of the page. It is usually loaded from a Yaml file using the supplied loader _skinYamlProvider_.
452
-
453
-
454
- - **_renderingPriority_**: this parameter is an enum that can have two values:
455
- - > **_content_first_**: the content is rendered as it appears in the markdown file, sequentially, block by block, from top to bottom. If a visual component is configured in the skin Yaml file, this visual component is used for rendering the content block. Otherwise, a default visual component is used
456
- - > **_skin_first_**: the rendering is fully prescribed by the skin Yaml file. The components are rendered in the order they appear in the Yaml file
457
- If a content block is not matched by any of the visual components in the skin Yaml file, it is not rendered at all. If a content block matches more than one visual component, each visual component is rendered, and the same content block will appear several times on the page.
458
-
459
-
460
- - **_componentProvider_**: the function returning an actual implementation of the component, given its name, as specified in the skin. The signature is as follows:
461
- > ```(componentName: string) => Promise<AsyncFunctionComponent>```
462
- >
463
- where AsyncFunctionComponent is the following function:
464
- > ```(props: NdSkinComponentProps) => Promise<JSX.Element>```
465
-
466
- the actual implementations, respecting the _AsyncFunctionComponent_ signature, are usually supplied via the component bundles, such as **_[nodoku-flowbite](https://github.com/nodoku/nodoku-flowbite)_** and **_[nodoku-mambaui](https://github.com/nodoku/nodoku-mambaui)_**
467
-
468
-
469
- - **_imageUrlProvider_**: the function allowing to customize the image URL conversion for rendering. It may so happen that the URL of images appearing in the MD file are different from those appearing on the page. For example, we often use the relative notation for images in the MD file, whereas this is not suitable for page rendering. The conversion between URL's in the MD file, and the URL's on the page can be provided using this parameter. The default implementation strips the leading dots, thus naively converting a relative url to an absolute one. Like this (more sophisticated patterns can be implemented, if required):
470
- > **_../images/my-image-123.png_** will be converted to **_/images/my-image-123.png_**
471
-
472
-
473
- - **_i18nextProvider_**: this parameter can be used to provide the localization mechanism for Nodoku. This function is supposed to return an object containing the **_t()_** function, which will further be used for translating the text.
474
- > type I18nextProvider = (lng: string) => Promise<{t: (text: NdTranslatedText) => string}>
475
-
476
- Note, that the provided **_t()_** function takes as an argument the whole NdTranslatedText structure, which contains the translation namespace, the key and the default value, extracted from the content.
477
-
478
- This parameter is optional. If not provided, the text from the content MD file is used.
479
-
480
-
481
-
482
- For more details see [nodoku-i18n](https://github.com/nodoku/nodoku-i18n)
483
-
484
- ### Nodoku component resolver
485
-
486
- The component resolver is expected to resolve each component name in a textual form, as extract from the skin file, to an actual component definition, among other things containing the actual Javascript function that should be called to render the component.
487
-
488
- > As the components are provided as external dependencies, Nodoku supplies a special script - **_nodoku-gen-component-resolver_** - that should be launched from command line to automatically generate the component resolver.
489
-
490
- Here is the typical example of the generated component resolver, that is crucial for successful functioning of Nodoku:
491
-
492
- ```ts
493
-
494
- import {AsyncFunctionComponent, DummyComp, NdComponentDefinition} from "nodoku-core";
495
-
496
- import { NodokuFlowbite } from "nodoku-flowbite";
497
-
498
- const components: Map<string, {compo: AsyncFunctionComponent, compoDef: NdComponentDefinition}> =
499
- new Map<string, {compo: AsyncFunctionComponent, compoDef: NdComponentDefinition}>();
500
-
501
- components.set("flowbite/card", {
502
- compo: NodokuFlowbite.Card,
503
- compoDef: new NdComponentDefinition(1,
504
- "./schemas/nodoku-flowbite/dist/schemas/components/card/default-theme.yml")
505
- });
506
-
507
- // other component definitions go here
508
-
509
- export function nodokuComponentResolver(componentName: string): Promise<{compo: AsyncFunctionComponent, compoDef: NdComponentDefinition}> {
510
- const f: {compo: AsyncFunctionComponent, compoDef: NdComponentDefinition} | undefined =
511
- components.get(componentName);
512
- return Promise.resolve(f ? f : {compo: DummyComp, compoDef: new NdComponentDefinition(1)});
513
- }
514
-
515
- ```
516
-
517
- Recall that the _componentResolver_ is one of the properties of the _RenderingPage_ component.
518
-
519
- Thanks to this function Nodoku can find the correspondence between the component name specified in the skin Yaml file, and the actual component implementation.
520
-
521
- It is worth noting here that **_nodoku-core_** is the Nodoku engine, which contains the resolving and parsing functionality, but it contains no visual components.
522
-
523
- The visual components are supposed to be supplied as external library dependency, such as _nodoku-flowbite_ and _nodoku-mambaui_.
524
-
525
- The static map, that is generated by the scirpt _nodoku-gen-component-resolver_, associates with each component name two attributes:
526
- - **_compo_**: the function that is to be called to render the component
527
- - **_compoDef_**: the definition of the component. The component defintion is loaded from the Nodoku manifest, supplied in the bundle where this component is defined. The component definition includes the following fields:
528
- - **_numBlocks_**: the maximum number of blocks this component supports
529
- - **_defaultThemeYaml_**: the Yaml file defining the default component visual configuration (usually uses Tailwind class names)
530
-
531
- These components parameters are defined in a special file - **_nodoku.manifest.json_** - which is supposed to be shipped within the component bundle. See below for more details.
532
-
533
- ## Configuring Nodoku project
534
-
535
- ### Tailwind configuration
536
-
537
- Since Nodoku is heavily relying on Tailwind, the Nodoku project is supposed to be configured accordingly. Otherwise, the Tailwind classes won't be parsed, and the page would not be rendered correctly.
538
-
539
- To understand how to configure Tailwind we first need to understand how Tailwind functions.
540
-
541
- Tailwind is a set of predefined CSS classes, each having its own responsibility and value.
542
-
543
- For example, ```mt-10``` results in the following CSS being genrated by Tailwind:
544
-
545
- ```css
546
- .mt-10 {
547
- margin-top: 2.5rem;
548
- }
549
- ```
550
- Since there are a lot of those Tailwind classes, not all of them are included in the final bindle, but only those that are actually used in the project.
551
-
552
- Hence, Tailwind needs to know what files to parse to look for actual Tailwind classes that are being used. Only the CSS classes discovered during this parsing, will be included in the final bundle.
553
-
554
- In order for Nodoku to function properly, one needs to include all the places where potentially Tailwind classes can be encountered.
555
-
556
- Here is the typical **_tailwind.config.ts_**
557
-
558
- ```ts
559
- import flowbite from "flowbite-react/tailwind";
560
- import type {Config} from "tailwindcss";
561
- import * as typo from '@tailwindcss/typography';
562
-
563
-
564
- const config: Config = {
565
- content: [
566
- "./src/**/*.ts",
567
- "./src/**/*.tsx",
568
- "./src/**/*.js",
569
- "./src/**/*.jsx",
570
-
571
- "./node_modules/nodoku-core/dist/esm/**/*.js",
572
- "./node_modules/nodoku-core/dist/esm/**/*.jsx",
573
- "./node_modules/nodoku-components/dist/esm/**/*.js",
574
- "./node_modules/nodoku-components/dist/esm/**/*.jsx",
575
- "./node_modules/nodoku-flowbite/dist/esm/**/*.js",
576
- "./node_modules/nodoku-flowbite/dist/esm/**/*.jsx",
577
- "./node_modules/nodoku-flowbite/dist/schemas/**/*.yml",
578
- "./node_modules/nodoku-mambaui/dist/esm/**/*.js",
579
- "./node_modules/nodoku-mambaui/dist/esm/**/*.jsx",
580
- "./node_modules/nodoku-mambaui/dist/schemas/**/*.yml",
581
- "./public/**/*.html",
582
- "./src/**/*.{html,js}",
583
- "./public/site/**/*.yaml",
584
- flowbite.content(),
585
- ],
586
-
587
- theme: {
588
- extend: {
589
- aspectRatio: {
590
- 'carousel': '4 / 1.61',
591
- },
592
- typography: {
593
- DEFAULT: {
594
- css: {
595
- maxWidth: 'unset',
596
- }
597
- }
598
- }
599
- },
600
- },
601
- plugins: [
602
- flowbite.plugin(),
603
- typo.default(),
604
- ],
605
- };
606
-
607
- export default config;
608
-
609
- ```
610
-
611
- Let's have a closer look at this configuration:
612
-
613
- - **_content_**: should contain all the paths where Tailwind classes are located, and consequently will be parsed by Tailwind for class extraction.
614
- - for each Nodoku component bundle it should contain the all the ```.js```, ```.jsx``` and ```.yml``` files, because it might contain the Tailwind classes
615
- - if a component bundle using _flowbite-react_ is used (such as _nodoku-flowbite_), its plugin should be included in the content
616
- - **_theme_**: this is a standard Tailwind's theme definition. For example, in this example we are defining a new aspectRation value called ```carousel```, which later can be used for styling Nodoku components
617
- - **_plugins_**: this section includes plugins needed for Nodoku components to work properly
618
- - **_flowbite.plugin()_** - if components based on flowbite-react are used, this plugin should be included
619
- - **_typo.default()_** - if the [Nodoku Typography](https://github.com/nodoku/nodoku-components) plugin is used, this plugin should be included
620
-
621
- For more details on Tailwind config see the section "[Configuring Tailwind](https://github.com/nodoku/nodoku-flowbite?tab=readme-ov-file#configuring-tailwind)" in one of the Nodoku component bundles - [nodoku-flowbite](https://github.com/nodoku/nodoku-flowbite).
622
-
623
- ### Webpack configuration
624
-
625
- It might so happen that we'll need to configure the Webpack in order for Nodoku to run properly.
626
-
627
- In particular this problem becomes apparent if we are using flowbite-react, in order to ensure that only instance of flowbite-react is included in the final bundle.
628
-
629
- Otherwise, the flowbite-react theme might not work properly, and all the customizations of this theme will not be applied.
630
-
631
- Webpack configuration in a NextJS project is not trivial, as we don't have access directly to the file webpack.config.js.
632
-
633
- Instead, we should be redefining the **_nextConfig_** in _next.config.mjs_ as follows:
634
-
635
- ```js
636
- import path from "node:path";
637
-
638
- const nextConfig = (phase, {defaultConfig}) => {
639
- /**
640
- * @type {import('next').NextConfig}
641
- */
642
- const nextConfig = {
643
- /* config options here */
644
- ...defaultConfig,
645
- webpack: ((config, opts) => {
646
- config.resolve.alias["flowbite-react"] = path.resolve('./node_modules/flowbite-react');
647
- config.resolve.alias["flowbite"] = path.resolve('./node_modules/flowbite');
648
- return config;
649
- })
650
- }
651
-
652
- return nextConfig
653
- }
654
-
655
- export default nextConfig;
656
- ```
657
-
658
- Note that we pin down the resolution of _flowbite_ and _flowbite-react_ to the particular dependencies in our _node_modules_ folder.
659
-
660
- # Nodoku component bundle
661
-
662
- As has already been mentioned above, Nodoku visual components are supplied as a component bundle - a standard NPM dependency, that can be installed using the standard NPM installation process
663
- > ```npm install <nodoku component bundle>```
664
-
665
- for example:
666
- > ```npm install nodoku-flowbite```
667
-
668
- ## Nodoku manifest
669
-
670
- Each component bundle should be shipped with a special file called **_nodoku.manifest.json_**.
671
-
672
- This json file is a visit card of the component bundle: it contains all the information required for Nodoku engine to treat correctly the component and use it following the configuration in the Nodoku skin Yaml file.
673
-
674
- Here is an excerpt from such file for nodoku-flowbite:
675
-
676
- ```json
677
- {
678
- "namespace": "NodokuFlowbite",
679
- "components": {
680
- ..
681
- "flowbite/carousel": {
682
- "schemaFile": "./dist/schemas/components/carousel/visual-schema.json",
683
- "optionsFile": "./dist/schemas/components/carousel/options-schema.json",
684
- "defaultThemeFile": "./dist/schemas/components/carousel/default-theme.yml",
685
- "implementation": "Carousel",
686
- "numBlocks": "unlimited"
687
- },
688
- ...
689
- }
690
- }
691
- ```
692
-
693
- Let's have a closer look at the attributes of each Nodoku visual component:
694
-
695
- - **_namespace_**: the Javascript namespace where out of which the component is being exported.
696
-
697
- The presence of the namespace attribute instructs the generator of the component resolver (nodoku-gen-component-resolver) to insert the component definition prefixed with the namespace.
698
- ```ts
699
- import { NodokuFlowbite } from "nodoku-flowbite";
700
- components.set("flowbite/carousel", {compo: NodokuFlowbite.Carousel, ...});
701
- ```
702
- The Nodoku manifest cannot have more than one namespace.
703
-
704
-
705
- - **_components_**: is a object associating a textual component name with the data structure that is used to extract the component definition. This data structure include:
706
-
707
-
708
- - **_schemaFile_**: is a Json schema file representing the data structure of the visual theme of the component (see [Schema for Nodoku skin Yaml file](#schema-for-nodoku-skin-yaml-file))
709
-
710
-
711
- - **_optionsFile_**: is a Json schema file representing the data structure of the functional options of the component (see [Schema for Nodoku skin Yaml file](#schema-for-nodoku-skin-yaml-file))
712
-
713
-
714
- - **_defaultThemeFile_**: a Yaml file containing the default Tailwind configuration of the component
715
-
716
-
717
- - **_implementation_**: the actual Javascript function rendering this compnent. (the signature should correspond to _AsyncFunctionComponent_)
718
-
719
-
720
- - **_numBlocks_**: number of content blocks this component can accept for rendering. This parameter can either be a positive number of the predefined value '_unlimited_'
721
-
722
-
723
-
724
- The _numBlocks_ parameter is used by the Nodoku engine to calculate the correct number of content blocks this visual component is capable of rendering. This parameter is normally either **_1_** or **_'unlimited'_**.
725
-
726
- The _defaultThemeYaml_ attribute is an important parameter designating the Yaml file where the default Tailwind configuration is located. Normally each Nodoku visual component comes as an empty JSX scaffolding, which delegates the styling - the actual Tailwind classes - to the external data structure, the component default theme.
727
-
728
- # Nodoku skin
729
-
730
-
731
- The Nodoku skin is a Yaml file which configures the Nodoku rendering.
732
-
733
- Recall, that in Nodoku there is a strict separation between the content and the visual representation.
734
-
735
- The content is provided via an MD file, whereas the mapping between the content blocks and visual components are defined in the _skin_.
736
-
737
- The skin also defines the necessary customizations and fine-tuning of the page visual representation, if required.
738
-
739
- ## Nodoku visual component theme
740
-
741
- Normally, each JSX block in a visual component is styled using the Tailwind class names. This styling is typically composed of two parts: _base_ and _decoration_.
742
-
743
- Consider the following example:
744
-
745
- ```tsx
746
- <h3 className={`${effectiveTheme.titleStyle?.base} ${effectiveTheme.titleStyle?.decoration}`}>
747
- {t(block.title)}
748
- </h3>
749
-
750
- <h4 className={`${effectiveTheme.subTitleStyle?.base} ${effectiveTheme.subTitleStyle?.decoration}`}>
751
- {t(block.subTitle)}
752
- </h4>
753
- ```
754
-
755
- The component theme is supplied to the component by the Nodoku engine, and comes in as a data structure containing two elements:
756
- - one for title styling _effectiveTheme.**titleStyle**_
757
- - and one for subtitle styling _effectiveTheme.**subTitleStyle**_
758
-
759
- Each such element is called _ThemeStyle_, and is composed of two string attributes: _base_ and _decoration_
760
-
761
- The component theme consists of several attributes of type _ThemeStyle_.
762
-
763
- Here is an excerpt from the component theme _flowbite/card_
764
-
765
- ```ts
766
- export class CardTheme {
767
- ...
768
- titleStyle?: ThemeStyle;
769
- subTitleStyle?: ThemeStyle;
770
- ...
771
- }
772
- ```
773
- where ThemeStyle is defined as follows:
774
- ```ts
775
- export class ThemeStyle {
776
- base: string;
777
- decoration: string;
778
- }
779
- ```
780
-
781
- For each component there is a default theme supplied in a Yaml file, and this file is defined for the given visual component in the Nodoku manifest.
782
-
783
- In addition, the theme of each component can further be customized in the skin Yaml file.
784
-
785
-
786
- # The Nodoku skin Yaml file
787
-
788
-
789
-
790
-
791
- The skin Yaml file has a predefined structure, which can be summarized as follows:
792
-
793
- ```yaml
794
- global:
795
- renderingPage:
796
- # styles applied to the whole page
797
- theme:
798
- # attributes defined on the global level for all the component themes
799
- themes:
800
- # contains the list of theme customizations, where each theme from the list is applied sequentially on each content block rendered by the component of a given type in a single row
801
- components:
802
- <component-name>:
803
- theme:
804
- # defines a theme customization for a particular component
805
- themes:
806
- # defines a list of theme customizations for a particular component, where each theme from the list is applied sequentially on each content block rendered by the component of this type in a single row
807
-
808
- rows:
809
- # list of rows
810
- - row:
811
- theme:
812
- # styling classes applied on the row level
813
- maxCols:
814
- # optional: a limit on a number of visual components in a row. By default, the max number of elements in a row is 12 (grid-cols-12 from Tailwind) but it can be reduced to 1, when we need content blocks to be located one beneat the other
815
- components:
816
- <component-name>:
817
- selector:
818
- # the data structure defining the selector to be applied on content blocks to select the matching content blocks that would be rendered by this component
819
- options:
820
- # a component may accept an options object, which can be defined here
821
- theme:
822
- # the customization of the component's scheme
823
- themes:
824
- # a list of theme customizations for the given component, where each theme from the list is applied sequentially on each content block rendered by the component of this type in a single row
825
- ```
826
-
827
- The customization is applied progressively, from the least specific theme to the most specific, starting from the default theme of the component.
828
-
829
- Since the Nodoku skin file is large, and can contain many customization options, it is desirable to have a schema file that would guide the user through the process of defining and editing of the skin.
830
-
831
- ## Schema for Nodoku skin Yaml file
832
-
833
- In order to make the Nodoku skin creation process more convenient and predictable, Nodoku provides a possibility to create a schema file for a project, with a given set of Nodoku component bundles, defined in NPM dependencies (in package.json).
834
-
835
- Similar to the generation script for Nodoku component resolver, there is a script for generation of the Nodoku skin schema: **_nodoku-gen-skin-schema_**
836
-
837
- One can run this script to automatically generate a Nodoku skin schema file.
838
-
839
- The schema file by default is generated in the folder
840
- > <project folder>/schemas
841
-
842
- After the schema generation script completes, the **_./schemas_** folder should look as follows:
843
-
844
- <figure>
845
- <img
846
- src="./docs/schemas-folder-expanded.png"
847
- alt="project schemas folder after schema generation"
848
- title="project schemas folder after schema generation"
849
- />
850
- <figcaption>
851
- <b>Figure 3</b>: The <b>./schemas</b> folder contains the schema file - <b>visual-schema.json</b> - to be applied on the Nodoku skin Yaml file
852
- </figcaption>
853
- </figure>
854
-
855
- The generated schema file - ./schemas/visual-schema.json - can be applied to the skin Yaml file in several ways:
856
- - use [the IDE mechanism of schema application](https://www.jetbrains.com/help/idea/yaml.html#json_schema)
857
- - use the **_yaml-language-server_** header (the first row) for the Yaml file as follows:
858
-
859
- ```yaml
860
- # yaml-language-server: $schema=../../../schemas/visual-schema.json
861
-
862
- global:
863
- renderingPage:
864
- base: dark:text-gray-200 text-gray-700
865
-
866
- rows:
867
- - row:
868
- theme:
869
- decoration: mb-10
870
-
871
- ```
872
-
873
- ## Customizing Nodoku page appearance
874
-
875
- The main principle of component customization in Nodoku consists of cascading application of themes, defined on different levels:
876
- - **_global level_**: the section **_global_** in the Yaml file. This level can further be divided to
877
- - **_theme level_**: where any attribute for any component can be defined
878
- - **_component level_**: where attributes for a given component can be defined
879
- - **_rows level_**: where the list of rows defines each its set of components
880
- - **_component in a row level_**: the most specific customization, where a particular component can be customized
881
-
882
- The process of customization starts from the component's default theme and works its way through all the defined customizations, until the most specific level is reached.
883
-
884
-
885
-
886
-
887
- # Nodoku generation scripts
888
-
889
- The nodoku-core provides the following scripts, that are used to generate component resolver and visual schema, by scanning the **node_modules** folder of the project
890
-
891
-
892
- - **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. For more details see [Nodoku component resolver](#nodoku-component-resolver)
893
-
894
-
895
- - **nodoku-gen-visual-schema**: generates the json schema file thate can be used to validate the Nodoku skin schema file. For more details see [Schema for Nodoku skin Yaml file](#schema-for-nodoku-skin-yaml-file)
896
-
897
- To simplify the use of these script it is recommended to add them in the project's package.json file as follows:
898
-
899
- ```json
900
- {
901
- ...
902
- "scripts": {
903
- "gen-component-resolver": "nodoku-gen-component-resolver",
904
- "gen-skin-schema": "nodoku-gen-skin-schema"
905
- },
906
- ...
907
- }
908
- ```
909
-
910
-
911
- # Nodoku license
912
-
913
- The Nodoku License to be used depends on the use case you plan to use Nodoku for:
914
-
915
- - using Nodoku to build an End Product, which is a webpage or a website for private or commercial use
916
- - in short, in this case Nodoku **_is free_** to use under the MIT License (see below for more details)
917
- - using Nodoku to build an End Product, which is a webpage or a website builder (online or offline)
918
- - in short, in this case the use of Nodoku **_is prohibited_**
919
-
920
-
921
- Note, that
922
- > in any case of the allowed, free-of-charge use of Nodoku, **_the present Nodoku license should be included in all copies of the Software, using Nodoku_**.
923
-
924
-
925
-
926
- ## Use for private, educational or commercial needs, excluding applications for webpage or website building tools
927
-
928
- Any use of Nodoku, **_except for webpage and website builder applications_**, is allowed free of charge, under the **_MIT license_** ( see the [LICENSE](./blob/main/LICENSE) )
929
-
930
-
931
-
932
- ## Use as part of website or webpage builders
933
-
934
- The use of Nodoku for the purposes to provide a solution for online (or offline) website or webpage builders is **_prohibited_**.
935
-
936
- This prohibition includes the direct or indirect use of the Nodoku library as part of webpage builder solution.
937
-
938
- Indirect use of Nodoku library would include building a software or a framework that would then be used as part of webpage or website builder.
939
-
940
- Webpage or Website builder application is any tool, online or offline, that would allow the End User to build a publishable webpage or website, using no code or low amount of code.
941
-
942
- This is prohibited under this license term since that would constitute a direct competition to Nodoku lo-code site builder.
943
-
944
- # Examples
945
-
946
- ## Proper use, free of charge, under the MIT License
947
-
948
- - building a webpage or a website for a private non-commercial project
949
- - building a webpage or a website for a private commercial project
950
- - building a webpage or a website for a public commercial or non-commercial blog
951
- - building a webpage or a website for a client, that would use it for commercial or non-commercial purposes
952
- - creating and distributing non-commercially (free of charge) custom Nodoku components
953
- - creating and distributing commercially custom Nodoku components (developing and selling out new Nodoku-compatible UI components)
954
- - developing new UI components and selling them out either as a Nodoku component, or a standalone component, or a component compatible with another website builder
955
-
956
- ## Prohibited use
957
-
958
- - building a website builder, that would use the Nodoku engine to provide the End User with a tool, that would allow them building their own webpages or websites
959
- - using Nodoku engine to provide the End User with an automatic, semi-automatic or manual framework, that would allow them building their own webpages or websites
960
- - using Nodoku libraries to use it as part of the solution that would allow the End User to build a webpage or a website
1
+ <!-- TOC -->
2
+ * [Nodoku foundation](#nodoku-foundation)
3
+ * [Nodoku content flow](#nodoku-content-flow)
4
+ * [Nodoku skin](#nodoku-skin)
5
+ * [Getting started](#getting-started)
6
+ * [Prerequisites](#prerequisites)
7
+ * [Installation](#installation)
8
+ * [Integrating Nodoku into a project](#integrating-nodoku-into-a-project)
9
+ * [Loding and parsing of the content MD file](#loding-and-parsing-of-the-content-md-file)
10
+ * [Explicitly providing content blockId](#explicitly-providing-content-blockid)
11
+ * [Automatic generation of content blockId](#automatic-generation-of-content-blockid)
12
+ * [Loading of the skin Yaml file](#loading-of-the-skin-yaml-file)
13
+ * [Rendering Nodoku](#rendering-nodoku)
14
+ * [Nodoku component resolver](#nodoku-component-resolver)
15
+ * [Configuring Nodoku project](#configuring-nodoku-project)
16
+ * [Tailwind configuration](#tailwind-configuration)
17
+ * [Webpack configuration](#webpack-configuration)
18
+ * [Nodoku component bundle](#nodoku-component-bundle)
19
+ * [Nodoku manifest](#nodoku-manifest-)
20
+ * [Nodoku skin](#nodoku-skin-1)
21
+ * [Nodoku visual component theme](#nodoku-visual-component-theme)
22
+ * [The Nodoku skin Yaml file](#the-nodoku-skin-yaml-file)
23
+ * [Schema for Nodoku skin Yaml file](#schema-for-nodoku-skin-yaml-file)
24
+ * [Customizing Nodoku page appearance](#customizing-nodoku-page-appearance)
25
+ * [Nodoku generation scripts](#nodoku-generation-scripts)
26
+ * [Nodoku license](#nodoku-license)
27
+ * [Use for private, educational or commercial needs, excluding applications for webpage or website building tools](#use-for-private-educational-or-commercial-needs-excluding-applications-for-webpage-or-website-building-tools)
28
+ * [Use as part of website or webpage builders](#use-as-part-of-website-or-webpage-builders)
29
+ * [Examples](#examples)
30
+ * [Proper use, free of charge, under the MIT License](#proper-use-free-of-charge-under-the-mit-license)
31
+ * [Prohibited use](#prohibited-use)
32
+ <!-- TOC -->
33
+
34
+ GitHub link: https://github.com/nodoku/nodoku-core
35
+
36
+ Nodoku is a static site generator, where the content is provided via MD (markdown) files, and the visual representation is controlled using Yaml files - called _skin_.
37
+
38
+ Nodoku promotes the content-first approach, when the content creation process is not being distracted by the considerations related to the visual representation and design.
39
+
40
+ Instead, the content is created first as an MD file, demarcated by the special markers into content blocks, and then each block is rendered using the configuration provided in a Yaml file called _skin_.
41
+
42
+ Figure 1 shows a screenshot of a part of a landing page created with Nodoku.
43
+
44
+ <figure>
45
+ <img
46
+ src="./docs/nodoku-way-card-screenshot.png"
47
+ alt="Nodoku landing page part with 3 cards"
48
+ title="Nodoku landing page part with 3 cards"
49
+ />
50
+ <figcaption>
51
+ <b>Figure 1</b>: A part of a landing page, created using Nodoku.
52
+ It has 3 cards, organized in a row, where the content of each card is supplied by an MD file. The mapping between content and visual representation is configured in a Yaml file.
53
+ </figcaption>
54
+ </figure>
55
+
56
+
57
+
58
+ Nodoku is a set of libraries, the most important of which is nodoku-core, intended to be used with the **_NextJS_** framework for generation of static sites.
59
+
60
+ Nodoku is intended to be used in server side rendering, it is not suitable for client side.
61
+
62
+ nodoku-core doesn't contain the visual representation for content blocks. Instead, the visual representation is supplied via separated dependencies, such as **_nodoku-flowbite_** (components based on [Flowbite](https://flowbite.com/docs/getting-started/introduction/)) and **_nodoku-mambaui_** (components based on [MambaUI](https://mambaui.com/components/hero)).
63
+
64
+ More set of components can be added, and included in the project as required.
65
+
66
+ The structure of the skin files is organized by rows, each row having one or more components.
67
+
68
+ The mapping between visual representation and the content block is performed using the concept of _selector_. Selector is a set of matching conditions, attached to a visual component in the skin Yaml file.
69
+
70
+ The actual rendering is performed in two steps:
71
+ - first, for a given visual component a set of matching content blocks is determined, using the selector and the meta-data of the content block
72
+
73
+
74
+ - second, this flow of content blocks is provided to the rendering mechanism of the visual component for actual rendering.
75
+
76
+ This decoupling allows for great level of flexibility and reuse between the content and the visual representation.
77
+
78
+ If the visual components support [Tailwind](https://tailwindcss.com/docs/installation), the Tailwind customization can be provided in the skin Yaml file to fine tune the visual representation.
79
+
80
+ You can learn more about the rationale behind Nodoku and the main principles in the blog article: [Nodoku: a lo-code static site generator, promoting a content-first approach based on Markdown files](https://epanikas.hashnode.dev/nodoku-a-lo-code-static-site-generator-promoting-a-content-first-approach-based-on-markdown-files)
81
+
82
+ # Nodoku foundation
83
+
84
+ Nodoku is organized around two flows of data:
85
+ - the content flow (supplied via a Markdown file)
86
+ - the visual representation flow (supplied via Yaml file called _skin_)
87
+
88
+ The Nodoku engine will take care of parsing these files and supply them to the designated visual components for rendering.
89
+
90
+ ## Nodoku content flow
91
+
92
+ As has been mentioned above, the Nodoku content flow is supplied via an MD file.
93
+
94
+ The content flow in Nodoku is organized around an entity called _content block_, which is a single piece of content suitable for rendering by a Nodoku visual component. The set of such components constitute the Nodoku content flow.
95
+
96
+ The content block has a predefined structure. It is designed to be more or less generic, and _visual representation agnostic_. In particular this is important to implement the _content-first_ principle, where the content can (and should!) be created first, without any visual design considerations.
97
+
98
+ Nodoku engine parses the MD file to extract the set of content blocks, that are contained in such file. The content blocks are delimited by a special markers - the content block delimiters.
99
+
100
+ The content block delimiter is a small piece of Yaml code snippet, embedded directly into the MD file.
101
+
102
+ For example, the content, that has been used to generate the screeshnot on Figure 1 is the following:
103
+
104
+ ```markdown
105
+
106
+ ```yaml
107
+ nd-block:
108
+ attributes:
109
+ sectionName: nodoku-way
110
+ ``
111
+
112
+ # Step 1: _Think_
113
+ ## Create content promoting your product or service as an **MD file**
114
+ ...
115
+ Concentrate on the subject of your product / service to highlight its advantages.
116
+ ...
117
+ |Get started|
118
+
119
+ # Step 2: _Skin_
120
+ ## Skin the MD file using simple **Yaml config** and available components
121
+ ...
122
+ Once you are happy with the message your landing page conveys,
123
+ start by skinning it up.
124
+ ...
125
+ |Get started|
126
+
127
+ # Step 3: _Fine tune_
128
+ ## Use configuration options to fine tune your landing page presentation
129
+ ...
130
+ If the default presentation doesn't suit your needs, you can tweak it up
131
+ using the config options of each component to fine tune it for your needs.
132
+ ...
133
+ |Get started|
134
+
135
+ ```
136
+
137
+ The first thing one would notice in this MD excerpt is the small Yaml code snippet.
138
+
139
+ ```yaml
140
+ nd-block:
141
+ attributes:
142
+ sectionName: nodoku-way
143
+ ```
144
+
145
+ This Yaml code snippet is a content block delimiter, and it contains the content block meta-data, such as _id_, _attributes_ and _tags_.
146
+
147
+ The schema for this Yaml code snippet can be found in the Json schema file : **_nodoku-core/schemas/md-content-block-delimiter.json_**
148
+
149
+ The content block has the following predefined, fixed structure:
150
+ ```typescript
151
+ class NdContentBlock {
152
+ // content block meta-data
153
+ id: string;
154
+ lng: string;
155
+ attributes: {key: string, value: string}[] = [];
156
+ tags: string[] = []
157
+ namespace: string;
158
+
159
+ // the actual textual content
160
+ title?: NdTranslatedText; // a title
161
+ subTitle?: NdTranslatedText; // a subtitle
162
+ h3?: NdTranslatedText; // h3 header
163
+ h4?: NdTranslatedText; // h4 header
164
+ h5?: NdTranslatedText; // h5 header
165
+ h6?: NdTranslatedText; // h6 header
166
+ footer?: NdTranslatedText; // footer
167
+ paragraphs: NdTranslatedText[]; // set of paragraphs
168
+ bgImageUrl?: NdTranslatedText; // background image
169
+ images: NdContentImage[] = []; // set of images
170
+ }
171
+ ```
172
+ where _NdTranslatedText_ represents a piece of text, that can be used for i18next translation (see below)
173
+ ```typescript
174
+ class NdTranslatedText {
175
+ key: string; // the translation key
176
+ ns: string; // the translation namespace
177
+ text: string; // the fallback text, extracted from content
178
+ }
179
+ ```
180
+
181
+ As one can see, the content block _cannot_ contain more than instance of each type of headers: title (h1), subtitle (h2), h3, etc.
182
+
183
+ But it _can_ contain several headers of different types, for example, a title (h1) and subtitle (h2)
184
+
185
+ > Hence, the parsing of the MD file goes as follows:
186
+ > - if the Yaml nd-block is encountered, a new content block is started, which would absord all the content elements that are discovered below, until a new content block is started
187
+ > - if a header (of any kind) is encountered
188
+ > - if a header of the same kind or below exists in the current block
189
+ > - finalize the current block
190
+ > - start a new block
191
+ > - and copy the metadata from the current block to the new one
192
+ > - otherwise
193
+ > - add the corresponding header to the current block
194
+
195
+ This approach allows for smooth content definition, where a common content block metadata is defined only once, and all the subsequent pieces of content are treated as new blocks with the same metadata.
196
+
197
+ Thanks to this parsing approach, the excerpt of the MD file, shown above, defines 3 content blocks - one per card shown on the screenshot of Figure 1 - instead of only one.
198
+
199
+
200
+ ## Nodoku skin
201
+
202
+ Nodoku skin is a Yaml file which configures the visual representation of the content defined in a markdown file.
203
+
204
+ The Nodoku page layout is organized as a set of rows, each row having its configured set of components.
205
+
206
+ Each layout row can have one or more visual components.
207
+
208
+ Here is an example of a typical Nodoku skin file:
209
+
210
+ ```yaml
211
+ rows:
212
+ - row:
213
+ components:
214
+ - mambaui/card:
215
+ selector:
216
+ attributes:
217
+ sectionName: nodoku-way
218
+ ```
219
+
220
+ In this example, we define a row containing components of type _mambaui/card_
221
+
222
+ The selector defines the content blocks that should be rendered using this visual component. In our case, these are all the content blocks having an attribute `sectionName` equal to `nodoku-way`.
223
+
224
+ Recall, that according to our MD file we are actually having 3 content blocks matching this criteria:
225
+ - Step 1: _Think_
226
+ - Step 2: _Skin_
227
+ - Step 3: _Fine tune_
228
+
229
+ Naturally, a single card component cannot display more than one content block.
230
+
231
+ Consequently, according to the skin Yaml file, the Nodoku engine will apply the same visual component definition to all the three matching content blocks.
232
+
233
+ And this process will end up rendering the screenshot presented on Figure 1.
234
+
235
+ It's worth noting that a visual component can support arbitrary number of content blocks. For example, the _flowbite/carousel_ component can display any number of content blocks.
236
+
237
+ Consequently, if the skin Yaml file had the following configuration:
238
+ ```yaml
239
+ rows:
240
+ - row:
241
+ components:
242
+ - flowbite/carousel:
243
+ selector:
244
+ attributes:
245
+ sectionName: nodoku-way
246
+ ```
247
+
248
+ it would have had the following visual rendering:
249
+
250
+ <figure>
251
+ <img
252
+ src="./docs/nodoku-way-carousel-screenshot.png"
253
+ alt="Nodoku landing page part with carousel"
254
+ title="Nodoku landing page part with carousel"
255
+ />
256
+ <figcaption>
257
+ <b>Figure 2</b>: Three content blocks, rendered as the carousel component.
258
+ </figcaption>
259
+ </figure>
260
+
261
+
262
+
263
+ # Getting started
264
+
265
+ ## Prerequisites
266
+
267
+ As has been mentioned above, Nodoku is a library intended to be used within the NextJS framework. The creation of a NextJS project is out of scope for the current documentation.
268
+
269
+ We assume that the user is already familiar with the following concepts:
270
+ - **_NextJS_**:
271
+ - Nodoku is written to be used in server side rendering in NextJS
272
+ - **_Typescript_**:
273
+ - Nodoku is written using Typescript (5.x), it uses the ECMAScript Modules (ESM) as module standard. It doesn't ship as CommonJS or UMD modules.
274
+ - **_Tailwind CSS_**:
275
+ - Nodoku heavily relies on Tailwind for visual appearance. All the Nodoku components are using Tailwind for their default visual configuration. And Tailwind can be used for skin customization.
276
+ - **_React and JSX_**:
277
+ - you need a basic knowledge of React and JSX as all the Nodoku visual compnents are async functions returning JSX (see the declaration of _AsyncFunctionComponent_).
278
+ - **_Webpack config_**:
279
+ - when using Nodoku with flowbite (see [nodoku-flowbite](https://github.com/nodoku/nodoku-flowbite)) it might be required to modify the default Webpack configuration to make sure only one instance of flowbite is used
280
+
281
+ ## Installation
282
+ In order to use Nodoku one needs to install the **_nodoku-core_** library (this one) and at least one component library for Nodoku (for example, nodoku-flowbite)
283
+
284
+
285
+ ```shell
286
+ npm install nodoku-core nodoku-flowbite
287
+ ```
288
+
289
+ ## Integrating Nodoku into a project
290
+
291
+ The entry point of a Nodoku library is the **_RenderingPage_** component.
292
+
293
+ This component receives as properties the flow of content blocks and the skin, and renders accordingly.
294
+
295
+ Here is a typical example of the usage of the RenderingPage TSX component:
296
+
297
+ ```tsx
298
+
299
+ // load and parse the content MD file
300
+ const content: NdContentBlock[] = await contentMarkdownProvider("<url location of the content file>.md", "en", "nodoku-landing")
301
+
302
+ // load the Yaml skin file
303
+ const skin: NdPageSkin = await skinYamlProvider("<url location of the skin file>.yaml")
304
+
305
+ ...
306
+
307
+ <RenderingPage
308
+ lng={lng}
309
+ renderingPriority={RenderingPriority.skin_first}
310
+ skin={skin}
311
+ content={content}
312
+ componentResolver={defaultComponentResolver}
313
+ />
314
+ ```
315
+
316
+ ### Loding and parsing of the content MD file
317
+
318
+ First, we parse the content MD file, using the predefined function _contentMarkdownProvider_.
319
+
320
+ The implementation of this function is intentionally kept simple, so that it can easily be replaced by a custom provider, if for some reason the standard one cannot be used.
321
+
322
+ ```ts
323
+ async function contentMarkdownProvider(
324
+ mdFileUrl: string,
325
+ contentLng: string,
326
+ ns: string): Promise<NdContentBlock[]> {
327
+
328
+ return await fetch(mdFileUrl)
329
+ .then(response => response.text())
330
+ .then(fileContent => {
331
+ return parseMarkdownAsContent(fileContent, contentLng, ns)
332
+ })
333
+ }
334
+ ```
335
+
336
+ The real parsing happens in the function _parseMarkdownAsContent_, which can also be used directly, if for some reason the URL of the MD file is not available.
337
+
338
+ The result of this phase is the set of content blocks, represented as an array of _NdContentBlock_ items.
339
+
340
+ Each content block that is extracted from the content MD file has an id - called **_blcokId_**.
341
+
342
+ The content block id is an important concept as it serves as a prefix for the translation keys, generated for that content block.
343
+
344
+ The Nodoku content blocks are made available for translation and localization out of the box.
345
+
346
+ Hence, the unique translation keys given for each piece of textual information is important.
347
+
348
+ The contenb blockId can
349
+ - either be provided directly as metadata in the delimiting Yaml code snippet
350
+ - or, if not explicitly provided, it is generated automatically, using the metadata available
351
+
352
+ #### Explicitly providing content blockId
353
+
354
+ The content blockId can be provided directly in the Yaml code snippet, delimiting the content blocks:
355
+
356
+ ```yaml
357
+ nd-block:
358
+ id: nodoku-way-think
359
+ ```
360
+
361
+ As this:
362
+
363
+ ```markdown
364
+ ```yaml
365
+ nd-block:
366
+ id: nodoku-way-think
367
+ attributes:
368
+ sectionName: nodoku-way
369
+ ``
370
+
371
+ # Step 1: _Think_
372
+ ## Create content promoting your product or service as an **MD file**
373
+
374
+ ```
375
+
376
+ This would generate the content block with blockId **_nodoku-way-think_**
377
+
378
+
379
+ and consequently the following translation keys will be generated for this block:
380
+ > - nodoku-way-think.title
381
+ > - nodoku-way-think.subTitle
382
+
383
+ #### Automatic generation of content blockId
384
+
385
+ It might be tedious to require the end user to explicitly provide a metadata, including blockId, for each and every content.
386
+
387
+ After all, Nodoku's philosophy is a _content first_ approach, where the textual content creation should be as seamless and straightforward, as possible.
388
+
389
+ Hence, Nodoku parser will generate the blockId automatically, if not provided, using the sequential index of the content block.
390
+
391
+
392
+ Consider this example:
393
+
394
+ ```markdown
395
+ ```yaml
396
+ nd-block:
397
+ attributes:
398
+ sectionName: nodoku-way
399
+ ``
400
+
401
+ # Step 1: _Think_
402
+ ## Create content promoting your product or service as an **MD file**
403
+ ```
404
+
405
+
406
+
407
+
408
+
409
+ ### Loading of the skin Yaml file
410
+
411
+ Likewise, there is a readily available parser for Yaml files, used as Nodoku skin. Recall, that _skin_ is a configuration for visual page rendering.
412
+
413
+ ```ts
414
+ async function skinYamlProvider(yamlFileUrl: string): Promise<NdPageSkin> {
415
+ return await fetch(yamlFileUrl)
416
+ .then(response => response.text())
417
+ .then(parseYamlContentAsSkin);
418
+ }
419
+ ```
420
+
421
+ If, for some reason, the standard implementation is not suitable for particular needs, one can easily replace this function by another one, using _parseYamlContentAsSkin_ as the actual parser.
422
+
423
+ The result of this parsing is an instance of _NdPageSkin_, which conveys the necessary information regarding the Nodoku skin.
424
+
425
+ ### Rendering Nodoku
426
+
427
+ Finally, when the skin and content are loaded, we can invoke the actual rendering using the provided component _RenderingPage_.
428
+
429
+ RenderingPage is an async JSX function, which is suitable for usage in the NextJS environment.
430
+
431
+ Let's have a closer look at its properties:
432
+
433
+ ```ts
434
+ class RenderingPageProps {
435
+ lng: string;
436
+ content: NdContentBlock[];
437
+ skin: NdPageSkin | undefined = undefined;
438
+ renderingPriority: RenderingPriority = RenderingPriority.content_first;
439
+ componentResolver: ComponentResolver | undefined = undefined;
440
+ imageUrlProvider: ImageUrlProvider | undefined = undefined;
441
+ i18nextProvider: I18nextProvider | undefined = undefined;
442
+ }
443
+ ```
444
+
445
+ - **_lng_**: the language in which the page should be rendered. Nodoku supports localization out of the box (see more on Nodoku localization in [nodoku-i18n](https://github.com/nodoku/nodoku-i18n))
446
+
447
+
448
+ - **_content_**: the content flow, represented as an array of NdContentBlock instances. The content blocks are usually parsed from a Markdown file using the provided parser _contentMarkdownProvider_.
449
+
450
+
451
+ - **_skin_**: an instance of NdPageSkin class representing the Nodoku skin - configuration of the visual representation of the page. It is usually loaded from a Yaml file using the supplied loader _skinYamlProvider_.
452
+
453
+
454
+ - **_renderingPriority_**: this parameter is an enum that can have two values:
455
+ - > **_content_first_**: the content is rendered as it appears in the markdown file, sequentially, block by block, from top to bottom. If a visual component is configured in the skin Yaml file, this visual component is used for rendering the content block. Otherwise, a default visual component is used
456
+ - > **_skin_first_**: the rendering is fully prescribed by the skin Yaml file. The components are rendered in the order they appear in the Yaml file
457
+ If a content block is not matched by any of the visual components in the skin Yaml file, it is not rendered at all. If a content block matches more than one visual component, each visual component is rendered, and the same content block will appear several times on the page.
458
+
459
+
460
+ - **_componentProvider_**: the function returning an actual implementation of the component, given its name, as specified in the skin. The signature is as follows:
461
+ > ```(componentName: string) => Promise<AsyncFunctionComponent>```
462
+ >
463
+ where AsyncFunctionComponent is the following function:
464
+ > ```(props: NdSkinComponentProps) => Promise<JSX.Element>```
465
+
466
+ the actual implementations, respecting the _AsyncFunctionComponent_ signature, are usually supplied via the component bundles, such as **_[nodoku-flowbite](https://github.com/nodoku/nodoku-flowbite)_** and **_[nodoku-mambaui](https://github.com/nodoku/nodoku-mambaui)_**
467
+
468
+
469
+ - **_imageUrlProvider_**: the function allowing to customize the image URL conversion for rendering. It may so happen that the URL of images appearing in the MD file are different from those appearing on the page. For example, we often use the relative notation for images in the MD file, whereas this is not suitable for page rendering. The conversion between URL's in the MD file, and the URL's on the page can be provided using this parameter. The default implementation strips the leading dots, thus naively converting a relative url to an absolute one. Like this (more sophisticated patterns can be implemented, if required):
470
+ > **_../images/my-image-123.png_** will be converted to **_/images/my-image-123.png_**
471
+
472
+
473
+ - **_i18nextProvider_**: this parameter can be used to provide the localization mechanism for Nodoku. This function is supposed to return an object containing the **_t()_** function, which will further be used for translating the text.
474
+ > type I18nextProvider = (lng: string) => Promise<{t: (text: NdTranslatedText) => string}>
475
+
476
+ Note, that the provided **_t()_** function takes as an argument the whole NdTranslatedText structure, which contains the translation namespace, the key and the default value, extracted from the content.
477
+
478
+ This parameter is optional. If not provided, the text from the content MD file is used.
479
+
480
+
481
+
482
+ For more details see [nodoku-i18n](https://github.com/nodoku/nodoku-i18n)
483
+
484
+ ### Nodoku component resolver
485
+
486
+ The component resolver is expected to resolve each component name in a textual form, as extract from the skin file, to an actual component definition, among other things containing the actual Javascript function that should be called to render the component.
487
+
488
+ > As the components are provided as external dependencies, Nodoku supplies a special script - **_nodoku-gen-component-resolver_** - that should be launched from command line to automatically generate the component resolver.
489
+
490
+ Here is the typical example of the generated component resolver, that is crucial for successful functioning of Nodoku:
491
+
492
+ ```ts
493
+
494
+ import {AsyncFunctionComponent, DummyComp, NdComponentDefinition} from "nodoku-core";
495
+
496
+ import { NodokuFlowbite } from "nodoku-flowbite";
497
+
498
+ const components: Map<string, {compo: AsyncFunctionComponent, compoDef: NdComponentDefinition}> =
499
+ new Map<string, {compo: AsyncFunctionComponent, compoDef: NdComponentDefinition}>();
500
+
501
+ components.set("flowbite/card", {
502
+ compo: NodokuFlowbite.Card,
503
+ compoDef: new NdComponentDefinition(1,
504
+ "./schemas/nodoku-flowbite/dist/schemas/components/card/default-theme.yml")
505
+ });
506
+
507
+ // other component definitions go here
508
+
509
+ export function nodokuComponentResolver(componentName: string): Promise<{compo: AsyncFunctionComponent, compoDef: NdComponentDefinition}> {
510
+ const f: {compo: AsyncFunctionComponent, compoDef: NdComponentDefinition} | undefined =
511
+ components.get(componentName);
512
+ return Promise.resolve(f ? f : {compo: DummyComp, compoDef: new NdComponentDefinition(1)});
513
+ }
514
+
515
+ ```
516
+
517
+ Recall that the _componentResolver_ is one of the properties of the _RenderingPage_ component.
518
+
519
+ Thanks to this function Nodoku can find the correspondence between the component name specified in the skin Yaml file, and the actual component implementation.
520
+
521
+ It is worth noting here that **_nodoku-core_** is the Nodoku engine, which contains the resolving and parsing functionality, but it contains no visual components.
522
+
523
+ The visual components are supposed to be supplied as external library dependency, such as _nodoku-flowbite_ and _nodoku-mambaui_.
524
+
525
+ The static map, that is generated by the scirpt _nodoku-gen-component-resolver_, associates with each component name two attributes:
526
+ - **_compo_**: the function that is to be called to render the component
527
+ - **_compoDef_**: the definition of the component. The component defintion is loaded from the Nodoku manifest, supplied in the bundle where this component is defined. The component definition includes the following fields:
528
+ - **_numBlocks_**: the maximum number of blocks this component supports
529
+ - **_defaultThemeYaml_**: the Yaml file defining the default component visual configuration (usually uses Tailwind class names)
530
+
531
+ These components parameters are defined in a special file - **_nodoku.manifest.json_** - which is supposed to be shipped within the component bundle. See below for more details.
532
+
533
+ ## Configuring Nodoku project
534
+
535
+ ### Tailwind configuration
536
+
537
+ Since Nodoku is heavily relying on Tailwind, the Nodoku project is supposed to be configured accordingly. Otherwise, the Tailwind classes won't be parsed, and the page would not be rendered correctly.
538
+
539
+ To understand how to configure Tailwind we first need to understand how Tailwind functions.
540
+
541
+ Tailwind is a set of predefined CSS classes, each having its own responsibility and value.
542
+
543
+ For example, ```mt-10``` results in the following CSS being genrated by Tailwind:
544
+
545
+ ```css
546
+ .mt-10 {
547
+ margin-top: 2.5rem;
548
+ }
549
+ ```
550
+ Since there are a lot of those Tailwind classes, not all of them are included in the final bindle, but only those that are actually used in the project.
551
+
552
+ Hence, Tailwind needs to know what files to parse to look for actual Tailwind classes that are being used. Only the CSS classes discovered during this parsing, will be included in the final bundle.
553
+
554
+ In order for Nodoku to function properly, one needs to include all the places where potentially Tailwind classes can be encountered.
555
+
556
+ Here is the typical **_tailwind.config.ts_**
557
+
558
+ ```ts
559
+ import flowbite from "flowbite-react/tailwind";
560
+ import type {Config} from "tailwindcss";
561
+ import * as typo from '@tailwindcss/typography';
562
+
563
+
564
+ const config: Config = {
565
+ content: [
566
+ "./src/**/*.ts",
567
+ "./src/**/*.tsx",
568
+ "./src/**/*.js",
569
+ "./src/**/*.jsx",
570
+
571
+ "./node_modules/nodoku-core/dist/esm/**/*.js",
572
+ "./node_modules/nodoku-core/dist/esm/**/*.jsx",
573
+ "./node_modules/nodoku-components/dist/esm/**/*.js",
574
+ "./node_modules/nodoku-components/dist/esm/**/*.jsx",
575
+ "./node_modules/nodoku-flowbite/dist/esm/**/*.js",
576
+ "./node_modules/nodoku-flowbite/dist/esm/**/*.jsx",
577
+ "./node_modules/nodoku-flowbite/dist/schemas/**/*.yml",
578
+ "./node_modules/nodoku-mambaui/dist/esm/**/*.js",
579
+ "./node_modules/nodoku-mambaui/dist/esm/**/*.jsx",
580
+ "./node_modules/nodoku-mambaui/dist/schemas/**/*.yml",
581
+ "./public/**/*.html",
582
+ "./src/**/*.{html,js}",
583
+ "./public/site/**/*.yaml",
584
+ flowbite.content(),
585
+ ],
586
+
587
+ theme: {
588
+ extend: {
589
+ aspectRatio: {
590
+ 'carousel': '4 / 1.61',
591
+ },
592
+ typography: {
593
+ DEFAULT: {
594
+ css: {
595
+ maxWidth: 'unset',
596
+ }
597
+ }
598
+ }
599
+ },
600
+ },
601
+ plugins: [
602
+ flowbite.plugin(),
603
+ typo.default(),
604
+ ],
605
+ };
606
+
607
+ export default config;
608
+
609
+ ```
610
+
611
+ Let's have a closer look at this configuration:
612
+
613
+ - **_content_**: should contain all the paths where Tailwind classes are located, and consequently will be parsed by Tailwind for class extraction.
614
+ - for each Nodoku component bundle it should contain the all the ```.js```, ```.jsx``` and ```.yml``` files, because it might contain the Tailwind classes
615
+ - if a component bundle using _flowbite-react_ is used (such as _nodoku-flowbite_), its plugin should be included in the content
616
+ - **_theme_**: this is a standard Tailwind's theme definition. For example, in this example we are defining a new aspectRation value called ```carousel```, which later can be used for styling Nodoku components
617
+ - **_plugins_**: this section includes plugins needed for Nodoku components to work properly
618
+ - **_flowbite.plugin()_** - if components based on flowbite-react are used, this plugin should be included
619
+ - **_typo.default()_** - if the [Nodoku Typography](https://github.com/nodoku/nodoku-components) plugin is used, this plugin should be included
620
+
621
+ For more details on Tailwind config see the section "[Configuring Tailwind](https://github.com/nodoku/nodoku-flowbite?tab=readme-ov-file#configuring-tailwind)" in one of the Nodoku component bundles - [nodoku-flowbite](https://github.com/nodoku/nodoku-flowbite).
622
+
623
+ ### Webpack configuration
624
+
625
+ It might so happen that we'll need to configure the Webpack in order for Nodoku to run properly.
626
+
627
+ In particular this problem becomes apparent if we are using flowbite-react, in order to ensure that only instance of flowbite-react is included in the final bundle.
628
+
629
+ Otherwise, the flowbite-react theme might not work properly, and all the customizations of this theme will not be applied.
630
+
631
+ Webpack configuration in a NextJS project is not trivial, as we don't have access directly to the file webpack.config.js.
632
+
633
+ Instead, we should be redefining the **_nextConfig_** in _next.config.mjs_ as follows:
634
+
635
+ ```js
636
+ import path from "node:path";
637
+
638
+ const nextConfig = (phase, {defaultConfig}) => {
639
+ /**
640
+ * @type {import('next').NextConfig}
641
+ */
642
+ const nextConfig = {
643
+ /* config options here */
644
+ ...defaultConfig,
645
+ webpack: ((config, opts) => {
646
+ config.resolve.alias["flowbite-react"] = path.resolve('./node_modules/flowbite-react');
647
+ config.resolve.alias["flowbite"] = path.resolve('./node_modules/flowbite');
648
+ return config;
649
+ })
650
+ }
651
+
652
+ return nextConfig
653
+ }
654
+
655
+ export default nextConfig;
656
+ ```
657
+
658
+ Note that we pin down the resolution of _flowbite_ and _flowbite-react_ to the particular dependencies in our _node_modules_ folder.
659
+
660
+ # Nodoku component bundle
661
+
662
+ As has already been mentioned above, Nodoku visual components are supplied as a component bundle - a standard NPM dependency, that can be installed using the standard NPM installation process
663
+ > ```npm install <nodoku component bundle>```
664
+
665
+ for example:
666
+ > ```npm install nodoku-flowbite```
667
+
668
+ ## Nodoku manifest
669
+
670
+ Each component bundle should be shipped with a special file called **_nodoku.manifest.json_**.
671
+
672
+ This json file is a visit card of the component bundle: it contains all the information required for Nodoku engine to treat correctly the component and use it following the configuration in the Nodoku skin Yaml file.
673
+
674
+ Here is an excerpt from such file for nodoku-flowbite:
675
+
676
+ ```json
677
+ {
678
+ "namespace": "NodokuFlowbite",
679
+ "components": {
680
+ ..
681
+ "flowbite/carousel": {
682
+ "schemaFile": "./dist/schemas/components/carousel/visual-schema.json",
683
+ "optionsFile": "./dist/schemas/components/carousel/options-schema.json",
684
+ "defaultThemeFile": "./dist/schemas/components/carousel/default-theme.yml",
685
+ "implementation": "Carousel",
686
+ "numBlocks": "unlimited"
687
+ },
688
+ ...
689
+ }
690
+ }
691
+ ```
692
+
693
+ Let's have a closer look at the attributes of each Nodoku visual component:
694
+
695
+ - **_namespace_**: the Javascript namespace where out of which the component is being exported.
696
+
697
+ The presence of the namespace attribute instructs the generator of the component resolver (nodoku-gen-component-resolver) to insert the component definition prefixed with the namespace.
698
+ ```ts
699
+ import { NodokuFlowbite } from "nodoku-flowbite";
700
+ components.set("flowbite/carousel", {compo: NodokuFlowbite.Carousel, ...});
701
+ ```
702
+ The Nodoku manifest cannot have more than one namespace.
703
+
704
+
705
+ - **_components_**: is a object associating a textual component name with the data structure that is used to extract the component definition. This data structure include:
706
+
707
+
708
+ - **_schemaFile_**: is a Json schema file representing the data structure of the visual theme of the component (see [Schema for Nodoku skin Yaml file](#schema-for-nodoku-skin-yaml-file))
709
+
710
+
711
+ - **_optionsFile_**: is a Json schema file representing the data structure of the functional options of the component (see [Schema for Nodoku skin Yaml file](#schema-for-nodoku-skin-yaml-file))
712
+
713
+
714
+ - **_defaultThemeFile_**: a Yaml file containing the default Tailwind configuration of the component
715
+
716
+
717
+ - **_implementation_**: the actual Javascript function rendering this compnent. (the signature should correspond to _AsyncFunctionComponent_)
718
+
719
+
720
+ - **_numBlocks_**: number of content blocks this component can accept for rendering. This parameter can either be a positive number of the predefined value '_unlimited_'
721
+
722
+
723
+
724
+ The _numBlocks_ parameter is used by the Nodoku engine to calculate the correct number of content blocks this visual component is capable of rendering. This parameter is normally either **_1_** or **_'unlimited'_**.
725
+
726
+ The _defaultThemeYaml_ attribute is an important parameter designating the Yaml file where the default Tailwind configuration is located. Normally each Nodoku visual component comes as an empty JSX scaffolding, which delegates the styling - the actual Tailwind classes - to the external data structure, the component default theme.
727
+
728
+ # Nodoku skin
729
+
730
+
731
+ The Nodoku skin is a Yaml file which configures the Nodoku rendering.
732
+
733
+ Recall, that in Nodoku there is a strict separation between the content and the visual representation.
734
+
735
+ The content is provided via an MD file, whereas the mapping between the content blocks and visual components are defined in the _skin_.
736
+
737
+ The skin also defines the necessary customizations and fine-tuning of the page visual representation, if required.
738
+
739
+ ## Nodoku visual component theme
740
+
741
+ Normally, each JSX block in a visual component is styled using the Tailwind class names. This styling is typically composed of two parts: _base_ and _decoration_.
742
+
743
+ Consider the following example:
744
+
745
+ ```tsx
746
+ <h3 className={`${effectiveTheme.titleStyle?.base} ${effectiveTheme.titleStyle?.decoration}`}>
747
+ {t(block.title)}
748
+ </h3>
749
+
750
+ <h4 className={`${effectiveTheme.subTitleStyle?.base} ${effectiveTheme.subTitleStyle?.decoration}`}>
751
+ {t(block.subTitle)}
752
+ </h4>
753
+ ```
754
+
755
+ The component theme is supplied to the component by the Nodoku engine, and comes in as a data structure containing two elements:
756
+ - one for title styling _effectiveTheme.**titleStyle**_
757
+ - and one for subtitle styling _effectiveTheme.**subTitleStyle**_
758
+
759
+ Each such element is called _ThemeStyle_, and is composed of two string attributes: _base_ and _decoration_
760
+
761
+ The component theme consists of several attributes of type _ThemeStyle_.
762
+
763
+ Here is an excerpt from the component theme _flowbite/card_
764
+
765
+ ```ts
766
+ export class CardTheme {
767
+ ...
768
+ titleStyle?: ThemeStyle;
769
+ subTitleStyle?: ThemeStyle;
770
+ ...
771
+ }
772
+ ```
773
+ where ThemeStyle is defined as follows:
774
+ ```ts
775
+ export class ThemeStyle {
776
+ base: string;
777
+ decoration: string;
778
+ }
779
+ ```
780
+
781
+ For each component there is a default theme supplied in a Yaml file, and this file is defined for the given visual component in the Nodoku manifest.
782
+
783
+ In addition, the theme of each component can further be customized in the skin Yaml file.
784
+
785
+
786
+ # The Nodoku skin Yaml file
787
+
788
+
789
+
790
+
791
+ The skin Yaml file has a predefined structure, which can be summarized as follows:
792
+
793
+ ```yaml
794
+ global:
795
+ renderingPage:
796
+ # styles applied to the whole page
797
+ theme:
798
+ # attributes defined on the global level for all the component themes
799
+ themes:
800
+ # contains the list of theme customizations, where each theme from the list is applied sequentially on each content block rendered by the component of a given type in a single row
801
+ components:
802
+ <component-name>:
803
+ theme:
804
+ # defines a theme customization for a particular component
805
+ themes:
806
+ # defines a list of theme customizations for a particular component, where each theme from the list is applied sequentially on each content block rendered by the component of this type in a single row
807
+
808
+ rows:
809
+ # list of rows
810
+ - row:
811
+ theme:
812
+ # styling classes applied on the row level
813
+ maxCols:
814
+ # optional: a limit on a number of visual components in a row. By default, the max number of elements in a row is 12 (grid-cols-12 from Tailwind) but it can be reduced to 1, when we need content blocks to be located one beneat the other
815
+ components:
816
+ <component-name>:
817
+ selector:
818
+ # the data structure defining the selector to be applied on content blocks to select the matching content blocks that would be rendered by this component
819
+ options:
820
+ # a component may accept an options object, which can be defined here
821
+ theme:
822
+ # the customization of the component's scheme
823
+ themes:
824
+ # a list of theme customizations for the given component, where each theme from the list is applied sequentially on each content block rendered by the component of this type in a single row
825
+ ```
826
+
827
+ The customization is applied progressively, from the least specific theme to the most specific, starting from the default theme of the component.
828
+
829
+ Since the Nodoku skin file is large, and can contain many customization options, it is desirable to have a schema file that would guide the user through the process of defining and editing of the skin.
830
+
831
+ ## Schema for Nodoku skin Yaml file
832
+
833
+ In order to make the Nodoku skin creation process more convenient and predictable, Nodoku provides a possibility to create a schema file for a project, with a given set of Nodoku component bundles, defined in NPM dependencies (in package.json).
834
+
835
+ Similar to the generation script for Nodoku component resolver, there is a script for generation of the Nodoku skin schema: **_nodoku-gen-skin-schema_**
836
+
837
+ One can run this script to automatically generate a Nodoku skin schema file.
838
+
839
+ The schema file by default is generated in the folder
840
+ > <project folder>/schemas
841
+
842
+ After the schema generation script completes, the **_./schemas_** folder should look as follows:
843
+
844
+ <figure>
845
+ <img
846
+ src="./docs/schemas-folder-expanded.png"
847
+ alt="project schemas folder after schema generation"
848
+ title="project schemas folder after schema generation"
849
+ />
850
+ <figcaption>
851
+ <b>Figure 3</b>: The <b>./schemas</b> folder contains the schema file - <b>visual-schema.json</b> - to be applied on the Nodoku skin Yaml file
852
+ </figcaption>
853
+ </figure>
854
+
855
+ The generated schema file - ./schemas/visual-schema.json - can be applied to the skin Yaml file in several ways:
856
+ - use [the IDE mechanism of schema application](https://www.jetbrains.com/help/idea/yaml.html#json_schema)
857
+ - use the **_yaml-language-server_** header (the first row) for the Yaml file as follows:
858
+
859
+ ```yaml
860
+ # yaml-language-server: $schema=../../../schemas/visual-schema.json
861
+
862
+ global:
863
+ renderingPage:
864
+ base: dark:text-gray-200 text-gray-700
865
+
866
+ rows:
867
+ - row:
868
+ theme:
869
+ decoration: mb-10
870
+
871
+ ```
872
+
873
+ ## Customizing Nodoku page appearance
874
+
875
+ The main principle of component customization in Nodoku consists of cascading application of themes, defined on different levels:
876
+ - **_global level_**: the section **_global_** in the Yaml file. This level can further be divided to
877
+ - **_theme level_**: where any attribute for any component can be defined
878
+ - **_component level_**: where attributes for a given component can be defined
879
+ - **_rows level_**: where the list of rows defines each its set of components
880
+ - **_component in a row level_**: the most specific customization, where a particular component can be customized
881
+
882
+ The process of customization starts from the component's default theme and works its way through all the defined customizations, until the most specific level is reached.
883
+
884
+
885
+
886
+
887
+ # Nodoku generation scripts
888
+
889
+ The nodoku-core provides the following scripts, that are used to generate component resolver and visual schema, by scanning the **node_modules** folder of the project
890
+
891
+
892
+ - **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. For more details see [Nodoku component resolver](#nodoku-component-resolver)
893
+
894
+
895
+ - **nodoku-gen-visual-schema**: generates the json schema file thate can be used to validate the Nodoku skin schema file. For more details see [Schema for Nodoku skin Yaml file](#schema-for-nodoku-skin-yaml-file)
896
+
897
+ To simplify the use of these script it is recommended to add them in the project's package.json file as follows:
898
+
899
+ ```json
900
+ {
901
+ ...
902
+ "scripts": {
903
+ "gen-component-resolver": "nodoku-gen-component-resolver",
904
+ "gen-skin-schema": "nodoku-gen-skin-schema"
905
+ },
906
+ ...
907
+ }
908
+ ```
909
+
910
+
911
+ # Nodoku license
912
+
913
+ The Nodoku License to be used depends on the use case you plan to use Nodoku for:
914
+
915
+ - using Nodoku to build an End Product, which is a webpage or a website for private or commercial use
916
+ - in short, in this case Nodoku **_is free_** to use under the MIT License (see below for more details)
917
+ - using Nodoku to build an End Product, which is a webpage or a website builder (online or offline)
918
+ - in short, in this case the use of Nodoku **_is prohibited_**
919
+
920
+
921
+ Note, that
922
+ > in any case of the allowed, free-of-charge use of Nodoku, **_the present Nodoku license should be included in all copies of the Software, using Nodoku_**.
923
+
924
+
925
+
926
+ ## Use for private, educational or commercial needs, excluding applications for webpage or website building tools
927
+
928
+ Any use of Nodoku, **_except for webpage and website builder applications_**, is allowed free of charge, under the **_MIT license_** ( see the [LICENSE](./blob/main/LICENSE) )
929
+
930
+
931
+
932
+ ## Use as part of website or webpage builders
933
+
934
+ The use of Nodoku for the purposes to provide a solution for online (or offline) website or webpage builders is **_prohibited_**.
935
+
936
+ This prohibition includes the direct or indirect use of the Nodoku library as part of webpage builder solution.
937
+
938
+ Indirect use of Nodoku library would include building a software or a framework that would then be used as part of webpage or website builder.
939
+
940
+ Webpage or Website builder application is any tool, online or offline, that would allow the End User to build a publishable webpage or website, using no code or low amount of code.
941
+
942
+ This is prohibited under this license term since that would constitute a direct competition to Nodoku lo-code site builder.
943
+
944
+ # Examples
945
+
946
+ ## Proper use, free of charge, under the MIT License
947
+
948
+ - building a webpage or a website for a private non-commercial project
949
+ - building a webpage or a website for a private commercial project
950
+ - building a webpage or a website for a public commercial or non-commercial blog
951
+ - building a webpage or a website for a client, that would use it for commercial or non-commercial purposes
952
+ - creating and distributing non-commercially (free of charge) custom Nodoku components
953
+ - creating and distributing commercially custom Nodoku components (developing and selling out new Nodoku-compatible UI components)
954
+ - developing new UI components and selling them out either as a Nodoku component, or a standalone component, or a component compatible with another website builder
955
+
956
+ ## Prohibited use
957
+
958
+ - building a website builder, that would use the Nodoku engine to provide the End User with a tool, that would allow them building their own webpages or websites
959
+ - using Nodoku engine to provide the End User with an automatic, semi-automatic or manual framework, that would allow them building their own webpages or websites
960
+ - using Nodoku libraries to use it as part of the solution that would allow the End User to build a webpage or a website