@rettangoli/sites 0.1.1 → 0.2.0-rc2

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,103 +1,295 @@
1
- # Sitic
1
+ # Rettangoli Sites
2
2
 
3
- Generate static sites using Markdown and YAML. Straightforward, zero-complexity. Complete toolkit for landing pages, blogs, documentation, admin dashboards, and more.
3
+ A straightforward, zero-complexity static site generator that uses Markdown and YAML to build websites. Perfect for landing pages, blogs, documentation, admin dashboards, and more.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Content-First Approach**: Write content in Markdown, structure with YAML
8
- - **No HTML/JS Required**: Create complex sites without writing a single line of code
9
- - **Versatile**: Build landing pages, documentation, blogs, admin dashboards, slides and more
10
- - **Component-Based**: Reusable components for consistent design
11
- - **Template System**: Liquid templates for flexible layouts
12
- - **Customizable**: Create custom components and templates
7
+ - 🚀 **Zero Configuration** - Works out of the box with sensible defaults
8
+ - 📝 **Markdown & YAML** - Write content in familiar formats
9
+ - 🎨 **Full-featured** - Templates, partials, collections, global data, nested pages, static assets, and extensible architecture
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install -g rtgl
15
+ ```
13
16
 
14
17
  ## Quick Start
15
18
 
16
- 1. Create a project structure:
19
+ ### 1. Create Your Project Structure
17
20
 
18
21
  ```
19
22
  my-site/
20
- ├── sitic/
21
- ├── data.yaml # Global site data
22
- ├── templates # Tempaltes
23
- ├── components # Components
24
- │ └── records/ # Data records (optional)
25
- └── pages/ # Your content pages
26
- ├── index.md
27
- └── about.md
23
+ ├── pages/ # Your content (YAML and Markdown files)
24
+ ├── templates/ # Reusable page templates (YAML)
25
+ ├── partials/ # Reusable components (YAML)
26
+ ├── data/ # Global data files (YAML)
27
+ ├── static/ # Static assets (CSS, JS, images)
28
+ └── _site/ # Generated output (created automatically)
28
29
  ```
29
30
 
30
- 2. Create a simple page (index.md):
31
+ ### 2. Create Your First Page
31
32
 
32
- ```markdown
33
+ **pages/index.yaml**
34
+ ```yaml
33
35
  ---
34
- layout: core/base
35
- title: My Site
36
+ title: Welcome
37
+ template: base
36
38
  ---
39
+ - heading: Welcome to My Site
40
+ - paragraph: This is a simple static site built with Rettangoli Sites.
41
+ ```
42
+
43
+ ### 3. Create a Template
44
+
45
+ **templates/base.yaml**
46
+ ```yaml
47
+ - tag: html
48
+ children:
49
+ - tag: head
50
+ children:
51
+ - tag: title
52
+ children: $title
53
+ - tag: body
54
+ children: $content
55
+ ```
37
56
 
38
- ```yaml components
39
- - component: core/hero1
40
- data:
41
- hero:
42
- subtitle: Welcome
43
- message: This is my first Sitic site
44
- - component: core/spacer
45
- data:
46
- height: 50
47
- - component: core/features1
48
- data:
49
- features:
50
- - title: Easy to Use
51
- description: Just write Markdown and YAML
52
- - title: Fast
53
- description: Generate static sites quickly
54
- - title: Flexible
55
- description: Create any type of site
56
- ```
57
-
58
- 3. Build your site:
57
+ ### 4. Build Your Site
59
58
 
60
59
  ```bash
61
- sitic build
60
+ npx rtgl sites build
62
61
  ```
63
62
 
64
63
  Your site will be generated in the `_site` directory.
65
64
 
66
- ## CLI Options
65
+ ## Core Concepts
67
66
 
