@rettangoli/sites 0.2.7 → 1.0.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,133 +1,164 @@
1
1
  # Rettangoli Sites
2
2
 
3
- A static site generator using Markdown and YAML.
3
+ `@rettangoli/sites` is the static-site engine used by Rettangoli. It renders pages from YAML and Markdown into `_site/`, with support for templates, partials, global data, collections, and watch mode.
4
+
5
+ It can run directly with `bunx rtgl`, so a site-level `package.json` is optional.
4
6
 
5
7
  ## Quick Start
6
8
 
7
9
  ```bash
8
- # Create a new site from template
9
- bunx rtgl sites init my-site # Uses 'default' template
10
- bunx rtgl sites init my-site -t default # Explicit template name
10
+ # scaffold
11
+ bunx rtgl sites init my-site
11
12
 
12
- # Install and build
13
+ # run
13
14
  cd my-site
14
- bun install
15
- bun run build
15
+ bunx rtgl sites build
16
16
  ```
17
17
 
18
- ## Project Structure
18
+ ## Package Contract
19
19
 
20
- ```
20
+ ```text
21
21
  my-site/
22
- ├── pages/ # Content (YAML and Markdown)
23
- ├── templates/ # Page layouts
24
- ├── partials/ # Reusable components
25
- ├── data/ # Global data files
26
- ├── static/ # Static assets
27
- ├── sites.config.js # Optional config
28
- ├── package.json
29
- └── _site/ # Generated output
22
+ pages/ # YAML or Markdown pages (with optional frontmatter)
23
+ templates/ # YAML templates
24
+ partials/ # YAML partials
25
+ data/ # Global YAML data
26
+ static/ # Static assets copied to _site/
27
+ sites.config.yaml # Optional site settings
28
+ _site/ # Generated output
30
29
  ```
31
30
 
32
- ## Pages
31
+ ## What It Supports
33
32
 
34
- **YAML page:**
35
- ```yaml
36
- ---
37
- template: base
38
- title: Home
39
- ---
40
- - h1: Welcome
41
- - p: Hello world
42
- ```
33
+ - YAML pages rendered through `jempl` + `yahtml`
34
+ - Markdown pages rendered through `markdown-it` + Shiki (default `rtglMarkdown`)
35
+ - Frontmatter (`template`, `tags`, arbitrary page metadata)
36
+ - Global data (`data/*.yaml`) merged with page frontmatter
37
+ - Collections built from page tags
38
+ - `$if`, `$for`, `$partial`, template functions
39
+ - Static file copying from `static/` to `_site/`
40
+ - Watch mode with local dev server + websocket reload
43
41
 
44
- **Markdown page:**
45
- ```markdown
46
- ---
47
- template: base
48
- title: About
49
- ---
50
- # About Us
42
+ ## Site Config
51
43
 
52
- Content in **markdown**.
44
+ Use `sites.config.yaml` (or `sites.config.yml`) with top-level `markdownit` for supported settings.
45
+ Legacy key `markdown` is still accepted as an alias.
46
+
47
+ ```yaml
48
+ markdownit:
49
+ preset: default
50
+ html: true
51
+ xhtmlOut: false
52
+ linkify: true
53
+ typographer: false
54
+ breaks: false
55
+ langPrefix: language-
56
+ quotes: "\u201c\u201d\u2018\u2019"
57
+ maxNesting: 100
58
+ shiki:
59
+ enabled: true
60
+ theme: slack-dark
61
+ codePreview:
62
+ enabled: false
63
+ showSource: true
64
+ theme: slack-dark
65
+ headingAnchors:
66
+ enabled: true
67
+ slugMode: unicode
68
+ wrap: true
69
+ fallback: section
70
+ build:
71
+ keepMarkdownFiles: false
53
72
  ```
54
73
 
55
- ## Syntax
74
+ In the default starter template, CDN runtime scripts are controlled via `data/site.yaml`:
56
75
 
57
76
  ```yaml
58
- # Element with class and id
59
- - div.container#main:
60
- - h1: Title
61
-
62
- # Attributes
63
- - 'a href="/about"': About Us
64
- - 'img src="/logo.png" alt="Logo"':
65
-
66
- # Variables
67
- - h1: ${title}
68
- - p: ${site.description}
69
-
70
- # Conditionals
71
- - $if showBanner:
72
- - div.banner: Hello!
73
-
74
- # Loops
75
- - $for item in items:
76
- - li: ${item.name}
77
-
78
- # Partials
79
- - $partial: header
80
- - $partial: card
81
- title: My Card
82
- description: Card content
77
+ assets:
78
+ loadUiFromCdn: true
79
+ loadConstructStyleSheetsPolyfill: true
83
80
  ```
84
81
 
85
- ## Data Files
82
+ Enable `codePreview` if you want fenced blocks like ```` ```html codePreview ```` to render a live preview panel.
83
+ Use `showSource` to show/hide the source pane and `theme` to override the highlight theme for preview blocks.
86
84
 
87
- `data/site.yaml` available as `${site.name}`, `${site.nav}`, etc.
85
+ Set `build.keepMarkdownFiles: true` to keep source Markdown files in output in addition to generated HTML.
86
+ Example mappings:
87
+ - `pages/index.md` -> `_site/index.html` and `_site/index.md`
88
+ - `pages/docs/intro.md` -> `_site/docs/intro/index.html` and `_site/docs/intro.md`
88
89
 
89
- ## Collections
90
+ If you want to publish a manual `llms.txt`, place it in `static/llms.txt`; it will be copied to `_site/llms.txt`.
90
91
 
91
- Tag pages to create collections:
92
+ ## Commands
92
93
 
93
- ```yaml
94
- # pages/blog/post.md
95
- ---
96
- tags: [blog]
97
- ---
94
+ ```bash
95
+ bunx rtgl sites build
96
+ bunx rtgl sites watch
97
+ bunx rtgl sites build --quiet
98
+ bunx rtgl sites watch --quiet
99
+ bunx rtgl sites watch --reload-mode full
100
+ bunx rtgl sites build --root-dir . --output-path dist
101
+ bunx rtgl sites watch --root-dir . --output-path dist --reload-mode full
98
102
  ```
99
103
 
104
+ `--reload-mode body` (default) does fast body replacement; `--reload-mode full` forces full page refresh.
105
+ `--root-dir`/`--output-path` are the preferred option names (`--rootDir`/`--outputPath` remain as legacy aliases).
106
+
107
+ ## Built-in Template Functions
108
+
109
+ Available in YAML templates/pages without extra setup:
110
+
111
+ - `encodeURI(value)`
112
+ - `encodeURIComponent(value)`
113
+ - `decodeURI(value)`
114
+ - `decodeURIComponent(value)`
115
+ - `jsonStringify(value, space = 0)`
116
+ - `formatDate(value, format = "YYYYMMDDHHmmss", useUtc = true)`
117
+ - `now(format = "YYYYMMDDHHmmss", useUtc = true)`
118
+ - `toQueryString(object)`
119
+
120
+ `formatDate` tokens: `YYYY`, `MM`, `DD`, `HH`, `mm`, `ss`.
121
+ `decodeURI`/`decodeURIComponent` return the original input when decoding fails.
122
+
123
+ ## Screenshots
124
+
125
+ `@rettangoli/sites` builds pages; screenshot capture is handled by `@rettangoli/vt`.
126
+
127
+ Use VT against your generated site:
128
+
129
+ 1. Add `vt/specs/*.html` specs (use frontmatter `url` for the page to capture).
130
+ 2. Add `vt` config in `rettangoli.config.yaml`.
131
+ 3. Run `rtgl vt generate`, `rtgl vt report`, and `rtgl vt accept`.
132
+
133
+ Example:
134
+
100
135
  ```yaml
101
- # pages/blog.yaml
102
- - $for post in collections.blog:
103
- - a href="${post.url}": ${post.data.title}
136
+ vt:
137
+ path: ./vt
138
+ url: http://127.0.0.1:4173
139
+ service:
140
+ start: bun run preview
141
+ sections:
142
+ - title: pages
143
+ files: .
104
144
  ```
105
145
 
106
- ## Configuration
107
-
108
- `sites.config.js`:
109
- ```javascript
110
- export default {
111
- mdRender: customMarkdownRenderer,
112
- functions: {
113
- sortDate: (list) => list.sort((a, b) =>
114
- new Date(b.data.date) - new Date(a.data.date)
115
- )
116
- }
117
- }
146
+ ```html
147
+ ---
148
+ title: home
149
+ url: /
150
+ ---
151
+ <div></div>
118
152
  ```
119
153
 
120
- ## Scripts
121
-
122
- ```bash
123
- bun run build # Build site to _site/
124
- bun run watch # Build + watch for changes
125
- bun run serve # Serve _site/
126
- bun run screenshot # Generate page screenshots
127
- ```
154
+ `bun run preview` (or any equivalent local server command) must serve your built site on `vt.url` (for example serving `_site/` on port `4173`).
128
155
 
129
- ## Templates
156
+ ## Full Architecture And Analysis
130
157
 
131
- Starter templates in `templates/`:
158
+ See `docs/architecture-and-analysis.md` for:
132
159
 
133
- - **default** - Basic site with homepage, blog, and about page
160
+ - End-to-end rendering flow
161
+ - Data/context model used during render
162
+ - URL/output mapping rules
163
+ - Config contract details
164
+ - Full robustness analysis and prioritized improvements
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rettangoli/sites",
3
- "version": "0.2.7",
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",
3
+ "version": "1.0.0-rc2",
4
+ "description": "Generate static sites using Markdown and YAML for docs, blogs, and marketing sites.",
5
5
  "author": {
6
6
  "name": "Luciano Hanyon Wu",
7
7
  "email": "han4wluc@yuusoft.com"
@@ -17,12 +17,11 @@
17
17
  "templates"
18
18
  ],
19
19
  "dependencies": {
20
+ "gray-matter": "^4.0.3",
20
21
  "jempl": "^0.3.1-rc1",
21
22
  "js-yaml": "^4.1.0",
22
23
  "markdown-it": "^14.1.0",
23
- "minimatch": "^10.0.3",
24
- "playwright": "^1.55.0",
25
- "sharp": "^0.34.3",
24
+ "shiki": "^3.13.0",
26
25
  "ws": "^8.18.0",
27
26
  "yahtml": "0.0.4"
28
27
  },
@@ -50,7 +49,7 @@
50
49
  "dashboard"
51
50
  ],
52
51
  "bugs": {
53
- "url": "https://github.com/yuusoft-org/sitic/issues"
52
+ "url": "https://github.com/yuusoft-org/rettangoli/issues"
54
53
  },
55
- "homepage": "https://github.com/yuusoft-org/sitic#readme"
54
+ "homepage": "https://github.com/yuusoft-org/rettangoli/tree/main/packages/rettangoli-sites#readme"
56
55
  }
@@ -0,0 +1,85 @@
1
+ function toDate(value) {
2
+ if (value instanceof Date) {
3
+ return value;
4
+ }
5
+
6
+ if (value === undefined || value === null || value === '') {
7
+ return new Date();
8
+ }
9
+
10
+ return new Date(value);
11
+ }
12
+
13
+ function pad2(value) {
14
+ return String(value).padStart(2, '0');
15
+ }
16
+
17
+ function formatDateImpl(value, format = 'YYYYMMDDHHmmss', useUtc = true) {
18
+ const date = toDate(value);
19
+ if (Number.isNaN(date.getTime())) {
20
+ return '';
21
+ }
22
+
23
+ const read = (localGetter, utcGetter) => (useUtc ? utcGetter.call(date) : localGetter.call(date));
24
+ const tokens = {
25
+ YYYY: String(read(date.getFullYear, date.getUTCFullYear)),
26
+ MM: pad2(read(date.getMonth, date.getUTCMonth) + 1),
27
+ DD: pad2(read(date.getDate, date.getUTCDate)),
28
+ HH: pad2(read(date.getHours, date.getUTCHours)),
29
+ mm: pad2(read(date.getMinutes, date.getUTCMinutes)),
30
+ ss: pad2(read(date.getSeconds, date.getUTCSeconds)),
31
+ };
32
+
33
+ return String(format).replace(/YYYY|MM|DD|HH|mm|ss/g, (token) => tokens[token]);
34
+ }
35
+
36
+ function jsonStringify(value, space = 0) {
37
+ const indent = Number.isFinite(Number(space))
38
+ ? Math.max(0, Math.min(10, Math.trunc(Number(space))))
39
+ : 0;
40
+ const result = JSON.stringify(value, null, indent);
41
+ return result === undefined ? '' : result;
42
+ }
43
+
44
+ function toQueryString(value) {
45
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
46
+ return '';
47
+ }
48
+
49
+ const params = new URLSearchParams();
50
+ for (const [key, raw] of Object.entries(value)) {
51
+ if (raw === undefined || raw === null) {
52
+ continue;
53
+ }
54
+ if (Array.isArray(raw)) {
55
+ for (const item of raw) {
56
+ params.append(key, String(item));
57
+ }
58
+ continue;
59
+ }
60
+ params.set(key, String(raw));
61
+ }
62
+ return params.toString();
63
+ }
64
+
65
+ function safeDecode(value, decoder) {
66
+ const input = String(value ?? '');
67
+ try {
68
+ return decoder(input);
69
+ } catch {
70
+ return input;
71
+ }
72
+ }
73
+
74
+ export const builtinTemplateFunctions = {
75
+ encodeURI: (value) => encodeURI(String(value ?? '')),
76
+ encodeURIComponent: (value) => encodeURIComponent(String(value ?? '')),
77
+ decodeURI: (value) => safeDecode(value, decodeURI),
78
+ decodeURIComponent: (value) => safeDecode(value, decodeURIComponent),
79
+ jsonStringify,
80
+ formatDate: formatDateImpl,
81
+ now: (format = 'YYYYMMDDHHmmss', useUtc = true) => formatDateImpl(new Date(), format, useUtc),
82
+ toQueryString,
83
+ };
84
+
85
+ export default builtinTemplateFunctions;
package/src/cli/build.js CHANGED
@@ -6,24 +6,31 @@ import { loadSiteConfig } from '../utils/loadSiteConfig.js';
6
6
  * Build the static site
7
7
  * @param {Object} options - Options for building the site
8
8
  * @param {string} options.rootDir - Root directory of the site (defaults to cwd)
9
+ * @param {string} options.outputPath - Output directory path (relative to rootDir by default)
9
10
  * @param {Object} options.md - Optional markdown renderer
10
11
  * @param {boolean} options.quiet - Suppress build output logs
11
- * @param {boolean} options.isScreenshotMode - Whether building for screenshot capture
12
+ * @param {boolean} options.isScreenshotMode - Optional build flag exposed to templates via build.isScreenshotMode
12
13
  */
