nibula 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eleventy.js +96 -0
- package/.eleventyignore +4 -0
- package/CHANGELOG.md +0 -0
- package/LICENSE +170 -0
- package/NOTICE +5 -0
- package/README.md +105 -0
- package/_tools/assistant.js +152 -0
- package/_tools/buildJs.js +37 -0
- package/_tools/cleanOutput.js +25 -0
- package/_tools/modules/constants.js +66 -0
- package/_tools/modules/pageComponents.js +77 -0
- package/_tools/modules/updateData.js +90 -0
- package/_tools/modules/updateOutputPath.js +112 -0
- package/_tools/modules/updatePage.js +162 -0
- package/_tools/modules/utils.js +27 -0
- package/_tools/modules/validation.js +30 -0
- package/_tools/res/templates/template.js +5 -0
- package/_tools/res/templates/template.njk +9 -0
- package/_tools/res/templates/template.scss +23 -0
- package/_tools/res/templates/template.ts +5 -0
- package/bin/create.js +407 -0
- package/bin/nibula.js +281 -0
- package/docs/Assistant CLI.md +66 -0
- package/docs/Backend.md +151 -0
- package/docs/Components.md +96 -0
- package/docs/Creating pages.md +65 -0
- package/docs/Deploy.md +101 -0
- package/docs/Head and SEO.md +117 -0
- package/docs/Javascript.md +53 -0
- package/docs/Styling with SCSS.md +136 -0
- package/nginx.conf +47 -0
- package/nibula-1.0.0.tgz +0 -0
- package/package.json +74 -0
- package/src/backend/.htaccess +7 -0
- package/src/backend/_core/composer.json +5 -0
- package/src/backend/_core/composer.lock +492 -0
- package/src/backend/_core/index.php +148 -0
- package/src/backend/_core/init.php +34 -0
- package/src/backend/_core/modules/RateLimiter.php +31 -0
- package/src/backend/_core/modules/Response.php +49 -0
- package/src/backend/api/protected/example-protected.php +17 -0
- package/src/backend/api/public/example-public.php +17 -0
- package/src/backend/database/Database.php +24 -0
- package/src/backend/database/migrations/create_example_db.sql +1 -0
- package/src/backend/example.config.php +28 -0
- package/src/backend/web.config +17 -0
- package/src/frontend/.htaccess +16 -0
- package/src/frontend/404.njk +17 -0
- package/src/frontend/assets/brand/favicon.svg +37 -0
- package/src/frontend/assets/brand/logo.svg +37 -0
- package/src/frontend/components/global/footer.njk +25 -0
- package/src/frontend/components/global/header.njk +7 -0
- package/src/frontend/components/welcome.njk +116 -0
- package/src/frontend/data/site.json +54 -0
- package/src/frontend/index.njk +9 -0
- package/src/frontend/js/modules/exampleModule.js +3 -0
- package/src/frontend/js/pages/404.js +7 -0
- package/src/frontend/js/pages/homepage.js +7 -0
- package/src/frontend/layouts/base.njk +142 -0
- package/src/frontend/layouts/page-components.njk +14 -0
- package/src/frontend/llms.njk +18 -0
- package/src/frontend/robots.njk +8 -0
- package/src/frontend/scss/modules/_animations.scss +25 -0
- package/src/frontend/scss/modules/_footer.scss +28 -0
- package/src/frontend/scss/modules/_global.scss +44 -0
- package/src/frontend/scss/modules/_header.scss +28 -0
- package/src/frontend/scss/modules/_mobile.scss +30 -0
- package/src/frontend/scss/modules/_root.scss +35 -0
- package/src/frontend/scss/modules/_typography.scss +15 -0
- package/src/frontend/scss/modules/frameworks/_bootstrap.scss +110 -0
- package/src/frontend/scss/modules/frameworks/_bulma.scss +109 -0
- package/src/frontend/scss/modules/frameworks/_foundation.scss +139 -0
- package/src/frontend/scss/modules/frameworks/_uikit.scss +110 -0
- package/src/frontend/scss/pages/404.scss +28 -0
- package/src/frontend/scss/pages/homepage.scss +23 -0
- package/src/frontend/sitemap.njk +18 -0
- package/src/frontend/ts/modules/exampleModule.ts +3 -0
- package/src/frontend/ts/pages/404.ts +7 -0
- package/src/frontend/ts/pages/homepage.ts +7 -0
- package/src/frontend/web.config +27 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Assistant CLI
|
|
2
|
+
|
|
3
|
+
> Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
|
|
4
|
+
|
|
5
|
+
An interactive CLI to manage pages without touching files manually.
|
|
6
|
+
|
|
7
|
+
Run it from anywhere inside a project:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
nib cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Menu
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1. Create page
|
|
17
|
+
2. Remove page
|
|
18
|
+
3. Rename page
|
|
19
|
+
4. Configure output path
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Use `CTRL/CMD + C` (or `CTRL + D`) to exit.
|
|
23
|
+
|
|
24
|
+
## Create page
|
|
25
|
+
|
|
26
|
+
Enter a page name in any format — the CLI converts it to kebab-case automatically.
|
|
27
|
+
|
|
28
|
+
For a page named `my-page`, the following files are created:
|
|
29
|
+
|
|
30
|
+
| File | Purpose |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `src/frontend/scss/pages/myPage.scss` | SCSS entry point |
|
|
33
|
+
| `src/frontend/js/pages/myPage.js` | JS entry point |
|
|
34
|
+
| `src/frontend/_routes/my-page.njk` | Nunjucks template |
|
|
35
|
+
|
|
36
|
+
It also adds an `elif` block in `page-components.njk` and a stub entry in `site.json`:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
"myPage": {
|
|
40
|
+
"seo": {
|
|
41
|
+
"title": "My page",
|
|
42
|
+
"description": "Description",
|
|
43
|
+
"keywords": "",
|
|
44
|
+
"noindex": false,
|
|
45
|
+
"canonical": ""
|
|
46
|
+
},
|
|
47
|
+
"cdn": {
|
|
48
|
+
"css": [],
|
|
49
|
+
"js": []
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Remove page
|
|
55
|
+
|
|
56
|
+
Deletes all source files for the page and cleans up the output directory, `page-components.njk`, and `site.json`.
|
|
57
|
+
|
|
58
|
+
## Rename page
|
|
59
|
+
|
|
60
|
+
Renames all three source files, updates the `elif` block in `page-components.njk`, and renames the record in `site.json` while preserving all existing fields.
|
|
61
|
+
|
|
62
|
+
## Configure output path
|
|
63
|
+
|
|
64
|
+
Updates the output directory across `.eleventy.js` and all relevant `package.json` scripts in one shot. The old output folder is deleted automatically.
|
|
65
|
+
|
|
66
|
+
> ⚠️ `homepage` and `404` are protected — they cannot be created, removed, or renamed via the CLI.
|
package/docs/Backend.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Backend
|
|
2
|
+
|
|
3
|
+
> **PHP requirement** — the backend runs only on servers with PHP support:
|
|
4
|
+
> - **Apache** (shared hosting, VPS) ✅
|
|
5
|
+
> - **Nginx** + PHP-FPM (VPS) ✅
|
|
6
|
+
> - **IIS** + FastCGI (Windows Server) ✅
|
|
7
|
+
> - **Static hosting** (Netlify, Vercel, GitHub Pages, Cloudflare Pages) ❌
|
|
8
|
+
|
|
9
|
+
The backend is a PHP REST API located in `src/backend/`, copied to the output directory automatically at build time.
|
|
10
|
+
|
|
11
|
+
## Structure
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
src/backend/
|
|
15
|
+
├── api/
|
|
16
|
+
│ └── protected/ # Endpoints requiring X-Api-Key header
|
|
17
|
+
│ ├── public/ # Endpoints accessible without an API key
|
|
18
|
+
├── _core/ # Framework internals (routing, modules) — do not edit
|
|
19
|
+
│ └── modules/ # Response, RateLimiter, ...
|
|
20
|
+
├── database/
|
|
21
|
+
│ ├── Database.php
|
|
22
|
+
│ └── migrations/
|
|
23
|
+
├── example.config.php # Template — versioned, safe to commit
|
|
24
|
+
└── config.php # Your local config, generated on setup — never commit this
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> A scaffolded project contains **both** files. `config.php` is generated automatically by `nib new` (a copy of the example) and is git-ignored, so your secrets stay local and are never pushed. `example.config.php` is kept and versioned: it's the file that ends up on GitHub, acting as a secret-free reference so anyone cloning the project knows which keys to set. If `config.php` is ever missing (e.g. after a fresh clone), just copy `example.config.php` to `config.php`.
|
|
28
|
+
|
|
29
|
+
> Note: in the boilerplate repo itself, only `example.config.php` exists — `config.php` is created at scaffold time, not shipped.
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
`config.php` works like a `.env` file — it holds secrets and environment settings that stay local and out of version control.
|
|
34
|
+
|
|
35
|
+
Fill in your values:
|
|
36
|
+
|
|
37
|
+
### config.php <small>(`src/backend/`)</small>
|
|
38
|
+
```php
|
|
39
|
+
// Default key for protected endpoints that don't have a specific key in CUSTOM_ENDPOINT_KEYS
|
|
40
|
+
'GENERAL_API_KEY' => 'DEFAULT_KEY',
|
|
41
|
+
|
|
42
|
+
// If you want restrict access to protected endpoints to specific clients, you can define custom keys for each endpoint
|
|
43
|
+
// For subfolder endpoints, use the relative path ('subfolder/endpoint')
|
|
44
|
+
'CUSTOM_ENDPOINT_KEYS' => [
|
|
45
|
+
'subfolder/endpoint' => 'custom-key',
|
|
46
|
+
],
|
|
47
|
+
|
|
48
|
+
'GENERAL_ALLOWED_ORIGINS' => [
|
|
49
|
+
'*',
|
|
50
|
+
// 'https://example.com',
|
|
51
|
+
],
|
|
52
|
+
|
|
53
|
+
'CUSTOM_ENDPOINT_ORIGINS' => [
|
|
54
|
+
'subfolder/endpoint' => ['https://app.example.com'],
|
|
55
|
+
],
|
|
56
|
+
|
|
57
|
+
// Database configuration
|
|
58
|
+
'DB_HOST' => '127.0.0.1',
|
|
59
|
+
'DB_NAME' => 'example_db',
|
|
60
|
+
'DB_USER' => 'root',
|
|
61
|
+
'DB_PASS' => '',
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### API keys
|
|
65
|
+
|
|
66
|
+
`GENERAL_API_KEY` is the fallback key for all protected endpoints. Use `CUSTOM_ENDPOINT_KEYS` to assign a different key to a specific endpoint — for subfolder endpoints, use the relative path as the key.
|
|
67
|
+
|
|
68
|
+
> ⚠️ The API key travels in the `X-Api-Key` header on every request. Use it only for server-to-server calls over HTTPS. Never embed it in frontend code, where it would be publicly visible.
|
|
69
|
+
|
|
70
|
+
### CORS (allowed origins)
|
|
71
|
+
|
|
72
|
+
Cross-origin access mirrors the API-key model:
|
|
73
|
+
|
|
74
|
+
- `GENERAL_ALLOWED_ORIGINS` — the default list of origins allowed to call any endpoint from a browser.
|
|
75
|
+
- `CUSTOM_ENDPOINT_ORIGINS` — overrides the default for a specific endpoint, keyed by the same relative path used in `CUSTOM_ENDPOINT_KEYS`.
|
|
76
|
+
|
|
77
|
+
Origins must be exact (scheme + host, no trailing slash), e.g. `https://example.com`. When a request's `Origin` is in the resolved list, it is reflected back in `Access-Control-Allow-Origin` (with `Vary: Origin`). An empty list sends no CORS header — the most restrictive setting; same-origin requests still work. Use `'*'` as the only element to allow any origin (not recommended for protected endpoints).
|
|
78
|
+
|
|
79
|
+
Resolution order for a given endpoint: `CUSTOM_ENDPOINT_ORIGINS[path]` if present, otherwise `GENERAL_ALLOWED_ORIGINS`.
|
|
80
|
+
|
|
81
|
+
## How routing works
|
|
82
|
+
|
|
83
|
+
The file path inside `api/` maps directly to the URL. Extra URL segments become route parameters available as `$requestParams[]`.
|
|
84
|
+
|
|
85
|
+
Every endpoint file has access to:
|
|
86
|
+
|
|
87
|
+
| Variable | Description |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `$method` | HTTP method (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) |
|
|
90
|
+
| `$requestParams` | Extra URL segments (e.g. `/api/posts/42` → `['42']`) |
|
|
91
|
+
|
|
92
|
+
## Creating a public endpoint
|
|
93
|
+
|
|
94
|
+
Create a `.php` file anywhere inside `api/public/`
|
|
95
|
+
|
|
96
|
+
### api/public/example.php
|
|
97
|
+
```php
|
|
98
|
+
<?php
|
|
99
|
+
declare(strict_types=1);
|
|
100
|
+
|
|
101
|
+
require_once CORE_PATH . '/modules/Response.php';
|
|
102
|
+
|
|
103
|
+
if ($method !== 'GET') {
|
|
104
|
+
Response::error('Method not allowed', 405);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
$id = isset($requestParams[0]) ? (int)$requestParams[0] : null;
|
|
108
|
+
|
|
109
|
+
Response::success(['id' => $id]);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Creating a protected endpoint
|
|
113
|
+
|
|
114
|
+
Create a `.php` file inside `api/protected/`. The API key check happens automatically before your file runs.
|
|
115
|
+
|
|
116
|
+
### api/protected/example.php
|
|
117
|
+
```php
|
|
118
|
+
<?php
|
|
119
|
+
declare(strict_types=1);
|
|
120
|
+
|
|
121
|
+
require_once CORE_PATH . '/modules/Response.php';
|
|
122
|
+
|
|
123
|
+
if ($method !== 'GET') {
|
|
124
|
+
Response::error('Method not allowed', 405);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
Response::success(['visits' => 1024]);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
To assign a dedicated key, add it to `config.php`:
|
|
131
|
+
|
|
132
|
+
```php
|
|
133
|
+
'CUSTOM_ENDPOINT_KEYS' => [
|
|
134
|
+
'endpoint' => 'custom-key',
|
|
135
|
+
],
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## The Response helper
|
|
139
|
+
|
|
140
|
+
```php
|
|
141
|
+
Response::success($data, $code); // default 200
|
|
142
|
+
Response::error($message, $code, $details); // default 400
|
|
143
|
+
Response::noContent(); // 204
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Pre-built endpoints
|
|
147
|
+
|
|
148
|
+
| Route | Method | Description |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `/api/public/example-public` | `GET` | Example endpoint that doesn't require any key |
|
|
151
|
+
| `/api/protected/example-protected` | `GET` | Example endpoint that requires X-API-KEY |
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Nunjucks (HTML) Components
|
|
2
|
+
|
|
3
|
+
## What is Nunjucks
|
|
4
|
+
|
|
5
|
+
Nunjucks (`.njk`) is an HTML file that supports logic like variables, `if` statements, and `for` loops. It can extend a base layout and include other `.njk` components
|
|
6
|
+
|
|
7
|
+
## Create a component
|
|
8
|
+
|
|
9
|
+
Create a new `.njk` file anywhere inside `src/frontend/components/`. You can organize them into subfolders freely
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
src/frontend/components/
|
|
13
|
+
├── global/
|
|
14
|
+
├── layouts/
|
|
15
|
+
├── modals/
|
|
16
|
+
│ └── privacyModal.njk # You can move it to a modals/subfolder
|
|
17
|
+
├── welcome.njk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Include a component
|
|
21
|
+
|
|
22
|
+
To render a component inside a page, navigate to `src/frontend/layouts/` and edit `page-components.njk`
|
|
23
|
+
|
|
24
|
+
### page-components.njk <small>(`src/frontend/layouts/`)</small>
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
{% if title == "homepage" %}
|
|
28
|
+
{% include "welcome.njk" %}
|
|
29
|
+
|
|
30
|
+
{% elif title == "examplePage" %}
|
|
31
|
+
{% include "exampleComponent1.njk" %}
|
|
32
|
+
{% include "subfolder/exampleComponent2.njk" %}
|
|
33
|
+
|
|
34
|
+
{% else %}
|
|
35
|
+
{% include "404/_404.njk" %}
|
|
36
|
+
{{ content | safe }}
|
|
37
|
+
{% endif %}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Add a new `{% elif %}` block for each page, listing its components in order. If a component lives in a subfolder, specify the relative path accordingly
|
|
41
|
+
|
|
42
|
+
> ⚠️ A new `elif` block is automatically added when you create a page via the Assistant CLI
|
|
43
|
+
|
|
44
|
+
> ⚠️ If you move or delete a component, always update `page-components.njk` or the site will break
|
|
45
|
+
|
|
46
|
+
### Using Markdown files in components
|
|
47
|
+
|
|
48
|
+
You can also render a `.md` file directly inside any `.njk` component using the `mdFile` shortcode:
|
|
49
|
+
|
|
50
|
+
```njk
|
|
51
|
+
{% mdFile "docs/your-file.md" %}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The path is relative to the project root (where `.eleventy.js` lives).
|
|
55
|
+
|
|
56
|
+
> ⚠️ The file is read at build time — changes to the `.md` file trigger a rebuild in watch mode.
|
|
57
|
+
|
|
58
|
+
## Nest components
|
|
59
|
+
|
|
60
|
+
A component can include other components. This is useful for breaking complex sections into smaller, reusable pieces.
|
|
61
|
+
|
|
62
|
+
### exampleComponent.njk
|
|
63
|
+
```js
|
|
64
|
+
<section class="hero">
|
|
65
|
+
{% include "ui/heroTitle.njk" %}
|
|
66
|
+
{% include "ui/heroButton.njk" %}
|
|
67
|
+
</section>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> The same path rules apply: if the included component is in a subfolder, specify the full relative path.
|
|
71
|
+
|
|
72
|
+
## Global components
|
|
73
|
+
|
|
74
|
+
Header and footer live in `src/frontend/components/global/` and are automatically included in every page via `base.njk`. Edit them to change the site-wide layout
|
|
75
|
+
|
|
76
|
+
## Site data in components
|
|
77
|
+
|
|
78
|
+
All values defined in `src/frontend/data/site.json` are globally available in every component via `{{ site.* }}`
|
|
79
|
+
|
|
80
|
+
### site.json <small>(`src/data/`)</small>
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"title": "My Site",
|
|
84
|
+
"logo": "/img/logo.png",
|
|
85
|
+
"legal": {
|
|
86
|
+
"privacy": "/privacy"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Usage in any `.njk` file
|
|
92
|
+
```js
|
|
93
|
+
<p>{{ site.title }}</p>
|
|
94
|
+
<a href="{{ site.legal.privacy }}">Privacy Policy</a>
|
|
95
|
+
<img src="{{ site.logo }}" alt="{{ site.title }}">
|
|
96
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Creating Pages
|
|
2
|
+
|
|
3
|
+
> Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
|
|
4
|
+
|
|
5
|
+
The recommended way is via the **Assistant CLI**
|
|
6
|
+
|
|
7
|
+
## What gets created
|
|
8
|
+
For a page named `my-page`:
|
|
9
|
+
|
|
10
|
+
| File | Purpose |
|
|
11
|
+
|---|---|
|
|
12
|
+
| `src/frontend/_routes_/my-page.njk` | Template with front matter |
|
|
13
|
+
| `src/frontend/scss/pages/myPage.scss` | Imports framework + modules |
|
|
14
|
+
| `src/frontend/js/pages/myPage.js` | Imports JS modules |
|
|
15
|
+
|
|
16
|
+
## Adding content
|
|
17
|
+
|
|
18
|
+
1. Create a component in `src/frontend/components/` (e.g. `_myPage.njk`)
|
|
19
|
+
2. Include it in `src/frontend/layouts/page-components.njk` inside the generated `elif` block:
|
|
20
|
+
|
|
21
|
+
```njk
|
|
22
|
+
{% elif title == "myPage" %}
|
|
23
|
+
{% include "_myPage.njk" %}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
See **Components** DOC file for more info
|
|
27
|
+
|
|
28
|
+
## URL and title
|
|
29
|
+
|
|
30
|
+
The URL is the kebab-case name (`/my-page/`). The `title` in the front matter is camelCase (`myPage`) and is used internally to load the correct CSS and JS files — do not change it.
|
|
31
|
+
|
|
32
|
+
## Subpages (nested URLs)
|
|
33
|
+
|
|
34
|
+
To create a URL like `domain.it/about/team`, edit the `permalink` in `src/frontend/_routes/team.njk` and add the parent segment before the final slash:
|
|
35
|
+
|
|
36
|
+
```njk
|
|
37
|
+
---
|
|
38
|
+
title: "team"
|
|
39
|
+
permalink: "about/team/"
|
|
40
|
+
layout: page-components.njk
|
|
41
|
+
---
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The parent path (`about`) does **not** need to exist as a real page — it's just a URL prefix. Only the last segment (`team`) must match the filename and the `title` in the front matter.
|
|
45
|
+
|
|
46
|
+
| Goal URL | permalink value | File |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `/team/` | `/team/` | `_routes/team.njk` |
|
|
49
|
+
| `/about/team/` | `/about/team/` | `_routes/team.njk` |
|
|
50
|
+
| `/company/about/team/` | `/company/about/team/` | `_routes/team.njk` |
|
|
51
|
+
|
|
52
|
+
## SEO
|
|
53
|
+
|
|
54
|
+
The CLI creates a stub entry in `src/frontend/data/site.json`. Fill it in:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
"myPage": {
|
|
58
|
+
"seo": {
|
|
59
|
+
"title": "My Page | Site Name",
|
|
60
|
+
"description": "Page description"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
See **Head & SEO** for all available options.
|
package/docs/Deploy.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Server Configuration
|
|
2
|
+
|
|
3
|
+
Nibula builds a static site into your output folder (`out`). To put it
|
|
4
|
+
online you upload that folder to a web server. Which server config you need
|
|
5
|
+
depends on **where** you publish.
|
|
6
|
+
|
|
7
|
+
## Where to publish
|
|
8
|
+
|
|
9
|
+
### Shared hosting (Apache — e.g. Aruba, most cPanel providers)
|
|
10
|
+
|
|
11
|
+
The most common and beginner-friendly option. You rent space on a server that's
|
|
12
|
+
already set up, and you just upload your files.
|
|
13
|
+
|
|
14
|
+
In short: take the **contents of the `out` folder** and upload them to the
|
|
15
|
+
hosting's public web root (often called `htdocs`, `public_html`, or `www`).
|
|
16
|
+
That's it — the site is live.
|
|
17
|
+
|
|
18
|
+
On this kind of hosting the server usually runs **Apache** (or **IIS** on Windows
|
|
19
|
+
hosts). This is where `.htaccess` (Apache) or `web.config` (IIS) come in: they're
|
|
20
|
+
already inside your `out` folder and get picked up automatically, with no extra
|
|
21
|
+
setup. They handle security and routing for you (see below).
|
|
22
|
+
|
|
23
|
+
> You don't need to understand the details to publish — upload `out`, and the
|
|
24
|
+
> config files do their job. The sections below explain what they protect.
|
|
25
|
+
|
|
26
|
+
### Your own server (Nginx — e.g. an Ubuntu VPS)
|
|
27
|
+
|
|
28
|
+
If you rent a bare server (a VPS) and install everything yourself, it typically
|
|
29
|
+
runs **Nginx** on **Ubuntu Server**. Here nothing is pre-configured: you set up
|
|
30
|
+
Nginx, PHP, and the security rules by hand. This is more powerful but requires
|
|
31
|
+
knowing what you're doing — if this is your path, read up on Nginx server
|
|
32
|
+
administration first. The Nginx section below gives you the essential rules to
|
|
33
|
+
start from.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Apache (`.htaccess`)
|
|
38
|
+
|
|
39
|
+
`.htaccess` files at `src/frontend/` and `src/backend/` are automatically copied
|
|
40
|
+
into the build output by Eleventy. No setup required — upload `out` and Apache
|
|
41
|
+
reads them on its own.
|
|
42
|
+
|
|
43
|
+
They cover:
|
|
44
|
+
- Directory listing disabled
|
|
45
|
+
- 403 / 404 → `/404.html`
|
|
46
|
+
- Sensitive files blocked (`web.config`, dotfiles, etc.)
|
|
47
|
+
- Backend source directory sealed — all access funnels through the front controller
|
|
48
|
+
- `/api/*` → `backend/_core/index.php`
|
|
49
|
+
- HTTPS redirect
|
|
50
|
+
|
|
51
|
+
This is the config that makes shared hosting like Aruba work out of the box.
|
|
52
|
+
|
|
53
|
+
## IIS (`web.config`)
|
|
54
|
+
|
|
55
|
+
For Windows-based hosting. `web.config` files at `src/frontend/` and
|
|
56
|
+
`src/backend/` are automatically copied into the build output by Eleventy, and
|
|
57
|
+
IIS reads them automatically — same idea as `.htaccess`, different server.
|
|
58
|
+
|
|
59
|
+
Covers the same rules as the Apache configuration above.
|
|
60
|
+
|
|
61
|
+
## Nginx
|
|
62
|
+
|
|
63
|
+
Used when you run your **own Ubuntu Server** (a VPS). Unlike Apache and IIS,
|
|
64
|
+
Nginx does **not** read per-directory config files, so there's nothing automatic
|
|
65
|
+
here.
|
|
66
|
+
|
|
67
|
+
**`nginx.conf` in the project root is not a ready-to-use file.** It is a set of
|
|
68
|
+
directives that you must take and adapt into a complete Nginx `server { }`
|
|
69
|
+
block — the one that defines your domain (`server_name`), ports (`listen`), and
|
|
70
|
+
SSL certificates. Paste these rules inside your own server block and adjust
|
|
71
|
+
`root` to point to your `out` folder.
|
|
72
|
+
|
|
73
|
+
These directives are the **minimum Nginx equivalent of `.htaccess` and
|
|
74
|
+
`web.config`**: their job is to not expose dangerous files (server config files,
|
|
75
|
+
the backend source directory) and to route the API correctly. Without them, an
|
|
76
|
+
Nginx server would serve your site but leave those files reachable.
|
|
77
|
+
|
|
78
|
+
They provide:
|
|
79
|
+
- `server_tokens off` — hides the Nginx version
|
|
80
|
+
- `autoindex off` — disables directory listing
|
|
81
|
+
- 403 / 404 → `/404.html`
|
|
82
|
+
- Backend source directory (`/backend/`) sealed with `return 404`
|
|
83
|
+
- Server config files (`.htaccess`, `web.config`) blocked anywhere in the tree
|
|
84
|
+
- `/api/*` routed to the PHP front controller
|
|
85
|
+
- Security headers on static responses
|
|
86
|
+
|
|
87
|
+
> Setting up Nginx correctly (SSL, HTTPS redirect, PHP-FPM) goes beyond these
|
|
88
|
+
> rules. If you plan to publish with Nginx, read up on Nginx + PHP-FPM on Ubuntu
|
|
89
|
+
> Server first — these directives are the security starting point, not a full
|
|
90
|
+
> server setup.
|
|
91
|
+
|
|
92
|
+
### PHP-FPM socket
|
|
93
|
+
|
|
94
|
+
The socket path in the `/api/` rule targets RHEL / Fedora by default. On Ubuntu
|
|
95
|
+
the socket name includes the PHP version — check with `ls /run/php/` and adjust:
|
|
96
|
+
|
|
97
|
+
| Distro | Path |
|
|
98
|
+
|---|---|
|
|
99
|
+
| RHEL / Fedora | `unix:/run/php-fpm/php-fpm.sock` |
|
|
100
|
+
| Debian / Ubuntu | `unix:/run/php/php8.3-fpm.sock` |
|
|
101
|
+
| TCP fallback | `127.0.0.1:9000` |
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Head & SEO
|
|
2
|
+
|
|
3
|
+
Global settings live in `src/frontend/data/site.json` and are available everywhere via `{{ site.* }}`.
|
|
4
|
+
|
|
5
|
+
## Global fields
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"site_name": "Site name",
|
|
10
|
+
"title": "Site title",
|
|
11
|
+
"description": "Site description",
|
|
12
|
+
"keywords": "keyword1, keyword2, keyword3",
|
|
13
|
+
"domain": "yoursite.com",
|
|
14
|
+
"url": "https://yoursite.com",
|
|
15
|
+
"lang": "en",
|
|
16
|
+
"author": "Name and surname",
|
|
17
|
+
"data_bs_theme": "dark",
|
|
18
|
+
"theme_color": {
|
|
19
|
+
"light": "#ffffff",
|
|
20
|
+
"dark": "#0d1117"
|
|
21
|
+
},
|
|
22
|
+
"favicon": "/assets/brand/favicon.svg",
|
|
23
|
+
"logo": "/assets/brand/logo.svg",
|
|
24
|
+
"legal": {
|
|
25
|
+
"privacy": "",
|
|
26
|
+
"cookie": "",
|
|
27
|
+
"cookieControls": "",
|
|
28
|
+
"terms": "",
|
|
29
|
+
"copyright": {
|
|
30
|
+
"year": "2026",
|
|
31
|
+
"text": "Copyright text"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| Field | Purpose |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `site_name` | Brand name (used in meta tags and JSON-LD) |
|
|
40
|
+
| `title` | Default page title — fallback when a page has no `seo.title` |
|
|
41
|
+
| `description` | Default meta description — fallback for pages |
|
|
42
|
+
| `keywords` | Default meta keywords — fallback for pages |
|
|
43
|
+
| `domain` / `url` | Canonical URLs, `og:url`, JSON-LD |
|
|
44
|
+
| `lang` | HTML `lang` attribute |
|
|
45
|
+
| `author` | Meta author and JSON-LD author |
|
|
46
|
+
| `data_bs_theme` | Bootstrap color scheme (`light` / `dark`) |
|
|
47
|
+
| `theme_color` | Browser UI / PWA color, per scheme (`light` / `dark`) |
|
|
48
|
+
| `favicon` | Path to the favicon |
|
|
49
|
+
| `logo` | Path to the logo, also used as social image |
|
|
50
|
+
| `legal.privacy` / `cookie` / `cookieControls` / `terms` | Legal page URLs |
|
|
51
|
+
| `legal.copyright.year` / `text` | Footer copyright |
|
|
52
|
+
|
|
53
|
+
## Per-page SEO and CDN
|
|
54
|
+
|
|
55
|
+
Each page is keyed by its camelCase `title` from the front matter:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
"pages": {
|
|
59
|
+
"examplePage": {
|
|
60
|
+
"seo": {
|
|
61
|
+
"title": "Example Page",
|
|
62
|
+
"description": "Description",
|
|
63
|
+
"keywords": "",
|
|
64
|
+
"noindex": false,
|
|
65
|
+
"canonical": ""
|
|
66
|
+
},
|
|
67
|
+
"cdn": {
|
|
68
|
+
"css": ["https://example.com/lib.min.css"],
|
|
69
|
+
"js": ["https://example.com/lib.min.js"]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
| Field | Purpose | If empty / absent |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `seo.title` | Page title | Falls back to global `title` |
|
|
78
|
+
| `seo.description` | Meta description | Falls back to global `description` |
|
|
79
|
+
| `seo.keywords` | Meta keywords | Falls back to global `keywords` |
|
|
80
|
+
| `seo.noindex` | If `true`, emits `<meta name="robots" content="noindex, follow">` and drops the page from the sitemap | Page is indexable |
|
|
81
|
+
| `seo.canonical` | Absolute canonical URL override | Computed as `url + page.url` |
|
|
82
|
+
| `cdn.css` / `cdn.js` | Extra per-page CDN links | No extra links loaded |
|
|
83
|
+
|
|
84
|
+
## Fallback logic
|
|
85
|
+
|
|
86
|
+
Global values are defaults, not duplicates. A page value is used when present; otherwise the global one applies:
|
|
87
|
+
|
|
88
|
+
```njk
|
|
89
|
+
{{ pageData.seo.title or title or site.title }}
|
|
90
|
+
{{ pageData.seo.description or site.description }}
|
|
91
|
+
{{ pageData.seo.keywords or site.keywords }}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`noindex` and `canonical` have no global default: `noindex` defaults to indexable behavior, `canonical` is computed from the URL. They exist per-page only.
|
|
95
|
+
|
|
96
|
+
## theme_color
|
|
97
|
+
|
|
98
|
+
Two tags are emitted so the browser bar follows the user's system scheme:
|
|
99
|
+
|
|
100
|
+
```njk
|
|
101
|
+
<meta name="theme-color" content="{{ site.theme_color.light }}" media="(prefers-color-scheme: light)">
|
|
102
|
+
<meta name="theme-color" content="{{ site.theme_color.dark }}" media="(prefers-color-scheme: dark)">
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This is independent from `data_bs_theme`, which controls the Bootstrap theme, not the browser UI.
|
|
106
|
+
|
|
107
|
+
## AI & SEO bots
|
|
108
|
+
|
|
109
|
+
`llms.txt`, `robots.txt` and `sitemap.xml` are generated from `.njk` templates and are publicly reachable (they must not be blocked by the server config).
|
|
110
|
+
|
|
111
|
+
| File | Purpose | Reachable at |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `llms.njk` | Tells AI models what the site is about | `yoursite.com/llms.txt` |
|
|
114
|
+
| `robots.njk` | Controls search-engine crawling | `yoursite.com/robots.txt` |
|
|
115
|
+
| `sitemap.njk` | Lists indexable pages (skips drafts, 404, and `noindex`) | `yoursite.com/sitemap.xml` |
|
|
116
|
+
|
|
117
|
+
To customize, edit `src/frontend/llms.njk` or `src/frontend/robots.njk` directly.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# JavaScript
|
|
2
|
+
|
|
3
|
+
> Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
|
|
4
|
+
|
|
5
|
+
## Page JS
|
|
6
|
+
|
|
7
|
+
Each page has its own JS entry point in `src/frontend/js/pages/`, bundled and minified by esbuild and loaded automatically by `base.njk`.
|
|
8
|
+
|
|
9
|
+
Import only what the page needs.
|
|
10
|
+
|
|
11
|
+
### examplePage.js <small>(`src/frontend/js/pages/`)</small>
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// import { initExampleModule } from '../modules/exampleModule.js';
|
|
15
|
+
|
|
16
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
17
|
+
// initExampleModule();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Page logic here
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Modules
|
|
24
|
+
|
|
25
|
+
Modules live in `src/frontend/js/modules/`. Modules that interact with the DOM must be called inside `DOMContentLoaded`; others can be called anywhere.
|
|
26
|
+
|
|
27
|
+
## Adding a module
|
|
28
|
+
|
|
29
|
+
Create a new `.js` file in `src/frontend/js/modules/`. Subfolders are allowed.
|
|
30
|
+
|
|
31
|
+
Use ESM syntax — esbuild handles the bundling:
|
|
32
|
+
|
|
33
|
+
### exampleModule.js <small>(`src/frontend/js/modules/`)</small>
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
export function exampleModule() {
|
|
37
|
+
// Module logic here
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then import it in the pages that need it:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import { exampleModule } from '../modules/exampleModule.js';
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
In TypeScript, omit the extension:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { exampleModule } from '../modules/exampleModule';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> ⚠️ Files inside `_tools/` run directly in Node.js without a bundler — use CommonJS (`require` / `module.exports`) there, not ESM.
|