68
- ```bash
69
- # Basic usage
70
- sitic build
67
+ ### Pages
68
+
69
+ Pages are the content of your site. They can be either:
70
+ - **YAML files** (`.yaml`) - Structured content with components
71
+ - **Markdown files** (`.md`) - Rich text content with frontmatter
72
+
73
+ Every page can have frontmatter (metadata) at the top:
74
+
75
+ ```yaml
76
+ ---
77
+ title: My Page Title
78
+ template: base
79
+ tags: [blog, tutorial]
80
+ ---
81
+ ```
82
+
83
+ ### Templates
84
+
85
+ Templates define the HTML structure for your pages. They're YAML files that describe HTML elements and can include dynamic content using the `$` prefix.
71
86
 
72
- # Specify custom directories
73
- sitic build --resources ./custom-sitic-folder
87
+ **Example template:**
88
+ ```yaml
89
+ - tag: article
90
+ children:
91
+ - tag: h1
92
+ children: $title
93
+ - tag: div
94
+ class: content
95
+ children: $content
74
96
  ```
75
97
 
76
- ## Component System
98
+ Templates can be organized in subdirectories:
99
+ - `templates/base.yaml` → Referenced as `template: base`
100
+ - `templates/blog/post.yaml` → Referenced as `template: blog/post`
101
+
102
+ ### Partials
77
103
 
78
- Siti uses a component-based approach that allows you to build complex layouts using simple YAML:
104
+ Partials are reusable components that can be included in pages and templates using `$partial`:
79
105
 
106
+ **partials/header.yaml**
80
107
  ```yaml