13
14
  export const buildSite = async (options = {}) => {
14
- const { rootDir = process.cwd(), md, functions, quiet = false, isScreenshotMode = false } = options;
15
+ const {
16
+ rootDir = process.cwd(),
17
+ outputPath = '_site',
18
+ md,
19
+ functions,
20
+ quiet = false,
21
+ isScreenshotMode = false
22
+ } = options;
15
23
 
16
- // Load config file if needed
17
- let config = {};
18
- if (!md || !functions) {
19
- config = await loadSiteConfig(rootDir);
20
- }
24
+ const config = await loadSiteConfig(rootDir);
21
25
 
22
26
  const build = createSiteBuilder({
23
27
  fs,
24
28
  rootDir,
25
- md: md || config.mdRender,
26
- functions: functions || config.functions || {},
29
+ outputPath,
30
+ md,
31
+ markdown: config.markdown || {},
32
+ keepMarkdownFiles: config.build?.keepMarkdownFiles === true,
33
+ functions: functions || {},
27
34
  quiet,
28
35
  isScreenshotMode
29
36
  });
package/src/cli/init.js CHANGED
@@ -11,9 +11,9 @@ export function initSite({ projectName, template = 'default' }) {
11
11
 
12
12
  // Check if template exists
13
13
  if (!existsSync(templatePath)) {
14
- const available = readdirSync(templatesDir).filter(f =>
15
- existsSync(resolve(templatesDir, f, 'package.json'))
16
- );
14
+ const available = readdirSync(templatesDir, { withFileTypes: true })
15
+ .filter((entry) => entry.isDirectory())
16
+ .map((entry) => entry.name);
17
17
  console.error(`Template "${template}" not found.`);
18
18
  console.error(`Available templates: ${available.join(', ')}`);
19
19
  process.exit(1);
@@ -33,6 +33,6 @@ export function initSite({ projectName, template = 'default' }) {
33
33
  console.log('');
34
34
  console.log('Next steps:');
35
35
  console.log(` cd ${projectName}`);
36
- console.log(' bun install');
37
- console.log(' bun run build');
36
+ console.log(' bunx rtgl sites build');
37
+ console.log(' bunx rtgl sites watch');
38
38
  }