81
- - component: core/hero1
82
- data:
83
- hero:
84
- subtitle: Section Title
85
- message: Your message here
108
+ - tag: header
109
+ children:
110
+ - tag: nav
111
+ children:
112
+ - tag: a
113
+ href: /
114
+ children: Home
115
+ - tag: a
116
+ href: /about
117
+ children: About
86
118
  ```
87
119
 
88
- ## Templates
120
+ **Using in a page:**
121
+ ```yaml
122
+ - $partial: header
123
+ - heading: Welcome
124
+ ```
125
+
126
+ ### Data Files
127
+
128
+ Global data files in the `data/` directory are automatically loaded and available in all pages and templates.
89
129
 
90
- The template system uses Liquid for flexible layouts. Create custom templates in the `templates` directory.
130
+ **data/site.yaml**
131
+ ```yaml
132
+ name: My Awesome Site
133
+ author: John Doe
134
+ social:
135
+ twitter: johndoe
136
+ github: johndoe
137
+ ```
91
138
 
92
- ## Examples
139
+ **Access in templates:**
140
+ ```yaml
141
+ - tag: footer
142
+ children:
143
+ - text: "© 2024 "
144
+ - text: $site.author
145
+ ```
93
146
 
94
- Check out the `examples` directory for complete site examples, including:
147
+ ### Collections
95
148
 
96
- - Landing pages
97
- - Documentation sites
98
- - Blogs
99
- - Admin dashboards
149
+ Collections group related content using tags. Any page with a `tags` field in its frontmatter becomes part of a collection.
100
150
 
101
- ## License
151
+ **pages/blog/post-1.md**
152
+ ```markdown
153
+ ---
154
+ title: My First Post
155
+ tags: blog
156
+ date: 2024-01-15
157
+ ---
158
+ # Hello World
159
+ This is my first blog post.
160
+ ```
102
161
 
103
- MIT
162
+ **pages/blog-index.yaml**
163
+ ```yaml
164
+ ---
165
+ title: Blog
166
+ ---
167
+ - heading: Recent Posts
168
+ - ul:
169
+ - $for post in collections.blog:
170
+ li:
171
+ - 'a href="${post.url}"':
172
+ - ${post.data.title}
173
+ ```
174
+
175
+ ### Static Files
176
+
177
+ Everything in the `static/` directory is copied directly to the output:
178
+ - `static/css/style.css` → `_site/css/style.css`
179
+ - `static/images/logo.png` → `_site/images/logo.png`
180
+ - `static/app.js` → `_site/app.js`
181
+
182
+ ## Advanced Features
183
+
184
+ ### Nested Pages
185
+
186
+ Create subdirectories in `pages/` to organize your content:
187
+ - `pages/index.yaml` → `_site/index.html`
188
+ - `pages/about.yaml` → `_site/about.html`
189
+ - `pages/blog/post-1.md` → `_site/blog/post-1.html`
190
+ - `pages/docs/api/reference.yaml` → `_site/docs/api/reference.html`
191
+
192
+ ### Markdown Support
193
+
194
+ Markdown files can use frontmatter and templates:
195
+
196
+ ```markdown
197
+ ---
198
+ title: My Blog Post
199
+ template: blog/post
200
+ tags: [blog, tutorial]
201
+ author: Jane Doe
202
+ ---
203
+ # Introduction
204
+
205
+ This is a **markdown** post with *formatting*.
206
+
207
+ ## Code Example
208
+
209
+ \`\`\`javascript
210
+ console.log('Hello, World!');
211
+ \`\`\`
212
+ ```
213
+
214
+ ### Custom Markdown Renderer
215
+
216
+ Configure a custom markdown renderer in `sites.config.js`:
217
+
218
+ ```javascript
219
+ import MarkdownIt from 'markdown-it';
220
+ import hljs from 'highlight.js';
221
+
222
+ const md = MarkdownIt({
223
+ highlight: function (str, lang) {
224
+ if (lang && hljs.getLanguage(lang)) {
225
+ return hljs.highlight(str, { language: lang }).value;
226
+ }
227
+ return '';
228
+ }
229
+ });
230
+
231
+ export default {
232
+ mdRender: md
233
+ }
234
+ ```
235
+
236
+ ### Dynamic Content with Jempl
237
+
238
+ Rettangoli Sites uses [Jempl](https://github.com/yuusoft-org/jempl) for templating, which provides powerful features for dynamic content including variable replacement, conditionals, loops, and partials.
239
+
240
+ For detailed syntax and usage examples, please refer to the [Jempl documentation](https://github.com/yuusoft-org/jempl).
241
+
242
+
243
+ ## Example Projects
244
+
245
+ ### Blog Site Structure
246
+ ```
247
+ blog-site/
248
+ ├── pages/
249
+ │ ├── index.yaml # Homepage
250
+ │ ├── about.md # About page
251
+ │ └── blog/
252
+ │ ├── index.yaml # Blog listing
253
+ │ ├── 2024-01-15-first-post.md
254
+ │ └── 2024-01-20-second-post.md
255
+ ├── templates/
256
+ │ ├── base.yaml # Main layout
257
+ │ └── blog/
258
+ │ ├── post.yaml # Blog post template
259
+ │ └── listing.yaml # Blog list template
260
+ ├── partials/
261
+ │ ├── header.yaml
262
+ │ ├── footer.yaml
263
+ │ └── post-card.yaml
264
+ ├── data/
265
+ │ └── site.yaml # Site metadata
266
+ └── static/
267
+ ├── css/
268
+ │ └── style.css
269
+ └── images/
270
+ └── logo.png
271
+ ```
272
+
273
+ ### Documentation Site Structure
274
+ ```
275
+ docs-site/
276
+ ├── pages/
277
+ │ ├── index.yaml
278
+ │ └── docs/
279
+ │ ├── getting-started.md
280
+ │ ├── api/
281
+ │ │ ├── overview.md
282
+ │ │ └── reference.yaml
283
+ │ └── guides/
284
+ │ ├── installation.md
285
+ │ └── configuration.md
286
+ ├── templates/
287
+ │ ├── base.yaml
288
+ │ └── docs.yaml
289
+ ├── partials/
290
+ │ ├── nav.yaml
291
+ │ └── sidebar.yaml
292
+ └── static/
293
+ └── css/
294
+ └── docs.css
295
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/sites",
3
- "version": "0.1.1",
3
+ "version": "0.2.0-rc2",
4
4
  "description": "Generate static sites using Markdown and YAML. Straightforward, zero-complexity. Complete toolkit for landing pages, blogs, documentation, admin dashboards, and more.git remote add origin git@github.com:yuusoft-org/sitic.git",
5
5
  "author": {
6
6
  "name": "Luciano Hanyon Wu",
@@ -17,22 +17,19 @@
17
17
  "templates"
18
18
  ],
19
19
  "dependencies": {
20
- "commander": "^13.1.0",
21
- "html-minifier-terser": "^7.2.0",
20
+ "jempl": "^0.2.0-rc1",
22
21
  "js-yaml": "^4.1.0",
23
- "liquidjs": "^10.21.0",
24
- "luxon": "^3.6.1",
25
22
  "markdown-it": "^14.1.0",
26
- "markdown-it-async": "^2.2.0",
27
- "shiki": "^3.3.0"
23
+ "yahtml": "^0.0.2-rc1"
28
24
  },
29
25
  "devDependencies": {
30
- "@types/bun": "^1.2.8"
26
+ "memfs": "^4.36.0",
27
+ "puty": "^0.0.4"
31
28
  },
32
- "type": "module",
33
- "bin": {
34
- "sitic": "src/cli.js"
29
+ "scripts": {
30
+ "test": "vitest run --reporter=verbose"
35
31
  },
32
+ "type": "module",
36
33
  "license": "MIT",
37
34
  "keywords": [
38
35
  "static",
package/src/cli/build.js CHANGED
@@ -1,166 +1,34 @@
1
- import { join, dirname } from "path";
2
- import { fileURLToPath } from "url";
3
- import { DateTime } from "luxon";
4
-
5
- // Create the equivalent of __dirname for ES modules
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = dirname(__filename);
8
-
9
- import {
10
- safeYamlLoad,
11
- safeReadFile,
12
- deepMerge,
13
- createTemplateRenderer,
14
- createFolderIfNotExists,
15
- configureMarkdown,
16
- loadCollections,
17
- createFileFormatHandlers,
18
- loadItems,
19
- copyDirRecursive,
20
- } from "../common.js";
21
- import { rm } from "fs/promises";
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { createSiteBuilder } from '../createSiteBuilder.js';
22
4
 
23
5
  /**
24
- * Copy pages to site with processing
25
- * @param {Object} options - Options for copying pages
26
- * @param {string} options.resourcesPath - Path to resources directory
27
- * @param {string} options.pagesPath - Path to pages directory
28
- * @param {string} options.outputPath - Path to output directory
6
+ * Build the static site
7
+ * @param {Object} options - Options for building the site
8
+ * @param {string} options.rootDir - Root directory of the site (defaults to cwd)
9
+ * @param {Object} options.mdRender - Optional markdown renderer
29
10
  */
30
- export const copyPagesToSite = async (options) => {
31
- const {
32
- resourcesPath = "./sitic",
33
- pagesPath = "./pages",
34
- outputPath = "./_site",
35
- } = options;
36
-
37
- const dataPath = join(resourcesPath, "data.yaml");
38
- const templatesPath = join(resourcesPath, "templates");
39
- const componentsPath = join(resourcesPath, "components");
40
- const recordsPath = join(resourcesPath, "records");
41
-
42
- // Load hello.yaml template data
43
- const inputYaml = await safeReadFile(dataPath);
44
-
45
- const templates = await loadItems({
46
- path: join(__dirname, "./templates"),
47
- name: "templates",
48
- isYaml: false,
49
- keepExtension: true,
50
- });
51
-
52
- if (templatesPath) {
53
- const customTemplates = await loadItems({
54
- path: templatesPath,
55
- name: "templates",
56
- isYaml: false,
57
- keepExtension: true,
58
- });
59
- Object.assign(templates, customTemplates);
11
+ export const buildSite = async (options = {}) => {
12
+ const { rootDir = process.cwd(), mdRender } = options;
13
+
14
+ // Try to load config file if it exists
15
+ let config = {};
16
+ if (!mdRender) {
17
+ try {
18
+ const configPath = path.join(rootDir, 'sites.config.js');
19
+ const configModule = await import(configPath);
20
+ config = configModule.default || {};
21
+ } catch (e) {
22
+ // Config file is optional, continue without it
23
+ }
60
24
  }
61
25
 
62
- // Load data
63
- const records = await loadItems({
64
- path: recordsPath,
65
- name: "records",
66
- isYaml: true,
26
+ const build = createSiteBuilder({
27
+ fs,
28
+ rootDir,
29
+ mdRender: mdRender || config.mdRender,
30
+ functions: config.functions || {}
67
31
  });
68
-
69
- const components = await loadItems({
70
- path: join(__dirname, "./components"),
71
- name: "components",
72
- isYaml: false,
73
- });
74
-
75
- if (componentsPath) {
76
- const customComponents = await loadItems({
77
- path: componentsPath,
78
- name: "components",
79
- isYaml: false,
80
- });
81
- Object.assign(components, customComponents);
82
- }
83
-
84
- const collections = await loadCollections(pagesPath);
85
-
86
- const liquidParse = createTemplateRenderer({
87
- templates,
88
- filters: {
89
- json: (obj) => JSON.stringify(obj),
90
- "json-escaped": (obj) => {
91
- if (!obj) {
92
- return "";
93
- }
94
- return encodeURIComponent(JSON.stringify(obj));
95
- },
96
- postDate: (dateObj) => {
97
- if (!dateObj || typeof dateObj !== 'string') {
98
- return ''; // Return empty string or some default value if dateObj is undefined or not a string
99
- }
100
- try {
101
- return DateTime.fromFormat(dateObj, "yyyy-MM-dd").toLocaleString(
102
- DateTime.DATE_MED
103
- );
104
- } catch (error) {
105
- console.error(`Error formatting date "${dateObj}":`, error.message);
106
- return dateObj; // Return the original date string if parsing fails
107
- }
108
- },
109
- },
110
- });
111
-
112
- let data;
113
- try {
114
- data = safeYamlLoad(liquidParse(inputYaml, { collections }));
115
- } catch (error) {
116
- console.error("Error creating template renderer:", error);
117
- throw error;
118
- }
119
-
120
- // Create global data object for templates
121
- const globalData = {
122
- data,
123
- collections,
124
- records,
125
- };
126
-
127
- const yamlComponentRenderer = (content) => {
128
- const renderedContent = liquidParse(content, globalData);
129
- const yamlContent = safeYamlLoad(renderedContent);
130
-
131
- return yamlContent
132
- .map(({ component, data }) => {
133
- const foundComponent = components[component];
134
- if (!foundComponent) {
135
- throw new Error(`Component not found for ${component}`);
136
- }
137
- return liquidParse(foundComponent, deepMerge(globalData, data));
138
- })
139
- .join("\n");
140
- };
141
-
142
- const md = configureMarkdown({
143
- yamlComponentRenderer,
144
- });
145
-
146
- const fileFormatHandlers = createFileFormatHandlers({
147
- basePath: pagesPath,
148
- templates,
149
- liquidParse,
150
- data: globalData.data,
151
- collections,
152
- md,
153
- });
154
-
155
- try {
156
- await rm(outputPath, { recursive: true, force: true });
157
- await createFolderIfNotExists(outputPath);
158
- await copyDirRecursive(pagesPath, outputPath, fileFormatHandlers);
159
- console.log(`Pages copied from ${pagesPath} to ${outputPath} successfully`);
160
- } catch (error) {
161
- console.error(
162
- `Error copying pages from ${pagesPath} to ${outputPath}:`,
163
- error
164
- );
165
- }
166
- };
32
+
33
+ build();
34
+ };
package/src/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { copyPagesToSite } from './build.js';
1
+ import { buildSite } from './build.js';
2
2
 
3
3
  export {
4
- copyPagesToSite,
4
+ buildSite,
5
5
  }